提示:解密org.w3.clearkey视频并播放
帮助:未实现clearkey加密,如有大神,请指导一下
### 文章目录
- [@[TOC](文章目录)](#文章目录 @TOC 前言 一、教程 二、org.w3.clearkey视频播放 三、效果 四、问题 总结)
- [前言](#文章目录 @TOC 前言 一、教程 二、org.w3.clearkey视频播放 三、效果 四、问题 总结)
- [一、教程](#文章目录 @TOC 前言 一、教程 二、org.w3.clearkey视频播放 三、效果 四、问题 总结)
- [二、org.w3.clearkey视频播放](#文章目录 @TOC 前言 一、教程 二、org.w3.clearkey视频播放 三、效果 四、问题 总结)
- [三、效果](#文章目录 @TOC 前言 一、教程 二、org.w3.clearkey视频播放 三、效果 四、问题 总结)
- [四、问题](#文章目录 @TOC 前言 一、教程 二、org.w3.clearkey视频播放 三、效果 四、问题 总结)
- [总结](#文章目录 @TOC 前言 一、教程 二、org.w3.clearkey视频播放 三、效果 四、问题 总结)
前言
ClearKey是一种基于JSON Web Key (JWK)格式的内容加密方案,主要用于在Web环境中保护媒体内容的安全传输和播放。ClearKey通过使用JSON Web Tokens (JWT)来传输密钥信息,确保只有持有正确密钥的用户才能解密和播放受保护的媒体内容。
ClearKey的工作原理1、密钥管理:ClearKey使用JWT来传输密钥信息,JWT包含三个部分:头部、有效载荷和签名。头部描述了令牌的类型和加密方法,有效载荷包含密钥信息,签名用于验证令牌的真实性。
2、密钥获取:在播放受保护的媒体内容时,浏览器会向服务器请求JWT,服务器验证请求者的身份后,返回包含密钥信息的JWT。
3、解密播放:浏览器使用JWT中的密钥信息对媒体内容进行解密,然后通过MediaSource Extensions (MSE)进行播放。
一、教程
二、org.w3.clearkey视频播放
test.html
c
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<video preload="auto" id="video" controls></video>
<script>
const KEYSYSTEM_TYPE = "org.w3.clearkey";
var videoFragments = [
// demo 视频
"video/init.mp4",
"video/seg-1.mp4",
"video/seg-2.mp4",
"video/seg-3.mp4",
"video/seg-4.mp4",
"video/seg-5.mp4",
"video/seg-6.mp4",
"video/seg-7.mp4"
];
var name = 'video';
// 音频类型
const audioContentType = 'audio/mp4; codecs="mp4a.40.2"'; // AAC-LC
// 视频类型
const videoContentType = 'video/mp4; codecs="avc1.42E01E"'
// 创建mediaSource
const videoSource = new MediaSource();
var keys = {
"2fef8ad812df429783e9bf6e5e493e53": "7f412f0575f44f718259beef56ec7771",
"7eaa636ee7d142fd945d1f764877d8db": "624db3d757bb496fb93e51f341d11716"
};
async function load() {
const videoEl = document.getElementById('video');
videoEl.src = URL.createObjectURL(videoSource);
videoEl.sessions = [];
// 解码
videoEl.addEventListener("encrypted", async(ev) => {
let options = [{
initDataTypes: ["cenc"],
videoCapabilities: [{
contentType: videoContentType
}],
audioCapabilities: [{
contentType: audioContentType
}]
}];
if (typeof(MediaKeySystemAccess.prototype.getConfiguration) == "undefined") options = [{
initDataType: "cenc",
videoType: videoContentType,
audioType: audioContentType
}];
// 获取系统允许
let systemAccess = await navigator.requestMediaKeySystemAccess(KEYSYSTEM_TYPE, options);
// 获取mediaKeys
var mediaKeys = await systemAccess.createMediaKeys();
// 设置播放器mediaKeys
await videoEl.setMediaKeys(mediaKeys);
// 生成session
let session = await videoEl.mediaKeys.createSession();
videoEl.sessions.push(session);
// 获取请求
let request = await session.generateRequest(ev.initDataType, ev.initData)
session.addEventListener("message", async(e) => {
let message = ArrayBufferToString(e.message);
let msg = JSON.parse(message);
let outKeys = [];
for (let i = 0; i < msg.kids.length; i++) {
// 获取单个message的id
let id = msg.kids[i];
// 获取全小写的hex格式的id
let hexId = Base64ToHex(id).toLowerCase();
// 获取keys中键为hexId的值
let key = keys[hexId];
if (key) outKeys.push({
"kty": "oct",
"alg": "A128KW",
"kid": id,
"k": HexToBase64(key)
});
}
if (outKeys.length < 1) return console.log('解码失败!!!')
let update = JSON.stringify({
"keys": outKeys,
"type": msg.type
});
await e.target.update(StringToArrayBuffer(update));
});
session.addEventListener("keystatuseschange", (e) => {});
})
videoSource.addEventListener('sourceopen', openSourceFile)
}
// Hex转base64
function HexToBase64(hex) {
var bin = "";
for (var i = 0; i < hex.length; i += 2) {
bin += String.fromCharCode(parseInt(hex.substr(i, 2), 16));
}
return window.btoa(bin).replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
}
// base64转Hex
function Base64ToHex(str) {
var bin = window.atob(str.replace(/-/g, "+").replace(/_/g, "/"));
var res = "";
for (var i = 0; i < bin.length; i++) {
res += ("0" + bin.charCodeAt(i).toString(16)).substr(-2);
}
return res;
}
// buffer转字符串
function ArrayBufferToString(arr) {
var str = '';
var view = new Uint8Array(arr);
for (var i = 0; i < view.length; i++) {
str += String.fromCharCode(view[i]);
}
return str;
}
// 字符串转buffer
function StringToArrayBuffer(str) {
var arr = new ArrayBuffer(str.length);
var view = new Uint8Array(arr);
for (var i = 0; i < str.length; i++) {
view[i] = str.charCodeAt(i);
}
return arr;
}
// 打开文件
async function openSourceFile() {
videoSource.removeEventListener('sourceopen', openSourceFile);
let sourceBuffer = videoSource.addSourceBuffer(videoContentType);
let bufferList = [];
let count = 0;
if (videoSource.readyState == 'closed') return console.log('视频readyState为关闭');
// for循环里直接appendBuffer会报错
for (let i = 0; i < videoFragments.length; i++) {
const buffer = await loadFile(videoFragments[i]);
if (buffer) bufferList.push(buffer);
}
// 首次appendBuffer
await sourceBuffer.appendBuffer(bufferList[count]);
sourceBuffer.addEventListener("updateend", async() => {
count++;
if (count < bufferList.length) await sourceBuffer.appendBuffer(bufferList[count]);
});
}
// 加载文件
function loadFile(src) {
return new Promise((resolve, reject) => {
let xhr = new XMLHttpRequest();
xhr.open('GET', src);
xhr.responseType = 'arraybuffer';
xhr.addEventListener("load", (buffer) => {
resolve((buffer.target || buffer.target).response)
});
xhr.addEventListener("error", () => {
console.log("error:" + src);
reject();
});
xhr.addEventListener("abort", () => {
console.log("aborted:" + src);
reject();
});
xhr.send()
});
}
load();
</script>
</body>
</html>
三、效果
demo仅加载视频示例,未加载音频
视频仅做测试使用,未进行任何商业用途, 遮挡一下看到播放效果即可,如有侵权,联系作者删除。
四、问题
加密插件:
shaka package:3.4.0、3.3.0、3.2.1执行无反应,使用3.2.0可执行加密,但不支持clearkey
c
packager input=libx264.mp4,stream=video,output=encrypted_video.mp4 --keys key_id=00112233445566778899aabbccddeeff:key=ffeeddccbbaa99887766554433221100 --enable_raw_key_encryption --protection_systems "Widevine"
ffmpeg:不支持DRM的ckearkey加密
c
// ffmpeg -i test2.mp4 -vcodec copy -acodec copy -encryption_scheme cenc-aes-ctr -encryption_key 76a6c65c5ea762046bd749a2e632ccbb -encryption_kid a7e61c373e219033c21091fa607bf3b8 output.mp4
// ffmpeg -i test2.mp4 -c:v libx264 -c:a aac -f mp4 -encryption_scheme cenc-aes-ctr -key 76a6c65c5ea762046bd749a2e632ccbb -encryption_kid a7e61c373e219033c21091fa607bf3b8 encrypted_video.mp4
mp4Box:未实现
c
// MP4Box.exe -crypt drm.xml test2.mp4 -out tempv.mp4
// MP4Box.exe -crypt drm.xml test2.mp4 -out tempa.mp4
// MP4Box.exe -dash 6000 -frag 6000 -mem-frags -rap -profile dashavc264:live -profile-ext urn:hbbtv:dash:profile:isoff-live:2012 -min-buffer 3000 -bs-switching no -sample-groups-traf -single-traf -subsegs-per-sidx 1 -segment-name $RepresentationID$_$Number$$Init=i -segment-timeline -out manifest.mpd tempv.mp4#trackID=1:id=v1:period=p0 tempa.mp4#trackID=1:id=a1:period=p0
总结
踩坑路漫漫长@~@