fix: avoid unbounded HTTP throttling and make buffer readout opt-in
This commit is contained in:
@@ -90,6 +90,7 @@ struct GateState {
|
||||
bool entered, released;
|
||||
uint64_t waiting, requests, ranges, bytes, total;
|
||||
double audioEnd, videoEnd;
|
||||
uint64_t connections;
|
||||
};
|
||||
|
||||
static GateState gateState(const std::string &base, const std::string &token) {
|
||||
@@ -98,7 +99,8 @@ static GateState gateState(const std::string &base, const std::string &token) {
|
||||
[state[@"waiting"] unsignedLongLongValue], [state[@"requests"] unsignedLongLongValue],
|
||||
[state[@"ranges"] unsignedLongLongValue], [state[@"bytes"] unsignedLongLongValue],
|
||||
[state[@"total"] unsignedLongLongValue],
|
||||
[state[@"prefix_ends"][@"audio"] doubleValue], [state[@"prefix_ends"][@"video"] doubleValue]};
|
||||
[state[@"prefix_ends"][@"audio"] doubleValue], [state[@"prefix_ends"][@"video"] doubleValue],
|
||||
[state[@"connections"] unsignedLongLongValue]};
|
||||
}
|
||||
|
||||
static void stillBlocked(const std::string &base, const std::string &token) {
|
||||
@@ -369,6 +371,43 @@ static void cachedWindow(Player *player, const std::string &base) {
|
||||
std::puts("PASS sliding cache: minute read-ahead/thirty-second history/keyframe replay/cached bidirectional seeks/miss refill/cancel");
|
||||
}
|
||||
|
||||
static void chunkedFetch(Player *player, const std::string &base, bool split) {
|
||||
const std::string videoToken = split ? "chunked-video" : "chunked-combined";
|
||||
const std::string audioToken = "chunked-audio";
|
||||
auto path = [](const std::string &token) {
|
||||
return "/chunked-media/" + token + "/cache.mp4?after_seconds=2";
|
||||
};
|
||||
require(openNetwork(player, base, path(videoToken) + (split ? "" : "&keep_alive=1"),
|
||||
split ? path(audioToken) : "", 120),
|
||||
"open recorded media whose CDN throttles oversized requests");
|
||||
require(player_play(player), "start playback while the minute reserve fills");
|
||||
until(player, [&](auto s) {
|
||||
const auto video = gateState(base, videoToken);
|
||||
const bool throttled = video.entered || (split && gateState(base, audioToken).entered);
|
||||
require(!(s.position > .5 && s.buffering && throttled),
|
||||
"oversized HTTP request starves playback before a minute can buffer");
|
||||
return s.status == PLAYER_PLAYING && buffered(s) && s.buffered_end - s.position >= 59;
|
||||
}, "finite HTTP requests build a minute of playable reserve without CDN throttling");
|
||||
require(gateState(base, videoToken).requests >= 2,
|
||||
"minute read-ahead continues across the first bounded HTTP response");
|
||||
if (split) require(gateState(base, audioToken).requests >= 2,
|
||||
"secondary audio also continues bounded HTTP requests");
|
||||
else require(gateState(base, videoToken).connections == 1,
|
||||
"minute reserve reuses one HTTP connection across bounded responses");
|
||||
require(player_pause(player), "pause before an out-of-cache seek");
|
||||
until(player, [](auto s) { return s.status == PLAYER_PAUSED; }, "pause chunked source");
|
||||
seekCompleted(player, 119.5, PLAYER_PAUSED);
|
||||
require(player_play(player), "resume the final partial HTTP range");
|
||||
until(player, [](auto s) { return s.status == PLAYER_ENDED; }, "bounded final response reaches true media EOF");
|
||||
require(!gateState(base, videoToken).entered &&
|
||||
(!split || !gateState(base, audioToken).entered),
|
||||
"initial fetch, continuation and seek never use CDN-throttled ranges");
|
||||
require(player_close(player), "close chunked source");
|
||||
closed(player);
|
||||
std::puts(split ? "PASS bounded split fetch: minute reserve/secondary audio/range continuation/seek/EOF"
|
||||
: "PASS bounded combined fetch: minute reserve/range continuation/seek/EOF");
|
||||
}
|
||||
|
||||
static void bufferCoverage(Player *player, const std::string &base) {
|
||||
|
||||
const std::string token = "buffer-release";
|
||||
@@ -436,12 +475,12 @@ static void cancellation(Player *player, const std::string &base, const std::str
|
||||
}
|
||||
seekCompleted(player, 28, (splitAudio || splitVideo) ? PLAYER_PLAYING : PLAYER_PAUSED);
|
||||
stillBlocked(base, token);
|
||||
auto afterSeek = gateState(base, token);
|
||||
require(afterSeek.requests > 1 && afterSeek.ranges > 1, "seek replaces stalled read with an actual new byte-range request");
|
||||
require(player_play(player), "resume at seek target with stale read still blocked");
|
||||
until(player, [](auto s) {
|
||||
return s.status == PLAYER_PLAYING && !s.seek_pending && s.position > 28.1 && buffered(s);
|
||||
}, "retained coverage follows new stalled-read seek target");
|
||||
until(player, [&](auto s) {
|
||||
auto afterSeek = gateState(base, token);
|
||||
return s.status == PLAYER_PLAYING && !s.seek_pending && s.position > 28.1 && buffered(s) &&
|
||||
afterSeek.requests > 1 && afterSeek.ranges > 1;
|
||||
}, "new byte-range refill reaches stalled-read seek target");
|
||||
stillBlocked(base, token);
|
||||
require(player_pause(player), "pause high-timestamp coverage before backward seek");
|
||||
until(player, [](auto s) { return s.status == PLAYER_PAUSED && buffered(s); },
|
||||
@@ -534,6 +573,8 @@ int main(int argc, char **argv) {
|
||||
styleMask:NSWindowStyleMaskBorderless backing:NSBackingStoreBuffered defer:NO];
|
||||
window.contentView = view;
|
||||
[window orderBack:nil];
|
||||
chunkedFetch(player.get(), base, false);
|
||||
chunkedFetch(player.get(), base, true);
|
||||
transport(player.get(), view, base, false);
|
||||
transport(player.get(), view, base, true);
|
||||
frameSteps(player.get(), view, base);
|
||||
|
||||
Reference in New Issue
Block a user