Skip to content

Commit 1452027

Browse files
committed
refactor: Simplify media stream handling and update helper functions
1 parent 94cd40b commit 1452027

File tree

5 files changed

+62
-230
lines changed

5 files changed

+62
-230
lines changed

quickstart/src/citrix-helpers.js

Lines changed: 38 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,44 @@
11
/**
2-
* A wrapper class for CitrixWebRTC MediaStream that implements the standard MediaStream interface
3-
* @extends MediaStream Extending from MediaStream allows an instance to be set as the srcObject of a media element (e.g. video, audio)
2+
* Custom MediaStream implementation for Citrix WebRTC
3+
* @property {function} createMediaStream - Create a MediaStream object
4+
* @property {function} mapElement - Register an HTML element to receive media stream data
45
*/
5-
class CustomMediaStream extends MediaStream {
6-
constructor(stream) {
7-
if (!window.CitrixWebRTC) {
8-
throw new Error('CitrixWebRTC is not available');
6+
const CustomMediaStream = {
7+
/**
8+
* Create a MediaStream object using Citrix WebRTC
9+
* @param {Array<MediaStreamTrack>} stremtracks - An array of MediaStreamTracks to include in the MediaStream
10+
* @returns {MediaStream} The created Citrix MediaStream object
11+
*/
12+
createMediaStream: function (streamTracks = []) {
13+
return window.CitrixWebRTC.createMediaStream(streamTracks);
14+
},
15+
/**
16+
* Attach a media track to a DOM element using Citrix WebRTC
17+
* @param {Object} track - The media track to attach
18+
* @param {HTMLElement} element - The DOM element to attach the track to
19+
*/
20+
mapElement: function (el) {
21+
const tagName = el.tagName.toLowerCase();
22+
if (tagName === 'video') {
23+
window.CitrixWebRTC.mapVideoElement(el);
24+
} else if (tagName === 'audio') {
25+
window.CitrixWebRTC.mapAudioElement(el);
926
}
10-
super();
11-
this.citrixMediaStream = window.CitrixWebRTC.createMediaStream(stream);
12-
}
13-
14-
// instance properties
15-
get id() { return this.citrixMediaStream.id; }
16-
get active() { return this.citrixMediaStream.active; }
17-
18-
// instance methods
19-
addTrack(track) { this.citrixMediaStream.addTrack(track); }
20-
clone() { return new CustomMediaStream(this.citrixMediaStream.getTracks()); }
21-
getAudioTracks() { return this.citrixMediaStream.getAudioTracks(); }
22-
getTrackById(id) { return this.citrixMediaStream.getTrackById(id); }
23-
getTracks() { return this.citrixMediaStream.getTracks(); }
24-
getVideoTracks() { return this.citrixMediaStream.getVideoTracks(); }
25-
removeTrack(track) { this.citrixMediaStream.removeTrack(track); }
26-
27-
// event listeners
28-
set onaddtrack(listener) { this.citrixMediaStream.onaddtrack = listener; }
29-
set onremovetrack(listener) { this.citrixMediaStream.onremovetrack = listener; }
30-
addEventListener(event, listener) {
31-
this.citrixMediaStream[`on${event}`] = listener;
32-
}
33-
removeEventListener(event) {
34-
this.citrixMediaStream[`on${event}`] = null;
35-
}
36-
}
27+
},
28+
/**
29+
* Detach a media track from a DOM element using Citrix WebRTC
30+
* @param {HTMLElement} element - The DOM element to detach the track from
31+
*/
32+
disposeElement: function (el) {
33+
const tagName = el.tagName.toLowerCase();
34+
if (tagName === 'video') {
35+
window.CitrixWebRTC.disposeVideoElement(el);
36+
} else if (tagName === 'audio') {
37+
window.CitrixWebRTC.disposeAudioElement(el);
38+
}
39+
},
40+
audioLoadingTimeout: 250
41+
};
3742

3843
/**
3944
* Adjust the client area offset to account for the Citrix WebRTC redirection
@@ -45,34 +50,6 @@ async function adjustClientAreaOffset() {
4550
window.CitrixWebRTC.setClientAreaOffset(0, offset, windowHandle);
4651
}
4752

48-
/**
49-
* Attach a media track to a DOM element using Citrix WebRTC
50-
* @param {Object} track - The media track to attach
51-
* @param {HTMLElement} element - The DOM element to attach the track to
52-
*/
53-
function attachTrackToElement(track, element) {
54-
if (track.kind === 'video') {
55-
window.CitrixWebRTC.mapVideoElement(element);
56-
} else {
57-
window.CitrixWebRTC.mapAudioElement(element);
58-
}
59-
element.srcObject = window.CitrixWebRTC.createMediaStream([track.mediaStreamTrack]);
60-
}
61-
62-
/**
63-
* Detach a media track from a DOM element using Citrix WebRTC
64-
* @param {HTMLElement} element - The DOM element to detach the track from
65-
*/
66-
function detachTrackFromElement(element) {
67-
if (element.tagName.toLowerCase() === 'video') {
68-
window.CitrixWebRTC.disposeVideoElement(element);
69-
} else {
70-
window.CitrixWebRTC.disposeAudioElement(element);
71-
}
72-
}
73-
7453

7554
module.exports.adjustClientAreaOffset = adjustClientAreaOffset;
76-
module.exports.attachTrackToElement = attachTrackToElement;
77-
module.exports.detachTrackFromElement = detachTrackFromElement;
7855
module.exports.CustomMediaStream = CustomMediaStream;

quickstart/src/citrix-polyfills.js

Lines changed: 0 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -39,19 +39,6 @@ function polyfillPeerConnectionEventListeners() {
3939
}
4040
}
4141

