Skip to content

Commit c2b8da5

Browse files
authored
Merge pull request #976 from MyPureCloud/STREAM-1153-support-multiple-screens
STREAM-1153: support multiple screens
2 parents 193d645 + b50a4bc commit c2b8da5

4 files changed

Lines changed: 286 additions & 103 deletions

File tree

changelog.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1414
* [STREAM-1178](https://inindca.atlassian.net/browse/STREAM-1178) - REVERT STREAM-825, removed ability to send multiple tracks when screensharing.
1515
* [STREAM-1130](https://inindca.atlassian.net/browse/STREAM-1130) - Updated TypeScript to v5.9.3 and Webpack to v5.105.0, removed legacy Spigot testing, added `.nvmrc`, demo app dependency updates, updated Node versions in testing matrices.
1616

17+
### Added
18+
* [STREAM-1153](https://inindca.atlassian.net/browse/STREAM-1153) - Add support for monitoring multiple screens during live monitoring sessions
19+
1720
# [v11.5.1](https://github.com/MyPureCloud/genesys-cloud-webrtc-sdk/compare/v11.5.0...v11.5.1)
1821
### Added
1922
* [STREAM-1034](https://inindca.atlassian.net/browse/STREAM-1034) - Added JWT support for live monitoring sessions

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/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
}

0 commit comments

Comments
 (0)