为了录制音频和视频,我正在MediaRecorder API的ondataavailable下创建webm文件。我必须分别播放每个创建的webm文件。
Mediarecorder api仅将标头信息插入第一个块(webm文件),因此,其余的块在没有标头信息的情况下不会单独播放。
如建议的link 1和link 2,我从第一个块中提取了标题信息,
// for the most regular webm files, the header information exists
// between 0 to 189 Uint8 array elements
const headerIinformation = arrayBufferFirstChunk.slice(0, 189);
并且将此标头信息附加到第二块中,仍然不能播放第二块,但是这次浏览器正在显示视频的海报(单帧)和两个块的总和持续时间,例如:10秒;每个块的持续时间为5秒。
我使用十六进制编辑器执行的同一标头信息。我在编辑器中打开了webm文件,并将第一个webm文件中的前190个元素复制到了第二个文件中,如下图所示,即使这次,第二个webm文件也无法播放,结果与前面的示例相同。
红色显示标题信息:
这次,我从第一个webm文件复制了标头和群集信息,将其放置到第二个文件中,如下图所示,但没有成功,
问题
我在这里做错了什么?
我们有什么方法可以分别播放webm文件/块吗?
注意:我无法使用MediaSource播放这些块。
编辑1
正如@Brad所建议的,我想将所有内容插入第一个群集之前的内容,再插入到后面的群集中。我有几个webm文件,每个文件的持续时间为5秒。深入研究文件后,我知道几乎每个备用文件都没有聚类点(没有0x1F43B675)。
这里我很困惑,我必须在每个文件的开头或每个第一个群集的开头插入标头信息(初始化数据)?如果我选择一个更高的选项,那么如何播放没有群集的webm文件?
或者,首先,我需要使每个webm文件在开始时就具有聚类的方式,因此我可以在这些文件中的聚类之前添加标头信息?
//对于最常规的webm文件,标头信息存在
//在0到189个Uint8数组元素之间
没有看到实际的文件数据,这很难说,但这可能是错误的。 “标题信息”必须是直到第一个Cluster element为止的所有内容。也就是说,您希望保留从文件开头到看到0x1F43B675
之前的所有数据,并将其视为初始化数据。每个文件可能/将会有所不同。在我的测试文件中,这种情况发生在1 KB英寸之后。
并且将此标头信息附加到第二块中,仍然不能播放第二块,但是这次浏览器正在显示视频的海报(单帧)和两个块的总和持续时间,例如:10秒;每个块的持续时间为5秒。
MediaRecorder输出的块与分段无关,并且可以在不同的时间发生。您实际上可能想在Cluster元素上进行拆分。这意味着您需要解析此WebM文件,至少要等到它们的标识符0x1F43B675
出现时才将它们拆分出来。
我们有什么方法可以分别播放webm文件/块吗?
您走在正确的道路上,只需将第一个群集之前的所有内容放在后面的群集中。
一旦您完成了这项工作,您可能会遇到的下一个问题是,您将无法仅使用任何集群来做到这一点。第一个Cluster must以关键帧开头,否则浏览器不会对其进行解码。 Chrome一定程度上会跳到下一个群集,但这并不可靠。不幸的是,无法使用MediaRecorder配置关键帧放置。如果您有幸能够处理此视频服务器端,请按照以下方法使用FFmpeg:https://stackoverflow.com/a/45172617/362536
好吧,这看起来并不像您必须遍历Blob来查找魔术值那样简单。
let offset = -1;
let value = 0;
const magicNumber = parseInt("0x1F43B675".match(/[a-fA-F0-9]{2}/g).reverse().join(''), 16)
while(value !== magicNumber) {
offset = offset + 1;
try {
const arr = await firstChunk.slice(offset, offset + 4).arrayBuffer().then(buffer => new Int32Array(buffer));
value = arr[0];
}
catch(error) {
return;
}
}
offset = offset + 4;
答案是 193
199
const header = firstChunk.slice(0, offset);
const blobType = firstChunk.type;
const blob = new Blob([header, chunk], { type: blobType });
然后就可以了。现在的问题是我怎么得到这个号码?为什么不是42
的倍数?
蛮力] >>
逻辑很简单,录制视频,收集块,切片第一个块,计算新的blob,然后尝试用HTMLVideoElement
播放。如果失败,则增加偏移量。
(async() => { const microphoneAudioStream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true }); const mediaRecorder = new MediaRecorder(microphoneAudioStream); let chunks = []; mediaRecorder.addEventListener('dataavailable', (event) => { const blob = event.data; chunks = [...chunks, blob]; }); mediaRecorder.addEventListener("stop", async () => { const [firstChunk, ...restofChunks] = chunks; const [secondBlob] = restofChunks; const blobType = firstChunk.type; let index = 0; const video = document.createElement("video"); while(index < 1000) { const header = firstChunk.slice(0, index); const blob = new Blob([header, secondBlob], { type: blobType }); const url = window.URL.createObjectURL(blob); try { video.setAttribute("src", url); await video.play(); console.log(index); break; } catch(error) { } window.URL.revokeObjectURL(url); index++; } }) mediaRecorder.start(200); const stop = () => { mediaRecorder.stop(); } setTimeout(stop, 400) })();
我注意到,对于timeslice
中较小的MediaRecorder.start
参数和setTimeout
中的超时参数,标头偏移变为1。可悲的是,它仍然不是42。