hls.js 是一个开源的 JavaScript 库,它实现了 HTTP Live Streaming (HLS) 协议,使浏览器能够通过标准的 HTML5 <video> 标签播放 HLS 视频流。HLS 是 Apple 公司开发的一种流媒体传输协议,被广泛应用于音视频内容的分发。由于原生浏览器通常只支持播放 MP4 等直接文件格式或特定 HLS 实现(如 Safari),而 Chrome、Firefox 等浏览器缺乏对 HLS 的原生支持,hls.js 填补了这一空白,让开发者能够在几乎所有现代浏览器中提供 HLS 流媒体服务。

核心思想:hls.js 通过 JavaScript 解析 HLS manifest (M3U8) 文件,动态加载和拼接 TS (MPEG-2 Transport Stream) 片段,并利用 Media Source Extensions (MSE) API 将这些片段喂给 HTML5 <video> 元素进行播放,从而实现浏览器对 HLS 流媒体的广泛支持。


一、为什么需要 hls.js?

HLS 协议是目前最流行的流媒体协议之一,它的优势包括:

  1. 自适应比特率 (Adaptive Bitrate Streaming - ABR):HLS 服务器会提供同一视频的多个不同分辨率和比特率的版本。客户端会根据网络带宽和设备性能动态切换,以提供最佳的观看体验。
  2. CDN 友好:基于 HTTP 协议,容易与 CDN (内容分发网络) 集成,实现高效的内容分发和缓存。
  3. 广泛支持:被绝大多数流媒体服务器和播放器支持。Safari 浏览器在 macOS 和 iOS 上原生支持 HLS。

然而,Chrome、Firefox、Edge (旧版) 等非 Safari 浏览器,并没有内置 HLS 的播放能力。如果直接将 HLS 的 .m3u8 文件作为 <video>src,这些浏览器通常无法播放。

hls.js 的作用
hls.js 利用了浏览器提供的 Media Source Extensions (MSE) API。MSE 允许 JavaScript 代码生成媒体流并将其传递给 <video><audio> 元素。hls.js 的工作流程大致如下:

  1. 加载 HLS 的主 manifest 文件 (.m3u8)。
  2. 解析 manifest 文件,获取可用的视频轨道(不同分辨率、比特率)。
  3. 根据网络状况和用户设置,选择合适的视频轨道。
  4. 加载该轨道中的媒体片段 (.ts 文件)。
  5. 将这些 .ts 片段通过 MSE API 喂给 <video> 元素。
  6. 在播放过程中,实时监控网络和设备性能,动态切换到更优的视频轨道。

通过这种方式,hls.js 实现了在不支持 HLS 的浏览器中播放 HLS 视频,确保了跨平台和跨浏览器的兼容性。

二、hls.js 的基本使用

2.1 安装

你可以通过 npm/yarn 安装 hls.js:

1
2
3
npm install hls.js
# 或
yarn add hls.js

或者通过 CDN 引入:

1
<script src="https://cdn.jsdelivr.net/npm/hls.js@latest"></script>

