mirror of
https://github.com/R0m1k3/xtremflow.git
synced 2026-10-11 17:30:00 +02:00
perf: faster stream startup
- Stop sending no-store on vendored player libraries: hls.min.js and mpegts.min.js (~750 KB) were re-downloaded on every player open - hls.js: start with 1 live segment instead of 3, lower buffer-first threshold 1.5s -> 0.8s, poll buffer every 100ms - mpegts.js: initial stash 512KB -> 128KB, start at 0.5s buffered - Live FFmpeg: -hls_init_time 1 closes the first segment after ~1s Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
dcf1c00cca
commit
92634dba13
5 files changed
+25
-13
No files matched your search
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"enabledPlugins": {
|
||||||
|
"frontend-design@claude-plugins-official": true
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -220,8 +220,11 @@ Handler createLiveStreamHandler(
|
|||||||
..._liveVideoArgs(quality, useNvidiaGpu),
|
..._liveVideoArgs(quality, useNvidiaGpu),
|
||||||
..._audioArgs(withFilters: false),
|
..._audioArgs(withFilters: false),
|
||||||
// HLS sliding window: 10 x 2s segments (lower live latency than the
|
// HLS sliding window: 10 x 2s segments (lower live latency than the
|
||||||
// previous 20-segment window while still safe for iOS)
|
// previous 20-segment window while still safe for iOS).
|
||||||
|
// hls_init_time 1: first segment closes after ~1s so playback can
|
||||||
|
// start sooner; subsequent segments use hls_time.
|
||||||
'-f', 'hls',
|
'-f', 'hls',
|
||||||
|
'-hls_init_time', '1',
|
||||||
'-hls_time', '2',
|
'-hls_time', '2',
|
||||||
'-hls_list_size', '10',
|
'-hls_list_size', '10',
|
||||||
'-hls_flags', 'delete_segments+independent_segments',
|
'-hls_flags', 'delete_segments+independent_segments',
|
||||||
|
|||||||
+7
-3
@@ -245,12 +245,16 @@ void main(List<String> args) async {
|
|||||||
FutureOr<Response> staticHandler(Request request) async {
|
FutureOr<Response> staticHandler(Request request) async {
|
||||||
final response = await baseStaticHandler(request);
|
final response = await baseStaticHandler(request);
|
||||||
|
|
||||||
// Disable cache for entry points to ensure updates are seen immediately
|
// Disable cache for entry points to ensure updates are seen immediately.
|
||||||
|
// Vendored player libraries (hls.js/mpegts.js, ~750 KB) are pinned
|
||||||
|
// versions: keep them cacheable or every player open re-downloads them.
|
||||||
final path = request.url.path;
|
final path = request.url.path;
|
||||||
if (path.isEmpty ||
|
final isVendored = path.startsWith('vendor/');
|
||||||
|
if (!isVendored &&
|
||||||
|
(path.isEmpty ||
|
||||||
path == 'index.html' ||
|
path == 'index.html' ||
|
||||||
path.endsWith('.js') ||
|
path.endsWith('.js') ||
|
||||||
path.endsWith('.json')) {
|
path.endsWith('.json'))) {
|
||||||
return response.change(
|
return response.change(
|
||||||
headers: {
|
headers: {
|
||||||
'Cache-Control': 'no-store, no-cache, must-revalidate, max-age=0',
|
'Cache-Control': 'no-store, no-cache, must-revalidate, max-age=0',
|
||||||
|
|||||||
+3
-3
@@ -264,7 +264,7 @@
|
|||||||
nudgeOffset: 0.8, // More aggressive jump over corrupted frames
|
nudgeOffset: 0.8, // More aggressive jump over corrupted frames
|
||||||
nudgeMaxRetries: 30,
|
nudgeMaxRetries: 30,
|
||||||
maxLiveSyncPlaybackRate: 1.1, // Slower catch-up to avoid chaos
|
maxLiveSyncPlaybackRate: 1.1, // Slower catch-up to avoid chaos
|
||||||
initialLiveManifestSize: 3, // Wait for more segments before starting
|
initialLiveManifestSize: 1, // Start as soon as one segment exists
|
||||||
abrEwmaDefaultEstimate: 1000000,
|
abrEwmaDefaultEstimate: 1000000,
|
||||||
manifestLoadingRetryDelay: 1000,
|
manifestLoadingRetryDelay: 1000,
|
||||||
levelLoadingRetryDelay: 1000
|
levelLoadingRetryDelay: 1000
|
||||||
@@ -286,12 +286,12 @@
|
|||||||
const checkBuffer = setInterval(() => {
|
const checkBuffer = setInterval(() => {
|
||||||
if (video.buffered.length > 0) {
|
if (video.buffered.length > 0) {
|
||||||
const bufferEnd = video.buffered.end(0);
|
const bufferEnd = video.buffered.end(0);
|
||||||
if (bufferEnd > 1.5) { // 1.5s buffer-first
|
if (bufferEnd > 0.8) { // Start fast, hls.js keeps buffering
|
||||||
clearInterval(checkBuffer);
|
clearInterval(checkBuffer);
|
||||||
video.play().catch(() => playOverlay.style.display = 'flex');
|
video.play().catch(() => playOverlay.style.display = 'flex');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, 200);
|
}, 100);
|
||||||
});
|
});
|
||||||
hls.on(Hls.Events.LEVEL_LOADED, (e, d) => {
|
hls.on(Hls.Events.LEVEL_LOADED, (e, d) => {
|
||||||
if (d.details && d.details.totalduration > injectedDuration) window.currentHlsDuration = d.details.totalduration;
|
if (d.details && d.details.totalduration > injectedDuration) window.currentHlsDuration = d.details.totalduration;
|
||||||
|
|||||||
@@ -258,7 +258,7 @@
|
|||||||
nudgeOffset: 0.8,
|
nudgeOffset: 0.8,
|
||||||
nudgeMaxRetries: 30,
|
nudgeMaxRetries: 30,
|
||||||
maxLiveSyncPlaybackRate: 1.1,
|
maxLiveSyncPlaybackRate: 1.1,
|
||||||
initialLiveManifestSize: 3,
|
initialLiveManifestSize: 1,
|
||||||
abrEwmaDefaultEstimate: 1000000,
|
abrEwmaDefaultEstimate: 1000000,
|
||||||
manifestLoadingRetryDelay: 1000,
|
manifestLoadingRetryDelay: 1000,
|
||||||
levelLoadingRetryDelay: 1000
|
levelLoadingRetryDelay: 1000
|
||||||
@@ -271,11 +271,11 @@
|
|||||||
hls.on(Hls.Events.MANIFEST_PARSED, () => {
|
hls.on(Hls.Events.MANIFEST_PARSED, () => {
|
||||||
hideLoading();
|
hideLoading();
|
||||||
const checkBuffer = setInterval(() => {
|
const checkBuffer = setInterval(() => {
|
||||||
if (video.buffered.length > 0 && video.buffered.end(0) > 1.5) {
|
if (video.buffered.length > 0 && video.buffered.end(0) > 0.8) {
|
||||||
clearInterval(checkBuffer);
|
clearInterval(checkBuffer);
|
||||||
video.play().catch(() => playOverlay.style.display = 'flex');
|
video.play().catch(() => playOverlay.style.display = 'flex');
|
||||||
}
|
}
|
||||||
}, 200);
|
}, 100);
|
||||||
});
|
});
|
||||||
|
|
||||||
hls.on(Hls.Events.ERROR, (event, data) => {
|
hls.on(Hls.Events.ERROR, (event, data) => {
|
||||||
@@ -302,7 +302,7 @@
|
|||||||
url: url
|
url: url
|
||||||
}, {
|
}, {
|
||||||
enableWorker: true,
|
enableWorker: true,
|
||||||
stashInitialSize: 512 * 1024,
|
stashInitialSize: 128 * 1024,
|
||||||
enableStashBuffer: true,
|
enableStashBuffer: true,
|
||||||
liveBufferLatencyChasing: false,
|
liveBufferLatencyChasing: false,
|
||||||
maxStashSize: 30 * 1024 * 1024
|
maxStashSize: 30 * 1024 * 1024
|
||||||
@@ -312,11 +312,11 @@
|
|||||||
player.load();
|
player.load();
|
||||||
|
|
||||||
const checkBuffer = setInterval(() => {
|
const checkBuffer = setInterval(() => {
|
||||||
if (video.buffered.length > 0 && video.buffered.end(0) > 1.0) {
|
if (video.buffered.length > 0 && video.buffered.end(0) > 0.5) {
|
||||||
clearInterval(checkBuffer);
|
clearInterval(checkBuffer);
|
||||||
player.play().catch(() => playOverlay.style.display = 'flex');
|
player.play().catch(() => playOverlay.style.display = 'flex');
|
||||||
}
|
}
|
||||||
}, 200);
|
}, 100);
|
||||||
|
|
||||||
player.on(mpegts.Events.ERROR, (type, detail, info) => {
|
player.on(mpegts.Events.ERROR, (type, detail, info) => {
|
||||||
log('MpegTS Error: ' + type + ' - ' + detail);
|
log('MpegTS Error: ' + type + ' - ' + detail);
|
||||||
|
|||||||
Reference in new issue
Block a user