HLS.js 播放时为什么总在切换清晰度后卡住?

慕容炳光 阅读 10

我用 HLS.js 做了一个支持多码率切换的播放器,但每次手动切换清晰度(比如从 720p 切到 1080p)之后,视频就会卡住几秒甚至直接黑屏。控制台没报错,network 里看到新的 m3u8 和 ts 文件其实已经加载了,就是画面不动。

我试过调用 hls.loadLevel 后再手动触发 video.play(),但还是不行。是不是我哪里配置漏了?下面是我初始化 HLS 的代码:

const hls = new Hls({
  enableWorker: true,
  maxBufferLength: 30,
  liveSyncDurationCount: 3
});
hls.loadSource('https://example.com/master.m3u8');
hls.attachMedia(videoElement);
hls.on(Hls.Events.MANIFEST_PARSED, () => {
  // 切换到最高清晰度
  hls.currentLevel = hls.levels.length - 1;
});
我来解答 赞 6 收藏
二维码
手机扫码查看
1 条解答
夏侯含含
这个问题我之前踩过坑,主要是两个原因:缓冲区没清理干净,以及 ABR 自动切换逻辑在跟你手动切换打架。

先说解决方案,把你初始化 HLS 的代码改成这样:

const hls = new Hls({
enableWorker: true,
maxBufferLength: 30,
maxMaxBufferLength: 60,
startLevel: -1, // 让它自动选择起始码率
capLevelToPlayerSize: false // 禁用根据播放器尺寸自动调整
});
hls.loadSource('https://example.com/master.m3u8');
hls.attachMedia(videoElement);

hls.on(Hls.Events.MANIFEST_PARSED, () => {
// 初始时不要急着切清晰度
});

// 手动切换清晰度的正确姿势
function switchLevel(levelIndex) {
if (hls.levels[levelIndex]) {
// 先停止加载
hls.stopLoad();
// 设置目标清晰度
hls.currentLevel = levelIndex;
// 重新开始加载
hls.startLoad();
}
}


关键点说下:

第一,你直接在 MANIFEST_PARSED 里设 currentLevel,这时候缓冲区还没建立好,容易出问题。建议等视频开始播放后再切换。

第二,切换清晰度时最好先 stopLoad()startLoad(),不然旧的 ts 分片请求可能还在队列里,新的请求排队等着,就卡住了。

第三,HLS.js 默认有 ABR 自动码率切换,你手动切完它可能又给你切回去。如果你要做纯手动切换,需要监听 FRAG_LOAD_EMERGENCY_ABORTED 这类事件,或者干脆用 hls.autoLevelEnabled = false 把自动切换关掉。

另外你说的黑屏问题,大概率是缓冲区 gap 造成的,可以加个监听:

hls.on(Hls.Events.BUFFER_CREATED, () => {
// 检查是否有 gap,有的话尝试跳过
if (videoElement.buffered.length > 0) {
const currentTime = videoElement.currentTime;
for (let i = 0; i < videoElement.buffered.length; i++) {
if (currentTime < videoElement.buffered.start(i)) {
videoElement.currentTime = videoElement.buffered.start(i) + 0.1;
break;
}
}
}
});


最后吐槽一句,HLS.js 这套 API 设计得确实有点反人类,currentLevelnextLevelloadLevel 这几个属性名字看着都差不多,文档还写得跟天书一样,踩坑太正常了。
点赞 1
2026-03-01 22:04