Eyeson Blog

Custom UI - Connection Information for your Solution

Written by Stefan Benicke | December 22, 2021

You can include a connection quality indicator in your custom UI. This will give your users quick feedback about their current audio/video quality.

In its simplest form, you can use it as a traffic-light display for good / ok / bad connection. We also provide some more details and values, like bitrate, jitter, packet loss, and round-trip time.

You can read more about the data in the help center article.


Initialize Statistics

You have to listen for the statistics_ready event and this event includes the statistics interface with its important method onUpdate.

onUpdate is triggered roughly every 1s and provides an object with all values.

eyeson.onEvent(event => {
    if (event.type === 'statistics_ready') {
        event.statistics.onUpdate(showStatistics);
    }
});

function showStatistics(stats) {
    /*...*/
}

eyeson.start(/*...*/);


Statistics data

The onUpdate function is called with a statistics object containing the following data:

  • status can be “good”, “ok”, “bad”, or even “no connection” (please use Status.GOOD etc.)
  • bitrateSend and bitrateRecv are audio+video bitrates either send or received. Values are in kbps.
  • jitter is the sender’s jitter and it takes the worse value, either audio or video. Values are in s.
  • packetLoss is the sender’s packet loss and it takes the worse value, either audio or video. Values are between 0 and 1.0.
  • roundTripTime is the sender’s round-trip time and it takes the worse value, either audio or video. Values are in s.
  • nack is the sender’s video negative-acknowledge count rate. Values are between 0 and 1.0.

Heads-up: In some cases values are null. There are browsers where not all parameters are available.

Good to know: All values are the average over the last 3 entries (roughly the last 3 seconds) to avoid high-value jumps.


Quality status

As the simplest approach, you might want to show only the connection quality status as a traffic-light indicator.

The ConnectionStatistics interface provides a Status object with named keys for the stats.status value.

Currently, it contains 4 named keys: GOODOKBAD, and NO_CONNECTION.

import { ConnectionStatistics } from 'eyeson';

const { Status } = ConnectionStatistics;

const connectionInfo = {};
connectionInfo[Status.GOOD] = { title: 'Excellent', className: 'good' };
connectionInfo[Status.OK] = { title: 'Unstable', className: 'ok' };
connectionInfo[Status.BAD] = { title: 'Poor', className: 'bad' };
connectionInfo[Status.NO_CONNECTION] = { title: 'No connection', className: 'none' };

function showStatistics(stats) {
    indicator.className = connectionInfo[stats.status].className;
    indicator.textContent = connectionInfo[stats.status].title;
}


Full example

The following example will show all available data and update automatically.

import { ConnectionStatistics } from 'eyeson';

const { Status } = ConnectionStatistics;
const numberFormat = new Intl.NumberFormat('en', { maximumFractionDigits: 0 });
const percentFormat = new Intl.NumberFormat('en', { style: 'percent', maximumFractionDigits: 0 });

const connectionInfo = {};
connectionInfo[Status.GOOD] = { title: 'Excellent', className: 'good' };
connectionInfo[Status.OK] = { title: 'Unstable', className: 'ok' };
connectionInfo[Status.BAD] = { title: 'Poor', className: 'bad' };
connectionInfo[Status.NO_CONNECTION] = { title: 'No connection', className: 'none' };

const showStatistics = stats => {
    statisticsIcon.className = connectionInfo[stats.status].className;
    outputStatus.textContent = connectionInfo[stats.status].title;
    outputBitrateSend.textContent = numberFormat.format(stats.bitrateSend) + ' kbps';
    outputBitrateReceive.textContent = numberFormat.format(stats.bitrateRecv) + ' kbps';
    if (stats.jitter !== null) {
        outputJitter.textContent = numberFormat.format(stats.jitter * 1000) + ' ms';
    }
    if (stats.packetLoss !== null) {
        outputPacketLoss.textContent = percentFormat.format(stats.packetLoss);
    }
    if (stats.roundTripTime !== null) {
        outputRoundTripTime.textContent = numberFormat.format(stats.roundTripTime * 1000) + ' ms';
    }
    if (stats.nack !== null) {
        outputNack.textContent = percentFormat.format(stats.nack);
    }
};

eyeson.onEvent(event => {
    if (event.type === 'statistics_ready') {
        event.statistics.onUpdate(showStatistics);
    }
});

eyeson.start(/*...*/);


More info

Whenever eyeson.destroy() is called and the session ends, the instance of the statistics will automatically shut down. So the onUpdate event is not triggered anymore.