Skip to content

Commit acc608c

Browse files
authored
Merge pull request #982 from MyPureCloud/release/v12.1.0
2 parents c099c91 + 1467957 commit acc608c

9 files changed

Lines changed: 303 additions & 113 deletions

File tree

changelog.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,15 @@ All notable changes to this project will be documented in this file.
33
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
44
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
55

6-
# [Unreleased](https://github.com/MyPureCloud/genesys-cloud-webrtc-sdk/compare/v12.0.0...HEAD)
6+
# [Unreleased](https://github.com/MyPureCloud/genesys-cloud-webrtc-sdk/compare/v12.1.0...HEAD)
7+
8+
# [v12.1.0](https://github.com/MyPureCloud/genesys-cloud-webrtc-sdk/compare/v12.0.0...v12.1.0)
9+
### Added
10+
* [STREAM-1153](https://inindca.atlassian.net/browse/STREAM-1153) - Add support for monitoring multiple screens during live monitoring sessions
11+
* [STREAM-1123](https://inindca.atlassian.net/browse/STREAM-1123) - Only update necessary tracks when changing devices. Prevent virtual background from getting replaced
12+
13+
### Changed
14+
* [STREAM-1211](https://inindca.atlassian.net/browse/STREAM-1178) - Update `axios` to `v1.13.5`.
715

816
# [v12.0.0](https://github.com/MyPureCloud/genesys-cloud-webrtc-sdk/compare/v11.5.1...v12.0.0)
917
### Added

package-lock.json

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "genesys-cloud-webrtc-sdk",
3-
"version": "12.0.0",
3+
"version": "12.1.0",
44
"description": "client for the interfacing with Genesys Cloud WebRTC",
55
"repository": "https://github.com/mypurecloud/genesys-cloud-webrtc-sdk",
66
"license": "MIT",
@@ -57,7 +57,7 @@
5757
"dependencies": {
5858
"@babel/runtime": "^7.24.6",
5959
"@babel/runtime-corejs3": "^7.24.6",
60-
"axios": "^1.12.2",
60+
"axios": "^1.13.5",
6161
"browserama": "^3.2.2",
6262
"core-js": "^3.37.1",
6363
"genesys-cloud-client-logger": "^4.2.17",

src/client.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -524,8 +524,11 @@ export class GenesysCloudWebrtcSdk extends (EventEmitter as { new(): StrictEvent
524524
});
525525

526526
if ((updateVideo || updateAudio) && this.sessionManager) {
527+
const updateOptions: any = {};
528+
if (updateVideo) updateOptions.videoDeviceId = options.videoDeviceId;
529+
if (updateAudio) updateOptions.audioDeviceId = options.audioDeviceId;
527530
promises.push(
528-
this.sessionManager.updateOutgoingMediaForAllSessions()
531+
this.sessionManager.updateOutgoingMediaForAllSessions(updateOptions)
529532
);
530533
}
531534

src/sessions/live-monitoring-session-handler.ts

Lines changed: 51 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -63,54 +63,73 @@ export class LiveMonitoringSessionHandler extends BaseSessionHandler {
6363
let addMediaPromise: Promise<any> = Promise.resolve();
6464
params.mediaStream.getTracks().forEach((track) => {
6565
addMediaPromise = addMediaPromise.then(() => {
66-
this.sdk.logger.info('Adding screen track to live screen monitoring session', { trackId: track.id, label: track.label, conversationId: session.conversationId, sessionType: this.sessionType });
67-
return session.pc.addTrack(track);
66+
const trackStream = createNewStreamWithTrack(track);
67+
this.sdk.logger.info('Adding screen track to live screen monitoring session', { streamId: trackStream.id, trackId: track.id, label: track.label, conversationId: session.conversationId, sessionType: this.sessionType });
68+
return session.pc.addTrack(track, trackStream);
6869
});
6970
});
7071
await addMediaPromise;
72+
73+
// Set unused video transceivers direction: inactive to not get sent to the observer clients
74+
const unusedTransceivers = session.pc.getTransceivers()
75+
.filter(transceiver => transceiver.receiver.track?.kind === 'video' && !transceiver.sender.track)
76+
this.sdk.logger.info(`Setting ${unusedTransceivers.length} unused video transceivers to inactive`, { conversationId: session.conversationId, sessionId: session.id });
77+
unusedTransceivers.forEach(transceiver => {
78+
transceiver.direction = "inactive";
79+
});
7180
}
7281

7382
async acceptSessionForObserver(session: LiveScreenMonitoringSession, params: IAcceptSessionRequest) {
74-
const videoElement = params.videoElement || this.sdk._config.defaults.videoElement;
83+
const videoElements = params.videoElements || (params.videoElement ? [params.videoElement] : [this.sdk._config.defaults.videoElement].filter(Boolean));
7584
const sessionInfo = { conversationId: session.conversationId, sessionId: session.id };
7685

77-
if (!videoElement) {
86+
if (!videoElements.length) {
7887
throw createAndEmitSdkError.call(this.sdk, SdkErrorTypes.invalid_options,
79-
'acceptSession for live monitoring observer requires a videoElement to be provided or in the default config',
88+
'acceptSession for live monitoring observer requires videoElements array or videoElement to be provided or in the default config',
8089
sessionInfo);
8190
}
8291

83-
const attachParams = { videoElement };
84-
85-
const handleIncomingTracks = (session: IExtendedMediaSession, tracks: MediaStreamTrack | MediaStreamTrack[]) => {
86-
if (!Array.isArray(tracks)) tracks = [tracks];
87-
88-
for (const track of tracks) {
89-
this.log('info', 'Incoming track from live monitoring session', {
90-
track,
91-
conversationId: session.conversationId,
92-
sessionId: session.id,
93-
sessionType: session.sessionType
92+
const tracks = session.pc.getReceivers()
93+
.filter(receiver => receiver.track)
94+
.map(receiver => receiver.track)
95+
.filter(track => track.kind === 'video');
96+
97+
this.log('info', `Accepting live screen monitoring session as observer with ${videoElements.length} available video elements for ${session.pc.getReceivers().length} receivers with ${tracks.length} video tracks`);
98+
99+
try {
100+
let addEmptyMediaPromise: Promise<any> = Promise.resolve();
101+
tracks.forEach((targetTrack) => {
102+
addEmptyMediaPromise = addEmptyMediaPromise.then(() => {
103+
const canvas = document.createElement('canvas');
104+
canvas.width = 1;
105+
canvas.height = 1;
106+
const emptyStream = canvas.captureStream(0);
107+
const emptyVideoTrack = emptyStream.getVideoTracks()[0];
108+
this.sdk.logger.info('Adding empty screen track to live screen monitoring session', { streamId: emptyStream.id, trackId: emptyVideoTrack.id, label: emptyVideoTrack.label, conversationId: session.conversationId, sessionType: this.sessionType });
109+
return session.pc.addTrack(emptyVideoTrack, emptyStream);
94110
});
111+
});
112+
await addEmptyMediaPromise;
113+
} catch (error: any) {
114+
this.sdk.logger.error('Error when adding empty video streams', error);
115+
}
95116

96-
this.attachIncomingTrackToElement(track, attachParams);
117+
let videoElementIndex = 0;
118+
for (const track of tracks) {
119+
if (videoElementIndex < videoElements.length) {
120+
const stream = createNewStreamWithTrack(track);
121+
const videoElement = videoElements[videoElementIndex];
122+
videoElement.muted = true;
123+
videoElement.autoplay = true;
124+
videoElement.srcObject = stream;
125+
this.log('info', `Attached stream to video element at index ${videoElementIndex}`, {
126+
streamId: videoElement.srcObject?.id,
127+
track: track,
128+
...sessionInfo,
129+
});
130+
videoElementIndex++;
97131
}
98-
99132
session.emit('incomingMedia');
100-
};
101-
102-
// Get existing tracks
103-
const tracks = session.pc.getReceivers()
104-
.filter(receiver => receiver.track)
105-
.map(receiver => receiver.track);
106-
107-
if (tracks.length) {
108-
handleIncomingTracks(session, tracks);
109-
} else {
110-
// Listen for tracks that arrive later
111-
session.on('peerTrackAdded', (track: MediaStreamTrack) => {
112-
handleIncomingTracks(session, track);
113-
});
114133
}
115134
}
116135

@@ -129,29 +148,6 @@ export class LiveMonitoringSessionHandler extends BaseSessionHandler {
129148
this.log('warn', 'Cannot update outgoing media for live monitoring sessions', { sessionId: session.id, sessionType: session.sessionType });
130149
throw createAndEmitSdkError.call(this.sdk, SdkErrorTypes.not_supported, 'Cannot update outgoing media for live monitoring sessions');
131150
}
132-
133-
/**
134-
* Attach incoming track to HTML element
135-
*/
136-
attachIncomingTrackToElement(
137-
track: MediaStreamTrack,
138-
{ videoElement }: { videoElement: HTMLVideoElement }
139-
): HTMLAudioElement | HTMLVideoElement {
140-
const element = videoElement;
141-
142-
if (track.kind === 'video') {
143-
if (element) {
144-
element.muted = true;
145-
}
146-
}
147-
148-
if (element) {
149-
element.autoplay = true;
150-
element.srcObject = createNewStreamWithTrack(track);
151-
}
152-
153-
return element;
154-
}
155151
}
156152

157153
export default LiveMonitoringSessionHandler;

src/sessions/session-manager.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -229,8 +229,7 @@ export class SessionManager {
229229
const promises = sessions.map(session => {
230230
return this.updateOutgoingMedia({
231231
session,
232-
videoDeviceId: opts.videoDeviceId,
233-
audioDeviceId: opts.audioDeviceId
232+
...opts
234233
});
235234
});
236235
return Promise.all(promises);

src/types/interfaces.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -678,6 +678,9 @@ export interface IAcceptSessionRequest extends ISdkMediaDeviceIds {
678678
/** video element to attach incoming video to. default is sdk `defaults.videoElement` */
679679
videoElement?: HTMLVideoElement;
680680

681+
/** array of video elements for live monitoring observers to attach multiple video streams */
682+
videoElements?: HTMLVideoElement[];
683+
681684
/** Flag set to true when the participant is a monitoring observer. default is `false` */
682685
liveMonitoringObserver?: boolean
683686
}

test/unit/client.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -825,7 +825,7 @@ describe('Client', () => {
825825
await sdk.updateDefaultDevices(options);
826826

827827
expect(sdk.headset.updateAudioInputDevice).toHaveBeenCalledWith(options.audioDeviceId);
828-
expect(sdk.sessionManager.updateOutgoingMediaForAllSessions).toHaveBeenCalledWith();
828+
expect(sdk.sessionManager.updateOutgoingMediaForAllSessions).toHaveBeenCalledWith({"audioDeviceId": "new-audio-device-id"});
829829
expect(sessionManagerMock.updateOutputDeviceForAllSessions).not.toHaveBeenCalled();
830830

831831
sessionManagerMock.updateOutgoingMediaForAllSessions.mockReset();

0 commit comments

Comments
 (0)