如何降低CMAF直播延迟?3秒延迟优化及分块传输问题求助
自建Nginx+FFmpeg实现CMAF直播:延迟优化至1秒+修复分块传输
问题背景
使用OBS推流至自建Nginx服务器,转码后通过CDN分发实现CMAF直播,当前存在两个问题:
- 延迟稳定在3秒,无法降至目标的1秒
- 分块传输未成功实现
当前配置代码
FFmpeg 转码命令
sudo ffmpeg -i rtmp://127.0.0.1:1935/live/stream -loglevel 40 -c copy -sc_threshold 0 -g 60 -bf 0 -map 0 -f dash -strict experimental -use_timeline 1 -use_template 1 -seg_duration 1 -window_size 5 -adaptation_sets "id=0,streams=v id=1,streams=a" -streaming 1 -dash_segment_type mp4 -utc_timing_url "http://time.akamai.com/?iso" -movflags frag_keyframe+empty_moov+default_base_moof -ldash 1 -hls_playlist 1 -master_m3u8_publish_rate 1 -remove_at_exit 1 /var/www/html/live/manifest.mpd
Nginx 配置
server_name myserver.com; add_header Access-Control-Allow-Origin *; add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS'; add_header Access-Control-Allow-Headers 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range'; add_header Access-Control-Expose-Headers 'Content-Length,Content-Range'; root /var/www/html; index index.html index.nginx-debian.html; location / { chunked_transfer_encoding on; }
HTML 播放器代码
<!DOCTYPE html> <html lang="zh-tw"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>streaming test</title> <script src="https://cdn.jsdelivr.net/npm/hls.js@latest"></script> <script src="https://cdn.dashjs.org/latest/dash.all.min.js"></script> <style> body { margin: 0; font-family: Arial, sans-serif; display: flex; justify-content: center; align-items: center; min-height: 100vh; background-color: #000; } #video { max-width: 100%; max-height: 100vh; } </style> </head> <body> <video id="video" controls></video> <script> const video = document.getElementById('video'); const hlsSrc = '/live/master.m3u8'; const dashSrc = '/live/stream.mpd'; function isHlsSupported() { return Hls.isSupported() || video.canPlayType('application/vnd.apple.mpegurl'); } function isDashSupported() { return !!window.MediaSource && !!MediaSource.isTypeSupported('video/mp4; codecs="avc1.4d401e,mp4a.40.2"'); } if (isHlsSupported()) { const hls = new Hls({ lowLatencyMode: true, liveSyncDurationCount: 1, liveMaxLatencyDurationCount: 2, maxBufferLength: 2, maxBufferSize: 1000 * 1000 * 100, liveBackBufferLength: 0 }); hls.loadSource(hlsSrc); hls.attachMedia(video); hls.on(Hls.Events.MANIFEST_PARSED, () => { video.play(); }); } else if (isDashSupported()) { const player = dashjs.MediaPlayer().create(); player.initialize(video, dashSrc, true); player.updateSettings({ streaming: { lowLatencyEnabled: true, liveDelay: 1, liveCatchUpPlaybackRate: 1.2, liveCatchUpMinDrift: 0.5, bufferTimeAtTopQuality: 3, bufferToKeep: 0, } }); } else { console.error('Neither HLS nor DASH playback is supported in this browser.'); } </script> </body> </html>
解决方案:延迟降至1秒+修复分块传输
1. 调整FFmpeg参数(核心优化)
修改后的命令:
sudo ffmpeg -i rtmp://127.0.0.1:1935/live/stream -loglevel 40 -c copy -sc_threshold 0 -g 30 -bf 0 -map 0 -f dash -strict experimental -use_timeline 0 -use_template 1 -seg_duration 1 -frag_duration 200 -window_size 3 -adaptation_sets "id=0,streams=v id=1,streams=a" -streaming 1 -dash_segment_type mp4 -utc_timing_url "http://time.akamai.com/?iso" -movflags frag_keyframe+empty_moov+default_base_moof+faststart -ldash 1 -hls_playlist 1 -master_m3u8_publish_rate 1 -remove_at_exit 1 /var/www/html/live/manifest.mpd
关键修改点:
-g 30:若使用30fps推流,设置关键帧间隔为1秒(与seg_duration匹配),避免等待关键帧产生延迟-use_timeline 0:禁用时间线模式,低延迟CMAF不需要全局时间线,减少manifest更新延迟-frag_duration 200:将1秒的segment拆分为5个200ms的分片,支持分块传输,让播放器提前加载部分内容-window_size 3:减少历史segment保留数量,降低播放器缓冲压力
2. 修复Nginx分块传输配置
修改Nginx的location块,确保支持字节范围请求并禁用缓存:
server_name myserver.com; add_header Access-Control-Allow-Origin *; add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS'; add_header Access-Control-Allow-Headers 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range'; add_header Access-Control-Expose-Headers 'Content-Length,Content-Range'; root /var/www/html; index index.html index.nginx-debian.html; location /live { chunked_transfer_encoding on; # 禁用缓存,确保播放器获取最新直播内容 add_header Cache-Control "no-cache, no-store, must-revalidate"; add_header Pragma "no-cache"; add_header Expires "0"; # 开启字节范围请求,支持分块传输 add_header Accept-Ranges bytes; autoindex off; }
关键修改点:
- 单独为
/live路径配置,避免全局影响 - 新增
Accept-Ranges bytes:这是分块传输成功的核心,让播放器可以请求文件的部分内容 - 完全禁用缓存:直播内容实时更新,缓存会导致延迟增加
3. 优化播放器参数
Hls.js配置调整
const hls = new Hls({ lowLatencyMode: true, liveSyncDurationCount: 1, // 仅同步最新的1个segment liveMaxLatencyDurationCount: 1, // 最大延迟限制为1秒 maxBufferLength: 1, // 播放器缓冲区最多保留1秒内容 maxBufferSize: 1000 * 1000 * 50, // 缩小缓冲区大小 liveBackBufferLength: 0, enableWorker: true, // 启用Worker提升加载效率 manifestLoadingRetryDelay: 100, // 缩短manifest重试间隔 levelLoadingRetryDelay: 100 });
Dash.js配置调整
player.updateSettings({ streaming: { lowLatencyEnabled: true, liveDelay: 1, // 目标延迟设为1秒 liveCatchUpPlaybackRate: 1.5, // 加快追帧速度 liveCatchUpMinDrift: 0.2, // 只要延迟超过0.2秒就启动追帧 bufferTimeAtTopQuality: 1, // 缓冲区上限设为1秒 bufferToKeep: 0, fastSwitchEnabled: true } });
4. 额外配置检查
- OBS推流设置:将关键帧间隔设为1秒(与FFmpeg的
-g参数匹配),关闭OBS内置的推流延迟优化 - CDN配置:确保CDN支持字节范围请求(byte-range),并设置缓存时间为1秒或禁用缓存,避免CDN缓存旧内容导致延迟
内容的提问来源于stack exchange,提问作者dannyomni
相关产品推荐
相关产品推荐

