Bitflix — Project Summary for the Bitmovin Team
A one-day, end-to-end OTT streaming demo built by Stefan + Claude Code on 2026-04-26, exercising the full Bitmovin stack (Encoding, Player, Analytics, AI Scene Analysis, Stream Lab, Live Encoder) on AWS — with two goals:
- Build something that actually works, end-to-end, without cutting corners.
- Document every place the Bitmovin product gets in its own way for an autonomous coding agent or first-time developer.
The output is a working demo at https://bitflix.slederer.com/ plus 37 detailed product/DX recommendations at https://bitflix.slederer.com/web/feedback.html.
GitHub: https://github.com/slederer/OS-streaming-v2
What got built
Live demo: https://bitflix.slederer.com/
| Surface | What | Stack |
|---|---|---|
| Catalog | 10 open-source films (Blender CC-BY + Public Domain) with hover-to-preview clips, posters built from the trick-play sprites | Static HTML/JS on CloudFront |
| Per-title VOD | H.264 + AAC, HLS + DASH, 3-rung Per-Title bitrate ladders, trick-play sprite + WebVTT, Bitmovin AI scenes.json with named characters/objects/dialogue, heuristic ad-marker overlays | Bitmovin Encoding Templates → S3 → CloudFront |
| Player | Bitmovin Player Web SDK v8 with chapter markers (white ticks) + ad-marker overlays (red ticks), Netflix-red theme over bmpui-* classes, custom Bitflix wordmark |
Bitmovin Player + Analytics, custom CSS |
| Live (button-launched) | "▶ Start 10-min Live Session" provisions a fresh Bitmovin Live Encoding, ffmpeg pushes Big Buck Bunny on loop for 10 min, auto-cleanup, viewable on the same player | API Gateway → Lambda → Bitmovin Live + ffmpeg in Lambda |
| Stream Lab QA | 20 countries × Chrome/Linux real device-matrix tests, 11/11 passing per country | Bitmovin Stream Lab targets API |
| Geo perf test | 50 countries × real Chromium via residential proxies, time-to-playing 3.7-13.7s spread | browser-use Cloud (wss://connect.browser-use.com?proxyCountryCode=<cc>) |
| Feedback page | recommendations.md auto-rendered to /web/feedback.html on every deploy |
Python markdown + custom Bitflix theme |
Numbers
- 10 titles ingested + per-title-encoded in ~5 min wall-clock (parallel Bitmovin VOD encoding)
- 9/10 titles got rich Bitmovin AI Scene Analysis automatically (the implicit-by-account behavior is excellent — but undocumented, see #25)
- 20-country Stream Lab run: 11/11 success per country, mean startup 2,248 ms, but all from one Klagenfurt cluster — the "VPN location" knob is egress-only
- 50-country browser-use Cloud run: 48/50 success, median 6,730 ms, range 3,758 ms (Sweden) → 13,672 ms (NZ) — ~100× the variance of the Stream Lab run, because real residential proxies expose actual CDN-edge routing
- Live encoding: 5-min and 30-min sessions both ran end-to-end, with 20 manifest samples returning 200 OK, TTFB 333-623 ms
- End-to-end build time: ~10 hours total across 5 working sessions
- Recurring AWS cost: ≈ $1-5/mo (S3 storage + CloudFront + Lambda idle = ~$0)
- Bitmovin spend on this build: ~$15 encoding + ~$5 live + ~$2 Stream Lab tests = ~$22 total
Architecture (one diagram)
┌───────────────────────────────────────────────────────────────┐
│ Bitflix demo │
└───────────────────────────────────────────────────────────────┘
ingest CLI Encoding Templates
archive.org ──────▶ S3 input ─────▶ Bitmovin Encoding ──▶ S3 output ─▶ CloudFront ─▶ bitflix.slederer.com
│ Per-Title │
│ Sprites │ Cloudflare DNS
│ AI Scene Analysis (implicit) │
▼ │
Default-Manifest API │
│ │
▼ ▼
master.m3u8 + stream.mpd ┌────────────────┐
│ Browser │
│ Bitmovin │
│ Player + UI │
│ Analytics SDK │
└────────────────┘
"▶ Start 10-min Live"
Click ──▶ API Gateway ─▶ live-start Lambda (returns URL <30s)
│ async
▼
live-push Lambda (≤15min, ffmpeg layer)
│
├── boto3 GetObject (S3 input → /tmp)
└── ffmpeg → RTMP → Bitmovin Live Encoder
│
▼
same S3 → CloudFront → Player
QA / Verification
├── pytest (unit tests, 14)
├── streaming verify (curl smoke)
├── streaming streamlab-test (Bitmovin Stream Lab, 20 countries × Chrome)
├── streaming geo-browser (browser-use Cloud, 50 countries × real Chromium)
└── streaming browsertest (local Playwright E2E for player+analytics)
What was hard, and why it matters
This was the CEO of Bitmovin building a demo on Bitmovin. We hit 37 distinct friction items, almost none of which were missing features. They were:
- Surface-area mismatches —
playerCfg.ui = truesilently breaks the UI but the player "loads";segment_{number}.m4sis treated as a literal filename in templates;gbrejected forukin browser-use - Discoverability gaps — Stream Lab device-matrix testing requires a 4-segment URL path (
/streams/<sid>/targets/<stid>/jobs) that an LLM agent will not guess; I wrongly concluded the API didn't exist before being corrected - Doc/SDK lag — AI Scene Analysis is heavily marketed but absent from
bitmovin-api-sdk-python 1.264.0; the JSON schema for Encoding Templates lives in a different repo from the docs page;bitmovin-api-sdkversions ≤1.234 pinrequests<2.25.0(incompatible with anything modern) - Dashboard ↔ API divergence — multi-license Analytics dashboards need explicit license selection (we had two, picked the wrong one, "no impressions" panic ensued); Stream Lab
notifiedis a terminal status the API uses but the docs don't list; testresults exposedurationMsper test but nosummary.metrics.{startupTime,avgBitrate}rollup
The full list, with reproductions, error messages, and one-line fixes per item, is at https://bitflix.slederer.com/web/feedback.html (also recommendations.md at the repo root).
Top 5 fixes that would 10× the developer/agent experience
Ranked by adoption impact:
playerCfg.uishould rejecttrueat construction. Silent UI failures are unforgivable. (Item #17)POST /encoding/templates/validatedry-run endpoint. 5 of my 7 wasted encoding cycles would not have happened. (Item original-1)- Multi-license Analytics dashboard default = "All licenses" + SDK
is_defaultflag. Eliminates the "no data showing" support tickets entirely. (Item #18) - Make AI Scene Analysis explicit. Today it runs implicitly at the account level when enabled, with no opt-out and no lifecycle messages. Surface it. (Items #25, #26)
- Structured-error responses with
path+expected+suggestion. "An unexpected error occurred — please contact support" is the worst possible thing to return to an automated client. (Item original-2)
Insights worth product-team discussion
| Topic | What we learned |
|---|---|
| Per-Title encoding | Excellent. Drop in mode: PER_TITLE_TEMPLATE + start.perTitle.h264Configuration.autoRepresentations and you get sensible bitrate ladders with no manual tuning. Showcase feature. |
| AI Scene Analysis | Output quality (when it runs) is genuinely impressive — character names, object categories, dialogue, scene descriptions. 9 of 10 titles got real AI output without me asking for it (account-level implicit). Failure for 1 title (Spring) traced to source bitrate (~800 kbps for 1146×480 — Bitmovin AI seems to need a quality floor). |
| Stream Lab — what it is | World-class for device-matrix QA: 31 real device targets including LG WebOS 2016-2024, Samsung Tizen 2016-2024, Vizio, Xbox Series S, PlayStation 5. Nothing else in the market touches this. |
| Stream Lab — what it isn't | A geographic CDN-performance benchmarker. The "VPN location" knob simulates egress only; the Chromium runner stays in eu:klu:b2. Across 20 countries, startup-time spread was 140 ms — ~100× compressed vs reality. We added a comparison table to recommendations.md (item #36). |
| Live Encoder | Provisioning + RTMP push + manifest serving + cleanup all worked end-to-end. The live.get(encoding_id) returns errorCode 2023 ("not available!") for 60-180s after live.start() — that 2-3 min provisioning gap is the single thing that forces a multi-Lambda async pattern when wiring up a browser-callable trigger. A wait_for_rtmp_ready(timeout) SDK helper would make this a 1-call setup. |
| Player + Analytics | Integrated in ~10 lines of JS. The ui: true silent UI break (item #17) is the only sharp edge. Worth fixing — automated tests can pass while the UI is fully broken. |
What this build proves about Bitmovin's market position
The product is genuinely capable. None of our 37 items are missing-feature gaps; every one is polish. That's a strong position to start from — the alternative would be missing capabilities, which is much harder to fix.
The specific differentiator that emerged: Bitmovin's per-title encoding output quality + sprite trick-play + AI Scene Analysis output quality, taken together, are visibly better than the Mux / AWS Elemental MediaConvert / JW Player stacks. We didn't have to manually tune anything to get a presentable per-title ladder, sensible scene chapters with dialogue summaries, and clean trick-play scrubbing.
The specific gap that emerged: there's no "single canonical worked example" anywhere in the product surface. The closest is bitmovin-api-sdk-examples, but it's imperative SDK Python — the wrong audience for someone authoring an Encoding Template YAML. A "starter pack" repo with one canonical YAML per common workflow (per-title VOD, sprites, AI, multi-DRM, live) would dramatically reduce the time-to-first-success that this build is, in a sense, a demonstration of.
What's next (suggestions)
If anyone on the team wants to extend this:
- Switch CloudFront to
PriceClass_Alland re-run the 50-country browser-use test. Should collapse the APAC/AF tail (currently 10-14s) to ~5s. Free win on Bitflix's side; great content for "your CDN edge selection matters" demos. - Add multi-DRM: Widevine + FairPlay + PlayReady via the Bitmovin Encoding's DRM block + DoveRunner. Then re-run Stream Lab against the smart-TV / console targets — that's where Stream Lab is uniquely strong.
- Add MediaTailor SSAI: insert real ads at the AI-detected ad-marker positions in the player.
- Schedule a one-time agent in 4 weeks to re-run the same 37-item probe against any fixes that ship, and diff the friction list. (
/scheduleis set up for this.)
Reading order for the team
- This document.
- https://bitflix.slederer.com/ — see it work. Click "▶ Start 10-min Live Session".
- https://bitflix.slederer.com/web/feedback.html — the 37 items. Skim P0/P1, skip P2 unless triaging.
- https://github.com/slederer/OS-streaming-v2 — the code.
templates/per_title.yaml.j2andinfra/lambda/live_push/handler.pyare the most "this is how you actually do this" parts.
— Stefan Lederer, 2026-04-26.