如何正确录制 MediaStream?
How can I properly record a MediaStream?
情况
我需要做以下事情:
从 <video>
获取视频并在 <canvas>
中播放
将来自 canvas 的流记录为 Blob
就是这样。第一段还行
对于第二部分,我设法记录了一个 Blob。问题是 Blob 是空的。
景色
<video id="video" controls="true" src="http://upload.wikimedia.org/wikipedia/commons/7/79/Big_Buck_Bunny_small.ogv"></video>
<canvas id="myCanvas" width="532" height="300"></canvas>
代码
// Init
console.log(MediaRecorder.isTypeSupported('video/webm')) // true
const canvas = document.querySelector("canvas")
const ctx = canvas.getContext("2d")
const video = document.querySelector("video")
// Start the video in the player
video.play()
// On play event - draw the video in the canvas
video.addEventListener('play', () => {
function step() {
ctx.drawImage(video, 0, 0, canvas.width, canvas.height)
requestAnimationFrame(step)
}
requestAnimationFrame(step);
// Init stream and recorder
const stream = canvas.captureStream()
const recorder = new MediaRecorder(stream, {
mimeType: 'video/webm',
});
// Get the blob data when is available
let allChunks = [];
recorder.ondataavailable = function(e) {
console.log({e}) // img1
allChunks.push(e.data);
}
// Start to record
recorder.start()
// Stop the recorder after 5s and check the result
setTimeout(() => {
recorder.stop()
const fullBlob = new Blob(allChunks, { 'type' : 'video/webm' });
const downloadUrl = window.URL.createObjectURL(fullBlob)
console.log({fullBlob}) // img2
}, 5000);
})
结果
这是 ondataavailable
事件的 console.log
:
这是 Blob 的 console.log
:
fiddle
这是 JSFiddle。您可以在控制台查看结果:
https://jsfiddle.net/1b7v2pen/
浏览器行为
此行为(Blob 数据大小:0)发生在 Chrome 和 Opera 上。
在 Firefox 上它的行为略有不同。
它记录了一个非常小的视频 Blob(725 字节)。视频长度应该是5秒,但就是黑屏
问题
从 canvas 录制流的正确方法是什么?
代码有问题吗?
为什么 Blob 出来是空的?
MediaRecorder.stop()
是 一种 异步方法。
在stop算法中,有一个调用requestData,它本身会排队一个任务来触发一个事件dataavailable 自上次此类事件以来的当前可用数据。
这意味着在您调用 MediaRecorder#stop() 之后同步获取的最后数据还不会成为您的 allChunks
数组的一部分。他们会在不久之后变得(通常在同一个事件循环中)。
因此,当您要保存从 MediaRecorder 制作的记录时,请务必始终从 MediaRecorder's onstop
事件构建最终的 Blob,这将发出 MediaRecorder 实际结束的信号,并触发了它的最后一个dataavailable 事件,一切都很好。
一开始我错过了一件事,就是您请求的是跨域视频。如果没有正确的跨源请求,这样做会使您的 canvas(和 MediaElement)受到污染,因此您的 MediaStream 将被静音。
由于您尝试请求的视频来自维基媒体,您可以简单地将其作为跨源资源请求,但对于其他资源,您必须确保服务器配置为允许这些请求。
const canvas = document.querySelector("canvas")
const ctx = canvas.getContext("2d")
const video = document.querySelector("video")
// Start the video in the player
video.play()
// On play event - draw the video in the canvas
video.addEventListener('play', () => {
function step() {
ctx.drawImage(video, 0, 0, canvas.width, canvas.height)
requestAnimationFrame(step)
}
requestAnimationFrame(step);
// Init stream and recorder
const stream = canvas.captureStream()
const recorder = new MediaRecorder(stream, {
mimeType: 'video/webm',
});
// Get the blob data when is available
let allChunks = [];
recorder.ondataavailable = function(e) {
allChunks.push(e.data);
}
recorder.onstop = (e) => {
const fullBlob = new Blob(allChunks, { 'type' : 'video/webm' });
const downloadUrl = window.URL.createObjectURL(fullBlob)
console.log({fullBlob})
console.log({downloadUrl})
}
// Start to record
recorder.start()
// Stop the recorder after 5s and check the result
setTimeout(() => {
recorder.stop()
}, 5000);
})
<!--add the 'crossorigin' attribute to your video -->
<video id="video" controls="true" src="https://upload.wikimedia.org/wikipedia/commons/7/79/Big_Buck_Bunny_small.ogv" crossorigin="anonymous"></video>
<canvas id="myCanvas" width="532" height="300"></canvas>
此外,我不得不指出,如果您不对 canvas 进行任何特殊绘图,您可能希望直接保存视频源,或者至少,直接录制
情况
我需要做以下事情:
从
中播放<video>
获取视频并在<canvas>
将来自 canvas 的流记录为 Blob
就是这样。第一段还行
对于第二部分,我设法记录了一个 Blob。问题是 Blob 是空的。
景色
<video id="video" controls="true" src="http://upload.wikimedia.org/wikipedia/commons/7/79/Big_Buck_Bunny_small.ogv"></video>
<canvas id="myCanvas" width="532" height="300"></canvas>
代码
// Init
console.log(MediaRecorder.isTypeSupported('video/webm')) // true
const canvas = document.querySelector("canvas")
const ctx = canvas.getContext("2d")
const video = document.querySelector("video")
// Start the video in the player
video.play()
// On play event - draw the video in the canvas
video.addEventListener('play', () => {
function step() {
ctx.drawImage(video, 0, 0, canvas.width, canvas.height)
requestAnimationFrame(step)
}
requestAnimationFrame(step);
// Init stream and recorder
const stream = canvas.captureStream()
const recorder = new MediaRecorder(stream, {
mimeType: 'video/webm',
});
// Get the blob data when is available
let allChunks = [];
recorder.ondataavailable = function(e) {
console.log({e}) // img1
allChunks.push(e.data);
}
// Start to record
recorder.start()
// Stop the recorder after 5s and check the result
setTimeout(() => {
recorder.stop()
const fullBlob = new Blob(allChunks, { 'type' : 'video/webm' });
const downloadUrl = window.URL.createObjectURL(fullBlob)
console.log({fullBlob}) // img2
}, 5000);
})
结果
这是 ondataavailable
事件的 console.log
:
这是 Blob 的 console.log
:
fiddle
这是 JSFiddle。您可以在控制台查看结果:
https://jsfiddle.net/1b7v2pen/
浏览器行为
此行为(Blob 数据大小:0)发生在 Chrome 和 Opera 上。
在 Firefox 上它的行为略有不同。
它记录了一个非常小的视频 Blob(725 字节)。视频长度应该是5秒,但就是黑屏
问题
从 canvas 录制流的正确方法是什么?
代码有问题吗?
为什么 Blob 出来是空的?
MediaRecorder.stop()
是 一种 异步方法。
在stop算法中,有一个调用requestData,它本身会排队一个任务来触发一个事件dataavailable 自上次此类事件以来的当前可用数据。
这意味着在您调用 MediaRecorder#stop() 之后同步获取的最后数据还不会成为您的 allChunks
数组的一部分。他们会在不久之后变得(通常在同一个事件循环中)。
因此,当您要保存从 MediaRecorder 制作的记录时,请务必始终从 MediaRecorder's onstop
事件构建最终的 Blob,这将发出 MediaRecorder 实际结束的信号,并触发了它的最后一个dataavailable 事件,一切都很好。
一开始我错过了一件事,就是您请求的是跨域视频。如果没有正确的跨源请求,这样做会使您的 canvas(和 MediaElement)受到污染,因此您的 MediaStream 将被静音。
由于您尝试请求的视频来自维基媒体,您可以简单地将其作为跨源资源请求,但对于其他资源,您必须确保服务器配置为允许这些请求。
const canvas = document.querySelector("canvas")
const ctx = canvas.getContext("2d")
const video = document.querySelector("video")
// Start the video in the player
video.play()
// On play event - draw the video in the canvas
video.addEventListener('play', () => {
function step() {
ctx.drawImage(video, 0, 0, canvas.width, canvas.height)
requestAnimationFrame(step)
}
requestAnimationFrame(step);
// Init stream and recorder
const stream = canvas.captureStream()
const recorder = new MediaRecorder(stream, {
mimeType: 'video/webm',
});
// Get the blob data when is available
let allChunks = [];
recorder.ondataavailable = function(e) {
allChunks.push(e.data);
}
recorder.onstop = (e) => {
const fullBlob = new Blob(allChunks, { 'type' : 'video/webm' });
const downloadUrl = window.URL.createObjectURL(fullBlob)
console.log({fullBlob})
console.log({downloadUrl})
}
// Start to record
recorder.start()
// Stop the recorder after 5s and check the result
setTimeout(() => {
recorder.stop()
}, 5000);
})
<!--add the 'crossorigin' attribute to your video -->
<video id="video" controls="true" src="https://upload.wikimedia.org/wikipedia/commons/7/79/Big_Buck_Bunny_small.ogv" crossorigin="anonymous"></video>
<canvas id="myCanvas" width="532" height="300"></canvas>
此外,我不得不指出,如果您不对 canvas 进行任何特殊绘图,您可能希望直接保存视频源,或者至少,直接录制