27

私は次のことをしたいプロジェクトに取り組んでいます:

  1. ビデオ js を読み込み、キャンバスに表示します。
  2. フィルターを使用して、キャンバス (したがってビデオ) の外観を変更します。
  3. MediaStream の captureStream() メソッドと MediaRecorder オブジェクトを使用して、キャンバスの表面と元のビデオのオーディオを記録します。
  4. キャンバスとオーディオの両方のストリームを HTML ビデオ要素で再生します。

この WebRTC デモ コードを微調整することで、動画要素にキャンバスの記録を表示することができました: https://webrtc.github.io/samples/src/content/capture/canvas-record/

とはいえ、キャンバスと一緒にビデオのオーディオを録音する方法がわかりません。2 つの異なるソース/要素から MediaStreamTrack インスタンスを含む MediaStream を作成することは可能ですか?

MediaStream API の仕様によると、理論的にはこれを達成する方法があるはずです: https://w3c.github.io/mediacapture-main/#introduction

「MediaStream API の 2 つの主要なコンポーネントは、MediaStreamTrack および MediaStream インターフェイスです。MediaStreamTrack オブジェクトは、ユーザー エージェント内の 1 つのメディア ソースから発生する単一タイプのメディアを表します。たとえば、Web カメラによって生成されたビデオです。MediaStream はグループ化に使用されます。複数の MediaStreamTrack オブジェクトを、メディア要素で記録またはレンダリングできる 1 つのユニットにします。」

4

2 に答える 2

42

2 つの異なるソース/要素から MediaStreamTrack インスタンスを含む MediaStream を作成することは可能ですか?

はい、MediaStream.addTrack()メソッドを使用してそれを行うことができます。

ただし、このバグが修正されるまで、Firefox は最初のストリームのトラックのみをレコーダーに使用します。


OP は、すべてを取得する方法を既に知っていますが、将来の読者のためのリマインダーを次に示します。

  • キャンバスから videoStream トラックを取得するには、canvas.captureStream(framerate)メソッドを呼び出します。

  • ビデオ要素からオーディオ streamTrack を取得するには、WebAudio API とそのcreateMediaStreamDestinationメソッドを使用できます。destこれにより、audioStream を含むMediaStreamDestination ノード ( ) が返されます。次に、ビデオ要素から作成されたMediaElementSourceを thisに接続する必要がありますdest。このストリームにさらにオーディオ トラックを追加する必要がある場合は、これらすべてのソースを に接続する必要がありますdest

キャンバス ビデオ用とオーディオ用の 2 つのストリームを取得したのでcanvasStream.addTrack(audioStream.getAudioTracks()[0])new MediaRecorder(canvasStream).

これは完全な例で、現在は chrome でのみ動作しますが、バグが修正されると、Firefox ですぐに動作する可能性があります。

var cStream,
  aStream,
  vid,
  recorder,
  analyser,
  dataArray,
  bufferLength,
  chunks = [];

function clickHandler() {

  this.textContent = 'stop recording';
  cStream = canvas.captureStream(30);
  cStream.addTrack(aStream.getAudioTracks()[0]);

  recorder = new MediaRecorder(cStream);
  recorder.start();

  recorder.ondataavailable = saveChunks;

  recorder.onstop = exportStream;

  this.onclick = stopRecording;

};

function exportStream(e) {

  if (chunks.length) {

    var blob = new Blob(chunks)
    var vidURL = URL.createObjectURL(blob);
    var vid = document.createElement('video');
    vid.controls = true;
    vid.src = vidURL;
    vid.onend = function() {
      URL.revokeObjectURL(vidURL);
    }
    document.body.insertBefore(vid, canvas);

  } else {

    document.body.insertBefore(document.createTextNode('no data saved'), canvas);

  }
}

function saveChunks(e) {

  e.data.size && chunks.push(e.data);

}

function stopRecording() {

  vid.pause();
  this.parentNode.removeChild(this);
  recorder.stop();

}

function initAudioStream(evt) {

  var audioCtx = new AudioContext();
  // create a stream from our AudioContext
  var dest = audioCtx.createMediaStreamDestination();
  aStream = dest.stream;
  // connect our video element's output to the stream
  var sourceNode = audioCtx.createMediaElementSource(this);
  sourceNode.connect(dest)
    // start the video
  this.play();

  // just for the fancy canvas drawings
  analyser = audioCtx.createAnalyser();
  sourceNode.connect(analyser);

  analyser.fftSize = 2048;
  bufferLength = analyser.frequencyBinCount;
  dataArray = new Uint8Array(bufferLength);
  analyser.getByteTimeDomainData(dataArray);

  // output to our headphones
  sourceNode.connect(audioCtx.destination)

  startCanvasAnim();

  rec.onclick = clickHandler;
  rec.disabled = false;
};

var loadVideo = function() {

  vid = document.createElement('video');
  vid.crossOrigin = 'anonymous';
  vid.oncanplay = initAudioStream;
  vid.src = 'https://dl.dropboxusercontent.com/s/bch2j17v6ny4ako/movie720p.mp4';

  
}

function startCanvasAnim() {
  // from MDN https://developer.mozilla.org/en/docs/Web/API/AnalyserNode#Examples
  var canvasCtx = canvas.getContext('2d');

  canvasCtx.fillStyle = 'rgb(200, 200, 200)';
  canvasCtx.lineWidth = 2;
  canvasCtx.strokeStyle = 'rgb(0, 0, 0)';

  var draw = function() {

    var drawVisual = requestAnimationFrame(draw);

    analyser.getByteTimeDomainData(dataArray);

    canvasCtx.fillRect(0, 0, canvas.width, canvas.height);
    canvasCtx.beginPath();

    var sliceWidth = canvas.width * 1.0 / bufferLength;
    var x = 0;

    for (var i = 0; i < bufferLength; i++) {

      var v = dataArray[i] / 128.0;
      var y = v * canvas.height / 2;

      if (i === 0) {
        canvasCtx.moveTo(x, y);
      } else {
        canvasCtx.lineTo(x, y);
      }

      x += sliceWidth;
    }

    canvasCtx.lineTo(canvas.width, canvas.height / 2);
    canvasCtx.stroke();

  };

  draw();

}

loadVideo();
<canvas id="canvas" width="500" height="200"></canvas>
<button id="rec" disabled>record</button>


Ps : FF チームはバグの修正に時間がかかるようですので、FF でも動作するように簡単に修正します。

を使用して 2 つのトラックをミックスすることもできますnew MediaStream([track1, track2])
ただし、現在、chrome はこのコンストラクターのプレフィックスを付けていますが、サポートしているためaddTrack、実際には必要ありません。

var mixedStream = 'MediaStream' in window ? 
  new MediaStream([cStream.getVideoTracks()[0], aStream.getAudioTracks()[0]]) :
  cStream;
recorder = new MediaRecorder(mixedStream);

FF と chrome の両方で機能するフィドル。

于 2016-09-03T03:37:16.120 に答える