Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ export interface IClientMediaCall {
held: boolean;
/* busy = state >= 'accepted' && state < 'hangup' */
busy: boolean;
/* if the other side has put the call on hold */
remoteHeld: boolean;

contact: CallContact;
transferredBy: CallContact | null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export interface IWebRTCProcessor extends IServiceProcessor<WebRTCInternalStateM
localAudioLevel: number;

getStats(selector?: MediaStreamTrack | null): Promise<RTCStatsReport | null>;
isRemoteHeld(): boolean;
}

export type WebRTCProcessorConfig = {
Expand Down
23 changes: 23 additions & 0 deletions packages/media-signaling/src/lib/Call.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,12 @@ export class ClientMediaCall implements IClientMediaCall {
return this.webrtcProcessor.held;
}

private _remoteHeld: boolean;

public get remoteHeld(): boolean {
return this._remoteHeld;
}

/** indicates the call is past the "dialing" stage and not yet over */
public get busy(): boolean {
return !this.isPendingAcceptance() && !this.isOver();
Expand Down Expand Up @@ -208,6 +214,7 @@ export class ClientMediaCall implements IClientMediaCall {
this._contact = null;
this._transferredBy = null;
this._service = null;
this._remoteHeld = false;

this.negotiationManager = new NegotiationManager(this, { logger: config.logger });
}
Expand Down Expand Up @@ -987,6 +994,20 @@ export class ClientMediaCall implements IClientMediaCall {
this.stateTimeoutHandlers.clear();
}

private updateRemoteHeld(): void {
if (!this.webrtcProcessor) {
return;
}

const isRemoteHeld = this.webrtcProcessor.isRemoteHeld();
if (isRemoteHeld === this._remoteHeld) {
return;
}

this._remoteHeld = isRemoteHeld;
this.emitter.emit('trackStateChange');
}

private onWebRTCInternalStateChange(stateName: keyof WebRTCInternalStateMap): void {
this.config.logger?.debug('ClientMediaCall.onWebRTCInternalStateChange');
if (!this.webrtcProcessor) {
Expand All @@ -1006,6 +1027,8 @@ export class ClientMediaCall implements IClientMediaCall {

this.requestStateReport();
}

this.updateRemoteHeld();
}

private onNegotiationNeeded(oldNegotiationId: string): void {
Expand Down
27 changes: 27 additions & 0 deletions packages/media-signaling/src/lib/services/webrtc/Processor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,33 @@ export class MediaCallWebRTCProcessor implements IWebRTCProcessor {
return this.peer.getStats(selector);
}

public isRemoteHeld(): boolean {
if (this.stopped) {
return false;
}

if (['closed', 'failed', 'new'].includes(this.peer.connectionState)) {
return false;
}

let anyTransceiverNotSending = false;
const transceivers = this.getAudioTransceivers();

for (const transceiver of transceivers) {
if (!transceiver.currentDirection || transceiver.currentDirection === 'stopped') {
continue;
}

if (transceiver.currentDirection.includes('send')) {
return false;
}

anyTransceiverNotSending = true;
}

return anyTransceiverNotSending;
}

public isStable(): boolean {
if (this.stopped) {
return false;
Expand Down
Loading