DEV Community

Ankush Banyal
Ankush Banyal

Posted on

Your Live Stream Is Playing. Is It Still Live?

Your Live Stream Is Playing. Is It Still Live?

You unplug the encoder. The video player still shows a sharp image. The red LIVE badge is still on. And nobody knows they’ve been looking at the same frame for the last 30 seconds.

That’s a bug worth testing before you ship a live streaming app. A player can look healthy when it has stopped receiving useful video. And the opposite can happen: the camera is publishing just fine, but one viewer’s browser has stalled.

If you’re building a WebRTC player with Ant Media Server, here’s a practical way to test both cases and decide what your UI should tell the viewer.

Start with a clock in the camera frame

Open a stopwatch on your phone and point a camera at it. Publish that camera feed to your test server and open the viewer in two browsers. If you can, put the viewers on different networks.

The stopwatch makes a frozen frame obvious. It also helps you spot a second failure: video that keeps moving but falls further behind the source. A shot of an empty room can hide both problems.

Before you break anything, write down what you expect viewers to see:

What you do What the viewer should see
Start the stream A loading state until video begins arriving
Disconnect one viewer That viewer sees a reconnecting message. The other keeps watching.
Stop the publisher Both viewers eventually get a clear message about the feed.
Restart the publisher The player either recovers or offers a clear way to retry.

Run one fault at a time. If you stop the publisher and disconnect a viewer together, the result won’t tell you which part failed.

Check whether the browser is presenting new frames

A playing event is useful when playback starts. It doesn’t keep telling you whether new frames are arriving. For that, browsers that support requestVideoFrameCallback can report when the video element submits a frame for composition.

Here’s a small monitor you can attach to the <video> element your player uses:

function watchVideoFrames(video, onState, { stallMs = 3000 } = {}) {
  if (typeof video.requestVideoFrameCallback !== "function") {
    onState("unknown");
    return () => {};
  }

  let lastFrameAt = null;
  let previousState;
  let callbackId;

  function report(state) {
    if (state === previousState) return;
    previousState = state;
    onState(state);
  }

  function onFrame(now) {
    lastFrameAt = now;
    callbackId = video.requestVideoFrameCallback(onFrame);
  }

  callbackId = video.requestVideoFrameCallback(onFrame);

  const timer = setInterval(() => {
    if (document.hidden || video.paused || video.seeking) {
      report("unknown");
      return;
    }

    if (lastFrameAt === null) {
      report("waiting");
      return;
    }

    const gap = performance.now() - lastFrameAt;
    report(gap >= stallMs ? "stalled" : "receiving-frames");
  }, 500);

  return () => {
    clearInterval(timer);
    video.cancelVideoFrameCallback(callbackId);
  };
}
Enter fullscreen mode Exit fullscreen mode

Attach the monitor when you start watching. Call the returned cleanup function when the player closes or switches streams. Give the waiting state its own startup deadline so a stream that never starts doesn’t leave the viewer looking at a spinner forever.

The three-second window is a starting point for testing. Tune it for your feed and the interruptions your viewers can tolerate. A low frame rate camera needs different expectations from a sports broadcast.

Give the viewer an honest status

The frame monitor can tell you that this browser has stopped presenting new frames. It can’t tell you whether the publisher went offline. It also can’t detect every upstream freeze: an encoder could keep sending fresh frames that all contain the same frozen picture.

For a useful status badge, combine the browser signal with publisher status from your backend:

  • Receiving video: The browser is presenting new frames. This alone doesn’t tell you how far behind the live source the viewer is.
  • Reconnecting: This viewer stopped receiving frames and the player is trying to recover.
  • Feed offline: Your backend confirms that the source is no longer publishing.
  • Playback unavailable: The viewer can’t start or recover, and the cause is still unclear. Offer a retry.

Keep publisher status checks on your backend if they need privileged credentials. Send the player just the status it needs.

Run the test that changes how you debug

With both viewers open, disable the network for one of them. If the other viewer’s stopwatch keeps moving, you’ve shown that the publisher and delivery path still work for at least one viewer. Start debugging the affected browser or network.

Now restore that viewer and stop the publisher. Compare what both players show and how long it takes them to update. If they sit on the last frame with a LIVE badge, you’ve found a bug a normal playback test would miss.

For each run, record three things: the time to the first visible frame, the time from failure to a useful message, and the time until moving video returns. Those measurements tell you where to work next. A stream that recovers quickly but leaves the viewer guessing needs a different fix from one that takes too long to reconnect.

The test passes when the viewer can tell what happened without opening DevTools.

Top comments (0)