mp4-muxer

mp4-muxer

轻量级JavaScript MP4封装器 支持WebCodecs和多种输出格式

mp4-muxer是一个纯TypeScript实现的MP4封装器,与WebCodecs API无缝集成。支持视频和音频封装,提供Fast Start和分段MP4等内部布局选项。该库性能优异,体积小巧,适用于文件创建和实时流媒体。支持多种主流视频编码(如H.264、H.265、VP9、AV1)和音频编码(AAC、Opus),为开发者提供灵活的媒体处理方案。

MP4 multiplexerWebCodecsJavaScriptTypeScript视频编码Github开源项目

mp4-muxer - JavaScript MP4 复用器

WebCodecs API 提供了对媒体编解码器的低级访问,但没有提供将编码后的媒体打包(复用)成可播放文件的方法。本项目用纯 TypeScript 实现了一个 MP4 复用器,具有高质量、快速和体积小的特点,支持视频和音频,以及各种内部布局,如快速启动或分片 MP4。

演示:复用到文件

演示:实时流媒体

**注意:**如果你想创建 WebM 文件,请查看 webm-muxer,它是 mp4-muxer 的姐妹库。

如果你觉得这个库有用并希望支持它,请考虑捐赠 ❤️

快速开始

以下是该库常见用法的示例:

import { Muxer, ArrayBufferTarget } from 'mp4-muxer'; let muxer = new Muxer({ target: new ArrayBufferTarget(), video: { codec: 'avc', width: 1280, height: 720 }, fastStart: 'in-memory' }); let videoEncoder = new VideoEncoder({ output: (chunk, meta) => muxer.addVideoChunk(chunk, meta), error: e => console.error(e) }); videoEncoder.configure({ codec: 'avc1.42001f', width: 1280, height: 720, bitrate: 1e6 }); /* 编码一些帧... */ await videoEncoder.flush(); muxer.finalize(); let { buffer } = muxer.target; // buffer 包含最终的 MP4 文件

动机

webm-muxer 因其易用性和与 WebCodecs API 的集成而获得关注后,创建了这个库,以便现在也能创建 MP4 文件,同时保持相同的开发体验。虽然 WebM 是一种更现代的格式,但 MP4 是一个既定标准,在更多设备上得到支持。

安装

使用 NPM,只需安装这个包:

npm install mp4-muxer

你可以像这样导入所有导出的类:

import * as Mp4Muxer from 'mp4-muxer'; // 或者,使用 CommonJS: const Mp4Muxer = require('mp4-muxer');

或者,你可以简单地在 HTML 中包含该库作为脚本,这将向全局对象添加一个 Mp4Muxer 对象,包含所有导出的类,如下所示:

<script src="build/mp4-muxer.js"></script>

使用方法

初始化

对于每个你想创建的 MP4 文件,像这样创建一个 Muxer 实例:

import { Muxer } from 'mp4-muxer'; let muxer = new Muxer(options);

可用选项由以下接口定义:

interface MuxerOptions { target: | ArrayBufferTarget | StreamTarget | FileSystemWritableFileStreamTarget, video?: { codec: 'avc' | 'hevc' | 'vp9' | 'av1', width: number, height: number, // 向文件添加旋转元数据 rotation?: 0 | 90 | 180 | 270 | TransformationMatrix }, audio?: { codec: 'aac' | 'opus', numberOfChannels: number, sampleRate: number }, fastStart: | false | 'in-memory' | 'fragmented' | { expectedVideoChunks?: number, expectedAudioChunks?: number } firstTimestampBehavior?: 'strict' | 'offset' | 'cross-track-offset' }

该库目前支持的编解码器包括视频的 AVC/H.264、HEVC/H.265、VP9 和 AV1,以及音频的 AAC 和 Opus。

target(必需)

此选项指定复用器创建的数据将被写入的位置。选项包括:

  • ArrayBufferTarget:文件数据将被写入一个大的缓冲区,然后存储在目标中。

