WebRTC overrides for Video JavaScript SDK is currently in Public Beta. Learn more about beta product support.
You can use WebRTC override APIs to replace the core WebRTC APIs with your own implementation. These overrides let the Video JavaScript SDK run in virtual environments, such as Citrix HDX, that use WebRTC redirection.
Overrides the browser's default getUserMedia
implementation to control how the media input is captured. Call this method with the media constraints as a parameter. This method returns a promise that resolves with a MediaStream object when the requested media is successfully obtained. This capability is available through the public methods: createLocalTracks
, createLocalAudioTrack
, createLocalVideoTrack
, and connect
.
Overrides the browser's default enumerateDevices
implementation to obtain the list of available input and output devices on the system. This method doesn't take any parameters and returns a promise that resolves with an array of MediaDeviceInfo objects describing all the available devices. This new capability is available through the public methods: createLocalTracks
, createLocalAudioTrack
, createLocalVideoTrack
, and connect
.
Overrides the browser's default MediaStream object used to create streams. For consistency, implement the EventTarget interface (directly or indirectly) on this object to match the browser's native MediaStream API.
A known issue with Citrix HDX is that stream objects created by their custom implementation don't implement the EventTarget
interface. The methods addEventListener
, removeEventListener
, and dispatchEvent
are unavailable. As a workaround, you can shim this functionality on the application side using the following polyfill:
1function polyfillMediaStreamEventListeners() {2const originalCreateMediaStream = window.CitrixWebRTC.createMediaStream;34window.CitrixWebRTC.createMediaStream = function(tracks) {5const mediaStream = originalCreateMediaStream.call(this, tracks);6const listeners = {};78mediaStream.addEventListener = function(eventName, listener) {9const methodName = `on${eventName}`;10if(!(methodName in this)) {11throw new Error(`Unrecognized event: ${eventName}. MediaStream does not support this event.`);12}1314// Get or initialize listeners for this event15const currentListeners = listeners[eventName] || [];16listeners[eventName] = currentListeners.concat(listener);1718this[methodName] = createEventHandler(listeners[eventName]);19}2021mediaStream.removeEventListener = function(eventName, listener) {22const methodName = `on${eventName}`;23const currentListeners = listeners[eventName] || [];24listeners[eventName] = currentListeners.filter(l => l !== listener);2526this[methodName] = createEventHandler(listeners[eventName]);27}2829return mediaStream;30}31}
This polyfill is expected to be included post-beta to facilitate the integration of Citrix HDX with Twilio Video. It will be available through the public methods: createLocalTracks
, createLocalAudioTrack
, createLocalVideoTrack
, and connect
.
Overrides the browser's RTCPeerConnection
implementation to represent a WebRTC connection. For consistency, implement the EventTarget interface (directly or indirectly) on this object to match the browser's native RTCPeerConnection
API.
A known issue with Citrix HDX is that the peerconnection
objects created by their custom RTCPeerConnection
don't implement the EventTarget interface. As a workaround, you can shim the following polyfill on the application side:
1function polyfillPeerConnectionEventListeners() {2const instanceListeners = new WeakMap();34window.CitrixWebRTC.CitrixPeerConnection.prototype.addEventListener = function(eventName, listener) {5const methodName = `on${eventName}`;6if(!(methodName in this)) {7throw new Error(`Unrecognized event: ${eventName}. CitrixPeerConnection does not support this event.`);8}910// Get or initialize listeners for this instance11let listeners = instanceListeners.get(this);12if (!listeners) {13listeners = {};14instanceListeners.set(this, listeners);15}1617// Get or initialize listeners for this event18const currentListeners = listeners[eventName] || [];19listeners[eventName] = currentListeners.concat(listener);2021this[methodName] = createEventHandler(listeners[eventName]);22}2324window.CitrixWebRTC.CitrixPeerConnection.prototype.removeEventListener = function(eventName, listener) {25const methodName = `on${eventName}`;26const listeners = instanceListeners.get(this);27if (!listeners) return;2829const currentListeners = listeners[eventName] || [];30listeners[eventName] = currentListeners.filter(l => l !== listener);3132this[methodName] = createEventHandler(listeners[eventName]);33}34}
This polyfill is expected to be included post-beta to facilitate the integration of Citrix HDX with Twilio Video. This new option will be available through the ConnectOptions
received by the public connect method.
This override is only needed on Citrix HDX environments where the MediaStream implementation can't be attached to media elements directly without passing those media elements through a mapping process. Citrix HDX exposes this mapping process through the window.CitrixWebRTC.mapVideoElement
and window.CitrixWebRTC.mapAudioElement
methods.
You should call the method based on the element type. This function will receive the element as a parameter and expect the application code to handle it according to your use case.
Note: The disposeMediaElement
override is experimental and subject to change.
This override is only needed on Citrix HDX environments where the media elements need special treatment for disposal. This method receives the media element to be cleaned up and the application should handle the element using window.CitrixWebRTC.mapVideoElement
or window.CitrixWebRTC.mapAudioElement
, depending on its type.
Note: The disposeMediaElement
override is experimental and subject to change.
1// Creating both tracks at the same time2const localTracks = Video.createLocalTracks({3audio: true,4video: true,5MediaStream: (streamTracks = []) => CitrixWebRTC.createMediaStream(streamTracks),6getUserMedia: (...args) => CitrixWebRTC.getUserMedia(...args),7enumerateDevices: CitrixWebRTC.enumerateDevices.bind(CitrixWebRTC),8mapMediaElement: (element) => attachMediaElement(element), // Application-specific function9disposeMediaElement: (element) => detachMediaElement(element), // Application-specific function10});1112// Creating only an audio track13const localAudioTrack = Video.createLocalAudioTrack({14MediaStream: (streamTracks = []) => CitrixWebRTC.createMediaStream(streamTracks),15getUserMedia: (...args) => CitrixWebRTC.getUserMedia(...args),16enumerateDevices: CitrixWebRTC.enumerateDevices.bind(CitrixWebRTC),17mapMediaElement: (element) => attachMediaElement(element), // Application-specific function18disposeMediaElement: (element) => detachMediaElement(element), // Application-specific function19});2021// Creating only a video track22const localVideoTrack = Video.createLocalVideoTrack({23MediaStream: (streamTracks = []) => CitrixWebRTC.createMediaStream(streamTracks),24getUserMedia: (...args) => CitrixWebRTC.getUserMedia(...args),25enumerateDevices: CitrixWebRTC.enumerateDevices.bind(CitrixWebRTC),26mapMediaElement: (element) => attachMediaElement(element), // Application-specific function27disposeMediaElement: (element) => detachMediaElement(element), // Application-specific function28});29
1const room = Video.connect('$TOKEN', {2tracks: localTracks,3MediaStream: (streamTracks = []) => CitrixWebRTC.createMediaStream(streamTracks),4getUserMedia: (...args) => CitrixWebRTC.getUserMedia(...args),5enumerateDevices: CitrixWebRTC.enumerateDevices.bind(CitrixWebRTC),6RTCPeerConnection: CitrixWebRTC.RTCPeerConnection.bind(CitrixWebRTC),7mapMediaElement: (element) => attachMediaElement(element), // Application-specific function8disposeMediaElement: (element) => detachMediaElement(element), // Application-specific function9});10
1const room = Video.connect('$TOKEN', {2MediaStream: (streamTracks = []) => CitrixWebRTC.createMediaStream(streamTracks),3getUserMedia: (...args) => CitrixWebRTC.getUserMedia(...args),4enumerateDevices: CitrixWebRTC.enumerateDevices.bind(CitrixWebRTC),5RTCPeerConnection: CitrixWebRTC.RTCPeerConnection.bind(CitrixWebRTC),6mapMediaElement: (element) => attachMediaElement(element), // Application-specific function7disposeMediaElement: (element) => detachMediaElement(element), // Application-specific function8});9