captureStream()中的第一帧未发送

时间:2018-06-19 13:02:13

标签: javascript html5 canvas video

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

将网络摄像头流捕获到可以编辑的画布元素中相对容易。在页面上找到canvas元素,并在其上执行.getContext('2d'), 使用一个开放库向其中添加编辑工具。像这样从该画布中抓取流:

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();
}); 

这将正确发布流,但是除非您在画布上绘制其他内容,否则不会发送第一帧。假设您画了2个圆圈,然后点击“分享”,该视频流将开始,但不会显示在收件人的一侧,除非您画一条线,另一个圆圈或任何其他内容。似乎它需要一个框架 change 才能发送数据。

我能够通过执行诸如context.fill();之类的东西来使用开发人员工具来强制执行此操作,但是当我尝试在发布函数之后添加它时,即使在then()中也没有运气。

关于如何强制这种“刷新”的任何想法?

1 个答案:

答案 0 :(得分:3)

所以看来这是预期的行为(因此会使我的FF越野车成为问题)。

来自the specs的有关帧请求算法的信息:

  

frameCaptureRequested true 并绘制画布时,需要从画布请求一个新帧。

我们将重点放在“ 所绘制的画布”上。这意味着我们需要两者这两个条件,并且 captureStream 本身,其frameRate参数变差或类似requestFrame的方法都将设置 frameCaptureRequested 标记为true,我们仍然需要新绘画...

规范中甚至有note声明

  

此算法导致捕获的轨道直到画布中的某些内容发生变化才开始。

如果在绘制画布后对 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>

因此,要解决此问题,您应该能够通过请求与画布上的第一个图形相同的操作来请求带有帧的流,例如上述示例中的stream1

或者,您可以在将其globalCompositeOperation设置为“ copy”以避免透明度问题之后,调用ctx.drawImage(ctx.canvas,0,0)来重绘画布上下文(假设它是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>