8

为了录制音频和视频,我在MediaRecorder API的ondataavailable下创建 webm 文件。我必须单独播放每个创建的 webm 文件。

Mediarecorder api 仅将标头信息插入第一个块(webm 文件),因此其余块在没有标头信息的情况下不会单独播放。

正如建议的链接 1链接 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 文件中复制了标题和集群信息,并将其放入第二个文件中,如下图所示,但没有成功,

webm 十六进制

问题

我在这里做错了什么?

有什么方法可以单独播放 webm 文件/块?

注意:我不能使用MediaSource播放这些块。

编辑 1

正如@Brad 建议的那样,我想将第一个集群之前的所有内容插入到后面的集群中。我有几个 webm 文件,每个文件的持续时间为 5 秒。深入研究文件后,我才知道,几乎每个备用文件都没有聚类点(没有 0x1F43B675)。

在这里我很困惑,我必须在每个文件的开头或每个第一个集群的开头插入标题信息(初始化数据)?如果我选择稍后的选项,那么如何播放没有任何集群的 webm 文件?

或者,首先我需要使每个 webm 文件在一开始就具有集群的方式,这样我就可以在这些文件中的集群之前添加头信息?

编辑 2

经过一番挖掘和阅读后我得出结论,每个 webm 文件都需要头信息、集群和实际数据。

4

2 回答 2

5

// 对于最常规的webm文件,头信息是存在的

// 0 到 189 个 Uint8 数组元素之间

没有看到实际的文件数据很难说,但这可能是错误的。“标题信息”需要是第一个Cluster 元素之前的所有内容。也就是说,您希望保留从文件开始到您看到之前的所有数据0x1F43B675并将其视为初始化数据。这可以/将因文件而异。在我的测试文件中,这发生在 1 KB 之后。

并将此标头信息附加到第二个块中,第二个块仍然无法播放,但这次浏览器显示视频海报(单帧)和两个块总和的持续时间,例如:10秒;每个块的持续时间为 5 秒。

MediaRecorder 输出的块与分段无关,并且可以在不同时间发生。您实际上希望在 Cluster 元素上进行拆分。这意味着您需要解析这个 WebM 文件,至少在它们的标识符0x1F43B675出现时将集群拆分出来。

有什么方法可以单独播放 webm 文件/块?

您走在正确的道路上,只需将第一个集群之前的所有内容添加到后面的集群即可。

一旦你完成了这项工作,你可能会遇到的下一个问题是你将无法使用任何集群来执行此操作。第一个集群必须以关键帧开头,否则浏览器不会对其进行解码。Chrome 会跳到下一个集群,但它并不可靠。不幸的是,没有办法使用 MediaRecorder 配置关键帧位置。如果你足够幸运能够在服务器端处理这个视频,下面是使用 FFmpeg 的方法: https ://stackoverflow.com/a/45172617/362536

于 2020-06-06T20:35:30.343 回答
0

好吧,看起来这并不像你必须扫描 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。

于 2020-06-06T22:59:26.037 回答