refactor: make the real-time policy layer testable (extract ABR + timeline normalizer) - #33
Merged
Merged
Conversation
…eline normalizer) Completes the StreamBroadcast-policy extraction for issue #21. The prior #20 work already moved the reconnect backoff, VideoFrameAdmission ratios, per-path bitrate ceiling, and the watchdog >-vs->= frozen-queue rule into StreamCore with tests; this lands the remaining two pieces so the whole real-time policy layer is @testable-import-able (CI-run) instead of resting on manual device testing. - StreamCore/AdaptiveBitRateState.swift (NEW): the adaptive-bitrate decision math — minimum floor, effectiveMaximum (path + thermal), insufficient-BW reduction (65% headroom / halve-on-zero), status stall halving + healthy accounting, the 30-tick upward probe (step + cap), per-path interface seeding (lastGoodTarget / 0.6x), thermal-ceiling clamp, frameInterval, and currentFrameRate — as a pure Sendable value type with mutating handlers returning an ABRDecision. Copied character-for-character (truncation + floor-division preserved); frameInterval10/30 redefined bit-identically to HaishinKit's. BroadcastAdaptiveBitRateController becomes a thin actor that unwraps the HaishinKit event, delegates, and applies once (−241 net lines). - StreamCore/MediaTimelineNormalizer.swift (NEW): MediaTimelineNormalizer + MediaTimelineKind moved out of the app target (pure CoreMedia, made public), with a pure static rebasedOffset() so the gap-accumulation math is testable with plain CMTime. +25 StreamCore tests (19 ABR reduction/probe/floor/seed/thermal/frameInterval cases + 6 normalizer rebase cases) — 124 total, all passing. Adversarially reviewed for byte-for-byte behavior preservation: no encoder-behavior change (a stall-vs-recovered log-line divergence the review caught is fixed here). Full app builds against the iOS 27 SDK. fixes #21 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.
Completes the
StreamBroadcast-policy extraction for #21. The prior #20 work already moved the reconnect backoff,VideoFrameAdmissionratios, per-path bitrate ceiling, and the watchdog>-vs->=frozen-queue rule into StreamCore with tests; this lands the remaining two pieces, so the whole real-time policy layer is now unit-tested on CI instead of resting on manual device testing.What moved
StreamCore/AdaptiveBitRateState.swift(new) — the adaptive-bitrate decision math as a pureSendable/Equatablevalue type with mutating handlers returning anABRDecision(shouldApply, severe): minimum floor,effectiveMaximum(path + thermal), insufficient-BW reduction (65% headroom / halve-on-zero), status stall-halving + healthy accounting, the 30-tick upward probe (step + cap), per-path interface seeding (lastGoodTarget/0.6×), thermal-ceiling clamp,frameInterval,currentFrameRate. Copied character-for-character (truncation + floor-division preserved);frameInterval10/30redefined bit-identically to HaishinKit's.BroadcastAdaptiveBitRateControllerbecomes a thin actor that unwraps the HaishinKit event, delegates, and applies once (−241 net lines).StreamCore/MediaTimelineNormalizer.swift(new) —MediaTimelineNormalizer+MediaTimelineKindmoved out of the app target (pure CoreMedia, madepublic), with a pure staticrebasedOffset()so the gap-accumulation math is testable with plainCMTime.Tests
+25 StreamCore tests (19 ABR: floor, effectiveMaximum, reduction bytesOut>0/==0/floor, stall halving, probe steps + cap, stall/probe mutual-exclusivity, path-seed + interface switch + raised-probe-restart + thermal-cap-not-defeated, thermal clamp, capture-paused no-apply, reset, frameInterval severe/congestion/thermal, currentFrameRate; + 6 normalizer rebase/passthrough) — 124 total, all passing.
Behavior preservation
Adversarially reviewed vs
main: no encoder-behavior change (state-fidelity + apply-equivalence dimensions found no functional divergence). The.statustwo-apply sites collapse into oneABRDecision— proven mutually exclusive per tick, so the singlesetVideoSettingsis byte-exact. One log-only divergence the review caught (a stall apply logging "recovered") is fixed here. Both publishers' call sites are unchanged.Full app builds against the iOS 27 SDK.
fixes #21
🤖 Generated with Claude Code