feat: retain streaming media and add opt-in automatic quality
This commit is contained in:
@@ -0,0 +1,145 @@
|
||||
import Foundation
|
||||
|
||||
@main
|
||||
struct AutomaticQualityChecks {
|
||||
static func require(_ condition: @autoclosure () -> Bool, _ message: String) {
|
||||
if !condition() { fatalError("FAIL: " + message) }
|
||||
}
|
||||
|
||||
static func sample(at now: Double, ahead: Double? = 30, height: Int? = 720) -> AutomaticQuality.Sample {
|
||||
.init(position: 100 + now, duration: 1000, ahead: ahead, height: height,
|
||||
heights: [240, 480, 720, 1080, 2160])
|
||||
}
|
||||
|
||||
static func main() {
|
||||
var policy = AutomaticQuality()
|
||||
for second in 0..<6 {
|
||||
require(policy.choose(sample(at: Double(second), ahead: 3), at: Double(second)) == nil,
|
||||
"brief low reserve does not change quality")
|
||||
}
|
||||
require(policy.choose(sample(at: 6, ahead: 3), at: 6) == 480, "persistent low reserve steps down one rung")
|
||||
policy.resetHealth() // Automatic reopen resets evidence without losing its cooldown.
|
||||
for second in 7..<36 {
|
||||
require(policy.choose(sample(at: Double(second), ahead: 3, height: 480), at: Double(second)) == nil,
|
||||
"automatic reopen preserves the 30-second switch cooldown")
|
||||
}
|
||||
require(policy.choose(sample(at: 36, ahead: 3, height: 480), at: 36) == 240,
|
||||
"continued shortage may downshift after cooldown")
|
||||
|
||||
policy = AutomaticQuality()
|
||||
for second in 0..<60 {
|
||||
require(policy.choose(sample(at: Double(second), height: 240), at: Double(second)) == nil,
|
||||
"upshift waits for sustained healthy recorded playback")
|
||||
}
|
||||
require(policy.choose(sample(at: 60, height: 240), at: 60) == 480,
|
||||
"healthy playback increases exactly one rung")
|
||||
for second in 61..<90 {
|
||||
require(policy.choose(sample(at: Double(second), ahead: 1, height: 480), at: Double(second)) == nil,
|
||||
"a sudden shortage cannot immediately reverse an upshift")
|
||||
}
|
||||
require(policy.choose(sample(at: 90, ahead: 1, height: 480), at: 90) == 240,
|
||||
"downshift becomes possible after upshift cooldown")
|
||||
|
||||
policy = AutomaticQuality()
|
||||
for second in 0...5 {
|
||||
require(policy.choose(sample(at: Double(second), ahead: 3), at: Double(second)) == nil, "low reserve warmup")
|
||||
}
|
||||
require(policy.choose(sample(at: 6, ahead: 8), at: 6) == nil, "middle reserve clears low-water evidence")
|
||||
for second in 7..<13 {
|
||||
require(policy.choose(sample(at: Double(second), ahead: 3), at: Double(second)) == nil,
|
||||
"hysteresis requires a fresh continuous shortage")
|
||||
}
|
||||
require(policy.choose(sample(at: 13, ahead: 3), at: 13) == 480, "new shortage has its own threshold")
|
||||
policy = AutomaticQuality()
|
||||
for second in 0...90 {
|
||||
require(policy.choose(sample(at: Double(second), ahead: 19, height: 480), at: Double(second)) == nil,
|
||||
"middle reserve is not healthy enough for an upshift")
|
||||
}
|
||||
|
||||
for suppression in ["disabled", "paused", "seeking", "opening", "local", "missing height", "empty heights", "EOF"] {
|
||||
policy = AutomaticQuality()
|
||||
for second in 0...150 {
|
||||
let now = Double(second)
|
||||
var input = sample(at: now, ahead: 1)
|
||||
switch suppression {
|
||||
case "disabled": input.enabled = false
|
||||
case "paused": input.playing = false
|
||||
case "seeking": input.seeking = true
|
||||
case "opening": input.opening = true
|
||||
case "local": input.network = false
|
||||
case "missing height": input.height = nil
|
||||
case "empty heights": input.heights = []
|
||||
default: input.duration = input.position + 14
|
||||
}
|
||||
require(policy.choose(input, at: now) == nil, suppression + " suppresses quality switching")
|
||||
}
|
||||
}
|
||||
for interruption in ["pause", "seek", "open"] {
|
||||
policy = AutomaticQuality()
|
||||
require(policy.choose(sample(at: 0), at: 0) == nil, "start healthy evidence")
|
||||
var input = sample(at: 59)
|
||||
input.playing = interruption != "pause"
|
||||
input.seeking = interruption == "seek"
|
||||
input.opening = interruption == "open"
|
||||
require(policy.choose(input, at: 59) == nil, "interrupt healthy evidence")
|
||||
require(policy.choose(sample(at: 60), at: 60) == nil, "resume starts fresh health")
|
||||
require(policy.choose(sample(at: 119), at: 119) == nil, "no pre-interruption health is reused")
|
||||
require(policy.choose(sample(at: 120), at: 120) == 1080, "fresh uninterrupted health can upshift")
|
||||
}
|
||||
|
||||
policy = AutomaticQuality()
|
||||
var sparse = sample(at: 0, ahead: 1)
|
||||
sparse.heights = [2160, 1080, 480, 144, 480, 0, -1]
|
||||
require(policy.choose(sparse, at: 0) == nil, "sparse heights warmup")
|
||||
sparse.position += 6
|
||||
require(policy.choose(sparse, at: 6) == 480, "nearest lower available rung handles sparse heights")
|
||||
policy = AutomaticQuality()
|
||||
sparse.height = 2160
|
||||
require(policy.choose(sparse, at: 0) == 1080, "enabling auto caps an existing high-resolution selection")
|
||||
policy = AutomaticQuality()
|
||||
for second in 0...150 {
|
||||
require(policy.choose(sample(at: Double(second), height: 1080), at: Double(second)) == nil,
|
||||
"automatic upshift never exceeds 1080p")
|
||||
}
|
||||
|
||||
for live in [false, true] {
|
||||
policy = AutomaticQuality()
|
||||
for second in 0..<120 {
|
||||
let now = Double(second)
|
||||
var input = sample(at: now, ahead: live ? 0 : nil, height: 480)
|
||||
input.live = live
|
||||
input.duration = 0
|
||||
require(policy.choose(input, at: now) == nil, "live/unknown reserve needs 120 seconds of stability")
|
||||
}
|
||||
var input = sample(at: 120, ahead: live ? 0 : nil, height: 480)
|
||||
input.live = live
|
||||
input.duration = 0
|
||||
require(policy.choose(input, at: 120) == 720, "live/unknown reserve can cautiously upshift")
|
||||
policy = AutomaticQuality()
|
||||
input.buffering = true
|
||||
for second in 0..<3 {
|
||||
require(policy.choose(input, at: Double(second)) == nil, "brief live/unknown stall is tolerated")
|
||||
}
|
||||
require(policy.choose(input, at: 3) == 240, "persistent live/unknown stall downshifts")
|
||||
}
|
||||
policy = AutomaticQuality()
|
||||
let frozen = sample(at: 0, ahead: nil)
|
||||
for second in 0..<6 {
|
||||
require(policy.choose(frozen, at: Double(second)) == nil, "position stall first waits for progress then hysteresis")
|
||||
}
|
||||
require(policy.choose(frozen, at: 6) == 480, "lack of playback progress also detects a stall")
|
||||
|
||||
policy = AutomaticQuality()
|
||||
require(policy.choose(sample(at: 0, ahead: 1), at: 0) == nil, "failure scenario warmup")
|
||||
require(policy.choose(sample(at: 6, ahead: 1), at: 6) == 480, "failure scenario requests a switch")
|
||||
policy.extractionFailed(at: 7)
|
||||
policy.resetHealth()
|
||||
for second in 8..<127 {
|
||||
require(policy.choose(sample(at: Double(second), ahead: 1), at: Double(second)) == nil,
|
||||
"extraction failure preserves old height and backs off for 120 seconds")
|
||||
}
|
||||
require(policy.choose(sample(at: 127, ahead: 1), at: 127) == 480,
|
||||
"failure backoff permits a later attempt, not a retry every poll")
|
||||
print("Automatic quality checks passed")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user