    import { Muxer, ArrayBufferTarget } from 'mp4-muxer'; let muxer = new Muxer({ target: new ArrayBufferTarget(), fastStart: 'in-memory', // ... }); // ... muxer.finalize(); let { buffer } = muxer.target;
  • StreamTarget:此目标定义了在有新数据可用时将被调用的回调 - 如果你想流式传输数据,例如将其传输到其他地方,这很有用。构造函数具有以下签名:

    constructor(options: { onData?: (data: Uint8Array, position: number) => void, chunked?: boolean, chunkSize?: number });

    onData 为每个新的可用数据块调用。position 参数指定数据必须写入的字节偏移量。由于复用器写入的数据并不总是连续的,请确保遵守此参数

    当使用 chunked: true 时,复用器创建的数据将首先累积,只有在达到足够大小时才会写出。这有助于减少总写入次数,但代价是增加延迟。它使用默认的 16 MiB 块大小,可以通过手动设置 chunkSize 为所需的字节长度来覆盖。

    如果你想将此目标用于实时流媒体,即在复用完成之前进行播放,你还需要设置 fastStart: 'fragmented'

    使用示例:

    import { Muxer, StreamTarget } from 'mp4-muxer'; let muxer = new Muxer({ target: new StreamTarget({ onData: (data, position) => { /* 处理数据 */ } }), fastStart: false, // ... });
  • FileSystemWritableFileStreamTarget:这本质上是一个分块 StreamTarget 的包装器,旨在简化该库与文件系统访问 API 的使用。将文件直接写入磁盘(在创建过程中)带来许多好处,例如创建远大于可用 RAM 的文件。

    你可以选择覆盖默认的 16 MiB chunkSize

    constructor( stream: FileSystemWritableFileStream, options?: { chunkSize?: number } );

    使用示例:

    import { Muxer, FileSystemWritableFileStreamTarget } from 'mp4-muxer'; let fileHandle = await window.showSaveFilePicker({ suggestedName: `video.mp4`, types: [{ description: 'Video File', accept: { 'video/mp4': ['.mp4'] } }], }); let fileStream = await fileHandle.createWritable(); let muxer = new Muxer({ target: new FileSystemWritableFileStreamTarget(fileStream), fastStart: false, // ... }); // ... muxer.finalize(); await fileStream.close(); // 确保关闭流

fastStart(必需)

默认情况下,MP4 元数据(轨道信息、采样时间等)存储在文件末尾 - 这使得文件写入更快、更容易。然而,将这些元数据放在文件的_开头_(称为"快速启动")提供了某些好处:文件更容易通过网络流式传输而无需范围请求,像 YouTube 这样的网站可以在上传时就开始处理视频。通过将 fastStart 设置为以下选项之一,该库提供了对元数据放置的完全控制:

  • false:禁用快速启动,将所有元数据放在文件末尾。这个选项是最快的,使用最少的内存。建议用于直接流式传输到磁盘的大型、无界文件。

  • 'in-memory':通过将所有媒体块保存在内存中直到文件完成,生成具有快速启动功能的文件。这个选项以更昂贵的完成步骤和更高的内存需求为代价,产生最紧凑的输出。当使用 ArrayBufferTarget 时,这是首选选项,因为它将产生更高质量的输出,而不会改变内存占用。

  • 'fragmented':生成一个_分片 MP4 (fMP4)_ 文件,通过将采样元数据分组到"片段"(短媒体段)中,均匀地将其放置在整个文件中,同时将通用元数据放在文件开头。分片文件非常适合流式传输,因为它们针对随机访问进行了优化,几乎不需要寻找。此外,无论文件变得多大,它们的创建都保持轻量级,因为它们不需要长时间将媒体保存在内存中。虽然分片文件不像常规 MP4 文件那样广泛支持,但这个选项提供了强大的好处,几乎没有什么缺点。更多详情见此

  • object:通过在复用开始时为元数据保留空间,生成具有快速启动功能的文件。为了知道需要保留多少字节才能安全,你需要提供以下数据:

    { expectedVideoChunks?: number, expectedAudioChunks?: number }

    注意,如果你有视频轨道,expectedVideoChunks 属性是_必需的_ - 音频也是如此。设置此选项后,你不能复用比指定数量更多的块(但少于指定数量是可以的)。

    这个选项比 'in-memory' 更快,不使用额外内存,但会产生稍大的输出,当你想将文件流式传输到磁盘同时保留快速启动功能时,这个选项很有用。

firstTimestampBehavior(可选)

指定如何处理每个轨道的第一个块具有非零时间戳的情况。在默认的严格模式下,时间戳必须从 0 开始以确保正确播放。然而,当直接将 MediaTrackStream 的视频帧或音频数据传输到编码器然后到复用器时,时间戳通常相对于文档的年龄或计算机的时钟,这通常不是我们想要的。必须明确设置这些时间戳的处理:

  • 使用 'offset' 将每个轨道的时间戳偏移该轨道第一个块的时间戳。这样,它从 0 开始。
  • 使用 'cross-track-offset' 将每个轨道的时间戳偏移_所有轨道第一个块时间戳的最小值_。这类似于 'offset',但应在所有轨道使用相同时钟时使用。

复用媒体块

然后,设置好 VideoEncoder 和 AudioEncoder 后,使用以下方法将编码的块发送到复用器:

addVideoChunk( chunk: EncodedVideoChunk, meta?: EncodedVideoChunkMetadata, timestamp?: number, compositionTimeOffset?: number ): void; addAudioChunk( chunk: EncodedAudioChunk, meta?: EncodedAudioChunkMetadata, timestamp?: number ): void;

这两个方法都接受一个可选的第三个参数 timestamp(微秒),如果指定,它将覆盖传入块的 timestamp 属性。

元数据来自传递给 VideoEncoder 或 AudioEncoder 构造函数的 output 回调的第二个参数,需要传递给复用器,如下所示:

let videoEncoder = new VideoEncoder({ output: (chunk, meta) => muxer.addVideoChunk(chunk, meta), error: e => console.error(e) }); videoEncoder.configure(/* ... */);

可选字段 compositionTimeOffset 可以在数据块的解码时间不等于其呈现时间时使用;这种情况出现在存在 B帧 时。使用 WebCodecs API 进行编码时不会出现 B 帧。解码时间通过从 timestamp 中减去 compositionTimeOffset 来计算,这意味着 timestamp 决定了呈现时间。

如果您从 WebCodecs API 以外的来源获得编码媒体数据,可以使用以下方法将原始数据直接发送到复用器:

addVideoChunkRaw( data: Uint8Array, type: 'key' | 'delta', timestamp: number, // 以微秒为单位 duration: number, // 以微秒为单位 meta?: EncodedVideoChunkMetadata, compositionTimeOffset?: number // 以微秒为单位 ): void; addAudioChunkRaw( data: Uint8Array, type: 'key' | 'delta', timestamp: number, // 以微秒为单位 duration: number, // 以微秒为单位 meta?: EncodedAudioChunkMetadata ): void;

完成

编码完成且所有编码器都已刷新后,在 Muxer 实例上调用 finalize 来完成 MP4 文件:

muxer.finalize();

使用 ArrayBufferTarget 时,最终的缓冲区可通过以下方式访问:

let { buffer } = muxer.target;

使用 FileSystemWritableFileStreamTarget 时,确保在调用 finalize 后关闭流:

await fileStream.close();

详细信息

可变帧率

MP4 文件支持可变帧率,但观察到一些播放器(如 QuickTime)在时间戳不规则时表现不佳。因此,尽可能尝试使用固定帧率。

关于分段 MP4 文件的其他说明

通过将媒体和相关元数据分解成小片段,fMP4 文件优化了随机访问,非常适合流媒体传输,同时即使对于长文件也能保持低成本写入。但是,您应该记住以下几点:

  • 媒体数据块缓冲: 在复用包含视频和音频轨道的文件时,复用器需要等待两种媒体的数据块来完成任何给定的片段。换句话说,如果另一种媒体尚未编码到该时间戳的数据块,它必须缓冲一种媒体的数据块。例如,如果您先编码所有视频帧,然后再编码音频,复用器将不得不将所有这些视频帧保存在内存中,直到音频数据块开始输入。如果您的视频很长,这可能会导致内存耗尽。当只有一个媒体轨道时,不会出现这个问题。因此,在复用多媒体文件时,请确保文件大小有一定限制,或者数据块以某种交错方式编码(如实时媒体的情况)。这将使内存使用保持在恒定的低水平。

  • 视频关键帧频率: 为了能够在不知道前面片段的情况下播放某个片段,每个轨道在片段中的第一个样本必须是关键帧。然而,这意味着复用器需要等待视频关键帧来开始一个新的片段。如果这些关键帧太不频繁,片段会变得太大,影响随机访问。因此,每 5-10 秒,您应该强制生成一个视频关键帧,如下所示:

    videoEncoder.encode(frame, { keyFrame: true });

实现与开发

MP4 文件基于 ISO 基本媒体格式,该格式将文件结构化为盒子(或原子)的层次结构。用于实现此库的标准包括 ISO/IEC 14496-1ISO/IEC 14496-12ISO/IEC 14496-14。此外,QuickTime MP4 规范 也是一个非常有用的资源。

对于开发,克隆此存储库,使用 npm install 安装所有内容,然后运行 npm run watch 将代码打包到 build 目录中。运行 npm run check 以运行 TypeScript 类型检查器,运行 npm run lint 以运行 ESLint。

编辑推荐精选

讯飞智文

讯飞智文

一键生成PPT和Word,让学习生活更轻松

讯飞智文是一个利用 AI 技术的项目,能够帮助用户生成 PPT 以及各类文档。无论是商业领域的市场分析报告、年度目标制定,还是学生群体的职业生涯规划、实习避坑指南,亦或是活动策划、旅游攻略等内容,它都能提供支持,帮助用户精准表达,轻松呈现各种信息。

AI办公办公工具AI工具讯飞智文AI在线生成PPTAI撰写助手多语种文档生成AI自动配图热门
讯飞星火

讯飞星火

深度推理能力全新升级,全面对标OpenAI o1

科大讯飞的星火大模型,支持语言理解、知识问答和文本创作等多功能,适用于多种文件和业务场景,提升办公和日常生活的效率。讯飞星火是一个提供丰富智能服务的平台,涵盖科技资讯、图像创作、写作辅助、编程解答、科研文献解读等功能,能为不同需求的用户提供便捷高效的帮助,助力用户轻松获取信息、解决问题,满足多样化使用场景。

热门AI开发模型训练AI工具讯飞星火大模型智能问答内容创作多语种支持智慧生活
Spark-TTS

Spark-TTS

一种基于大语言模型的高效单流解耦语音令牌文本到语音合成模型

Spark-TTS 是一个基于 PyTorch 的开源文本到语音合成项目,由多个知名机构联合参与。该项目提供了高效的 LLM(大语言模型)驱动的语音合成方案,支持语音克隆和语音创建功能,可通过命令行界面(CLI)和 Web UI 两种方式使用。用户可以根据需求调整语音的性别、音高、速度等参数,生成高质量的语音。该项目适用于多种场景,如有声读物制作、智能语音助手开发等。

Trae

Trae

字节跳动发布的AI编程神器IDE

Trae是一种自适应的集成开发环境(IDE),通过自动化和多元协作改变开发流程。利用Trae,团队能够更快速、精确地编写和部署代码,从而提高编程效率和项目交付速度。Trae具备上下文感知和代码自动完成功能,是提升开发效率的理想工具。

AI工具TraeAI IDE协作生产力转型热门
咔片PPT

咔片PPT

AI助力,做PPT更简单!

咔片是一款轻量化在线演示设计工具,借助 AI 技术,实现从内容生成到智能设计的一站式 PPT 制作服务。支持多种文档格式导入生成 PPT,提供海量模板、智能美化、素材替换等功能,适用于销售、教师、学生等各类人群,能高效制作出高品质 PPT,满足不同场景演示需求。

讯飞绘文

讯飞绘文

选题、配图、成文,一站式创作,让内容运营更高效

讯飞绘文,一个AI集成平台,支持写作、选题、配图、排版和发布。高效生成适用于各类媒体的定制内容,加速品牌传播,提升内容营销效果。

热门AI辅助写作AI工具讯飞绘文内容运营AI创作个性化文章多平台分发AI助手
材料星

材料星

专业的AI公文写作平台,公文写作神器

AI 材料星,专业的 AI 公文写作辅助平台,为体制内工作人员提供高效的公文写作解决方案。拥有海量公文文库、9 大核心 AI 功能,支持 30 + 文稿类型生成,助力快速完成领导讲话、工作总结、述职报告等材料,提升办公效率,是体制打工人的得力写作神器。

openai-agents-python

openai-agents-python

OpenAI Agents SDK,助力开发者便捷使用 OpenAI 相关功能。

openai-agents-python 是 OpenAI 推出的一款强大 Python SDK,它为开发者提供了与 OpenAI 模型交互的高效工具,支持工具调用、结果处理、追踪等功能,涵盖多种应用场景,如研究助手、财务研究等,能显著提升开发效率,让开发者更轻松地利用 OpenAI 的技术优势。

Hunyuan3D-2

Hunyuan3D-2

高分辨率纹理 3D 资产生成

Hunyuan3D-2 是腾讯开发的用于 3D 资产生成的强大工具,支持从文本描述、单张图片或多视角图片生成 3D 模型,具备快速形状生成能力,可生成带纹理的高质量 3D 模型,适用于多个领域,为 3D 创作提供了高效解决方案。

3FS

3FS

一个具备存储、管理和客户端操作等多种功能的分布式文件系统相关项目。

3FS 是一个功能强大的分布式文件系统项目,涵盖了存储引擎、元数据管理、客户端工具等多个模块。它支持多种文件操作,如创建文件和目录、设置布局等,同时具备高效的事件循环、节点选择和协程池管理等特性。适用于需要大规模数据存储和管理的场景,能够提高系统的性能和可靠性,是分布式存储领域的优质解决方案。

下拉加载更多