every.channel/evolution/proposals/ECP-0117-live-fragment-duration-and-audio-unlock.md
every.channel bd5d9857ed
Some checks are pending
ci-gates / checks (push) Waiting to run
deploy-cloudflare / checks (push) Waiting to run
deploy-cloudflare / deploy (push) Blocked by required conditions
Stabilize hosted live video playback
2026-05-03 22:38:39 -07:00

3.6 KiB

ECP-0117: Live Fragment Duration and Audio Unlock

Status: Draft

Problem / context

Hosted live playback can subscribe to a local HDHomeRun stream while the visible frame stays frozen and audio stays muted. Browser inspection showed currentTime advancing through keyframe-spaced buffered ranges, but each range was only one microsecond long. The web wrapper also left the <moq-watch muted> attribute in place, so a user gesture could be overwritten back to muted.

Decision

Publish WebTransport fMP4 with keyframe fragments where every emitted video frame is a keyframe (g=1, keyint_min=1). For HDHomeRun-style live input, cap the default WebTransport transcode to 6 fps so the hosted watcher receives independently decodable video groups at a sustainable cadence. Expose publisher knobs for the ffmpeg video filter, GOP interval, and fMP4 movflags so runtime operators can tune without another code edit. Keep wt-publish / nbc-wt-publish on the non-passthrough CMAF sample path. On the hosted web player, render live video through the <moq-watch> canvas/WebCodecs path instead of the <video>/MSE path, remove the muted attribute, and reapply unmuted state to the watcher backend after a user gesture.

Consequences

  • Browser playback receives continuous media ranges without turning each GOP into a single playback jump.
  • Live playback and observation diffing receive independently decodable video groups at frame cadence.
  • WebTransport video publishing uses more bandwidth per frame, but the 6 fps cap keeps group churn lower than full-rate all-intra OTA publishing.
  • Operators can raise or lower --video-filter, --gop-frames, and --movflags from publisher configuration instead of rebuilding.
  • Hosted live rendering avoids the upstream MSE path that side-browser validation showed repeatedly skipping slow groups.
  • Relay subscribers receive video0.m4s and audio0.m4s media groups by default instead of catalog-only passthrough announcements.
  • Audio remains gesture-gated for autoplay policy, but the gesture now actually unmutes the player.

Alternatives considered

  • Raise web jitter again. Rejected because the buffered media ranges were effectively zero-length; more latency does not turn still ranges into playable media.
  • Keep passthrough mode as the default. Rejected because relay probes received only catalog.json while the non-passthrough sample path delivered video and audio media groups.
  • Use keyframe-duration fragments. Rejected after side-browser validation showed repeated seeking forward/backward corrections and GOP-sized visual jumps even though audio was healthy.
  • Keep 48-frame GOPs with every-frame fMP4 fragments. Rejected because relay archive proof still showed 48-frame video groups; moq-mux groups video by keyframe in the non-passthrough path.
  • Use full-rate or 12 fps all-intra every-frame fragments. Rejected because relay proof showed one-frame video groups, but side-browser validation produced heavy skipping slow group churn from too many tiny media groups.
  • Keep the <video> child renderer. Rejected because the hosted side browser showed the MSE renderer repeating frames and emitting slow-group skips while subscribed.

Rollout / teardown

Rebuild/restart local and hosted publishers, deploy the updated web asset, and verify hosted playback by checking canvas frame hashes over time plus side-browser console seek corrections. Teardown is setting --gop-frames 48, setting --video-filter back to the prior source cadence, restoring passthrough defaults to true, and restoring the prior <video> child plus muted wrapper behavior.