如何用JavaScript在浏览器端合并HLS流音视频并单文件下载?
浏览器端纯JS合并HLS音视频流并下载
原代码只能分别下载音视频文件,因为HLS的音视频是分开的分片流,直接拼接二进制数据没法生成可播放的单个媒体文件,得借助FFmpeg.wasm(浏览器端的FFmpeg移植版)来处理容器合并。
步骤说明
引入FFmpeg.wasm依赖
在HTML中添加CDN链接:<script src="https://unpkg.com/@ffmpeg/ffmpeg@0.10.1/dist/ffmpeg.min.js"></script>修改原代码核心逻辑
- 保留原有的音视频数据收集逻辑
- 将
stopRecord函数改为用FFmpeg合并音视频后再下载
完整修改后的代码
var arrayRecord = []; // 初始化FFmpeg实例 const { createFFmpeg, fetchFile } = FFmpeg; const ffmpeg = createFFmpeg({ log: true }); function arrayConcat(inputArray) { var totalLength = inputArray.reduce(function (prev, cur) { return prev + cur.length }, 0); var result = new Uint8Array(totalLength); var offset = 0; inputArray.forEach(function (element) { result.set(element, offset); offset += element.length; }); return result; } function saveAs(blob, filename) { var url = URL.createObjectURL(blob); var a = document.createElement("a"); document.body.appendChild(a); a.style = "display: none"; a.href = url; a.download = filename; a.click(); window.URL.revokeObjectURL(url); } async function stopRecord() { // 加载FFmpeg核心 if (!ffmpeg.isLoaded()) { await ffmpeg.load(); } arrayRecord.forEach(async function (item) { // 将收集的音视频数据转为Blob const videoBlob = new Blob([arrayConcat(item.data['video'])], { type: 'video/mp4' }); const audioBlob = new Blob([arrayConcat(item.data['audio'])], { type: 'audio/mp4' }); // 写入FFmpeg虚拟文件系统 ffmpeg.FS('writeFile', 'input-video.mp4', await fetchFile(videoBlob)); ffmpeg.FS('writeFile', 'input-audio.mp4', await fetchFile(audioBlob)); // 执行合并命令:将音视频合并为output.mp4 await ffmpeg.run('-i', 'input-video.mp4', '-i', 'input-audio.mp4', '-c:v', 'copy', '-c:a', 'copy', 'output.mp4'); // 读取合并后的文件并下载 const data = ffmpeg.FS('readFile', 'output.mp4'); const outputBlob = new Blob([data.buffer], { type: 'video/mp4' }); saveAs(outputBlob, "merged-stream.mp4"); // 清理虚拟文件和HLS实例 ffmpeg.FS('unlink', 'input-video.mp4'); ffmpeg.FS('unlink', 'input-audio.mp4'); ffmpeg.FS('unlink', 'output.mp4'); item.hls.destroy(); }); } function startRecord() { var video = document.getElementById('video'); var dataStream = { 'video': [], 'audio': [] }; var hls = new Hls(); hls.loadSource("Your playlist"); // 替换成你的m3u8地址 hls.attachMedia(video); hls.on(Hls.Events.MANIFEST_PARSED, function () { video.play(); hls.on(Hls.Events.BUFFER_APPENDING, function (event, data) { console.log("appending"); dataStream[data.type].push(data.data); }); }); arrayRecord.push({ hls: hls, data: dataStream }); video.onended = function (e) { stopRecord() } }
注意事项
- FFmpeg.wasm体积较大,首次加载会有延迟
- 跨域问题:如果m3u8流和页面不同域,需要目标服务器配置CORS
- 长时长直播流会占用较多内存,因为所有分片数据都存在内存中
内容的提问来源于stack exchange,提问作者EmbeEmbe
相关产品推荐
相关产品推荐

