Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
"scripts": {
"build": "turbo run build",
"build:services": "turbo run build --filter=rocketchat-services...",
"dev": "turbo run dev --env-mode=loose --parallel --filter=@rocket.chat/meteor...",
"dev": "yarn workspace @rocket.chat/core-typings run build && yarn workspace @rocket.chat/livechat run build && turbo run dev --env-mode=loose --parallel --filter=@rocket.chat/meteor",
"dsv": "turbo run dsv --env-mode=loose --filter=@rocket.chat/meteor...",
"ms": "turbo run ms --env-mode=loose",
"fossify": "TS_NODE_COMPILER_OPTIONS='{\"module\": \"commonjs\"}' ts-node scripts/fossify.ts",
Expand Down
2 changes: 1 addition & 1 deletion packages/i18n/src/locales/en.i18n.json
Original file line number Diff line number Diff line change
Expand Up @@ -7250,4 +7250,4 @@
"Avatar_preview_updated": "Avatar preview updated",
"Select_message_from_user": "Select message from {{username}}",
"Select_message_from_user_with_preview": "Select message from {{username}}: {{message}}"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ export interface IClientMediaCall {
accept(): void;
reject(): void;
hangup(): void;
setMuted(muted: boolean): void;
setHeld(onHold: boolean): void;
requestScreenShare(requested: boolean): void;
setScreenVideoTrack(videoTrack: MediaStreamTrack | null): Promise<void>;
hasScreenVideoTrack(): boolean;
Expand Down
22 changes: 3 additions & 19 deletions packages/media-signaling/src/lib/Session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -479,7 +479,7 @@ export class MediaSignalingSession extends Emitter<MediaSignalingEvents> {

this.currentDeviceId = this.deviceId;

let userMedia: MediaStream | null = null;
let userMedia: MediaStream | null;
this.callsToGetUserMedia++;
try {
userMedia = await this.config.mediaStreamFactory({ audio: this.getAudioConstraints() }).catch(() => null);
Expand All @@ -495,12 +495,12 @@ export class MediaSignalingSession extends Emitter<MediaSignalingEvents> {
}

if (!userMedia) {
return this.hangupCallsThatNeedInput();
return;
}

const tracks = userMedia.getAudioTracks();
if (!tracks.length) {
return this.hangupCallsThatNeedInput();
return;
}

const inputTrack = tracks[0];
Expand All @@ -519,22 +519,6 @@ export class MediaSignalingSession extends Emitter<MediaSignalingEvents> {
return this.setInputTrack(inputTrack);
}

private hangupCallsThatNeedInput(): void {
this.config.logger?.debug('MediaSignalingSession.hangupCallsThatNeedInput');

for (const call of this.knownCalls.values()) {
if (!call.needsInputTrack()) {
continue;
}

try {
call.hangup('input-error');
} catch {
//
}
}
}

private mayNeedInputTrack(): boolean {
for (const call of this.knownCalls.values()) {
if (call.mayNeedInputTrack()) {
Expand Down
30 changes: 19 additions & 11 deletions packages/media-signaling/src/lib/services/webrtc/Processor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,9 +157,6 @@ export class MediaCallWebRTCProcessor implements IWebRTCProcessor {
if (this.stopped) {
throw new Error('WebRTC Processor has already been stopped.');
}
if (!this.inputTrack) {
throw new Error('no-input-track');
}

await this.initialization;

Expand Down Expand Up @@ -329,8 +326,11 @@ export class MediaCallWebRTCProcessor implements IWebRTCProcessor {
private updateAudioDirectionBeforeNegotiation(): void {
// Before the negotiation, we set the direction based on our own state only
// We'll tell the SDK that we want to send audio and, depending on the "on hold" state, also receive it
const desiredDirection = this.held ? 'sendonly' : 'sendrecv';
const desiredDirection = this.getDesiredAudioDirection();

if (!this.getTransceivers('audio').length) {
this.peer.addTransceiver('audio', { direction: desiredDirection });
}
this.updateDirectionBeforeNegotiation('audio', desiredDirection);
}

Expand All @@ -341,8 +341,8 @@ export class MediaCallWebRTCProcessor implements IWebRTCProcessor {

// If we didn't do this, everything would still work, but the browser would trigger redundant renegotiations whenever the directions mismatch

const desiredDirection = this.held ? 'sendonly' : 'sendrecv';
const acceptableDirection = this.held ? 'inactive' : 'recvonly';
const desiredDirection = this.getDesiredAudioDirection();
const acceptableDirection = this.held || !this.inputTrack ? 'inactive' : 'recvonly';

this.updateDirectionAfterNegotiation('audio', desiredDirection, acceptableDirection);
}
Expand Down Expand Up @@ -438,8 +438,8 @@ export class MediaCallWebRTCProcessor implements IWebRTCProcessor {
return;
}

const desiredDirection = this.held ? 'sendonly' : 'sendrecv';
const acceptableDirection = this.held ? 'inactive' : 'recvonly';
const desiredDirection = this.getDesiredAudioDirection();
const acceptableDirection = this.held || !this.inputTrack ? 'inactive' : 'recvonly';

const transceivers = this.getTransceivers('audio');
for (const transceiver of transceivers) {
Expand All @@ -454,6 +454,14 @@ export class MediaCallWebRTCProcessor implements IWebRTCProcessor {
}
}

private getDesiredAudioDirection(): RTCRtpTransceiverDirection {
if (!this.inputTrack) {
return this.held ? 'inactive' : 'recvonly';
}

return this.held ? 'sendonly' : 'sendrecv';
}

private createDataChannel(): void {
if (this._dataChannel || this._dataChannelEnded || !this.config.call.hasFlag('create-data-channel')) {
return;
Expand All @@ -477,7 +485,7 @@ export class MediaCallWebRTCProcessor implements IWebRTCProcessor {

channel.onopen = (_event) => {
this.config.logger?.debug('Data Channel Open', channel.label);
if (!this._dataChannel || this._dataChannel.readyState !== 'open') {
if (this._dataChannel?.readyState !== 'open') {
this._dataChannel = channel;
}

Expand Down Expand Up @@ -533,8 +541,8 @@ export class MediaCallWebRTCProcessor implements IWebRTCProcessor {

private getCommandFromDataChannelMessage(message: string): P2PCommand | null {
try {
const obj = JSON.parse(message);
if (obj.command && this.isValidCommand(obj.command)) {
const obj = JSON.parse(message) as { command?: unknown };
if (typeof obj.command === 'string' && this.isValidCommand(obj.command)) {
return obj.command;
}
} catch {
Expand Down
4 changes: 4 additions & 0 deletions packages/ui-voip/src/context/MediaCallViewContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ type MediaCallViewContextValue = {
onForward: () => void;
onTone: (tone: string) => void;
onEndCall: () => void;
onToggleScreenShare: () => Promise<void>;
onCall: () => Promise<void>;
onAccept: () => Promise<void>;
onSelectPeer: (peerInfo: PeerInfo) => void;
Expand All @@ -40,6 +41,8 @@ const defaultSessionState: SessionState = {
held: false,
remoteMuted: false,
remoteHeld: false,
screenSharing: false,
remoteScreenSharing: false,
callId: undefined,
supportedFeatures: ['audio', 'transfer', 'hold'],
};
Expand All @@ -52,6 +55,7 @@ export const defaultMediaCallContextValue: MediaCallViewContextValue = {
onForward: () => undefined,
onTone: () => undefined,
onEndCall: () => undefined,
onToggleScreenShare: () => Promise.resolve(undefined),
onCall: () => Promise.resolve(undefined),
onAccept: () => Promise.resolve(undefined),
onSelectPeer: () => undefined,
Expand Down
2 changes: 2 additions & 0 deletions packages/ui-voip/src/context/definitions.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ interface IBaseSession {
held: boolean;
remoteMuted: boolean;
remoteHeld: boolean;
screenSharing: boolean;
remoteScreenSharing: boolean;
startedAt?: Date;
hidden: boolean;
supportedFeatures: readonly CallFeature[];
Expand Down
9 changes: 7 additions & 2 deletions packages/ui-voip/src/providers/MediaCallViewProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,6 @@ const MediaCallViewProvider = ({ children }: MediaCallViewProviderProps) => {
stopTracks(stream);
} catch (error) {
console.error('Media Call - Error requesting device', error);
return;
}

if ('userId' in peerInfo) {
Expand All @@ -125,10 +124,11 @@ const MediaCallViewProvider = ({ children }: MediaCallViewProviderProps) => {
const stream = await requestDevice({ actionType: 'incoming' });
stopTracks(stream);
} catch (error) {
console.error('Media Call - Error requesting device', error);
if (error instanceof PermissionRequestCancelledCallRejectedError) {
controls.endCall();
return;
}
return;
}

controls.acceptCall();
Expand Down Expand Up @@ -201,6 +201,10 @@ const MediaCallViewProvider = ({ children }: MediaCallViewProviderProps) => {
controls.endCall();
};

const onToggleScreenShare = useCallback(async () => {
controls.toggleScreenSharing();
}, [controls]);

const onSelectPeer = (peerInfo: PeerInfo) => {
selectPeer(peerInfo);
};
Expand Down Expand Up @@ -238,6 +242,7 @@ const MediaCallViewProvider = ({ children }: MediaCallViewProviderProps) => {
onForward,
onTone,
onEndCall,
onToggleScreenShare,
onCall,
onAccept,
onSelectPeer,
Expand Down
11 changes: 11 additions & 0 deletions packages/ui-voip/src/providers/useMediaSession.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ const defaultSessionInfo: SessionState = {
held: false,
remoteMuted: false,
remoteHeld: false,
screenSharing: false,
remoteScreenSharing: false,
startedAt: undefined,
hidden: false,
supportedFeatures: ['audio', 'transfer', 'hold'],
Expand Down Expand Up @@ -155,6 +157,8 @@ export const useMediaSession = (instance?: MediaSignalingSession): MediaSessionS
hidden: false,
remoteHeld: false,
remoteMuted: false,
screenSharing: false,
remoteScreenSharing: false,
callId: instanceState.tempCallId,
startedAt: undefined,
supportedFeatures: [],
Expand All @@ -169,8 +173,11 @@ export const useMediaSession = (instance?: MediaSignalingSession): MediaSessionS
activeTimestamp: startedAt,
features: supportedFeatures,
transferredBy: callTransferredBy,
localParticipant,
remoteParticipant: { muted: remoteMuted, held: remoteHeld, contact },
} = instanceState;
const localMediaStream = localParticipant.getMediaStream('screen-share');
const remoteMediaStream = instanceState.remoteParticipant.getMediaStream('screen-share');

const transferredBy = callTransferredBy?.displayName || callTransferredBy?.username || undefined;

Expand All @@ -187,6 +194,8 @@ export const useMediaSession = (instance?: MediaSignalingSession): MediaSessionS
hidden,
remoteHeld,
remoteMuted,
screenSharing: Boolean(localMediaStream?.hasVideo()),
remoteScreenSharing: Boolean(remoteMediaStream?.hasVideo()),
callId,
startedAt,
supportedFeatures,
Expand Down Expand Up @@ -221,6 +230,8 @@ export const useMediaSession = (instance?: MediaSignalingSession): MediaSessionS
hidden,
remoteHeld,
remoteMuted,
screenSharing: Boolean(localMediaStream?.hasVideo()),
remoteScreenSharing: Boolean(remoteMediaStream?.hasVideo()),
callId,
startedAt,
supportedFeatures,
Expand Down
Loading