我们正在开发一个项目,人们可以使用网络摄像头在聊天室中,他们可以抓取某人当时摄像头的快照,在其上做一些注释,然后将修改后的图片分享为如果是他们自己的网络摄像头(例如共享白板)。
将网络摄像头流捕获到可以编辑的 Canvas 元素中相对容易。在我们的页面上找到 canvas 元素并对其执行 .getContext('2d') , 使用开放库向其中添加编辑工具。从 Canvas 中获取流的操作如下:
var canvasToSend = document.querySelector('canvas');
var stream = canvasToSend.captureStream(60);
var room = osTwilioVideoWeb.getConnectedRoom();
var mytrack = null;
room.localParticipant.publishTrack(stream.getTracks()[0]).then((publication) => {
mytrack = publication.track;
var videoElement = mytrack.attach();
});
这会正常发布流,但除非您在 Canvas 上绘制其他内容,否则第一帧将不会被发送。假设您画了 2 个圆圈,然后点击“共享”,流将启动,但不会显示在接收者一侧,除非您画一条线、另一个圆圈或其他任何东西。看起来它需要一个帧更改才能发送数据。
我可以通过执行诸如 context.fill(); 之类的操作来使用开发人员工具强制执行此操作;但是当我尝试在发布函数之后添加此操作时,即使在 then() 中...也没有运气。
关于如何强制这种“刷新”发生有什么想法吗?
最佳答案
所以这似乎是预期的行为(因此会使我的 FF 出现问题)。
来自the specs关于帧请求算法:
A new frame is requested from the canvas when frameCaptureRequested is true and the canvas is painted.
让我们重点关注“和已绘制的 Canvas ”。这意味着我们需要两个这些条件,并且 while captureStream 本身或其frameRate 参数 ellapsing 或像 requestFrame 这样的方法都会将frameCaptureRequested标志设置为true,我们仍然需要新的绘画...
规范甚至有 note说明
This algorithm results in a captured track not starting until something changes in the canvas.
如果在绘制 Canvas 后调用 captureStream,Chrome 似乎确实会生成一个空的 CanvasCaptureMediaStreamTrack。
const ctx = document.createElement('canvas')
.getContext('2d');
ctx.fillRect(0,0,20,20);
// let's request a stream from before it gets painted
// (in the same frame)
const stream1 = ctx.canvas.captureStream();
vid1.srcObject = stream1;
// now let's wait that a frame ellapsed
// (rAF fires before next painting, so we need 2 of them)
requestAnimationFrame(()=>
requestAnimationFrame(()=> {
const stream2 = ctx.canvas.captureStream();
vid2.srcObject = stream1;
})
);
<p>stream initialised in the same frame as the drawings (i.e before paiting).</p>
<video id="vid1" controls autoplay></video>
<p>stream initialised after paiting.</p>
<video id="vid2" controls autoplay></video>
因此,要解决此问题,您应该能够通过与 Canvas 上第一次绘图相同的操作请求流来获取带有帧的流,如上面示例中的 stream1
。
或者,您可以在将其 globalCompositeOperation 设置为“copy”后调用 ctx.drawImage(ctx.canvas,0,0)
来重新绘制 Canvas 上下文(假设它是 2d 上下文) '以避免透明度问题。
const ctx = document.createElement('canvas')
.getContext('2d');
ctx.font = '15px sans-serif';
ctx.fillText('if forced to redraw it should work', 20, 20);
// produce a silent stream again
requestAnimationFrame(() =>
requestAnimationFrame(() => {
const stream = ctx.canvas.captureStream();
forcePainting(stream);
vid.srcObject = stream;
})
);
// beware will work only for canvas intialised with a 2D context
function forcePainting(stream) {
const ctx = (stream.getVideoTracks()[0].canvas ||
stream.canvas) // FF has it wrong...
.getContext('2d');
const gCO = ctx.globalCompositeOperation;
ctx.globalCompositeOperation = 'copy';
ctx.drawImage(ctx.canvas, 0, 0);
ctx.globalCompositeOperation = gCO;
}
<video id="vid" controls autoplay></video>
关于javascript - captureStream() 中的第一帧未发送,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50929159/