Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,9 +1,22 @@
public struct WebRTCBitratePolicy: Equatable, Sendable {
/// Below this the stream is not worth keeping alive at any resolution.
public static let absoluteMinimumBitsPerSecond = 250_000

public let minimumBitsPerSecond: Int
public let maximumBitsPerSecond: Int

/// The estimator starts at the target (the owner passes the maximum as the
/// starting estimate), so ramp-up stays instant on healthy paths. The
/// minimum only bounds how far congestion control may back off: at 10% of
/// target it can shrink into what a constrained path actually carries.
/// The previous minimum was 90% of target, which pinned the estimator into
/// a band the path sometimes could not carry — sustained overshoot, loss,
/// recovery keyframes, and freezes instead of a quality dip.
public init(targetBitsPerSecond: Int) {
minimumBitsPerSecond = targetBitsPerSecond * 9 / 10
minimumBitsPerSecond = min(
targetBitsPerSecond,
max(Self.absoluteMinimumBitsPerSecond, targetBitsPerSecond / 10)
)
maximumBitsPerSecond = targetBitsPerSecond
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,31 @@ import XCTest
@testable import StreamingPolicy

final class WebRTCBitratePolicyTests: XCTestCase {
func testKeepsAdaptiveBandwidthBetweenNinetyPercentAndConfiguredTarget() {
func testLeavesCongestionControlRoomBelowTheConfiguredTarget() {
let policy = WebRTCBitratePolicy(targetBitsPerSecond: 6_000_000)

XCTAssertEqual(policy.minimumBitsPerSecond, 5_400_000)
// 10% of target: under congestion the estimator may back off to a
// survivable rate instead of overrunning a path that cannot carry
// 90% of target — the old floor turned every real dip into loss,
// recovery keyframes, and freezes.
XCTAssertEqual(policy.minimumBitsPerSecond, 600_000)
XCTAssertEqual(policy.maximumBitsPerSecond, 6_000_000)
}

func testMinimumNeverDropsBelowASurvivableAbsoluteFloor() {
let policy = WebRTCBitratePolicy(targetBitsPerSecond: 1_000_000)

// 10% would be 100 kbps — below what keeps the stream alive.
XCTAssertEqual(policy.minimumBitsPerSecond, 250_000)
XCTAssertEqual(policy.maximumBitsPerSecond, 1_000_000)
}

func testMinimumIsClampedToTinyConfiguredTargets() {
let policy = WebRTCBitratePolicy(targetBitsPerSecond: 100_000)

// The CLI accepts targets down to 100 kbps; the minimum must never
// exceed the maximum.
XCTAssertEqual(policy.minimumBitsPerSecond, 100_000)
XCTAssertEqual(policy.maximumBitsPerSecond, 100_000)
}
}