javascript - captureStream() 中的第一帧未发送

标签 javascript canvas mediarecorder requestframe capturestream

我们正在开发一个项目,人们可以使用网络摄像头在聊天室中,他们可以抓取某人当时摄像头的快照,在其上做一些注释,然后将修改后的图片分享为如果是他们自己的网络摄像头(例如共享白板)。

将网络摄像头流捕获到可以编辑的 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/

相关文章:

javascript - 单击另一个 select2 下拉列表时如何保持 select2 下拉事件打开

javascript - 使用 imageData 获取第一行和最后一行像素

css - 在 html Canvas 圆圈中有多个可点击的部分,在每个被点击的区域填充颜色

Android MediaRecorder 和 setOutputFile

c# - 无法对 json 响应对象执行 for 循环

javascript - 使用 JQuery 创建后未检查具有必需 Prop 的输入

javascript - Jquery一题

javascript - 如何将表单的结果转换为图像

Android 取消媒体录制

android - 无法打开文件(权限被拒绝)