用 JavaScript 构建自己的 M3U8 播放器
8 min read
项目搭建
<!DOCTYPE html> <html> <head> <script src="https://cdn.jsdelivr.net/npm/hls.js@latest"></script> </head> <body> <input id="urlInput" placeholder="粘贴 M3U8 URL" /> <button id="loadBtn">加载</button> <select id="qualitySelect"></select> <video id="video" controls></video> <div id="stats"></div> </body> </html>
核心播放实现
const video = document.getElementById("video");
const urlInput = document.getElementById("urlInput");
const loadBtn = document.getElementById("loadBtn");
let hls = null;
function loadStream(url) {
if (hls) hls.destroy();
if (Hls.isSupported()) {
hls = new Hls();
hls.loadSource(url);
hls.attachMedia(video);
hls.on(Hls.Events.MANIFEST_PARSED, updateQualityList);
} else if (video.canPlayType("application/vnd.apple.mpegurl")) {
video.src = url;
}
}
loadBtn.addEventListener("click", () => {
const url = urlInput.value.trim();
if (url) loadStream(url);
});画质选择 UI
function updateQualityList() {
const select = document.getElementById("qualitySelect");
select.innerHTML = "";
const auto = new Option("自动", "-1");
select.appendChild(auto);
hls.levels.forEach((level, index) => {
select.appendChild(
new Option(
level.height + "p (" + (level.bitrate / 1e6).toFixed(1) + " Mbps)",
String(index)
)
);
});
select.onchange = () => {
hls.currentLevel = Number(select.value);
};
}简单统计面板
setInterval(() => {
if (!hls || hls.currentLevel < 0) return;
const level = hls.levels[hls.currentLevel];
const buffered =
video.buffered.length > 0
? (video.buffered.end(video.buffered.length - 1) - video.currentTime).toFixed(1)
: "0.0";
document.getElementById("stats").textContent =
level.width + "x" + level.height + " | " +
Math.round(level.bitrate / 1000) + " kbps | buffer " + buffered + "s";
}, 1000);错误处理基础
监听 Hls.Events.ERROR。非致命错误可忽略或重试;致命网络/媒体错误可能需要 startLoad()、recoverMediaError() 或彻底重启。深入:hls.js 无法工作。入门:hls.js 教程。
不想自建时更快上线
若只需要可用的生产 UI,直接嵌入或使用我们免费的在线 M3U8 播放器,不必维护自己的控件。
常见问题解答
自建 HLS 播放器难吗?
基础播放用 hls.js 很直接;打磨体验更费时。
必须 React/Vue 吗?
不必——原生 JS 足够。
如何支持 Safari?
检测原生 HLS 并设置 video.src。
能加离线下载吗?
下载流水线应与播放器分开实现。