Media Server independent WebRTC player
Tags: webrtc, opensource, javascript
Published: 2022-04-07 15:23:26

While original focus of WebRTC was on video conferencing it can also be used for real-time broadcast or streaming with ultra-low latency ([DASH-IF WebRTC report](https://dashif.org/webRTC/report)). However, the adoption rate for using WebRTC in this context has been slow and one of the reasons is the lack of standard protocol for ingest (producer) and playback (viewer). 

The ingest side is being addressed by the draft IETF standard [WHIP](https://www.ietf.org/archive/id/draft-ietf-wish-whip-01.html) that allows you to send media from a WHIP compliant sender software to a WHIP compliant media server. WHIP proposes a simple HTTP based protocol that will allow WebRTC based ingest of content into streaming services and/or CDNs.

On the playback side there are standardization initiatives in discussion but not as far progressed as on the ingest side with WHIP. WebRTC media servers provides WebRTC players but they are often tied to the specific WebRTC media server. 

As we have not yet reached a common standard (transport method) for the SDP exchange between media server (broadcaster) and the player in this context we have launched an open source project for a [media server independent WebRTC player](https://github.com/Eyevinn/webrtc-player).

![Example of WebRTC player](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6lfidqn7cfks9kqc8kod.png)

A player with media server adapters where you either can use one of the included ones or build a custom adapter that you use on your own. We welcome contributions to this project in the form of media server adapters and this blog post will walk you through how to use and add an adapter

## How to use it

If you just want to use the WebRTC player with any of the included adapters the HTML and Javascript below gives you an example.

```html
<html>
  <head><script type="module" src="index.ts"></script></head>
  <body>
    <video autoplay muted controls>
    <button id="play">Play</button>
  </body>
</html>
```

And the Javascript (typescript) code.

```javascript
import { WebRTCPlayer, ListAvailableAdapters } from "@eyevinn/webrtc-player";

const video = document.querySelector("video");
const iceServers = [{ urls: "stun:stun.l.google.com:19302" }];
document.querySelector<HTMLButtonElement>("#play").addEventListener("click", async () => {
  const channelUrl = "<media-server-url-to-channel>";
  const player = new WebRTCPlayer({ 
    video: video,
    type: "se.eyevinn.webrtc",
    iceServers: iceServers 
  });
  await player.load(new URL(channelUrl));
});
```

The string `se.eyevinn.webrtc` specifies to use the included adapter for an [Eyevinn WebRTC WHIP-broadcaster](https://github.com/Eyevinn/whip). To obtain a list of available adapters you can do the following.

```javascript
ListAvailableAdapters().forEach(adapterType => {
  console.log(adapterType);
}
```

## Develop a custom adapter

If you want to use this WebRTC player but with a media server of your own you can develop a custom adapter and use that one instead. When you create an instance of the player you provide an `AdapterFactoryFunction` that will return a new instance of an adapter extending the base class `BaseAdapter`.

```javascript
const player = new WebRTCPlayer({
  video: video, 
  type: "custom", 
  adapterFactory: (peer: RTCPeerConnection, channelUrl: URL) => {
    return new CustomAdapter(peer, channelUrl);
  }
});
```
Set `type === "custom"` and the factory function in the constructor. A template for a custom adapter below.

```javascript
import { BaseAdapter } from "@eyevinn/webrtc-player";

class CustomAdapter extends BaseAdapter {
  constructor(peer: RTCPeerConnection, channelUrl: URL) {
    super(peer, channelUrl);
  }

  // Overload SDP exchange method
  async exchangeSdp() {
    // do stuff here
  }
}
```

The `exchangeSdp()` function is where you implement the media server specific way of exchanging SDPs. For example:

```javascript
async exchangeSdp() {
  const response = await fetch(this.channelUrl.href, {
    method: "POST",
    headers: {
      "Content-Type": "application/json"
    },
    body: JSON.stringify({ sdp: this.localPeer.localDescription.sdp })
  });
  if (response.ok) {
    const { sdp } = await response.json();
    this.localPeer.setRemoteDescription({ type: "answer", sdp: sdp });
  }
}
```

In the example above the local SDP is sent with an HTTP POST to the media server which in return responds with the remote SDP.

## Contributing

If you want to include support for your media server or want to contribute in general we are very happy for a pull request to [this project](https://github.com/Eyevinn/webrtc-player). To add an adapter to the project you first create the adapter in the same way as described above but also include it in the repository in the `adapters` folder. Then add your unique string to the list of adapters in `adapters/factory.ts` together with a factory function.

## Related open source projects

- [Client and Server modules for WebRTC HTTP Ingestion Protocol](https://github.com/Eyevinn/whip)
- [Super simple Webcast application based on WHIP](https://github.com/Eyevinn/webcast)

## About Eyevinn Technology
Eyevinn Technology is an independent consultant firm specialized in video and streaming. Independent in a way that we are not commercially tied to any platform or technology vendor.

At Eyevinn, every software developer consultant has a dedicated budget reserved for open source development and contribution to the open source community. This give us room for innovation, team building and personal competence development. And also gives us as a company a way to contribute back to the open source community.

Want to know more about Eyevinn and how it is to work here. Contact us at work@eyevinn.se!