我有一个持续时间为9200毫秒的视频和一个显示用户摄像头视频的画布。我的目标是在原始视频播放时录制网络摄像头视频,以创建一个持续时间完全相同的输出blob。MediaRecorder
但似乎总是得到一个较长的视频长度(通常约9400ms)。
我发现,如果我把持续时间的差异,在输出视频中提前跳过这个量,基本上就能和原始视频同步了,但我希望不必使用这个黑客。了解到这一点后,我以为差异是由于HTML5视频的 play()
函数是异步的,但即使调用 recorder.start()
里面 .then()
之后 play()
承诺的结果仍然是输出一个持续时间较长的blob。
I start()
的 MediaRecorder
之后 play()
原始视频,并调用 stop()
里面 requestAnimationFrame
当我看到原始视频已经结束时,就会循环播放。改变 MediaRecorder.start()
始于 requestAnimationFrame
循环后才检查原始视频的播放也会导致较长的输出blob。
输出时间较长的原因可能是什么?从文档中看,似乎并没有 MediaRecorder
的启动或停止函数是异步的,那么是否有什么方法可以保证HTML5视频的准确启动时间和 MediaRecorder
?
是的,是的。start()
和 stop()
是异步的,这就是为什么我们有了 onstart
和 onstop
事件的发生。
const stream = makeEmptyStream();
const rec = new MediaRecorder(stream);
rec.onstart = (evt) => { console.log( "took %sms to start", performance.now() - begin ); };
const begin = performance.now();
rec.start();
setTimeout( () => {
rec.onstop = (evt) => { console.log( "took %sms to stop", performance.now() - begin ); };
const begin = performance.now();
rec.stop();
}, 1000 );
function makeEmptyStream() {
const canvas = document.createElement('canvas');
canvas.getContext('2d').fillRect(0,0,1,1);
return canvas.captureStream();
}
因此,你可以尝试在视频准备好后暂停播放,然后等待录像机启动后再重新开始播放视频。然而,由于HTMLMediaElement和MediaRecorder中的所有内容都是异步的,所以无法得到完美的1对1关系......
const vid = document.querySelector('video');
onclick = (evt) => {
onclick = null;
vid.play().then( () => {
// pause immediately the main video
vid.pause();
// we may have advanced of a few µs already, so go back to beginning
vid.currentTime = 0;
// only when we're back to beginning
vid.onseeked = (evt) => {
console.log( 'recording will begin shortly, please wait until the end of the video' );
console.log( 'original %ss', vid.duration );
const stream = vid.captureStream ? vid.captureStream() : vid.mozCaptureStream();
const chunks = [];
const rec = new MediaRecorder( stream );
rec.ondataavailable = (evt) => {
chunks.push( evt.data );
};
rec.onstop = (evt) => {
logVideoDuration( new Blob( chunks ), "recorded %ss" );
};
vid.onended = (evt) => {
rec.stop();
};
// wait until the recorder is ready before playing the video again
rec.onstart = (evt) => {
vid.play();
};
rec.start();
};
} );
function logVideoDuration( blob, name ) {
const el = document.createElement('video');
el.src = URL.createObjectURL( blob );
el.play().then( () => {
el.pause();
el.onseeked = (evt) => console.log( name, el.duration );
el.currentTime = 10e25;
} );
}
};
video { pointer-events: none; width: 100% }
click to start<br>
<video src="https://upload.wikimedia.org/wikipedia/commons/a/a4/BBH_gravitational_lensing_of_gw150914.webm" controls crossorigin></video>
另外要注意的是,您的媒体声明的持续时间、录制媒体的计算持续时间和它们的实际持续时间可能会有一些差异。事实上,这些持续时间通常只是文件元数据中硬编码的一个值,但考虑到MediaRecorder API的工作方式,在那里很难做到这一点,所以例如Chrome会产生没有持续时间的文件,而播放器会尝试根据他们可以在媒体中寻找的最后一个点来近似这个持续时间。