Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
74 changes: 10 additions & 64 deletions ee/packages/media-calls/src/internal/SignalProcessor.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
import type { IMediaCall, IUser } from '@rocket.chat/core-typings';
import { Emitter } from '@rocket.chat/emitter';
import {
isPendingState,
type ClientMediaSignal,
type ClientMediaSignalRegister,
type ClientMediaSignalRequestCall,
type ServerMediaSignal,
type ServerMediaSignalRejectedCallRequest,
import { isPendingState } from '@rocket.chat/media-signaling';
import type {
ClientMediaSignal,
ClientMediaSignalRegister,
ClientMediaSignalRequestCall,
ServerMediaSignal,
ServerMediaSignalRejectedCallRequest,
} from '@rocket.chat/media-signaling';
import { MediaCalls } from '@rocket.chat/models';

import type { InternalCallParams } from '../definition/common';
import { logger } from '../logger';
import { mediaCallDirector } from '../server/CallDirector';
import { UserActorAgent } from './agents/UserActorAgent';
import { getNewCallTransferredBy } from '../server/getNewCallTransferredBy';
import { buildNewCallSignal } from '../server/buildNewCallSignal';
import { stripSensitiveDataFromSignal } from '../server/stripSensitiveData';

export type SignalProcessorEvents = {
Expand Down Expand Up @@ -147,44 +147,7 @@ export class GlobalSignalProcessor {
await mediaCallDirector.renewCallId(call._id);
}

const transferredBy = getNewCallTransferredBy(call);

if (isCaller) {
this.sendSignal(uid, {
callId: call._id,
type: 'new',
service: call.service,
kind: call.kind,
role: 'caller',
self: {
...call.caller,
},
contact: {
...call.callee,
},
...(call.callerRequestedId && { requestedCallId: call.callerRequestedId }),
...(call.parentCallId && { replacingCallId: call.parentCallId }),
...(transferredBy && { transferredBy }),
});
}

if (isCallee) {
this.sendSignal(uid, {
callId: call._id,
type: 'new',
service: call.service,
kind: call.kind,
role: 'callee',
self: {
...call.callee,
},
contact: {
...call.caller,
},
...(call.parentCallId && { replacingCallId: call.parentCallId }),
...(transferredBy && { transferredBy }),
});
}
this.sendSignal(uid, buildNewCallSignal(call, role));

if (call.state === 'active') {
this.sendSignal(uid, {
Expand Down Expand Up @@ -278,24 +241,7 @@ export class GlobalSignalProcessor {
this.rejectCallRequest(uid, { ...rejection, reason: 'already-requested' });
}

const transferredBy = getNewCallTransferredBy(call);

this.sendSignal(uid, {
callId: call._id,
type: 'new',
service: call.service,
kind: call.kind,
role: 'caller',
self: {
...call.caller,
},
contact: {
...call.callee,
},
requestedCallId: signal.callId,
...(call.parentCallId && { replacingCallId: call.parentCallId }),
...(transferredBy && { transferredBy }),
});
this.sendSignal(uid, buildNewCallSignal(call, 'caller'));

return call;
}
Expand Down
23 changes: 3 additions & 20 deletions ee/packages/media-calls/src/internal/agents/UserActorAgent.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import type { IMediaCall, MediaCallSignedContact } from '@rocket.chat/core-typings';
import { isBusyState, type ClientMediaSignal, type ServerMediaSignal, type ServerMediaSignalNewCall } from '@rocket.chat/media-signaling';
import { isBusyState, type ClientMediaSignal, type ServerMediaSignal } from '@rocket.chat/media-signaling';
import { MediaCallNegotiations, MediaCalls } from '@rocket.chat/models';

import { UserActorSignalProcessor } from './CallSignalProcessor';
import { BaseMediaCallAgent } from '../../base/BaseAgent';
import { logger } from '../../logger';
import { getNewCallTransferredBy } from '../../server/getNewCallTransferredBy';
import { buildNewCallSignal } from '../../server/buildNewCallSignal';
import { getMediaCallServer } from '../../server/injection';

export class UserActorAgent extends BaseMediaCallAgent {
Expand Down Expand Up @@ -77,7 +77,7 @@ export class UserActorAgent extends BaseMediaCallAgent {
await this.getOrCreateChannel(call, call.caller.contractId);
}

await this.sendSignal(this.buildNewCallSignal(call));
await this.sendSignal(buildNewCallSignal(call, this.role));
}

public async onRemoteDescriptionChanged(callId: string, negotiationId: string): Promise<void> {
Expand Down Expand Up @@ -166,21 +166,4 @@ export class UserActorAgent extends BaseMediaCallAgent {
logger.debug({ msg: 'UserActorAgent.onDTMF', callId, dtmf, duration });
// internal calls have nothing to do with DTMFs
}

protected buildNewCallSignal(call: IMediaCall): ServerMediaSignalNewCall {
const transferredBy = getNewCallTransferredBy(call);

return {
callId: call._id,
type: 'new',
service: call.service,
kind: call.kind,
role: this.role,
self: this.getMyCallActor(call),
contact: this.getOtherCallActor(call),
...(call.parentCallId && { replacingCallId: call.parentCallId }),
...(transferredBy && { transferredBy }),
...(call.callerRequestedId && this.role === 'caller' && { requestedCallId: call.callerRequestedId }),
};
}
}
42 changes: 42 additions & 0 deletions ee/packages/media-calls/src/server/buildNewCallSignal.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import type { IMediaCall } from '@rocket.chat/core-typings';
import type { CallFlag, CallRole, ServerMediaSignalNewCall } from '@rocket.chat/media-signaling';

import { getNewCallTransferredBy } from './getNewCallTransferredBy';

function getCallFlags(call: IMediaCall, role: CallRole): CallFlag[] {
const flags: CallFlag[] = [];

const isInternal = call.caller.type === 'user' && call.callee.type === 'user';
const shouldCreateDataChannel = isInternal && role === 'caller';

if (isInternal) {
flags.push('internal');

if (shouldCreateDataChannel) {
flags.push('create-data-channel');
}
}

return flags;
}

export function buildNewCallSignal(call: IMediaCall, role: CallRole): ServerMediaSignalNewCall {
const self = role === 'caller' ? call.caller : call.callee;
const contact = role === 'caller' ? call.callee : call.caller;
const transferredBy = getNewCallTransferredBy(call);
const flags = getCallFlags(call, role);

return {
callId: call._id,
type: 'new',
service: call.service,
kind: call.kind,
role,
self: { ...self },
contact: { ...contact },
flags,
...(call.parentCallId && { replacingCallId: call.parentCallId }),
...(transferredBy && { transferredBy }),
...(call.callerRequestedId && role === 'caller' && { requestedCallId: call.callerRequestedId }),
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,13 @@ export type CallRejectedReason =
| 'invalid-call-params' // something is wrong with the params (eg. no valid route between caller and callee)
| 'forbidden'; // one of the actors on the call doesn't have permission for it

export type CallFlag = 'internal' | 'create-data-channel';

export interface IClientMediaCall {
callId: string;
role: CallRole;
service: CallService | null;
flags: readonly CallFlag[];

state: CallState;
ignored: boolean;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { CallContact, CallRole, CallService } from '../../call';
import type { CallContact, CallRole, CallService, CallFlag } from '../../call';

/** Sent by the server to notify an agent that there's a new call for their actor */
export type ServerMediaSignalNewCall = {
Expand All @@ -17,4 +17,7 @@ export type ServerMediaSignalNewCall = {
replacingCallId?: string;
/** If this new call initiated from a transfer, this will hold the information of the user who requested the transfer */
transferredBy?: CallContact;

// A list of flags that may be sent to the client to toggle custom behaviors
flags?: CallFlag[];
};
9 changes: 9 additions & 0 deletions packages/media-signaling/src/lib/Call.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import type {
CallService,
CallHangupReason,
CallActorType,
CallFlag,
} from '../definition/call';
import type { ClientContractState, ClientState } from '../definition/client';
import type { IMediaSignalLogger } from '../definition/logger';
Expand Down Expand Up @@ -181,6 +182,12 @@ export class ClientMediaCall implements IClientMediaCall {
return this.webrtcProcessor?.localAudioLevel || 0;
}

private _flags: CallFlag[];

public get flags(): CallFlag[] {
return this._flags;
}

constructor(
private readonly config: IClientMediaCallConfig,
callId: string,
Expand Down Expand Up @@ -215,6 +222,7 @@ export class ClientMediaCall implements IClientMediaCall {
this._transferredBy = null;
this._service = null;
this._remoteHeld = false;
this._flags = [];

this.negotiationManager = new NegotiationManager(this, { logger: config.logger });
}
Expand Down Expand Up @@ -278,6 +286,7 @@ export class ClientMediaCall implements IClientMediaCall {
this.hasRemoteData = true;
this._service = signal.service;
this._role = signal.role;
this._flags = signal.flags || [];

this._transferredBy = signal.transferredBy || null;
this.changeContact(signal.contact);
Expand Down
44 changes: 44 additions & 0 deletions packages/media-signaling/src/lib/services/webrtc/Processor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import type { IWebRTCProcessor, WebRTCInternalStateMap, WebRTCProcessorConfig, W
import type { ServiceStateValue } from '../../../definition/services/IServiceProcessor';
import { getExternalWaiter, type PromiseWaiterData } from '../../utils/getExternalWaiter';

const DATA_CHANNEL_LABEL = 'rocket.chat';

export class MediaCallWebRTCProcessor implements IWebRTCProcessor {
public readonly emitter: Emitter<WebRTCProcessorEvents>;

Expand Down Expand Up @@ -59,6 +61,8 @@ export class MediaCallWebRTCProcessor implements IWebRTCProcessor {
return this._localAudioLevel;
}

private _dataChannel: RTCDataChannel | null;

constructor(private readonly config: WebRTCProcessorConfig) {
this.localMediaStream = new MediaStream();
this.remoteMediaStream = new MediaStream();
Expand All @@ -67,6 +71,7 @@ export class MediaCallWebRTCProcessor implements IWebRTCProcessor {
this._audioLevel = 0;
this._localAudioLevel = 0;
this._audioLevelTracker = null;
this._dataChannel = null;

this.peer = new RTCPeerConnection(config.rtc);

Expand Down Expand Up @@ -119,6 +124,7 @@ export class MediaCallWebRTCProcessor implements IWebRTCProcessor {
}
}

this.createDataChannel();
this.updateAudioDirectionBeforeNegotiation();

if (iceRestart) {
Expand Down Expand Up @@ -387,6 +393,38 @@ export class MediaCallWebRTCProcessor implements IWebRTCProcessor {
.filter((transceiver) => transceiver.sender.track?.kind === 'audio' || transceiver.receiver.track?.kind === 'audio');
}

private createDataChannel(): void {
if (this._dataChannel || !this.config.call.flags.includes('create-data-channel')) {
return;
}

this.config.logger?.debug('MediaCallWebRTCProcessor.createDataChannel');
const channel = this.peer.createDataChannel(DATA_CHANNEL_LABEL);
this.initializeDataChannel(channel);
}
Comment thread
pierre-lehnen-rc marked this conversation as resolved.

private initializeDataChannel(channel: RTCDataChannel): void {
if (channel.label !== DATA_CHANNEL_LABEL) {
this.config.logger?.warn('Unexpected Data Channel', channel.label);
return;
}

if (this._dataChannel) {
this.config.logger?.warn('Duplicated Data Channel', channel.label);
return;
}

channel.onopen = (_event) => {
this.config.logger?.debug('Data Channel Open', channel.label);
};

channel.onmessage = (event) => {
this.config.logger?.debug('Data Channel Message', event.data);
};

this._dataChannel = channel;
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

private registerPeerEvents() {
const { peer } = this;

Expand All @@ -398,6 +436,7 @@ export class MediaCallWebRTCProcessor implements IWebRTCProcessor {
peer.onnegotiationneeded = () => this.onNegotiationNeeded();
peer.onicegatheringstatechange = () => this.onIceGatheringStateChange();
peer.onsignalingstatechange = () => this.onSignalingStateChange();
peer.ondatachannel = (event) => this.onDataChannel(event);
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}

private unregisterPeerEvents() {
Expand Down Expand Up @@ -559,6 +598,11 @@ export class MediaCallWebRTCProcessor implements IWebRTCProcessor {
this.clearIceGatheringWaiters();
}

private onDataChannel(event: RTCDataChannelEvent) {
this.config.logger?.debug('MediaCallWebRTCProcessor.onDataChannel');
this.initializeDataChannel(event.channel);
}

private clearIceGatheringData(iceGatheringData: PromiseWaiterData, error?: Error) {
this.config.logger?.debug('MediaCallWebRTCProcessor.clearIceGatheringData');
if (this.iceGatheringWaiters.has(iceGatheringData)) {
Expand Down
Loading