能否用变量替代stream.key文件定义HLS流密钥?如何外部指定密钥位置?
问题描述
我用FFmpeg加密MP4视频后,生成了stream.m3u8播放列表、stream.m3u8.key密钥文件和若干.ts分片文件。直接通过本地路径加载stream.m3u8的代码能正常播放:
<head> <link href="https://vjs.zencdn.net/7.4.1/video-js.css" rel="stylesheet"> <script src="https://vjs.zencdn.net/7.4.1/video.min.js"></script> </head> <body> <video id="example-video" class="video-js vjs-default-skin" controls="controls" width="640" height="360"> <source src="./stream.m3u8" type="application/x-mpegURL" /> </video> <script> var player = videojs('example-video'); </script> </body>
但用<input>文件选择功能加载stream.m3u8时,播放器能识别视频时长却无法播放——因为播放列表里定义的密钥文件路径无法被本地文件URL(blob:协议)正确解析,密钥加载失败。相关代码:
<html> <head> <link href="https://vjs.zencdn.net/7.4.1/video-js.css" rel="stylesheet"> <script src="https://vjs.zencdn.net/7.4.1/video.min.js"></script> </head> <body> <label for="input">Choose a video file to process:</label> <input type="file" id="input" name="input_video"> <p id="demo"><p/> <video id="video" class="video-js vjs-default-skin" width="320" height="240" controls style="display: none;"> <source id="vid" type="application/x-mpegURL" /> </video> </body> <script> document.getElementById("input").addEventListener("change", function() { var media = URL.createObjectURL(this.files[0]); var vid = document.getElementById("vid"); vid.src = media; var video = document.getElementById("video"); video.style.display = "block"; video.play(); document.getElementById("demo").innerHTML = vid.src; var player = videojs('video'); //player.play(); }); </script> </html>
需要解决两个问题:
- 如何在播放列表外部(比如JS代码中)定义密钥文件位置?
- 能否用变量替代
stream.key文件来读取和定义HLS流密钥?
解决方案
1. 在播放列表外部定义密钥文件位置
Video.js依赖的HLS处理逻辑支持通过自定义密钥加载器,拦截并替换密钥请求路径,强制使用你指定的密钥文件:
实现代码
document.getElementById("input").addEventListener("change", async function() { // 新增密钥文件选择逻辑 const keyFile = await new Promise(resolve => { const keyInput = document.createElement('input'); keyInput.type = 'file'; keyInput.accept = '.key'; keyInput.onchange = () => resolve(keyInput.files[0]); keyInput.click(); }); const mediaUrl = URL.createObjectURL(this.files[0]); const keyUrl = URL.createObjectURL(keyFile); const vid = document.getElementById("vid"); vid.src = mediaUrl; const video = document.getElementById("video"); video.style.display = "block"; const player = videojs('video'); // 播放器就绪后替换密钥加载逻辑 player.ready(() => { const hls = player.tech({ IWillNotUseThisInPlugins: true }).hls; hls.keyLoader = function(hls, uri) { // 忽略播放列表中的密钥路径,使用用户选择的密钥文件 return fetch(keyUrl) .then(response => response.arrayBuffer()) .then(buffer => new Uint8Array(buffer)); }; }); });
2. 使用变量替代密钥文件
完全可以直接用变量存储密钥内容,不需要依赖本地密钥文件。只要能拿到密钥的二进制数据,就能在密钥加载器中直接返回:
示例代码
场景1:已知密钥二进制数据
// 替换为你的真实密钥二进制数据 const keyData = new Uint8Array([0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef, 0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef]); document.getElementById("input").addEventListener("change", function() { const mediaUrl = URL.createObjectURL(this.files[0]); const vid = document.getElementById("vid"); vid.src = mediaUrl; const video = document.getElementById("video"); video.style.display = "block"; const player = videojs('video'); player.ready(() => { const hls = player.tech({ IWillNotUseThisInPlugins: true }).hls; hls.keyLoader = function(hls, uri) { // 直接返回预定义的密钥变量 return Promise.resolve(keyData); }; }); });
场景2:密钥为字符串格式
如果密钥是字符串,先转成Uint8Array再使用:
const keyString = "your-real-key-string"; const keyData = new TextEncoder().encode(keyString);
关键说明
- 当使用
URL.createObjectURL加载本地m3u8时,播放器处于blob:协议环境,无法直接访问本地文件系统的密钥文件,必须通过自定义加载逻辑绕过限制。 - 自定义密钥加载器可以根据
uri参数做精细匹配,支持多密钥场景下的针对性替换。
内容的提问来源于stack exchange,提问作者Saeed Eisakhani
相关产品推荐
相关产品推荐

