DRM(数字权限管理技术)防截屏录屏----解密org.w3.clearkey视频并播放

提示:解密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)进行播放。

一、教程

mse-eme教程

二、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

总结

踩坑路漫漫长@~@

相关推荐
Macdo_cn2 小时前
My Metronome for Mac v1.4.2 我的节拍器 支持M、Intel芯片
macos·音视频
kiramario3 小时前
【结束】JS如何不通过input的onInputFileChange使用本地mp4文件并播放,nextjs下放入public文件的视频用video标签无法打开
开发语言·javascript·音视频
余~~185381628005 小时前
矩阵碰一碰发视频的后端源码技术,支持OEM
线性代数·矩阵·音视频
划水哥~6 小时前
高清下载油管视频到本地
音视频
Luke Ewin12 小时前
根据音频中的不同讲述人声音进行分离音频 | 基于ai的说话人声音分离项目
人工智能·python·音视频·语音识别·声纹识别·asr·3d-speaker
Macdo_cn1 天前
Infuse Pro for Mac v8.1 全能视频播放器 支持M、Intel芯片
macos·音视频
我爱蛋蛋后1 天前
Linux驱动开发之音频驱动与基础应用编程
linux·c语言·驱动开发·音视频
Macdo_cn1 天前
Screen Wonders for Mac v3.3.1 3D屏保应用 支持M、Intel芯片
macos·音视频
苏三福1 天前
rk3588/3576板端编译程序无法运行视频推理
arm开发·音视频
江同学_1 天前
RTSP场景下RTP协议详解及音视频打包全流程
音视频