fix(stream): let congestion control back off below the bitrate target - #116
Draft
szdziedzic wants to merge 1 commit into
Draft
szdziedzic wants to merge 1 commit into
szdziedzic wants to merge 1 commit into
Conversation
WebRTCBitratePolicy pinned the estimator's minimum at 90% of target, so on a path that could not carry ~2.7 Mbps the sender kept overshooting: standing queues, loss, recovery keyframes, and freezes instead of a quality dip. It also blocks raising the ceiling — with a 90% floor, a higher target forces a higher floor onto constrained links. The minimum is now 10% of target with a 250 kbps absolute floor, clamped to the target for tiny configs. The starting estimate stays at the target (applyBitrateSettings already passes the maximum as the current estimate), so ramp-up on healthy paths is unchanged. Measured on loopback (3 Mbps target, 60 fps, idle -> 8 s scroll burst -> idle): old policy held 2810 kbps and decayed onto its 2700 floor; new policy held flat at 2813 kbps through all three phases with fps at 59-60 — the estimate does not sag toward the lower floor on a clean path, it only gains room to shrink when the path is genuinely constrained. With the floor fixed, deployments can raise --video-bitrate (e.g. 3M -> 6M) to double the bits available to scrolls and transitions without risking overshoot freezes on bad networks. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
WebRTCBitratePolicysets the bandwidth estimator's minimum to 10% of the target with a 250 kbps absolute floor, clamped to the target for tiny configs. The maximum and the starting estimate stay at the target.Why
The minimum was pinned at 90% of target. On a path that could not carry that rate the sender kept overshooting: standing queues, loss, recovery keyframes and freezes instead of a quality dip. The pin also made raising
--video-bitraterisky, because a higher target forced a higher floor onto constrained links.How
WebRTCBitratePolicy:minimumBitsPerSecond = min(target, max(250_000, target / 10)).applyBitrateSettingsalready passes the maximum as the current estimate. Measured on loopback (3 Mbps target, 60 fps, idle → 8 s scroll → idle): the estimate held flat at 2813 kbps through all three phases; it only gains room to shrink when the path is genuinely constrained.Part of the tap-to-photon stack (bottom → top): latency probe → send frames on arrival → 240 Hz seed poll → bitrate floor → playout window → data-channel input → lossy moves lane. Everything is on by default, so a package built from any layer works with the unchanged EAS launch flags (
--transport webrtc --webrtc-codec vp8 --max-dimension 960 --video-bitrate 6000000 --video-fps 60).Measured with
scripts/latency-probe(M5 Pro, loopback, iPhone 17 / iOS 26.5, 25 taps): 0.1.51 p50 32–37 ms / p95 ≈ 43 ms → top of stack p50 27 ms / p95 38 ms, no outliers above 40 ms.🤖 Generated with Claude Code