Ami*_*mit 17 video html5-video webcodecs
我想从上传到我的网站的多个图像创建一个视频文件。
到目前为止,我所做的就是拍摄这些图像,将它们一一绘制在画布上,然后使用 APIMediaRecorder来记录它们。不过,空闲时间还是很多的。
相反,我想使用VideoEncoderAPI。
我创建了一个编码器,将每个块保存为缓冲区:
const chunks = [];
let encoder = new VideoEncoder({
output: (chunk) => {
const buffer = new ArrayBuffer(chunk.byteLength)
chunk.copyTo(buffer);
chunks.push(buffer);
},
error: (e) => console.error(e.message)
});
Run Code Online (Sandbox Code Playgroud)
并使用我的设置对其进行配置:
encoder.configure({
codec: 'vp8',
width: 256,
height: 256,
bitrate: 2_000_000,
framerate: 25
});
Run Code Online (Sandbox Code Playgroud)
然后,我将每个图像编码为帧:
const frame = new VideoFrame(await createImageBitmap(image));
encoder.encode(frame, {keyFrame: true});
frame.close();
Run Code Online (Sandbox Code Playgroud)
最后,我尝试用它创建一个视频:
await encoder.flush();
const blob = new Blob(chunks, {type: 'video/webm; codecs=vp8'});
const url = URL.createObjectURL(blob);
Run Code Online (Sandbox Code Playgroud)
但是,该 URL 块无法播放。如果我尝试下载它,VLC 不会显示它。如果我将其设置为元素的源video,我会得到:
DOMException:该元素没有受支持的源。
如何将多个帧编码为可播放的视频?
我如何知道支持哪些编解码器/blob 类型?
以下代码笔是将上述代码连接并连接成一个函数。 https://codepen.io/AmitMY/pen/OJxgPoG?editors=0010
Mac*_*iej 14
VideoEncoder以及 WebCodecs API 中的其他类为您提供了将图像编码为视频流中的帧的方法,但是编码只是创建可播放的多媒体文件的第一步。像这样的文件可能包含多个流 - 例如,当您有一个带声音的视频时,它已经至少有一个视频和一个音频流,所以总共有两个。您需要额外的容器格式来存储流,以便您不必在单独的文件中发送流。要从任意数量的流(甚至只有一个)创建容器文件,您需要一个多路复用器(简称复用器)。很好的主题总结可以在这个Stack Overflow 答案中找到,但要引用重要的部分:
\n\n\n
\n- 创建多媒体文件时,您使用编码器算法对视频和音频数据进行编码,然后使用复用器将流一起放入文件(容器)中。为了播放文件,解复用器分解流并将它们输入解码器以获得视频和音频数据。
\n- Codec的意思是编码器/解码器,是与容器格式不同的概念。许多容器格式可以容纳许多不同类型的格式(AVI 和 QuickTime/MOV 非常通用)。其他格式仅限于一种或两种媒体类型。
\n
您可能会想“我只有一个流,我真的需要一个容器吗?” 但多媒体播放器希望接收到的数据(从文件读取的数据或通过网络传输的数据)采用容器格式。即使您只有一个视频流,您仍然需要将其打包到容器中以便他们识别。
\n将字节缓冲区连接到一大块数据中是行不通的:
\nconst blob = new Blob(chunks, {type: \'video/webm; codecs=vp8\'});\nRun Code Online (Sandbox Code Playgroud)\n在这里,您尝试将所有块粘合在一起并告诉浏览器将其解释为 WebM 视频(视频/webm MIME 类型),但它无法做到这一点,因为它的格式不正确。这又是错误的根源。为了使其工作,您必须将相关元数据附加到块(通常格式化为具有特定格式的二进制数据缓冲区,具体取决于容器和编解码器的类型)并将其传递给复用器。如果您使用旨在处理原始视频流(例如来自 WebCodecs API 的视频流)的多路复用库,那么它可能会为您处理元数据。作为一名程序员,您很可能不必手动处理此问题,但是如果您想更多地了解整个过程,那么我建议您阅读有关各种容器格式中存在的元数据(例如,此答案下面的 VC.Ones 评论) 。
\n遗憾的是,到目前为止,复用器似乎还不是 WebCodecs API 的一部分。API官方存储库中的示例使用该函数作为编码器输出回调:muxAndSend()
const videoEncoder = new VideoEncoder({\n output: muxAndSend,\n error: onEncoderError,\n});\nRun Code Online (Sandbox Code Playgroud)\n在上面的代码中我们可以看到这个函数需要由程序员提供(原注释):
\n// The app provides a way to serialize/containerize encoded media and upload it.\n// The browser provides the app byte arrays defined by a codec such as vp8 or opus\n// (not in a media container such as mp4 or webm).\nfunction muxAndSend(encodedChunk) { ... };\nRun Code Online (Sandbox Code Playgroud)\n这是有关向浏览器添加多路复用支持的讨论的链接,这是跟踪此功能的官方存储库中的一个问题。截至目前,似乎还没有针对您的问题的内置解决方案。
\n为了解决这个问题,您可以使用第三方库,例如mux.js或类似的库(这里是他们的“基本用法”示例的链接,可能会对您有所帮助)。或者,该项目声称用VideoEncoder编码数据创建 WebM 容器。他们的演示描述的摘录似乎正是您想要实现的目标(除了使用网络摄像头作为源VideoFrame,而不是画布):
\n\n当您单击“开始”按钮时,浏览器将要求您\xe2\x80\x99授予捕获相机和麦克风的权限。然后,来自每个的数据被传递给两个独立的工作人员,他们使用 WebCodecs 浏览器 API 将视频编码为 VP9,将音频编码为 Opus。
\n来自每个工作器的编码视频和音频被传递到第三个工作器,该工作器将其复用为 WebM 格式。
\n
我无法为您提供代码示例,因为我自己没有使用任何提到的库,但我确信在了解编码器和复用器之间的关系后,您应该能够自己解决问题。
\n编辑: \n我找到了另一个可能对您有帮助的库。根据他们的自述文件:
\n\n\n支持什么:
\n\n
\n- MP4 视频复用(获取已编码的 H264 帧并将其包装在 MP4 容器中)
\n- 通过 WebCodecs 进行 MP4/H264 编码和复用
\n
我在网上找到的许多库和源似乎都是基于 WASM 的,通常用 C 或其他语言编译为本机代码来实现。这可能是因为存在处理各种媒体格式的大型库(首先想到的是ffmpeg ),这就是它们的编写内容。JS 库通常被编写为与所述本机代码的绑定以避免重新发明轮子。此外,我认为性能也可能是一个因素。
\n免责声明:虽然您在代码示例中使用 video/webm 作为 MIME 类型,但您没有明确说明您希望输出的文件格式是什么,因此我允许自己引用一些生成其他格式的库。
\n编辑2:
\nDavid Kanal 下面的回答提供了另一个可用于混合 WebM 的库的示例。
\n制作了 MP4 的复用器:https ://github.com/Vanilagy/mp4-muxer
由于我为此主题找到的库不足以满足我的需求,因此我创建了自己的库: https: //github.com/Vanilagy/webm-muxer
这是一个纯 TypeScript 中的全功能WebM 混合器(视频 + 音频),不需要大量的 wasm 文件。自述文件中详细解释了用法。该库为我的基于浏览器的游戏中的视频录制功能提供支持。
我想我应该在这个话题上花两分钱,因为我最近在努力解决OP提到的完全相同的事情。
我设法找到了渲染和导出 WebM 文件的解决方案,尽管没有音频。
我在这里找到了 W3C 的官方示例:https://w3c.github.io/webcodecs/samples/capture-to-file/capture-to-file.html。它捕获网络摄像头的视频流并将其保存为磁盘上的 .webm 文件。深入研究代码,负责获取编码视频块并将其写入(混合)到可播放的 WebM 中的代码是webm-writer2.js
将该文件包含在站点中后,编写 WebM 文件所需要做的就是:
// Acquire `fileHandle` somewhere, I use
// https://developer.mozilla.org/en-US/docs/Web/API/Window/showSaveFilePicker
let fileWritableStream = await fileHandle.createWritable();
// This WebMWriter thing comes from the third-party library
let webmWriter = new WebMWriter({
fileWriter: fileWritableStream,
codec: 'VP9',
width: width,
height: height
});
let encoder = new VideoEncoder({
output: chunk => webmWriter.addFrame(chunk),
error: e => console.error(e)
});
// Configure to your liking
encoder.configure({
codec: "vp09.00.10.08",
width: width,
height: height,
bitrate: bitrate,
latencyMode: 'realtime'
});
Run Code Online (Sandbox Code Playgroud)
然后,像往常一样使用将帧泵入编码器encoder.encode(videoFrame)。
希望这对某人有帮助。