2.2 基本播放示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>HLS.js Basic Example</title>
<style>
body { font-family: sans-serif; text-align: center; }
video { width: 80%; max-width: 800px; height: auto; border: 1px solid #ccc; margin-top: 20px; }
</style>
</head>
<body>
<h1>HLS.js Video Player</h1>
<video id="video" controls autoplay></video>

<script src="https://cdn.jsdelivr.net/npm/hls.js@latest"></script>
<script>
document.addEventListener('DOMContentLoaded', () => {
const video = document.getElementById('video');
const videoSrc = 'https://demo.unified-streaming.com/k8s/features/stable/video/tears-of-steel/tears-of-steel.ism/.m3u8'; // 示例 HLS 流地址

if (Hls.isSupported()) {
const hls = new Hls();
hls.loadSource(videoSrc); // 加载 HLS manifest
hls.attachMedia(video); // 将 HLS 实例与 video 元素关联
hls.on(Hls.Events.MANIFEST_PARSED, () => {
video.play(); // manifest 解析成功后自动播放
});
hls.on(Hls.Events.ERROR, (event, data) => {
console.error('HLS Error:', event, data);
if (data.fatal) {
switch (data.type) {
case Hls.ErrorTypes.NETWORK_ERROR:
// 尝试恢复网络错误
console.log('Fatal network error, trying to recover...');
hls.recoverMediaError();
break;
case Hls.ErrorTypes.MEDIA_ERROR:
// 尝试恢复媒体错误
console.log('Fatal media error, trying to recover...');
hls.recoverMediaError();
break;
default:
// 无法恢复的错误,销毁 HLS 实例
hls.destroy();
break;
}
}
});
} else if (video.canPlayType('application/vnd.apple.mpegurl')) {
// 如果浏览器原生支持 HLS (如 Safari),直接设置 src
video.src = videoSrc;
video.addEventListener('loadedmetadata', () => {
video.play();
});
} else {
console.error('This browser does not support HLS playback natively or via hls.js');
alert('您的浏览器不支持 HLS 视频播放。');
}
});
</script>
</body>
</html>

代码解释:

  1. Hls.isSupported():检查当前浏览器是否支持 Media Source Extensions (MSE) API,即是否可以使用 hls.js。
  2. new Hls():创建一个 hls.js 实例。
  3. hls.loadSource(videoSrc):告诉 hls.js 要加载的 HLS manifest 文件地址。
  4. hls.attachMedia(video):将 hls.js 实例与 HTML <video> 元素关联起来。hls.js 会通过 MSE API 将解析和缓冲的媒体数据发送给这个 <video> 元素。
  5. hls.on(Hls.Events.EVENT_NAME, callback):hls.js 提供了丰富的事件系统,可以监听播放状态、错误、质量切换等。MANIFEST_PARSED 是 manifest 文件解析成功时触发的事件。
  6. 错误处理:示例中包含了基本的错误处理逻辑,对于致命错误 (data.fatal 为 true),尝试恢复或销毁 hls.js 实例。
  7. 原生 HLS 支持:如果浏览器原生支持 HLS(video.canPlayType('application/vnd.apple.mpegurl') 为 true,通常是 Safari),则直接将 HLS URL 设置为 <video>src 属性,无需 hls.js。

三、核心功能与配置

hls.js 提供了丰富的功能和配置选项,以满足不同的播放需求。

3.1 自适应比特率 (ABR)

hls.js 自动管理 ABR 逻辑,根据网络带宽和缓冲区状态动态选择最佳的视频流质量。

  • hls.currentLevel: 获取当前播放的质量级别索引。
  • hls.levels: 获取所有可用的质量级别数组。
  • hls.nextLevel: 设置下一个要播放的质量级别。
  • hls.startLevel: 设置初始播放质量级别(索引)。

配置选项 (Hls 构造函数的第二个参数 config 对象)

1
2
3
4
5
6
const hls = new Hls({
autoStartLoad: true, // 默认 true,自动开始加载媒体
startLevel: -1, // -1 表示自动选择最佳初始级别,0 表示最低级别
debug: false, // 是否开启调试日志
// ... 更多配置
});

3.2 事件系统

hls.js 提供了大量的事件,用于监控播放器的状态和处理各种情况。

  • Hls.Events.MEDIA_ATTACHED: 媒体元素已附加。
  • Hls.Events.MANIFEST_PARSED: manifest 文件已解析。
  • Hls.Events.LEVEL_LOADED: 某个质量级别的数据已加载。
  • Hls.Events.FRAG_LOADED: 媒体片段已加载。
  • Hls.Events.BUFFER_APPENDED: 媒体数据已添加到缓冲区。
  • Hls.Events.ERROR: 发生错误。

示例:获取可用的视频质量并显示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
hls.on(Hls.Events.MANIFEST_PARSED, (event, data) => {
console.log('Available video qualities:', data.levels); // data.levels 是一个包含 { height, width, bitrate } 等信息的数组

const qualitySelect = document.createElement('select');
qualitySelect.addEventListener('change', (e) => {
hls.currentLevel = parseInt(e.target.value, 10); // 切换到用户选择的质量级别
});

data.levels.forEach((level, index) => {
const option = document.createElement('option');
option.value = index;
option.textContent = `${level.height}p (${Math.round(level.bitrate / 1000)} kbps)`;
qualitySelect.appendChild(option);
});
document.body.appendChild(qualitySelect);
});

3.3 错误处理与恢复

健壮的错误处理对于流媒体播放至关重要。hls.js 提供详细的错误信息,并允许尝试恢复。

  • Hls.ErrorTypes:错误类型,如 NETWORK_ERROR, MEDIA_ERROR
  • Hls.ErrorDetails:错误详情,如 MANIFEST_LOAD_ERROR, FRAG_LOAD_ERROR
  • data.fatal: 布尔值,表示错误是否致命。
  • hls.recoverMediaError(): 尝试从媒体错误中恢复。
  • hls.recoverNetworkError(): 尝试从网络错误中恢复。

3.4 DRM (数字版权管理)

hls.js 支持通过 Media Source Extensions (MSE) 和 Encrypted Media Extensions (EME) API 来处理 DRM 内容。你需要与 DRM 解决方案提供商(如 Widevine, PlayReady, FairPlay)结合使用。

通常流程:

  1. HLS manifest 中包含 DRM 信息 (如 EXT-X-KEYDASH-IF DRM 描述符)。
  2. hls.js 解析 DRM 信息,并将其传递给 EME API。
  3. EME API 向 DRM 授权服务器发送许可请求。
  4. 获得许可后,浏览器使用密钥解密媒体数据。

这部分实现较为复杂,通常需要专门的 DRM 客户端库配合。

3.5 实时流 (Live Streaming)

hls.js 也支持播放实时 HLS 流。它会定期刷新 manifest 文件,并加载最新的媒体片段。

  • 配置liveSyncDurationCountliveSyncDuration 等配置项可以控制播放器与实时边缘的同步程度。
  • DVR 功能:通过 hls.levelController.currentLiveSyncPosition 可以获取直播流的当前时间戳。

3.6 销毁与清除

当不再需要播放器时,应该销毁 hls.js 实例,释放资源。

1
2
hls.destroy();
// 解除与 video 元素的关联并清除内部状态

四、高级用法与注意事项

  1. 兼容性
    • 现代浏览器 (Chrome, Firefox, Edge, Opera):支持 Media Source Extensions (MSE),可以使用 hls.js。
    • Safari (macOS, iOS):原生支持 HLS,直接使用 <video src="your_hls.m3u8"> 即可。此时不应使用 hls.js。
    • IE11:支持 MSE,但性能可能不佳。hls.js 通常可以工作。
    • 旧版浏览器:不支持 MSE,无法使用 hls.js。需要提供备用方案(如Flash播放器或提示用户升级浏览器)。
  2. CDN 配置:HLS 片段通常通过 CDN 分发。确保你的 CDN 配置正确,支持 .m3u8.ts 文件的 MIME 类型。
  3. CORS (跨域资源共享):HLS manifest 和媒体片段通常托管在不同的域或子域上。请确保服务器配置了正确的 CORS 头,允许播放器加载这些资源。
    • Access-Control-Allow-Origin: * 或指定你的播放器域名。
    • Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept, Range
  4. 优化与性能
    • 预加载:HLS 可以通过 preload="auto"<video> 标签上开启预加载,但 hls.js 内部也有自己的加载逻辑。
    • Keep-alive:服务器应支持 HTTP Keep-alive 以减少 TCP 连接建立开销。
    • 缓存:CDN 缓存 HLS 片段至关重要。
  5. 与其他播放器框架集成:hls.js 可以与许多流行的播放器框架(如 Video.js, Plyr, flowplayer 等)集成,提供更丰富的 UI 和控制功能。通常是通过插件的形式将 hls.js 的功能添加到这些播放器。

五、总结

hls.js 是一个功能强大且高度可配置的 JavaScript 库,它有效地解决了在非 Safari 浏览器中播放 HLS 流媒体的兼容性问题。通过利用 Media Source Extensions API,hls.js 能够解析 HLS manifest、管理自适应比特率、处理媒体片段、提供丰富的事件和错误处理机制。对于希望在 Web 浏览器中提供高质量、跨平台 HLS 视频播放体验的开发者来说,hls.js 是一个不可或缺的工具。理解其工作原理、配置选项和事件系统,是构建健壮流媒体应用的基石。