Flussonic Media Server Documentation

Contents

Using WebRTC for Video Playback from Flussonic Media Server

WebRTC

WebRTC is a P2P protocol of communication between two clients over an already established connection. For example, to communicate with each other by WebRTC, two browsers need to be connected by opening the same website in the Internet. Connection can also be established by means of a mediator, so called signaling server.

So there are two clients and a signaling server that connects these clients. Before starting to transmit video data, the clients need to establish the connection. To do so, they exchange data of two types about the connection:

  • Textual descriptions of media streams in the SDP format
  • ICE Candidates as part of an SDP

The signaling server (the mediator) makes it possible to transfer the data about the connection from one client to the other.

About playback via WebRTC from Flussonic

Flussonic Media Server uses WebRTC for playback a media stream from Flussonic (the source) to a client device or app (the recipient). Flussonic also acts as the signaling server during connection establishment to exchange data about the connection.

Why do we use WebRTC to send media data between clients? Because with the WebRTC mechanism we can provide ultra-low latency.

Therefore, the exchange of video via Flussonic cannot be called peer-to-peer; rather, we call it video publication to Flussonic Media Server via WebRTC and video playback via WebRTC.

The diagram shows the process of initiating the connection between Flussonic and a client device, for playback:

WebRTC Playback

Parties should exchange SDPs via the mediator (signaling server - Flussonic), and then start the direct data transfer. In the case of video playback, it's the Flussonic server (video source) that initiates the process and sends an SDP offer.

Connection is established via WebSocket, and then video is transferred via RTP.

How to organize the playback of pubished streams via WebRTC

On the Flussonic server, a published stream must be configured where clients can publish video and from where we will take it for playback.

stream STREAMNAME {
  url publish://;
}

The code must be run on the client side that plays video from the published stream. To write the code, use the Flussonic WebRTC player library.

The description of the library classes and the example code can be found at npm.

Installing the library components via NPM and webpack

To import our library to your project with webpack, download the package:

npm install --save @flussonic/flussonic-webrtc-player

Then import components to your application:

import {
  PUBLISHER_EVENTS,
  PLAYER_EVENTS,
  Player,
  Publisher,
} from "@flussonic/flussonic-webrtc-player";

The description of the library classes can be found at npm.

See also the demo application.

Installing the library components without NPM and webpack

Add this line to the script section of your HTML page:

<script src="https://cdn.jsdelivr.net/npm/@flussonic/flussonic-webrtc-player/dist/index.min.js"></script>

The example of a webpage containing the player code is below.

Player examples — with Webpack and without Webpack

Our demo application that uses Webpack to import components:

Demo WebRTC player on JavaScript that obtains components via <script>:

  • The Flussonic WebRTC player library code for implementing the WebRTC player is available in the CDN https://www.jsdelivr.com, and you can import it to your web page. To do this, add the following line to the script section of your HTML file <script src="https://cdn.jsdelivr.net/npm/@flussonic/flussonic-webrtc-player/dist/index.min.js"></script>.

