Is it possible to create a MediaStream containing MediaStreamTrack instances from two different sources/elements?
Да, вы можете сделать это с помощью метода MediaStream.addTrack()
.
Но Firefox будет использовать дорожки начального потока только в рекордере, пока не будет исправлено this bug.
OP уже известно, как получить все это, но здесь является напоминанием для будущих читателей:
Чтобы получить видеопоток дорожки из холста, вы можете вызвать метод canvas.captureStream(framerate)
.
Чтобы получить аудиопоток из видеоэлемента, вы можете использовать API WebAudio и его метод createMediaStreamDestination
. Это вернет узел MediaStreamDestination (dest
), содержащий наш аудиопоток. Затем вам нужно будет подключить MediaElementSource, созданный с вашего видеоэлемента, к этому dest
. Если вам нужно добавить больше аудиодорожек в этот поток, вы должны подключить все эти источники к dest
.
Теперь, когда у нас есть два потока, один для холста видео и для аудио, мы можем использовать canvasStream.addTrack(audioStream.getAudioTracks()[0])
только перед инициализацией нашей new MediaRecorder(canvasStream)
.
Вот полный пример, который будет работать только в хроме сейчас, и, вероятно, в скором времени в 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.
Вы также можете смешивать две дорожки, используя new MediaStream([track1, track2])
.
Однако, хром в настоящее время префиксы этот конструктор, но так как он поддерживает addTrack
, это на самом деле не нужны, и мы можем прийти с чем-то, как некрасиво, как
var mixedStream = 'MediaStream' in window ?
new MediaStream([cStream.getVideoTracks()[0], aStream.getAudioTracks()[0]]) :
cStream;
recorder = new MediaRecorder(mixedStream);
Working fiddle for both FF and chrome.
, поскольку принятый ответ не конкретно о p5.js, вы не возражаете, если я удалю его из названия и тела вашего вопроса? – Kaiido
Совсем нет, иди за ним. – dsing7