我正在开发的chrome扩展,使以下:
- 访问从
chrome.tabCapture.capture
获得的流(捕获视频时,我们暂时忽略音频捕获,因为它与我面临的问题无关) - 将此
tabStream
传递给URL.createObjectURL(tabStream)
- 将生成的url用作DOM视频元素
videoEl.src = URL.createObjectURL(tabStream)
的src
- 调用
videoEl.play()
,当调用canplay
事件时 - 将
videoEl
作为参数传递给画布的上下文drawImage
方法 - 由于现在视频帧被渲染到画布元素中,因此可以对该帧执行许多有用的操作(裁剪、水印等)。
到目前为止,所有的工作都很完美。但是下面的最后两个步骤不起作用:
- 使用
canvasStream = canvasEl.captureStream(20)
从画布元素创建流 - 将此流传递到
MediaRecorder
(recorder = new MediaRecorder(canvasStream)
)并开始记录:recorder.start()
基本上,如果在chrome扩展的背景之外使用这种方法(如:https://jsfiddle.net/williamwallacebrave/2rgv7pgj/7/)都能很好地工作。但是当在chrome扩展背景中使用时,我可以清楚地检测到视频帧在画布元素中发送和渲染,但不知何故,canvasEl.captureStream()
没有推送数据或记录器无法拾取它们。此外,如果在内容脚本中再次使用这种方法,所有工作都很完美。但在内容脚本中,我'我无法访问tabCapture
流。
这是我的清单文件:
{
"name": "super app",
"manifest_version": 2,
"description": "...",
"version": "0.0.1",
"content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'",
"page_action": {
"default_title": "app",
"default_icon": "static/images/logo.png"
},
"icons": {
"128": "static/images/logo.png"
},
"background": {
"page": "background.html"
},
"content_scripts": [
{
"matches": ["<all_urls>"],
"exclude_matches": ["http://localhost:3000/*"],
"css": [
"static/css/style.css"
],
"js": [
"vendor/system.js",
"vendor/jquery.min.js",
"content/config.js",
"content/index.js"
]
}
],
"web_accessible_resources": [
"background/*",
"vendor/*",
"content/*",
"common/*.js",
"capturer.html",
"static/*",
"*"
],
"externally_connectable": {
"matches": [
"http://localhost:3000/*"
]
},
"permissions": [
"tabs",
"activeTab",
"<all_urls>",
"clipboardRead",
"clipboardWrite",
"tabCapture",
"notifications",
"tts"
]
}
下面是虚拟代码,当作为内容脚本运行时,它可以很好地工作,但不能作为后台运行:
// SOURCE: http://stackoverflow.com/questions/39302814/mediastream-capture-canvas-and-audio-simultaneously#39302994
var cStream,
aStream,
recorder,
chunks = [],
canvasEl = document.createElement('canvas');
canvasEl.width = 400;
canvasEl.height = 400;
document.body.appendChild(canvasEl);
/*
create and run external video
*/
var videoEl = document.createElement('video');
videoEl.crossOrigin = 'anonymous';
videoEl.src = 'https://dl.dropboxusercontent.com/s/bch2j17v6ny4ako/movie720p.mp4';
videoEl.play();
videoEl.addEventListener('play', function(){
var audioCtx = new AudioContext();
var dest = audioCtx.createMediaStreamDestination();
aStream = dest.stream;
var sourceNode = audioCtx.createMediaElementSource(this);
console.log('connected audio');
sourceNode.connect(dest);
// output to our headphones
sourceNode.connect(audioCtx.destination)
var canvasCtx = canvasEl.getContext('2d');
console.log('play video in canvas');
draw(this, canvasCtx);
startRecording();
setTimeout(() => {
stopRecording();
}, 10000)
}, false);
function exportStream(e) {
console.log('exportStream', chunks.length);
if (chunks.length) {
var blob = new Blob(chunks),
videoURL = URL.createObjectURL(blob),
resultVideoEl = document.createElement('video');
resultVideoEl.controls = true;
resultVideoEl.src = videoURL;
resultVideoEl.onend = function() {
URL.revokeObjectURL(videoURL);
}
document.body.insertBefore(resultVideoEl, canvasEl);
} else {
document.body.insertBefore(
document.createTextNode('no data saved'), canvasEl);
}
}
function saveChunks(e) {
console.log('save chunks', e.data.size);
e.data.size && chunks.push(e.data);
}
function stopRecording() {
console.log('STOP RECORDING');
videoEl.pause();
recorder.stop();
}
function startRecording() {
console.log('START RECORDING');
cStream = canvasEl.captureStream(30);
cStream.addTrack(aStream.getAudioTracks()[0]);
recorder = new MediaRecorder(cStream);
recorder.start();
// =============================================
// THIS PART IS NOT FIRED WHEN RUN IN BACKGROUND
// and final chunks is always an empty array.
// =============================================
recorder.ondataavailable = saveChunks;
recorder.onstop = exportStream;
}
function draw(v,ctx) {
if(videoEl.paused || videoEl.ended) return false;
// here I'm cropping the video frames and taking only 400 by 400
// square shifted by 100, 100 vector
ctx.drawImage(v, 100, 100, 400, 400, 0, 0, 400,400);
setTimeout(draw,20,v,ctx);
}
另外,请注意,这个captureStream和MediaRecorder相对较新,因此您需要Chrome 51+才能运行该示例
1条答案
按热度按时间368yc8dk1#
这很可能与我几个月前提交的Chromium错误有关:https://bugs.chromium.org/p/chromium/issues/detail?id=639105。
以下是负责该API的Chrome工程师的回应:
画布不绘制/渲染新的帧时,背景,因此,画布捕捉不包含任何新的项目。AFAIK,有没有一种方法来绘制和捕捉画布时,标签是背景的权利,现在的方式,你期望在演示使用Chrome。
请将您的使用案例张贴在那里,并显示您对修复此问题的支持。