Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(calling): adding metrics for bnr #3161

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from 4 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
26 changes: 16 additions & 10 deletions docs/samples/calling/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ const directoryNumberCFA = document.querySelector('#directoryNumber');
const cfaDataElem = document.querySelector('#callforwardalways-data');
const makeCallBtn = document.querySelector('#create-call-action');
const muteElm = document.getElementById('mute_button');
const bnrButton = document.getElementById('bnr_button');

let base64;
let audio64;
Expand Down Expand Up @@ -615,22 +616,27 @@ async function getMediaStreams() {
makeCallBtn.disabled = false;
}

async function addNoiseReductionEffect() {
async function toggleNoiseReductionEffect() {
effect = await localAudioStream.getEffect('background-noise-removal');

if (!effect) {
effect = await Calling.createNoiseReductionEffect(tokenElm.value);

await localAudioStream.addEffect('background-noise-removal', effect);
}

await effect.enable();
}

async function removeNoiseReductionEffect() {
effect = await localAudioStream.getEffect('background-noise-removal');
if (effect) {
await effect.disable();
await effect.enable();
console.log('pkesari_Created new effect and enabling it: ', effect.isEnabled);
bnrButton.innerHTML = 'Disable BNR()';
} else {
console.log('pkesari_Effect is present and is enabled: ', effect.isEnabled);
if (effect.isEnabled) {
await effect.disable();
console.log('pkesari_Disabling effect: ', effect.isEnabled);
bnrButton.innerHTML = 'Enable BNR()';
} else {
await effect.enable();
console.log('pkesari_Enabling effect: ', effect.isEnabled);
bnrButton.innerHTML = 'Disable BNR()';
}
}
}

Expand Down
4 changes: 1 addition & 3 deletions docs/samples/calling/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -120,10 +120,8 @@ <h2 class="collapsible">Call initialization</h2>
class="btn-code">getMediaStreams()</button>
</div>
<div class="u-mv">
<button id="sd-add-bnr" type="button" onclick="addNoiseReductionEffect()"
<button id="bnr_button" type="button" onclick="toggleNoiseReductionEffect()"
class="btn-code">Enable BNR()</button>
<button id="sd-add-bnr" type="button" onclick="removeNoiseReductionEffect()"
class="btn-code">Disable BNR()</button>
</div>
</fieldset>

Expand Down
2 changes: 1 addition & 1 deletion packages/@webex/media-helpers/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
"dependencies": {
"@webex/internal-media-core": "^2.0.0",
"@webex/ts-events": "^1.1.0",
"@webex/web-media-effects": "^2.13.1"
"@webex/web-media-effects": "^2.13.3"
},
"browserify": {
"transform": [
Expand Down
179 changes: 159 additions & 20 deletions packages/calling/src/CallingClient/calling/call.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@
/* eslint-disable dot-notation */
/* eslint-disable @typescript-eslint/no-shadow */
import * as MediaSDK from '@webex/internal-media-core';
import EventEmitter from 'events';
import {ERROR_TYPE, ERROR_LAYER} from '../../Errors/types';
import * as Utils from '../../common/Utils';
import {CALL_EVENT_KEYS, CallEvent, RoapEvent, RoapMessage} from '../../Events/types';
import {DEFAULT_SESSION_TIMER} from '../constants';
import {CallDirection, CallType, ServiceIndicator, WebexRequestPayload} from '../../common/types';
import {METRIC_EVENT, TRANSFER_ACTION, METRIC_TYPE} from '../../Metrics/types';
import {METRIC_EVENT, TRANSFER_ACTION, METRIC_TYPE, MEDIA_EFFECT_ACTION} from '../../Metrics/types';
import {Call, createCall} from './call';
import {
MobiusCallState,
Expand Down Expand Up @@ -36,25 +37,6 @@ const defaultServiceIndicator = ServiceIndicator.CALLING;
const activeUrl = 'FakeActiveUrl';
const mockLineId = 'e4e8ee2a-a154-4e52-8f11-ef4cde2dce72';

// class MockMediaStream {
// private track;

// constructor(track: any) {
// this.track = track;
// }
// }

// globalThis.MediaStream = MockMediaStream;

// // eslint-disable-next-line @typescript-eslint/no-unused-vars
// jest.spyOn(window, 'MediaStream').mockImplementation((tracks: MediaStreamTrack[]) => {
// return {} as MediaStream;
// });

// // Object.defineProperty(window, 'MediaStream', {
// // writable: true,
// // });

describe('Call Tests', () => {
const deviceId = '55dfb53f-bed2-36da-8e85-cee7f02aa68e';
const dest = {
Expand Down Expand Up @@ -111,6 +93,30 @@ describe('Call Tests', () => {
enabled: false,
} as MediaStreamTrack;

const mockEffect = {
isEnabled: true,
};

class MockMediaStream extends EventEmitter {
Kesari3008 marked this conversation as resolved.
Show resolved Hide resolved
public effect;
public outputStream;

constructor() {
super();

this.outputStream = {
getAudioTracks: () => {
return [mockTrack];
},
};
this.effect = {};
}

getEffect = () => {
return this.effect;
};
}

const roapMediaConnectionConfig = {
skipInactiveTransceivers: true,
iceServers: [],
Expand Down Expand Up @@ -286,6 +292,9 @@ describe('Call Tests', () => {
getAudioTracks: jest.fn().mockReturnValue([mockTrack]),
},
on: jest.fn(),
getEffect: jest.fn().mockImplementation(() => {
return mockEffect;
}),
};

const localAudioStream = mockStream as unknown as MediaSDK.LocalMicrophoneStream;
Expand All @@ -302,6 +311,8 @@ describe('Call Tests', () => {
defaultServiceIndicator
);

const mediaMetricSpy = jest.spyOn(call['metricManager'], 'submitMediaMetric');

call.dial(localAudioStream);

expect(mockTrack.enabled).toEqual(true);
Expand All @@ -311,6 +322,30 @@ describe('Call Tests', () => {
expect.any(String)
);
expect(call['mediaStateMachine'].state.value).toBe('S_SEND_ROAP_OFFER');

expect(mediaMetricSpy.mock.calls).toEqual([
[
METRIC_EVENT.MEDIA,
MEDIA_EFFECT_ACTION.BNR_ENABLED,
METRIC_TYPE.BEHAVIORAL,
call.getCallId(),
call.getCorrelationId(),
undefined,
undefined,
undefined,
],
[
METRIC_EVENT.MEDIA,
'S_SEND_ROAP_OFFER',
METRIC_TYPE.BEHAVIORAL,
call.getCallId(),
call.getCorrelationId(),
undefined,
undefined,
undefined,
],
]);

/* Now change the state and recall to check for error */
call['mediaStateMachine'].state.value = 'S_SEND_ROAP_OFFER';
call.dial(localAudioStream);
Expand All @@ -327,6 +362,9 @@ describe('Call Tests', () => {
getAudioTracks: jest.fn().mockReturnValue([mockTrack]),
},
on: jest.fn(),
getEffect: jest.fn().mockImplementation(() => {
return mockEffect;
}),
};

const localAudioStream = mockStream as unknown as MediaSDK.LocalMicrophoneStream;
Expand All @@ -344,6 +382,8 @@ describe('Call Tests', () => {
);
/** Cannot answer in idle state */

const mediaMetricSpy = jest.spyOn(call['metricManager'], 'submitMediaMetric');

call.answer(localAudioStream);
expect(mockTrack.enabled).toEqual(true);
expect(mockMediaSDK.RoapMediaConnection).toBeCalledOnceWith(
Expand All @@ -361,6 +401,105 @@ describe('Call Tests', () => {
call['callStateMachine'].state.value = 'S_SEND_CALL_PROGRESS';
call.answer(localAudioStream);
expect(call['callStateMachine'].state.value).toBe('S_SEND_CALL_CONNECT');

expect(mediaMetricSpy.mock.calls).toEqual([
[
METRIC_EVENT.MEDIA,
'S_ROAP_IDLE',
METRIC_TYPE.BEHAVIORAL,
call.getCallId(),
call.getCorrelationId(),
undefined,
undefined,
undefined,
],
[
METRIC_EVENT.MEDIA,
MEDIA_EFFECT_ACTION.BNR_ENABLED,
METRIC_TYPE.BEHAVIORAL,
call.getCallId(),
call.getCorrelationId(),
undefined,
undefined,
undefined,
],
]);
});

it('testing enabling/disabling the BNR on an active call', async () => {
const localAudioStream = new MockMediaStream() as unknown as MediaSDK.LocalMicrophoneStream;
sreenara marked this conversation as resolved.
Show resolved Hide resolved
const onSpy = jest.spyOn(localAudioStream, 'on');

const call = createCall(
activeUrl,
webex,
dest,
CallDirection.OUTBOUND,
deviceId,
mockLineId,
deleteCallFromCollection,
defaultServiceIndicator
);

call.dial(localAudioStream);

expect(mockTrack.enabled).toEqual(true);
expect(mockMediaSDK.RoapMediaConnection).toBeCalledOnceWith(
roapMediaConnectionConfig,
roapMediaConnectionOptions,
expect.any(String)
);
expect(call['mediaStateMachine'].state.value).toBe('S_SEND_ROAP_OFFER');

const updateLocalTracksSpy = jest.spyOn(call['mediaConnection'], 'updateLocalTracks');
const mediaMetricSpy = jest.spyOn(call['metricManager'], 'submitMediaMetric');

/* Send metrics for BNR enabled */
jest.spyOn(localAudioStream, 'getEffect').mockReturnValue(mockEffect as any);

expect(onSpy).toBeCalledOnceWith(
MediaSDK.LocalStreamEventNames.OutputTrackChange,
expect.any(Function)
);

mediaMetricSpy.mockClear();
onSpy.mock.calls[0][1](mockTrack as MediaStreamTrack);

expect(updateLocalTracksSpy).toBeCalledOnceWith({audio: mockTrack});

expect(mediaMetricSpy).toBeCalledOnceWith(
METRIC_EVENT.MEDIA,
MEDIA_EFFECT_ACTION.BNR_ENABLED,
METRIC_TYPE.BEHAVIORAL,
call.getCallId(),
call.getCorrelationId(),
undefined,
undefined,
undefined
);

/* Clear the mocks */
updateLocalTracksSpy.mockClear();
mediaMetricSpy.mockClear();

/* Send metrics for BNR disabled */
mockEffect.isEnabled = false;
jest.spyOn(localAudioStream, 'getEffect').mockReturnValue(mockEffect as any);

onSpy.mock.calls[0][1](mockTrack as MediaStreamTrack);

expect(updateLocalTracksSpy).toBeCalledOnceWith({audio: mockTrack});

expect(mediaMetricSpy).toBeCalledOnceWith(
METRIC_EVENT.MEDIA,
MEDIA_EFFECT_ACTION.BNR_DISABLED,
METRIC_TYPE.BEHAVIORAL,
call.getCallId(),
call.getCorrelationId(),
undefined,
undefined,
undefined
);
});
});

Expand Down
Loading