7

使用CanvasCaptureMediaStream和 MediaRecorder 时,有没有办法在每一帧上获取一个事件?

我需要的是没有什么不同requestAnimationFrame(),但我需要 CanvasCaptureMediaStream(和/或 MediaRecorder)而不是窗口。MediaRecorder 可能以与窗口不同的帧速率运行(可能以不规则的可分速率运行,例如 25 FPS 与 60 FPS),因此我想以其帧速率而不是窗口的帧速率更新画布。

4

1 回答 1

9

目前,此示例仅在Firefox上完全工作,因为Chrome在Tab模糊时只是停止帆布流... (可能与此错误有关,但好吧,我的计时器似乎在工作,但录制却没有...)

[编辑]:它现在实际上只适用于 chrome,因为他们已经修复了这个错误,但由于这个问题(由 e10s 引起),FF 不再适用。


MediaStream 上似乎没有任何事件让您知道帧何时被渲染,MediaRecorder 上也没有。

甚至currentTimeMediaStream 的属性(目前仅在 FF 中可用)似乎也没有随着captureStream()方法中传递的 fps 参数而相应改变。

但是您似乎想要的是一个可靠的计时器,当当前选项卡没有聚焦时(这发生在 rAF),它不会失去其频率。
幸运的是,WebAudio API 也有一个高精度计时器,它基于硬件时钟,而不是屏幕刷新率。

所以我们可以提供一个替代的定时循环,即使标签模糊也能保持它的频率。

/*
	An alternative timing loop, based on AudioContext's clock

	@arg callback : a callback function 
		with the audioContext's currentTime passed as unique argument
	@arg frequency : float in ms;
	@returns : a stop function
	
*/
function audioTimerLoop(callback, frequency) {

  // AudioContext time parameters are in seconds
  var freq = frequency / 1000;

  var aCtx = new AudioContext();
  // Chrome needs our oscillator node to be attached to the destination
  // So we create a silent Gain Node
  var silence = aCtx.createGain();
  silence.gain.value = 0;
  silence.connect(aCtx.destination);

  onOSCend();

  var stopped = false;
  function onOSCend() {
    osc = aCtx.createOscillator();
    osc.onended = onOSCend;
    osc.connect(silence);
    osc.start(0);
    osc.stop(aCtx.currentTime + freq);
    callback(aCtx.currentTime);
    if (stopped) {
      osc.onended = function() {
        return;
      };
    }
  };
  // return a function to stop our loop
  return function() {
    stopped = true;
  };
}


function start() {

  // start our loop @25fps
  var stopAnim = audioTimerLoop(anim, 1000 / 25);
  // maximum stream rate set as 25 fps
  cStream = canvas.captureStream(25);

  let chunks = [];
  var recorder = new MediaRecorder(cStream);
  recorder.ondataavailable = e => chunks.push(e.data);
  recorder.onstop = e => {
    // we can stop our loop
    stopAnim();
    var url = URL.createObjectURL(new Blob(chunks));
    var v = document.createElement('video');
    v.src = url;
    v.controls = true;
    document.body.appendChild(v);
  }
  recorder.start();
  // stops the recorder in 20s, try to change tab during this time
  setTimeout(function() {
    recorder.stop();
  }, 20000)
}


// make something move on the canvas
var ctx = canvas.getContext('2d');
var x = 0;
function anim() {
  x = (x + 2) % (canvas.width + 100);
  ctx.fillStyle = 'ivory';
  ctx.fillRect(0, 0, canvas.width, canvas.height);
  ctx.fillStyle = 'red';
  ctx.fillRect(x - 50, 20, 50, 50)
};

btn.onclick = start;
<button id="btn">begin</button>
<canvas id="canvas" width="500" height="200"></canvas>

Nota Bene
在这个例子中,我将频率设置为 25fps,但我们可以将其设置为 60fps,即使在我的旧笔记本上它似乎也能正常工作,至少在这样一个简单的动画中是这样。

于 2016-11-19T09:12:25.207 回答