openvibers/openvibe.link Commits

stream-viewer: install aiohttp and decode the QR's string; record the production latency (#111)

OP OpenVibers committed 7 days ago · 5b5399eedb · 3 files, +16 −12

The viewer needs aiohttp but the bootstrap did not install it, and OpenCV's detectAndDecode returns the decoded
string first and the points array second, so the old unpacking crashed on a real frame. The 2026-11-06 production
check over WHEP measured p50 148.5 ms, p95 168.5 ms and max 186 ms over 60 s (597 QR frames), with the curtain seen as
Private screen and the stream ended and its ticket revoked.

Changes

ddiff --git a/docs/stream.md b/docs/stream.md
iindex d425f7b..4d03fcc 100644
---- a/docs/stream.md
++++ b/docs/stream.md
@@@ -101,12 +101,15 @@ OPENVIBE_VIEW_TICKET=wvt_… scripts/stream-viewer.sh --whep https://openvibe.li
The probe draws a QR of `time.Now().UnixMilli()` in the corner of every frame with the built-in PCM encoder (no ffmpeg,
no display) and publishes it with the same WHIP path and silent audio. The viewer joins over WHEP, decodes the QR from
each frame with OpenCV, and reports p50/p95/max; the S1 pass line is **p95 under 1000 ms over 60 s**. `--self-test`
--checks the statistics and the decoder without a network.
--
--**The production number is not measured yet.** Live's S1 routes are being built in
--`live-agent-streams-s1`; the viewer needs a WHEP endpoint (the design allows one for third-party players), and Live's
--S1 player speaks mediasoup over `/ws/broadcast`. Once that endpoint exists, run the two commands above and put the p95
--in this file and in the PR.
++checks the statistics and the decoder without a network. `scripts/stream-viewer.sh` installs `aiohttp` with the other
++dependencies, and the viewer reads OpenCV's decoded string (the points are the second return value of
++`detectAndDecode`).
++
++**Production, 2026-11-06:** a private agent stream on openvibe.live, published by the v0.41.0 linux/amd64 binary's
++`stream --probe` from Xvfb `:99` and watched through the WHEP endpoint for 60 s, measured **p50 148.5 ms, p95 168.5 ms,
++max 186 ms** (597 QR frames). The S1 pass line (p95 under 1000 ms) passed. Raising and lowering the curtain during the
++check made the embed show "Private screen" and clear it again; the stream was ended and its publish ticket revoked
++afterwards.
## Tests
ddiff --git a/scripts/stream-viewer.py b/scripts/stream-viewer.py
iindex 66568ab..0dd73d4 100755
---- a/scripts/stream-viewer.py
++++ b/scripts/stream-viewer.py
@@@ -99,11 +99,12 @@ async def run_whep(args):
img = frame.to_ndarray(format="bgr24")
except Exception:
continue
-- ok, decoded, *_ = decoder.detectAndDecode(img)
-- if not ok or not decoded:
++ # detectAndDecode returns (decoded_info, points, straight_qrcode): points is an array, so test the string.
++ decoded, *_ = decoder.detectAndDecode(img)
++ if not decoded:
continue
try:
-- sent = int(decoded.strip())
++ sent = int(str(decoded).strip())
except ValueError:
continue
seen["qr"] += 1
ddiff --git a/scripts/stream-viewer.sh b/scripts/stream-viewer.sh
iindex 466afa9..29ab875 100755
---- a/scripts/stream-viewer.sh
++++ b/scripts/stream-viewer.sh
@@@ -15,9 +15,9 @@ if [ ! -x "$venv/bin/python" ]; then
}
fi
--if ! "$venv/bin/python" -c 'import aiortc, cv2, numpy' >/dev/null 2>&1; then
-- echo "stream-viewer: installing aiortc and OpenCV once into $venv" >&2
-- "$venv/bin/python" -m pip install --quiet --disable-pip-version-check aiortc opencv-python-headless numpy || {
++if ! "$venv/bin/python" -c 'import aiortc, aiohttp, cv2, numpy' >/dev/null 2>&1; then
++ echo "stream-viewer: installing aiortc, aiohttp and OpenCV once into $venv" >&2
++ "$venv/bin/python" -m pip install --quiet --disable-pip-version-check aiortc aiohttp opencv-python-headless numpy || {
echo "stream-viewer: pip install failed (no network?)" >&2
exit 2
}