feat: retain streaming media and add opt-in automatic quality
This commit is contained in:
+47
-22
@@ -33,8 +33,8 @@ static void validBuffer(PlayerState state) {
|
||||
require(std::isfinite(state.duration) && state.duration > 0 &&
|
||||
state.buffered_start >= 0 && buffered(state) && state.buffered_end <= state.duration,
|
||||
"retained range is nonempty and bounded by finite media duration");
|
||||
require(std::abs(state.buffered_start - state.position) < .00001,
|
||||
"retained range follows the authoritative playback position, not a loaded prefix");
|
||||
require(state.buffered_start <= state.position && state.position <= state.buffered_end,
|
||||
"the retained interval covers the current playhead");
|
||||
}
|
||||
|
||||
// Only these deadlines use elapsed time: a stuck command/read must fail the check,
|
||||
@@ -330,20 +330,46 @@ static void prefixBound(PlayerState state, GateState gate, bool splitAudio = fal
|
||||
"retained coverage cannot bridge withheld audio/video packets or reordered B-frames");
|
||||
}
|
||||
|
||||
static void bufferCoverage(Player *player, const std::string &base) {
|
||||
require(openNetwork(player, base, "/media/streaming.mp4", "", 30),
|
||||
"open long network fixture for bounded retained coverage");
|
||||
auto initial = until(player, [](auto s) { return s.status == PLAYER_PAUSED && buffered(s); },
|
||||
"paused network source exposes actual retained media");
|
||||
require(initial.buffered_end < initial.duration,
|
||||
"bounded queues do not report an entire downloaded movie as retained");
|
||||
require(player_play(player), "play to advance retained network coverage");
|
||||
until(player, [=](auto s) {
|
||||
return s.status == PLAYER_PLAYING && s.position > initial.buffered_end && buffered(s) &&
|
||||
s.buffered_end > initial.buffered_end;
|
||||
}, "retained horizon advances with real playback and refills");
|
||||
require(player_close(player), "close advancing buffered source");
|
||||
static void cachedWindow(Player *player, const std::string &base) {
|
||||
const std::string token = "cached-window";
|
||||
require(openNetwork(player, base, "/stall/" + token + "/cache.mp4?after_seconds=100", "", 120),
|
||||
"open a two-minute source with a controlled future network stall");
|
||||
auto initial = until(player, [](auto s) {
|
||||
return s.status == PLAYER_PAUSED && buffered(s) && s.buffered_end >= 59;
|
||||
}, "paused playback prefetches a minute without waiting to start");
|
||||
require(initial.buffered_end < 65, "read-ahead is time-bounded rather than a full download");
|
||||
seekCompleted(player, 50, PLAYER_PAUSED);
|
||||
auto retained = until(player, [&](auto s) {
|
||||
return s.status == PLAYER_PAUSED && buffered(s) && s.buffered_end > 95 &&
|
||||
gateState(base, token).entered;
|
||||
}, "cache follows a new playhead until the future network gate");
|
||||
require(retained.position - retained.buffered_start >= 29,
|
||||
"a reusable thirty-second rewind window survives forward playback");
|
||||
require(retained.buffered_start > 15, "old media is evicted instead of caching the whole movie");
|
||||
const auto before = gateState(base, token);
|
||||
auto rewind = seekCompleted(player, 20.5, PLAYER_PAUSED);
|
||||
require(buffered(rewind), "cached rewind preserves the retained interval");
|
||||
auto after = gateState(base, token);
|
||||
require(after.requests == before.requests && after.ranges == before.ranges,
|
||||
"rewinding within history makes no new HTTP request");
|
||||
stillBlocked(base, token);
|
||||
seekCompleted(player, 45, PLAYER_PAUSED);
|
||||
after = gateState(base, token);
|
||||
require(after.requests == before.requests && after.ranges == before.ranges,
|
||||
"forward seeking inside retained media makes no new HTTP request");
|
||||
stillBlocked(base, token);
|
||||
seekCompleted(player, 5, PLAYER_PAUSED);
|
||||
until(player, [&](auto s) {
|
||||
return s.status == PLAYER_PAUSED && buffered(s) && s.buffered_end > 6 &&
|
||||
gateState(base, token).requests > before.requests;
|
||||
}, "seeking before retained history refills missing media over HTTP");
|
||||
require(player_close(player), "close cache while the original future response remains blocked");
|
||||
closed(player);
|
||||
stillBlocked(base, token);
|
||||
std::puts("PASS sliding cache: minute read-ahead/thirty-second history/keyframe replay/cached bidirectional seeks/miss refill/cancel");
|
||||
}
|
||||
|
||||
static void bufferCoverage(Player *player, const std::string &base) {
|
||||
|
||||
const std::string token = "buffer-release";
|
||||
require(openNetwork(player, base, "/stall/" + token + "/streaming.mp4", "", 30),
|
||||
@@ -373,7 +399,7 @@ static void bufferCoverage(Player *player, const std::string &base) {
|
||||
}, "released server gate advances retained coverage beyond withheld timestamps");
|
||||
require(player_close(player), "close released buffered source");
|
||||
closed(player);
|
||||
std::puts("PASS retained coverage: bounded queues/advancing clock/stalled timestamp bounds/B-frame dependencies/gate-release refill");
|
||||
std::puts("PASS retained coverage: supplied timestamp bounds/B-frame dependencies/gate-release refill");
|
||||
}
|
||||
|
||||
static void blockedPlayback(Player *player, const std::string &base, const std::string &token,
|
||||
@@ -421,19 +447,17 @@ static void cancellation(Player *player, const std::string &base, const std::str
|
||||
until(player, [](auto s) { return s.status == PLAYER_PAUSED && buffered(s); },
|
||||
"retain high-timestamp media before invalidating it");
|
||||
seekCompleted(player, .5, PLAYER_PAUSED);
|
||||
auto rewound = until(player, [](auto s) {
|
||||
until(player, [](auto s) {
|
||||
return s.status == PLAYER_PAUSED && !s.seek_pending && buffered(s);
|
||||
}, "backward seek rebuilds retained coverage at the new target");
|
||||
require(rewound.buffered_end < 28,
|
||||
"backward seek drops the old high-water horizon instead of retaining distant media");
|
||||
stillBlocked(base, token);
|
||||
require(player_close(player), "close resumed formerly stalled source");
|
||||
closed(player);
|
||||
std::puts(splitAudio
|
||||
? "PASS stalled secondary audio: required-track bound/seek invalidation/new-target coverage/backward high-water clearing"
|
||||
? "PASS stalled secondary audio: required-track bound/seek acknowledgement/new-target coverage/backward refill"
|
||||
: splitVideo
|
||||
? "PASS stalled primary video: required-track bound/seek invalidation/new-target coverage/backward high-water clearing"
|
||||
: "PASS stalled combined input: bounded prefix/pause/seek invalidation/new-target coverage/backward high-water clearing");
|
||||
? "PASS stalled primary video: required-track bound/seek acknowledgement/new-target coverage/backward refill"
|
||||
: "PASS stalled combined input: bounded prefix/pause/seek acknowledgement/new-target coverage/backward refill");
|
||||
}
|
||||
|
||||
blockedOpening(player, base, "opening-close");
|
||||
@@ -515,6 +539,7 @@ int main(int argc, char **argv) {
|
||||
frameSteps(player.get(), view, base);
|
||||
nativeHLS(player.get(), view, base);
|
||||
liveAndReplacement(player.get(), base, fixtures);
|
||||
cachedWindow(player.get(), base);
|
||||
bufferCoverage(player.get(), base);
|
||||
cancellation(player.get(), base, fixtures);
|
||||
failures(player.get(), base);
|
||||
|
||||
Reference in New Issue
Block a user