perf: switch to Low-Latency HLS for ~1-3s live stream latency
Build and Push Container / build (push) Has been cancelled
Build and Push Container / build (push) Has been cancelled
Previously mediamtx used standard MPEG-TS HLS (2s segments, 3-segment
playlist window) and hls.js was configured with lowLatencyMode:false.
The default liveSyncDurationCount:3 and liveMaxLatencyDurationCount:10
allowed 20+ second drift before catch-up triggered.
Changes:
- mediamtx: hlsVariant lowLatency — generates 200ms parts inside 2s
segments; LL-HLS clients receive each part as it completes instead of
waiting for the full segment. hlsSegmentCount raised to 7 for playlist
compatibility with non-LL-HLS clients.
- hls.js: lowLatencyMode:true enables part fetching and blocking playlist
requests (_HLS_msn/_HLS_part); maxBufferLength:4 prevents buffering far
ahead; liveSyncDurationCount:1 / liveMaxLatencyDurationCount:3 keep the
player within ~2-4s of the live edge.
- Express schedule gate: endsWith('.m3u8') → includes('.m3u8') so blocking
playlist requests (index.m3u8?_HLS_msn=N&_HLS_part=N) are also gated.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -51,7 +51,14 @@ export default function VideoPlayer({ src, onOffline }: Props) {
|
||||
};
|
||||
|
||||
if (Hls.isSupported()) {
|
||||
const hls = new Hls({ enableWorker: true, lowLatencyMode: false });
|
||||
const hls = new Hls({
|
||||
enableWorker: true,
|
||||
lowLatencyMode: true, // use LL-HLS parts + blocking playlist requests
|
||||
backBufferLength: 30, // keep 30s of playback history (seeking)
|
||||
maxBufferLength: 4, // don't buffer more than 4s ahead of playback
|
||||
liveSyncDurationCount: 1, // target 1 segment from the live edge
|
||||
liveMaxLatencyDurationCount: 3, // catch up if drift exceeds 3 segments
|
||||
});
|
||||
hlsRef.current = hls;
|
||||
|
||||
hls.loadSource(src);
|
||||
|
||||
@@ -28,9 +28,10 @@ rtspAddress: 127.0.0.1:8554
|
||||
hls: yes
|
||||
hlsAddress: :8888
|
||||
hlsAlwaysRemux: yes
|
||||
hlsVariant: mpegts
|
||||
hlsVariant: lowLatency
|
||||
hlsSegmentCount: 7
|
||||
hlsSegmentDuration: 2s
|
||||
hlsSegmentCount: 3
|
||||
hlsPartDuration: 200ms
|
||||
hlsAllowOrigins: ['*']
|
||||
|
||||
# Authenticate every publish action via Express
|
||||
|
||||
+3
-2
@@ -22,9 +22,10 @@ srtAddress: :7000
|
||||
hls: yes
|
||||
hlsAddress: :8888
|
||||
hlsAlwaysRemux: yes
|
||||
hlsVariant: mpegts
|
||||
hlsVariant: lowLatency
|
||||
hlsSegmentCount: 7
|
||||
hlsSegmentDuration: 2s
|
||||
hlsSegmentCount: 3
|
||||
hlsPartDuration: 200ms
|
||||
hlsAllowOrigins: ['*']
|
||||
|
||||
# Authenticate every publish action via Express
|
||||
|
||||
+1
-1
@@ -67,7 +67,7 @@ async function main() {
|
||||
// the early-start window (host connected, but scheduled window not open yet).
|
||||
// Only check .m3u8 playlist requests — segment requests are already gated
|
||||
// by the playlist not being delivered.
|
||||
if (streamKey && req.url.endsWith('.m3u8')) {
|
||||
if (streamKey && req.url.includes('.m3u8')) {
|
||||
try {
|
||||
const stream = await getStreamByKey(streamKey);
|
||||
if (stream && !await isWithinSchedule(stream.id)) {
|
||||
|
||||
Reference in New Issue
Block a user