如何使用 Chrome 中的 javascript 检测 HTML 视频是否受 DRM 保护?
How to detect whether a HTML video is DRM-protected with javascript in Chrome?
快速背景:我正在编写一个浏览器扩展程序,它可以在浏览器中播放视频时对其进行操作。脚本本身应该是通用的,它应该 运行 在任何有视频的网站上。
我的视频操作依赖于能够操作视频中的像素数据。 HTML5 将视频像素转换为可以在 javascript 中使用的东西的方法是 canvas2dContext.drawImage()
将当前视频帧绘制到 canvas 的函数,以及 context.getImageData()
获取上述数据。
当前代码归结为(我正在简化事情):
let video = document.findElementsByTagName('video')[0];
let canvas = document.createElement('canvas');
let context = canvas.getContext('2d');
handleVideoFrame() {
context.drawImage(video, 0, 0, canvas.width, canvas.height);
let data = context.getImageData(0,0,canvas.width, canvas.height);
processData(data);
}
processData(data) {
// a lot of heavy calculations going on here
}
window.requestAnimationFrame(handleVideoFrame);
问题:DRM
这非常有效,除非您尝试在 Netflix、Disney+ 或其他相同级别的网站上执行此操作。这些网站使用 DRM,如果该视频有 DRM,context.drawImage()
将不起作用。请注意,我不想捕获受 DRM 保护的视频的实际帧,但我想知道 DRM 是否存在。
在 Firefox 中,这不是什么大问题,因为如果您尝试在受 DRM 保护的视频上调用 context.drawImage()
,you will get an exception。然后您可以捕获该异常,不要 运行 沉重 processData()
,提醒用户您的脚本由于 DRM 而无法在该站点上运行,然后关闭整个程序下来。
在Chrome(以及与此相关的其他 Chromium reskins)中,另一方面,context.drawImage()
不会失败。相反,context.drawImage()
会在不抛出异常的情况下绘制一个 100% 不透明的黑色方块,这是一个问题,因为:
- 您可以永远明确判断视频是否受 DRM 保护
- 因此你不能通知用户,他们会责怪你的脚本
- 虽然您可以检查框架是否为黑色并避免调用繁重的
processData()
如果是,您仍在进行 drawImage()
不需要进行的调用
我尝试过的解决方案
context.getImageData()
returns 一个包含每个像素 RGBA 值数组的对象。我最初希望我可以通过查看 alpha 值来确定视频是否受 DRM 保护。然而,drawImage()
绘制的框架总是¹完全不透明,这意味着这是一个死胡同。
¹ 除非视频尚未加载,否则它是透明的。
我希望避免的解决方案
- 任何涉及我根据框架已经黑了一段时间的事实做出假设的事情。当然,我可以 运行 播放 n 秒的视频,如果到那时为止我检查过的所有帧都是全黑的,我会发出警告。如果 n 太低,我就有误报的风险。如果 n 太高,视频播放和 'whoops DRM' 警告之间的延迟可能会太长。
- 维护我知道使用 DRM 的已知网站列表
您可以简单地查看HTMLMediaElement's mediaKeys
属性,如果设置了,则视频受DRM保护:
const isDRMProtected = (elem) => elem.mediaKeys instanceof MediaKeys;
快速背景:我正在编写一个浏览器扩展程序,它可以在浏览器中播放视频时对其进行操作。脚本本身应该是通用的,它应该 运行 在任何有视频的网站上。
我的视频操作依赖于能够操作视频中的像素数据。 HTML5 将视频像素转换为可以在 javascript 中使用的东西的方法是 canvas2dContext.drawImage()
将当前视频帧绘制到 canvas 的函数,以及 context.getImageData()
获取上述数据。
当前代码归结为(我正在简化事情):
let video = document.findElementsByTagName('video')[0];
let canvas = document.createElement('canvas');
let context = canvas.getContext('2d');
handleVideoFrame() {
context.drawImage(video, 0, 0, canvas.width, canvas.height);
let data = context.getImageData(0,0,canvas.width, canvas.height);
processData(data);
}
processData(data) {
// a lot of heavy calculations going on here
}
window.requestAnimationFrame(handleVideoFrame);
问题:DRM
这非常有效,除非您尝试在 Netflix、Disney+ 或其他相同级别的网站上执行此操作。这些网站使用 DRM,如果该视频有 DRM,context.drawImage()
将不起作用。请注意,我不想捕获受 DRM 保护的视频的实际帧,但我想知道 DRM 是否存在。
在 Firefox 中,这不是什么大问题,因为如果您尝试在受 DRM 保护的视频上调用 context.drawImage()
,you will get an exception。然后您可以捕获该异常,不要 运行 沉重 processData()
,提醒用户您的脚本由于 DRM 而无法在该站点上运行,然后关闭整个程序下来。
在Chrome(以及与此相关的其他 Chromium reskins)中,另一方面,context.drawImage()
不会失败。相反,context.drawImage()
会在不抛出异常的情况下绘制一个 100% 不透明的黑色方块,这是一个问题,因为:
- 您可以永远明确判断视频是否受 DRM 保护
- 因此你不能通知用户,他们会责怪你的脚本
- 虽然您可以检查框架是否为黑色并避免调用繁重的
processData()
如果是,您仍在进行drawImage()
不需要进行的调用
我尝试过的解决方案
context.getImageData()
returns 一个包含每个像素 RGBA 值数组的对象。我最初希望我可以通过查看 alpha 值来确定视频是否受 DRM 保护。然而,drawImage()
绘制的框架总是¹完全不透明,这意味着这是一个死胡同。
¹ 除非视频尚未加载,否则它是透明的。
我希望避免的解决方案
- 任何涉及我根据框架已经黑了一段时间的事实做出假设的事情。当然,我可以 运行 播放 n 秒的视频,如果到那时为止我检查过的所有帧都是全黑的,我会发出警告。如果 n 太低,我就有误报的风险。如果 n 太高,视频播放和 'whoops DRM' 警告之间的延迟可能会太长。
- 维护我知道使用 DRM 的已知网站列表
您可以简单地查看HTMLMediaElement's mediaKeys
属性,如果设置了,则视频受DRM保护:
const isDRMProtected = (elem) => elem.mediaKeys instanceof MediaKeys;