42-
/**
43-
* Polyfills the CitrixPeerConnection.addTransceiver method.
44-
* By default, CitrixPeerConnection.addTransceiver requires at least an empty init object to be passed as the second argument.
45-
* This polyfill allows for the use of the `addTransceiver` method without the init following the WebRTC spec.
46-
*/
47-
function polyfillAddTransceiver() {
48-
const originalAddTransceiver = window.CitrixWebRTC.CitrixPeerConnection.prototype.addTransceiver;
49-
50-
window.CitrixWebRTC.CitrixPeerConnection.prototype.addTransceiver = function(trackOrKind, init = {}) {
51-
return originalAddTransceiver.call(this, trackOrKind, init);
52-
}
53-
}
54-
5542
/**
5643
* Polyfills the CitrixDataChannel event listeners.
5744
* By default, CitrixDataChannel handles events through the `on<event>` method.
@@ -126,118 +113,6 @@ function polyfillMediaStreamEventListeners() {
126113
}
127114
}
128115

129-
/**
130-
* Polyfills the CitrixPeerConnection.getStats method.
131-
* By default, CitrixPeerConnection.getStats does not support a selector argument.
132-
* This polyfill allows for the use of the `getStats` method with a selector argument
133-
* by filtering the stats to only include the relevant stats for the specified track.
134-
*
135-
* @param {Object} selector - The selector object with the id property
136-
* @returns {Map} A map of stats with the stat ID as the key and the stat object as the value
137-
*/
138-
function polyfillGetStats() {
139-
const originalGetStats = window.CitrixWebRTC.CitrixPeerConnection.prototype.getStats;
140-
141-
window.CitrixWebRTC.CitrixPeerConnection.prototype.getStats = async function(selector) {
142-
const rawStatsArray = await originalGetStats.call(this);
143-
144-
// Convert array to a Map as per WebRTC specs
145-
const rawStats = new Map();
146-
rawStatsArray.forEach(stat => {
147-
rawStats.set(stat.id, stat);
148-
});
149-
150-
// If no selector is provided, return all stats as a Map
151-
if (!selector) {
152-
return rawStats;
153-
}
154-
155-
const filteredReport = new Map();
156-
const trackId = selector.id;
157-
const relevantIds = new Set();
158-
159-
// First pass: Find media sources matching the track ID and identify related stats
160-
for (const [id, stat] of rawStats) {
161-
// Add media source that matches the track
162-
if (stat.type === 'media-source' && stat.trackIdentifier === trackId) {
163-
filteredReport.set(id, stat);
164-
relevantIds.add(id);
165-
}
166-
}
167-
// If no matching media source was found, return empty report
168-
if (relevantIds.size === 0) {
169-
return filteredReport;
170-
}
171-
172-
// Second pass: Find all stats that reference the relevant IDs
173-
for (const [id, stat] of rawStats) {
174-
// Add outbound-rtp stats referencing the media source
175-
if (stat.type === 'outbound-rtp' && relevantIds.has(stat.mediaSourceId)) {
176-
filteredReport.set(id, stat);
177-
relevantIds.add(id);
178-
179-
// Also add the referenced codec
180-
if (stat.codecId) {
181-
relevantIds.add(stat.codecId);
182-
}
183-
184-
// Add the transport
185-
if (stat.transportId) {
186-
relevantIds.add(stat.transportId);
187-
}
188-
}
189-
190-
// Add inbound-rtp stats referencing the media source
191-
else if (stat.type === 'inbound-rtp' && relevantIds.has(stat.mediaSourceId)) {
192-
filteredReport.set(id, stat);
193-
relevantIds.add(id);
194-
195-
if (stat.codecId) {
196-
relevantIds.add(stat.codecId);
197-
}
198-
199-
if (stat.transportId) {
200-
relevantIds.add(stat.transportId);
201-
}
202-
}
203-
204-
// Add remote-inbound-rtp stats that reference our outbound-rtp
205-
else if (stat.type === 'remote-inbound-rtp' && relevantIds.has(stat.localId)) {
206-
filteredReport.set(id, stat);
207-
208-
if (stat.codecId) {
209-
relevantIds.add(stat.codecId);
210-
}
211-
212-
if (stat.transportId) {
213-
relevantIds.add(stat.transportId);
214-
}
215-
}
216-
217-
// Add remote-outbound-rtp stats that reference our inbound-rtp
218-
else if (stat.type === 'remote-outbound-rtp' && relevantIds.has(stat.remoteId)) {
219-
filteredReport.set(id, stat);
220-
221-
if (stat.codecId) {
222-
relevantIds.add(stat.codecId);
223-
}
224-
225-
if (stat.transportId) {
226-
relevantIds.add(stat.transportId);
227-
}
228-
}
229-
}
230-
231-
// Third pass: Add all the referenced stats like codec, transport, etc.
232-
for (const [id, stat] of rawStats) {
233-
if (relevantIds.has(id) && !filteredReport.has(id)) {
234-
filteredReport.set(id, stat);
235-
}
236-
}
237-
return filteredReport;
238-
}
239-
}
240-
241116
/**
242117
* Creates an event handler function that calls all registered listeners with the event
243118
* @param {Function[]} listeners - Array of event listener functions to call
@@ -252,12 +127,9 @@ function createEventHandler(listeners) {
252127
function installCitrixWebRTCPolyfills() {
253128
// PeerConnection Polyfills
254129
polyfillPeerConnectionEventListeners();
255-
polyfillAddTransceiver();
256130
polyfillDataChannelEventListeners();
257131
// MediaStream Polyfills
258132
polyfillMediaStreamEventListeners();
259-
// getStats Polyfills
260-
polyfillGetStats();
261133
}
262134

263135

quickstart/src/index.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ const joinRoom = require('./joinroom');
55
const selectMedia = require('./selectmedia');
66
const selectRoom = require('./selectroom');
77
const showError = require('./showerror');
8-
const { adjustClientAreaOffset, attachTrackToElement } = require('./citrix-helpers');
8+
const { adjustClientAreaOffset } = require('./citrix-helpers');
99
const { installCitrixWebRTCPolyfills } = require('./citrix-polyfills');
1010

1111
const $modals = $('#modals');
@@ -114,7 +114,7 @@ async function selectCamera() {
114114
deviceIds.video = await selectMedia('video', $selectCameraModal, videoTrack => {
115115
const $video = $('video', $selectCameraModal);
116116
const videoElement = $video.get(0);
117-
attachTrackToElement(videoTrack, videoElement);
117+
videoTrack.attach(videoElement);
118118
});
119119
} catch (error) {
120120
showError($showErrorModal, error);

0 commit comments

Comments
 (0)