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。

编辑推荐精选

Keevx

Keevx

AI数字人视频创作平台

Keevx 一款开箱即用的AI数字人视频创作平台,广泛适用于电商广告、企业培训与社媒宣传,让全球企业与个人创作者无需拍摄剪辑,就能快速生成多语言、高质量的专业视频。

即梦AI

即梦AI

一站式AI创作平台

提供 AI 驱动的图片、视频生成及数字人等功能,助力创意创作

扣子-AI办公

扣子-AI办公

AI办公助手,复杂任务高效处理

AI办公助手,复杂任务高效处理。办公效率低?扣子空间AI助手支持播客生成、PPT制作、网页开发及报告写作,覆盖科研、商业、舆情等领域的专家Agent 7x24小时响应,生活工作无缝切换,提升50%效率!

TRAE编程

TRAE编程

AI辅助编程,代码自动修复

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

AI工具TraeAI IDE协作生产力转型热门
蛙蛙写作

蛙蛙写作

AI小说写作助手,一站式润色、改写、扩写

蛙蛙写作—国内先进的AI写作平台,涵盖小说、学术、社交媒体等多场景。提供续写、改写、润色等功能,助力创作者高效优化写作流程。界面简洁,功能全面,适合各类写作者提升内容品质和工作效率。

AI辅助写作AI工具蛙蛙写作AI写作工具学术助手办公助手营销助手AI助手
问小白

问小白

全能AI智能助手,随时解答生活与工作的多样问题

问小白,由元石科技研发的AI智能助手,快速准确地解答各种生活和工作问题,包括但不限于搜索、规划和社交互动,帮助用户在日常生活中提高效率,轻松管理个人事务。

热门AI助手AI对话AI工具聊天机器人
Transly

Transly

实时语音翻译/同声传译工具

Transly是一个多场景的AI大语言模型驱动的同声传译、专业翻译助手,它拥有超精准的音频识别翻译能力,几乎零延迟的使用体验和支持多国语言可以让你带它走遍全球,无论你是留学生、商务人士、韩剧美剧爱好者,还是出国游玩、多国会议、跨国追星等等,都可以满足你所有需要同传的场景需求,线上线下通用,扫除语言障碍,让全世界的语言交流不再有国界。

讯飞智文

讯飞智文

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

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

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

讯飞星火

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

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

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

Spark-TTS

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

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

下拉加载更多