The example of a page with the player in JavaScript (the similar code is included in the demo application):

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8"/>
    <meta name="apple-mobile-web-app-capable" content="yes" />
        <style>
      .app {
        display: flex;
        flex-direction: column;
        justify-content: space-between;
        height: calc(100vh - 16px);
      }
      .container {
        margin-bottom: 32px;
      }
      .video-container {
        display: flex;
      }
      .controls {
      }
      .config {
      }
      #player {
        width: 640px; height: 480px; border-radius: 1px
      }
      .button {
        height: 20px;
        width: 96px;
      }
    </style>
    <script src="https://cdn.jsdelivr.net/npm/@flussonic/flussonic-webrtc-player/dist/index.min.js"></script>
  </head>
  <body>
    <div class="app">
      <div class="video-container">
        <video
                id="player"
                controls
                muted
                autoplay
                playsinline
        >
        </video>
        <pre id="debug"></pre>
      </div>

    <div class="container">
      <div class="config" id="config">
        <span id="hostContainer">
          <label for="host">Host: </label><input name="host" id="host" value="" />
        </span>
        <span id="nameContainer">
          <label for="name">Stream: </label><input name="name" id="name" value="" />
        </span>
      </div>
      <div class="controls" id="controls">
        <select id="quality">
          <option value="4:3:240">4:3 320x240</option>
          <option value="4:3:360">4:3 480x360</option>
          <option value="4:3:480">4:3 640x480</option>
          <option value="16:9:360" selected>16:9 640x360</option>
          <option value="16:9:540">16:9 960x540</option>
          <option value="16:9:720">16:9 1280x720 HD</option>
        </select>
        <button id="publish" class="button">Publish</button>
        <button id="play" class="button">Play</button>
        <button id="stop" class="button">Stop all</button>
      </div>
      <div class="errorMessageContainer" id="errorMessageContainer"></div>
    </div>
  </body>
  <script>
    let wrtcPlayer = null;
    let publisher = null;

    const { Player, Publisher, PUBLISHER_EVENTS, PLAYER_EVENTS } = this.FlussonicWebRTC; 

    const getHostElement = () => document.getElementById('host');
    const getHostContainerElement = () => document.getElementById('hostContainer');
    const getNameElement = () => document.getElementById('name');
    const getNameContainerElement = () => document.getElementById('nameContainer');
    const getPlayerElement = () => document.getElementById('player');
    const getPlayElement = () => document.getElementById('play');
    const getPublishElement = () => document.getElementById('publish');
    const getStopElement = () => document.getElementById('stop');
    const getQualityElement = () => document.getElementById('stop');

    const getStreamUrl = (
      hostElement = getHostElement(),
      nameElement = getNameElement(),
    ) =>
      `${hostElement && hostElement.value}/${nameElement && nameElement.value}`;
    const getPublisherOpts = () => {
      const [, , height] = document.getElementById('quality').value.split(/:/);
      return {
        preview: document.getElementById('preview'),
        constraints: {
          // video: {
          //   height: { exact: height }
          // },
          video: true,
          audio: true,
        },
      };
    };

    const getPlayer = (
      playerElement = getPlayerElement(),
      streamUrl = getStreamUrl(),
      playerOpts = {
        retryMax: 10,
        retryDelay: 1000,
      },
      shouldLog = true,
      log = (...defaultMessages) => (...passedMessages) =>
        console.log(...[...defaultMessages, ...passedMessages]),
    ) => {
      const player = new Player(playerElement, streamUrl, playerOpts, true);
      player.on(PLAYER_EVENTS.PLAY, log('Started playing', streamUrl));
      player.on(PLAYER_EVENTS.DEBUG, log('Debugging play'));
      return player;
    };

    const stopPublishing = () => {
      if (publisher) {
        publisher.stop && publisher.stop();
        publisher = null;
      }
    };

    const stopPlaying = () => {
      if (wrtcPlayer) {
        wrtcPlayer.destroy && wrtcPlayer.destroy();
        wrtcPlayer = null;
      }
    };

    const stop = () => {
      stopPublishing();
      stopPlaying();

      getPublishElement().innerText = 'Publish';
      getPlayElement().innerText = 'Play';
    };

    const play = () => {
      wrtcPlayer = getPlayer();
      getPlayElement().innerText = 'Playing...';
      wrtcPlayer.play();
    };

    const publish = () => {
      if (publisher) publisher.stop();

      publisher = new Publisher(getStreamUrl(), getPublisherOpts(), true);
      publisher.on(PUBLISHER_EVENTS.STREAMING, () => {
        getPublishElement().innerText = 'Publishing...';
      });
      publisher.start();
    };

    const setDefaultValues = () => {
        getHostElement().value = config.host;
        getNameElement().value = config.name;
    };

    const setEventListeners = () => {
      // Set event listeners
      getPublishElement().addEventListener('click', publish);
      getPlayElement().addEventListener('click', play);
      getStopElement().addEventListener('click', stop);
      getQualityElement().onchange = publish;
    };

    const main = () => {
      setDefaultValues();
      setEventListeners();
    };

    window.addEventListener('load', main);
  </script>
</html>


Copy this code to a file, for example index.html, and open in the browser to check how the player works.