使用 JS 判断 PSSH Box
现在 AI 已经非常牛逼了,如果你有这样的需求,非常明确的,建议你直接使用 AI。这是谷歌 Gemini 给出的结果:
使用 JavaScript 检测 MP4 文件中的 PSSH(保护系统特定标头)是一项复杂的任务,主要是因为:
二进制数据:MP4 文件是二进制的,而不是基于文本的。浏览器环境中的 JavaScript 在低级别上限制了对二进制数据的直接访问。
文件结构:PSSH 嵌入在 MP4 的原子结构中,需要解析才能找到它。
浏览器安全性:浏览器限制直接文件系统访问,限制您直接读取任意 MP4 文件的能力。
但是,您可以通过多种技术组合来实现这一点,主要涉及用于检索文件的 fetch API、用于二进制数据处理的 ArrayBuffer 和自定义解析逻辑。以下是一般方法的细分:
- 获取 MP4 文件:
async function getPSSH(url) {
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const arrayBuffer = await response.arrayBuffer();
return findPSSH(arrayBuffer);
} catch (error) {
console.error("Error fetching or parsing MP4:", error;
return null;
}
}
- 解析 MP4 原子结构:
这是最具挑战性的部分。您需要实现一个能够理解 MP4 原子结构的解析器。这是一个简化的示例(您可能需要一个更强大的解析器来应对实际场景):
function findPSSH(arrayBuffer) {
const dataView = new DataView(arrayBuffer);
let offset = 0;
while (offset < dataView.byteLength) {
const size = dataView.getUint32(offset);
const type = String.fromCharCode(
dataView.getUint8(offset + 4),
dataView.getUint8(offset + 5),
dataView.getUint8(offset + 6),
dataView.getUint8(offset + 7)
);
if (type === "pssh") {
// 找到 PSSH 原子
const psshData = new Uint8Array(
arrayBuffer.slice(offset + 8, offset + size)
);
//这里是原始 pssh 数据。
return psshData;
}
offset += size;
}
return null; // 未找到 PSSH
}
- 提取 PSSH 数据:
找到“pssh”原子后,即可提取其中的数据。PSSH 结构本身有自己的格式,您可能需要根据具体用例进一步解析。
getPSSH("your_mp4_file.mp4").then((pssh) => {
if (pssh) {
console.log("PSSH found:", pssh);
// 处理 pssh 数据。
// 您可以将 Uint8Array 转换为十六进制字符串以便于查看。
const hexPssh = Array.from(pssh).map(byte => byte.toString(16).padStart(2, '0')).join('');
console.log("PSSH hex:", hexPssh);
} else {
console.log("PSSH not found.");
}
});
重要注意事项:
复杂性:MP4 解析很复杂。可靠的实现通常依赖于专门的库。
性能:在 JavaScript 中解析大型 MP4 文件可能非常耗费资源。
跨域请求:如果您的 MP4 文件位于不同的域中,则需要通过在服务器上设置适当的标头来处理 CORS(跨域资源共享)。
浏览器限制:浏览器具有安全限制,可能会限制您执行某些操作的能力。
替代方法:
对于服务器端处理,带有 mp4box.js 或 FFMPEG 等库的 Node.js 会更高效。
对于处理 DRM 的客户端应用程序,媒体源扩展 (MSE) API 和加密媒体扩展 (EME) API 是标准机制,它们以更受控制的方式提供对 PSSH 数据的访问。
EME 和 MSE:如果您正在使用受 DRM 保护的内容,则 EME 和 MSE API 是处理 pssh 的正确方法。EME 将为您提供 pssh 信息。
此信息提供了一般方向。对于生产级应用程序,强烈建议使用专用库或服务器端处理。
不到三分钟,这个问题就解决了,但是,这里有一个瑕疵,自己在测试中发现的,
findPSSH
中使用 while 循环进行 type 类型的对比没有任何问题,但是 mp4 box 的结构一般如下:
我发现运行到 moov 后就停止了。
const size = dataView.getUint32(offset);
我们获取到了 moov 的长度后,理论应该继续进入 moov box 进行嗅探,然而我们并没有,因此我们可以尝试一下递归
export function findPSSH(arrayBuffer, offset) {
const dataView = new DataView(arrayBuffer);
while (offset < dataView.byteLength) {
const size = dataView.getUint32(offset);
const type = String.fromCharCode(
dataView.getUint8(offset + 4),
dataView.getUint8(offset + 5),
dataView.getUint8(offset + 6),
dataView.getUint8(offset + 7)
);
if (type === "moov") {
return findPSSH(arrayBuffer, offset + 8);
}
if (type === "pssh") {
// Found the PSSH atom
const psshData = new Uint8Array(
arrayBuffer.slice(offset + 8, offset + size)
);
//Here you have the raw pssh data.
return psshData;
}
offset += size;
}
return null; // PSSH not found
}
最后 debug 效果还不错。 所以现在 AI 质量真的非常高,我们只需要稍许 debug 就可以了,无需要浪费太多时间。