# JavaScript WebRTC overrides

> \[!IMPORTANT]
>
> WebRTC overrides for Video JavaScript SDK is currently in **Public Beta**. Learn more about [beta product support](https://help.twilio.com/hc/en-us/articles/115002413087-Twilio-Beta-product-support).

## Overview

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](https://www.citrix.com/solutions/hdx.html), that use WebRTC redirection.

## JavaScript WebRTC overrides

### `getUserMedia`

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`.

### `enumerateDevices`

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`.

### MediaStream

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.

> \[!NOTE]
>
> 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:
>
> ```js
> function polyfillMediaStreamEventListeners() {
>   const originalCreateMediaStream = window.CitrixWebRTC.createMediaStream;
>
>   window.CitrixWebRTC.createMediaStream = function(tracks) {
>     const mediaStream = originalCreateMediaStream.call(this, tracks);
>     const listeners = {};
>
>     mediaStream.addEventListener = function(eventName, listener) {
>       const methodName = `on${eventName}`;
>       if(!(methodName in this)) {
>         throw new Error(`Unrecognized event: ${eventName}. MediaStream does not support this event.`);
>       }
>
>       // Get or initialize listeners for this event
>       const currentListeners = listeners[eventName] || [];
>       listeners[eventName] = currentListeners.concat(listener);
>       
>       this[methodName] = createEventHandler(listeners[eventName]);
>     }
>
>     mediaStream.removeEventListener = function(eventName, listener) {
>       const methodName = `on${eventName}`;
>       const currentListeners = listeners[eventName] || [];
>       listeners[eventName] = currentListeners.filter(l => l !== listener);
>       
>       this[methodName] = createEventHandler(listeners[eventName]);
>     }
>
>     return mediaStream;
>   }
> }
> ```
>
> 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`.

### `RTCPeerConnection`

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.

> \[!NOTE]
>
> 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:
>
> ```js
> function polyfillPeerConnectionEventListeners() {
>   const instanceListeners = new WeakMap();
>
>   window.CitrixWebRTC.CitrixPeerConnection.prototype.addEventListener = function(eventName, listener) {
>     const methodName = `on${eventName}`;
>     if(!(methodName in this)) {
>       throw new Error(`Unrecognized event: ${eventName}. CitrixPeerConnection does not support this event.`);
>     }
>
>     // Get or initialize listeners for this instance
>     let listeners = instanceListeners.get(this);
>     if (!listeners) {
>       listeners = {};
>       instanceListeners.set(this, listeners);
>     }
>
>     // Get or initialize listeners for this event
>     const currentListeners = listeners[eventName] || [];
>     listeners[eventName] = currentListeners.concat(listener);
>     
>     this[methodName] = createEventHandler(listeners[eventName]);
>   }
>
>   window.CitrixWebRTC.CitrixPeerConnection.prototype.removeEventListener = function(eventName, listener) {
>     const methodName = `on${eventName}`;
>     const listeners = instanceListeners.get(this);
>     if (!listeners) return;
>
>     const currentListeners = listeners[eventName] || [];
>     listeners[eventName] = currentListeners.filter(l => l !== listener);
>     
>     this[methodName] = createEventHandler(listeners[eventName]);
>   }
> }
> ```
>
> 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.

### `mapMediaElement` (Experimental)

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.

### `disposeMediaElement` (Experimental)

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.

## Code examples

### Create local audio and video tracks using the WebRTC overrides

```js
// Creating both tracks at the same time
const localTracks = Video.createLocalTracks({
  audio: true,
  video: true,
  MediaStream: (streamTracks = []) => CitrixWebRTC.createMediaStream(streamTracks),
  getUserMedia: (...args) => CitrixWebRTC.getUserMedia(...args),
  enumerateDevices: CitrixWebRTC.enumerateDevices.bind(CitrixWebRTC),
  mapMediaElement: (element) => attachMediaElement(element), // Application-specific function
  disposeMediaElement: (element) => detachMediaElement(element), // Application-specific function
});

// Creating only an audio track
const localAudioTrack = Video.createLocalAudioTrack({
  MediaStream: (streamTracks = []) => CitrixWebRTC.createMediaStream(streamTracks),
  getUserMedia: (...args) => CitrixWebRTC.getUserMedia(...args),
  enumerateDevices: CitrixWebRTC.enumerateDevices.bind(CitrixWebRTC),
  mapMediaElement: (element) => attachMediaElement(element), // Application-specific function
  disposeMediaElement: (element) => detachMediaElement(element), // Application-specific function
});

// Creating only a video track
const localVideoTrack = Video.createLocalVideoTrack({
  MediaStream: (streamTracks = []) => CitrixWebRTC.createMediaStream(streamTracks),
  getUserMedia: (...args) => CitrixWebRTC.getUserMedia(...args),
  enumerateDevices: CitrixWebRTC.enumerateDevices.bind(CitrixWebRTC),
  mapMediaElement: (element) => attachMediaElement(element), // Application-specific function
  disposeMediaElement: (element) => detachMediaElement(element), // Application-specific function
});

```

### Connect to a Video Room using preacquired tracks

```js
const room = Video.connect('$TOKEN', {
  tracks: localTracks,
  MediaStream: (streamTracks = []) => CitrixWebRTC.createMediaStream(streamTracks),
  getUserMedia: (...args) => CitrixWebRTC.getUserMedia(...args),
  enumerateDevices: CitrixWebRTC.enumerateDevices.bind(CitrixWebRTC),
  RTCPeerConnection: CitrixWebRTC.RTCPeerConnection.bind(CitrixWebRTC),
  mapMediaElement: (element) => attachMediaElement(element), // Application-specific function
  disposeMediaElement: (element) => detachMediaElement(element), // Application-specific function
});

```

### Connect to a Video Room without providing tracks

```js
const room = Video.connect('$TOKEN', {
  MediaStream: (streamTracks = []) => CitrixWebRTC.createMediaStream(streamTracks),
  getUserMedia: (...args) => CitrixWebRTC.getUserMedia(...args),
  enumerateDevices: CitrixWebRTC.enumerateDevices.bind(CitrixWebRTC),
  RTCPeerConnection: CitrixWebRTC.RTCPeerConnection.bind(CitrixWebRTC),
  mapMediaElement: (element) => attachMediaElement(element), // Application-specific function
  disposeMediaElement: (element) => detachMediaElement(element), // Application-specific function
});

```

## Limitations

* WebRTC overrides aren't supported in the Citrix browser. Only the Citrix desktop application is supported.
* VP8 is not enabled by default in Citrix environments and H.264 is the recommended codec to use. Developers should not specify the VP8 as the preferred codec when connecting to a Video Room in a Citrix environment.
