Skip to content

Commit 4cc7586

Browse files
author
Aidan Zimmermann
committed
STREAM-1153: simplify attachment of video streams
1 parent dd9f775 commit 4cc7586

2 files changed

Lines changed: 56 additions & 68 deletions

File tree

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

Lines changed: 9 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -92,40 +92,15 @@ export class LiveMonitoringSessionHandler extends BaseSessionHandler {
9292
for (const track of tracks) {
9393
if (videoElementIndex < videoElements.length) {
9494
const stream = createNewStreamWithTrack(track);
95-
if (videoElementIndex < 2) {
96-
const mainVideoElement = videoElements[videoElementIndex];
97-
mainVideoElement.muted = true;
98-
mainVideoElement.autoplay = true;
99-
mainVideoElement.srcObject = stream;
100-
this.log('info', `Attached stream to main video element`, {
101-
streamId: mainVideoElement.srcObject?.id,
102-
track: track,
103-
...sessionInfo,
104-
});
105-
videoElementIndex++;
106-
107-
const firstVideoPreviewElement = videoElements[videoElementIndex];
108-
firstVideoPreviewElement.muted = true
109-
firstVideoPreviewElement.autoplay = true;
110-
firstVideoPreviewElement.srcObject = stream;
111-
this.log('info', `Attached stream to first video preview element ${videoElementIndex}`, {
112-
streamId: mainVideoElement.srcObject?.id,
113-
track: track,
114-
...sessionInfo,
115-
});
116-
117-
} else {
118-
const videoElement = videoElements[videoElementIndex];
119-
videoElement.muted = true;
120-
videoElement.autoplay = true;
121-
videoElement.srcObject = stream;
122-
this.log('info', `Attached stream to video preview element ${videoElementIndex}`, {
123-
streamId: videoElement.srcObject?.id,
124-
track: track,
125-
...sessionInfo,
126-
});
127-
}
128-
95+
const videoElement = videoElements[videoElementIndex];
96+
videoElement.muted = true;
97+
videoElement.autoplay = true;
98+
videoElement.srcObject = stream;
99+
this.log('info', `Attached stream to video element at index ${videoElementIndex}`, {
100+
streamId: videoElement.srcObject?.id,
101+
track: track,
102+
...sessionInfo,
103+
});
129104
videoElementIndex++;
130105
}
131106
session.emit('incomingMedia');

test/unit/sessions/live-monitoring-session-handler.test.ts

Lines changed: 47 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -206,34 +206,42 @@ describe('acceptSessionForObserver', () => {
206206
expect(createNewStreamSpy).toHaveBeenCalledWith(mockVideoTrack1);
207207
expect(createNewStreamSpy).toHaveBeenCalledWith(mockVideoTrack2);
208208

209-
// First track should attach to both main and first preview element
209+
// Each video track should attach to one video element sequentially
210210
expect(video1.srcObject).toBeDefined();
211211
expect(video1.muted).toBe(true);
212212
expect(video1.autoplay).toBe(true);
213213
expect(video2.srcObject).toBeDefined();
214214
expect(video2.muted).toBe(true);
215215
expect(video2.autoplay).toBe(true);
216-
217-
// Second track should attach to third element
218-
expect(video3.srcObject).toBeDefined();
219-
expect(video3.muted).toBe(true);
220-
expect(video3.autoplay).toBe(true);
216+
expect(video3.srcObject).toBeUndefined(); // Third element unused
221217

222218
expect(emitSpy).toHaveBeenCalledWith('incomingMedia');
223219
expect(emitSpy).toHaveBeenCalledTimes(2); // Once per video track
224220
expect(logSpy).toHaveBeenCalledWith('info', expect.stringContaining('Accepting live screen monitoring session as observer'));
225221
});
226222

227-
it('should handle insufficient video elements gracefully', async () => {
223+
it('should handle less than maximum video elements gracefully', async () => {
228224
const video1 = document.createElement('video');
229-
const videoElements = [video1]; // Only one video element, but code tries to access two for first track
225+
const videoElements = [video1]; // Only one video element for two tracks
230226

231-
const mockReceivers = [{ track: mockVideoTrack1 }];
227+
const mockReceivers = [
228+
{ track: mockVideoTrack1 },
229+
{ track: mockVideoTrack2 }
230+
];
232231
session.pc.getReceivers = jest.fn().mockReturnValue(mockReceivers);
233232

234-
// This test exposes the bug in the current implementation
235-
await expect(handler.acceptSessionForObserver(session, { videoElements } as any))
236-
.rejects.toThrow('Cannot set properties of undefined');
233+
const emitSpy = jest.spyOn(session, 'emit');
234+
235+
await handler.acceptSessionForObserver(session, { videoElements } as any);
236+
237+
// Only first track should be attached since we only have one video element
238+
expect(video1.srcObject).toBeDefined();
239+
expect(video1.muted).toBe(true);
240+
expect(video1.autoplay).toBe(true);
241+
242+
// Both tracks should still emit incomingMedia events
243+
expect(emitSpy).toHaveBeenCalledTimes(2);
244+
expect(createNewStreamSpy).toHaveBeenCalledTimes(1);
237245
});
238246

239247
it('should use videoElement field when no videoElements provided', async () => {
@@ -242,9 +250,15 @@ describe('acceptSessionForObserver', () => {
242250
const mockReceivers = [{ track: mockVideoTrack1 }];
243251
session.pc.getReceivers = jest.fn().mockReturnValue(mockReceivers);
244252

245-
// This will also fail due to the bug - it needs at least 2 elements for the first track
246-
await expect(handler.acceptSessionForObserver(session, { videoElement } as any))
247-
.rejects.toThrow('Cannot set properties of undefined');
253+
const emitSpy = jest.spyOn(session, 'emit');
254+
255+
await handler.acceptSessionForObserver(session, { videoElement } as any);
256+
257+
expect(videoElement.srcObject).toBeDefined();
258+
expect(videoElement.muted).toBe(true);
259+
expect(videoElement.autoplay).toBe(true);
260+
expect(emitSpy).toHaveBeenCalledWith('incomingMedia');
261+
expect(createNewStreamSpy).toHaveBeenCalledWith(mockVideoTrack1);
248262
});
249263

250264
it('should use default video element when no videoElements or videoElement provided', async () => {
@@ -254,9 +268,15 @@ describe('acceptSessionForObserver', () => {
254268
const mockReceivers = [{ track: mockVideoTrack1 }];
255269
session.pc.getReceivers = jest.fn().mockReturnValue(mockReceivers);
256270

257-
// This will also fail due to the bug - it needs at least 2 elements for the first track
258-
await expect(handler.acceptSessionForObserver(session, { videoElement: defaultVideo } as any))
259-
.rejects.toThrow('Cannot set properties of undefined');
271+
const emitSpy = jest.spyOn(session, 'emit');
272+
273+
await handler.acceptSessionForObserver(session, {} as any);
274+
275+
expect(defaultVideo.srcObject).toBeDefined();
276+
expect(defaultVideo.muted).toBe(true);
277+
expect(defaultVideo.autoplay).toBe(true);
278+
expect(emitSpy).toHaveBeenCalledWith('incomingMedia');
279+
expect(createNewStreamSpy).toHaveBeenCalledWith(mockVideoTrack1);
260280
});
261281

262282
it('should handle case with no video tracks', async () => {
@@ -275,7 +295,7 @@ describe('acceptSessionForObserver', () => {
275295
await handler.acceptSessionForObserver(session, { videoElements } as any);
276296

277297
expect(createNewStreamSpy).not.toHaveBeenCalled();
278-
expect(video1.srcObject).toBeUndefined(); // Changed from toBeNull to toBeUndefined
298+
expect(video1.srcObject).toBeUndefined();
279299
expect(emitSpy).not.toHaveBeenCalled();
280300
});
281301

@@ -296,7 +316,7 @@ describe('acceptSessionForObserver', () => {
296316
expect.stringContaining('Accepting live screen monitoring session as observer with 3 available video elements for 1 receivers with 1 video tracks')
297317
);
298318
expect(logSpy).toHaveBeenCalledWith('info',
299-
expect.stringContaining('Attached stream to main video element'),
319+
expect.stringContaining('Attached stream to video element at index 0'),
300320
expect.objectContaining({ streamId: expect.any(String) })
301321
);
302322
});
@@ -306,7 +326,7 @@ describe('acceptSessionForObserver', () => {
306326
const video2 = document.createElement('video');
307327
const video3 = document.createElement('video');
308328
const video4 = document.createElement('video');
309-
const videoElements = [video1, video2, video3, video4]; // Need 6 elements for 2 tracks
329+
const videoElements = [video1, video2, video3, video4];
310330

311331
const mockReceivers = [
312332
{ track: mockVideoTrack1 },
@@ -315,22 +335,15 @@ describe('acceptSessionForObserver', () => {
315335
session.pc.getReceivers = jest.fn().mockReturnValue(mockReceivers);
316336

317337
const emitSpy = jest.spyOn(session, 'emit');
338+
const logSpy = jest.spyOn(handler, 'log' as any);
318339

319340
await handler.acceptSessionForObserver(session, { videoElements } as any);
320341

321-
// Let's trace through the logic:
322-
// Track 1 (videoElementIndex starts at 0):
323-
// - videoElementIndex < 2, so uses main + preview logic
324-
// - Sets video1 (index 0), then videoElementIndex++, now index = 1
325-
// - Sets video2 (index 1), then videoElementIndex++ at end, now index = 2
326-
// Track 2 (videoElementIndex is now 2):
327-
// - videoElementIndex >= 2, so uses else logic
328-
// - Sets video3 (index 2), then videoElementIndex++ at end, now index = 3
329-
330-
expect(video1.srcObject).toBeDefined(); // Track 1 main
331-
expect(video2.srcObject).toBeDefined(); // Track 1 preview
332-
expect(video3.srcObject).toBeDefined(); // Track 2
333-
expect(video4.srcObject).toBeUndefined(); // Should be unused
342+
// Each track should attach to one video element sequentially
343+
expect(video1.srcObject).toBeDefined(); // Track 1
344+
expect(video2.srcObject).toBeDefined(); // Track 2
345+
expect(video3.srcObject).toBeUndefined(); // Unused
346+
expect(video4.srcObject).toBeUndefined(); // Unused
334347

335348
expect(emitSpy).toHaveBeenCalledTimes(2);
336349
});

0 commit comments

Comments
 (0)