Workflow Tools

How to run a low-cost viewer latency lab with raspberry pi relays and cloud encoders to measure real-world delay

How to run a low-cost viewer latency lab with raspberry pi relays and cloud encoders to measure real-world delay

I wanted a repeatable, low-cost way to measure real-world viewer latency across different streaming stacks, so I built a little lab that uses Raspberry Pi-controlled relays to simulate viewer interactions and cloud encoders to generate test streams. The goal was simple: measure the end-to-end delay a real viewer experiences when watching a live stream served from a variety of CDNs and encoders, using hardware that’s cheap and easy to reproduce.

Why build a latency lab like this?

Most latency claims from vendors come with caveats — controlled networks, single-path delivery, or custom players. But creators care about the real experience: how long between a live moment on stage and the moment a typical viewer on Wi‑Fi sees it. I wanted measurements that reflect real conditions: cloud encoder variability, CDN hops, player buffer behavior, and last‑mile differences. And I wanted it to be inexpensive so teams without deep infra budgets could validate changes in their stack.

Core idea and components

The lab uses two simple principles:

  • Generate a known live signal in the cloud (a timestamped frame or visual change).
  • Detect when that change reaches a distributed set of viewers simulated by Raspberry Pis that log arrival time.
  • Key components I used:

  • Raspberry Pi 4 (x3–5): inexpensive, runs a headless browser or a minimal HTML5 player, and can control GPIO pins.
  • USB webcams or HDMI capture dongles: for local verification when needed.
  • Relay boards (8-channel): controlled via GPIO to simulate a "viewer click" or to generate an external time-sync pulse.
  • Cloud encoder: I used Amazon EC2 with FFmpeg and a short‑latency streaming setup; alternatives include AWS Elemental MediaLive, Wowza, or a simple NGINX-RTMP + SRT source.
  • CDNs / streaming endpoints: a mixture — e.g., Cloudfront, BunnyCDN, Mux, and a custom S3/CloudFront origin.
  • Time server: NTP or Chrony for good time sync across Pis and cloud instances.
  • Simple visual test signal: a timestamped image or a colored full-frame flash generated every N seconds.
  • Cost outline: each Raspberry Pi ~£35–£60, relay board £8–£15, cloud instance small (~£0.01–£0.05/hr for spot/t3.micro), plus CDN egress as applicable. The whole setup can be built for under £200 if you reuse hardware.

    How the test signal works

    I needed a deterministic visual event the Pi "viewers" could detect. My approach:

  • Encode a 1080p (or 720p) H.264 stream with a full-frame color flash (e.g., frame turns pure white for one frame) every 10 seconds, alongside a burned-in UTC timestamp.
  • Also expose an audio beep aligned to the visual flash for audio-only checks.
  • Generating the visual: I scripted FFmpeg on a cloud VM to overlay the current UTC timestamp on the video and to insert a color flash on a specific frame interval. For example, I have a 1‑second region where a specific pixel changes color for a single frame — easy to detect by sampling pixel RGB in the player.

    Viewer simulation with Raspberry Pi relays

    Each Raspberry Pi runs a lightweight Chromium in kiosk mode pointed to the stream player (HLS or DASH). A small Node.js/Python script captures the video element’s canvas at a high frequency (e.g., 15–30 fps) and checks a known pixel coordinate for the flash or compares the timestamp text using OCR (Tesseract) if you prefer software detection.

    When the Pi detects the flash, it logs the timestamp and toggles a relay. The relay toggle is important because it creates an external, hardware-level event you can tie back into other measuring systems — for example, a logic analyzer, a second Pi that acts as a collector, or a USB DAQ device that timestamps changes with higher precision. I also keep a local CSV log (UTC time) of detection events.

    Time synchronization and accuracy

    Accurate latency needs good time sync. I made sure every Raspberry Pi and the cloud encoder VM synchronize using NTP/Chrony to the same stratum‑1 or 2 servers. On the cloud side, I set up a small script to write the encoder’s frame creation timestamp into the video frame (burned-in timestamp) so the detection doesn’t rely solely on network time. That provides a secondary verification: comparing burned-in timestamp to detection timestamp gives you a measure of system clock offsets.

    Typical accuracy I achieved: software detection on Pi gives ±33 ms at 30 fps sampling; with relay + external timestamp capture I could push sub-10 ms consistency for the detection event timestamp, depending on the external hardware.

    Measurement workflow

    Step-by-step:

  • Start the cloud encoder generating a test stream with periodic flashes and burned-in timestamps.
  • Publish that stream to target CDNs / endpoints you want to test.
  • Spin up Raspberry Pi "viewers" in different networks — on wired LAN, on Wi‑Fi, a 4G hotspot, or even through a VPN to emulate geographic distance.
  • Each Pi runs the detection script and toggles its relay when the flash is seen; it writes a local log and sends periodic summaries to a central server.
  • Collect logs centrally and compute viewer latency as: detection_time_utc - burned_in_frame_time_utc. Also compute jitter (std dev), packet loss correlates, and median/90th/99th percentiles.
  • What to measure and why it matters

    Metrics I track:

  • Median latency: what most viewers experience.
  • P90 / P99 latency: tail latency — important for synchronous features like chat or polls.
  • Startup time: from player request to first frame.
  • Jitter: variability between flashes across viewers.
  • Correlation with buffer size: run tests with different player buffer target settings (e.g., HTML5 video buffer, low-latency HLS with chunked encoding) to see trade-offs.
  • These numbers let you answer practical questions: does switching to a low-latency HLS reduce median delay by 2 seconds? Does CDN X have worse P99 than CDN Y for European viewers? Are player buffer caps causing more delay than CDN propagation?

    Common pitfalls and my troubleshooting notes

  • Clock drift: If your Pi clocks are off, you’ll get nonsense. Enforce strict NTP sync and keep burned-in timestamps as a fallback.
  • Browser throttling: Headless or background Chromium on Pi can drop frame rates. Run in kiosk/fullscreen and disable power-saving features.
  • Player-level buffering: Many players pre-buffer segments; experiment with low-latency modes and forced small buffer durations.
  • Network variance: Wi‑Fi conditions vary — run many repeats and use multiple network types to get robust statistics.
  • Codec buffering: Some encoders add internal latency (lookahead, B‑frames). Document encoder settings (gop, keyframe interval, tune) and run controlled swaps.
  • Variants and extensions

    Once you have the basic lab, you can expand:

  • Use mobile phones as additional viewer nodes (Android with Termux or a small app that detects visual flashes).
  • Add audio-only tests by detecting audio beeps with a microphone and a tiny spectral detector on Pi.
  • Test real player SDKs (Roku, Smart TV) by connecting Pi-based capture devices or using vendor emulators.
  • Simulate viewer actions beyond detection: after a flash, script the Pi to post an API call (e.g., chat message) and measure round-trip latency to the application server.
  • Data analysis and what I usually report

    I collect CSV logs and visualize them in Grafana and simple Python notebooks. Key plots I produce:

  • Latency CDFs (to show median vs tails).
  • Time-series of latency across a long stream (to spot drift or outages).
  • Boxplots by network type / CDN / region.
  • Practical outcomes from my experiments: small encoder setting changes (keyframe interval reduction, lower GOP) and switching to chunked low-latency HLS shaved 1.2–2.5 seconds off median for European viewers in one test. However, some CDN endpoints showed worse P99 latency — indicating edge routing variability — which only showed up because I ran tests from multiple last-mile networks.

    If you want, I can share the scripts I use for the FFmpeg generator, the Raspberry Pi detection agent, and a sample dashboard configuration. I’ve iterated this setup to be resilient and reproducible — it’s the kind of lab that helps you make confident, measurable choices about encoder settings, CDN selection, and player configurations without breaking the bank.

    You should also check the following news:

    How to build an automated clip triage pipeline with whisper and ffmpeg that surfaces monetizable moments
    Workflow Tools

    How to build an automated clip triage pipeline with whisper and ffmpeg that surfaces monetizable moments

    I want to walk you through how I built a lean, automated clip triage pipeline that uses OpenAI...

    Sep 09 Read more...
    How to build a repeatable sponsor delivery tracker using airtable and zapier that proves ROI in 30 days
    Workflow Tools

    How to build a repeatable sponsor delivery tracker using airtable and zapier that proves ROI in 30 days

    When I work with creators or small teams who rely on sponsorships, one of the most common problems...

    Aug 15 Read more...