音视频视频处理音频处理【免费下载链接】mediabunnyPure TypeScript media toolkit for reading, writing, and converting video and audio files, directly in the browser.项目地址https://gitcode.com/gh_mirrors/me/mediabunny点击查看免费下载本文围绕 Mediabunny Codec Registry 中 MP3MPEG-1/2 Audio Layer III音频编解码的官方注册定义展开逐一说明其合法 codec 字符串、EncodedPacket帧数据格式、AudioDecoderConfig的构造规则并结合仓库内 MP3 解复用器demuxer、封装器muxer与编码扩展包的源码实现帮助你从注册表规范到底层代码完整掌握 Mediabunny 中 MP3 数据的读写与转换原理。读完本文你将能够理解为何 MP3 的 packet type 恒为key知道如何构造合法的 MP3 解码配置与编码包并能在浏览器中完成任意音频到 MP3 文件的转换。什么是 Codec RegistryMP3 条目在其中扮演什么角色Mediabunny Codec Registry见 docs/codec-registry/overview.md是一个形式化定义库中所有视频与音频编解码精确规格的契约层。对任意一个编解码它规定了EncodedPacket中数据data必须遵循的字节格式VideoDecoderConfig/AudioDecoderConfig必须遵循的 codec 字符串与description字段所有进出 Mediabunny 的 packet 都必须遵守该注册表。该注册表是 WebCodecs Codec Registry 正是对 MP3 这一条目的权威定义。从整体库的层面看MP3 同时出现在三处形成了注册定义—输入输出—编码扩展的闭环src/codec.ts 的NON_PCM_AUDIO_CODECS常量数组中包含mp3位于第 82 行它是库内已知的压缩音频编解码之一docs/guide/supported-formats-and-codecs.md 将mp3列为受支持音频编解码并注明 MP3 可用于.mp4、.mov、.mkv、.mp3与.ts容器由于 WebCodecs 本身不支持 MP3 编码仓库额外提供了mediabunny/mp3-encoder扩展包见下文。Codec IDmp3注册表规定 MP3 的 Codec ID 是mp3在源码中mp3贯穿整个库的编解码识别逻辑最典型的证据在 src/codec.ts 中buildAudioCodecStringsrc/codec.ts在编码 MP3 时直接返回mp3第 730-731 行extractAudioCodecStringsrc/codec.ts在从输入轨道提取 MP3 的 codec 字符串时同样返回mp3第 782-783 行。值得一提的细节inferCodecFromCodecStringsrc/codec.ts在解析外部 codec 字符串时除了精确的mp3还会把以下变体一律归入 MP3mp3 mp4a.69 mp4a.6B mp4a.6b mp4a.40.34这是为了兼容历史容器中如 ISO-BMFF / MP4使用 MPEG-4 风格 codec 参数描述 MP3 轨道的情况。也就是说注册表要求 Mediabunny 输出的 codec 字符串严格为mp3但对外部输入做了更宽松的兼容识别。EncodedPacket数据一包一帧的 MP3 frame注册表规定packet 的data必须是一个 MP3 frame帧即 ISO/IEC 13818-3 第 2.4.2.2 节所定义的格式。这意味着 Mediabunny 的 MP3 包不做跨帧聚合、也不做帧内拆分——每个EncodedPacket恰好承载一个完整帧。理解一个 MP3 frame的边界需要掌握帧头的位级结构。仓库在 shared/mp3-misc.ts 中实现了完整的帧头解析readMp3FrameHeader见 shared/mp3-misc.ts从代码可以直接还原帧头四个字节的语义位域含义代码中的关键逻辑第 1 字节同步字须为0xFFfirstByte ! 0xff即判定无效并前进 1 字节重试第 2 字节高 3 位同步字续(secondByte 0xe0) ! 0xe0无效同步校验的一部分第 2 字节MPEG 版本MPEG-1/2/2.5与 LayerIIImpegVersionId、layer位域解析第 3 字节高 4 位码率索引bitrate index经KILOBIT_RATES表查得实际码率第 3 字节采样率索引、padding 位结合版本推导实际采样率第 4 字节声道模式、mode extension、copyright、original、emphasischannel、modeExtension等由此得到的Mp3FrameHeadershared/mp3-misc.ts携带totalSize、bitrate、sampleRate、audioSamplesInFrame等字段而帧的实际字节长度由computeMp3FrameSizeshared/mp3-misc.ts按 Layer 分别计算Layer III(floor(12 * bitrate / sampleRate) padding) * 4Layer IIfloor(144 * bitrate / sampleRate) paddingLayer Ifloor(144 * bitrate / (sampleRate lowSamplingFrequency)) padding。MP3 支持的标准采样率来自SAMPLING_RATES [44100, 48000, 32000]shared/mp3-misc.tsMPEG-2 与 MPEG-2.5 依次右移一位与两位得到 22050/24000/16000 与 11025/12000/8000每帧的音频采样数audioSamplesInFrame也随版本与 Layer 不同MPEG-1 Layer III 为 1152MPEG-2/2.5 Layer III 为 576Layer II 为 1152Layer I 为 384。实用推论因为每个 packet 就是一个完整帧帧头本身就携带采样率、声道数等全部解码所需参数MP3 流因此是自带参数、可随时进入的流格式——这正是注册表中description可以被省略的根本原因详见下文。EncodedPackettype恒为key注册表规定MP3 的 packet type 永远是key。这与 MP3 的编码机制直接相关MP3 帧之间不存在跨帧预测依赖每一帧都是可以独立解码的关键帧。从源码可以找到这一约束的实际落地Mp3AudioTrackBacking.getPacketAtIndex在构造 packet 时硬编码了keyreturn new EncodedPacket( data, key, // MP3 帧永远是可独立解码的关键包 rawSample.timestamp, rawSample.duration, sampleIndex, rawSample.dataSize, );见 src/mp3/mp3-demuxer.ts。这也意味着在 Mediabunny 的 packet 语义 中key packet 可以脱离其他 packet 单独解码因此对 MP3 轨道做随机定位seek时任意位置都可以直接解码播放无需像 AVC/HEVC 那样先找 IDR 帧。AudioDecoderConfigcodec 字符串与description注册表对 MP3 的解码配置规定了两点codec 字符串mp3description字段不使用description is not used for this codec。description不用携带的原因是MP3 的全部解码参数采样率、声道模式、码率、Layer 等都内嵌在每一帧的 4 字节帧头中解码器可以直接从首个帧头获取必要信息不需要像 AACAudioSpecificConfig或 Vorbisidentification header那样由容器额外提供带外解码元数据。源码同样印证了这一约定。Mp3Demuxer中Mp3AudioTrackBacking.getDecoderConfigsrc/mp3/mp3-demuxer.ts产出的配置只包含三个字段完全不设置descriptionreturn { codec: mp3, numberOfChannels: getMp3ChannelCount(this.demuxer.firstFrameHeader.channel), sampleRate: this.demuxer.firstFrameHeader.sampleRate, };其中getMp3ChannelCountshared/mp3-misc.ts将帧头中的声道模式映射为实际声道数channel 3单声道返回 1其余立体声/联合立体声/双声道返回 2。库中的 codec 能力查询函数canDecode(mp3)也以 2 声道、48 kHz 作为默认探测配置见 docs/guide/supported-formats-and-codecs.md。源码链路一从 MP3 文件到EncodedPacket读取/解复用Mediabunny 读取 MP3 的完整链路由三部分构成格式探测、帧扫描、包生产。格式探测Mp3InputFormat._canReadInputMp3InputFormatsrc/input-format.ts的探测逻辑_canReadInput见 src/input-format.ts分三步验证输入是否是 MP3跳过所有开头的 ID3v2 标签定位到第一帧可能的位置在当前位置 4096 字节范围内寻找第一个合法 MP3 帧头若该位置出现 Xing 或 Info 标记直接判定为 MP3否则紧邻第一个帧结束位置继续寻找第二个帧头并要求两帧的channel与sampleRate一致——两个相邻且参数匹配的帧是判断 MP3 的强信号。库内以单例形式导出MP3格式对象src/input-format.ts可直接作为Input的formats之一使用。帧扫描Mp3Demuxer与readNextMp3FrameHeaderMp3Demuxersrc/mp3/mp3-demuxer.ts负责把文件切分为一帧一包advanceReadersrc/mp3/mp3-demuxer.ts以 64 KiBCHUNK_SIZE 2 ** 16为块粒度请求读取切片逐字扫描帧头并跳过 Xing/Info 这类不含音频数据的帧readNextMp3FrameHeadersrc/mp3/mp3-reader.ts实现了对畸形流的容错恢复当传入参考帧头ref时只接受与参考帧拥有相同sampleRate、mpegVersionId、layer与声道数的帧从而在坏帧之间锁定合法的帧序列每个合法帧按audioSamplesInFrame / sampleRate计算时长、按帧内采样数累加时间戳形成Sample记录。仓库用真实的畸形文件验证了这一能力test/browser/mp3.test.ts直接加载test/public/malformed-join.mp3进行读取测试而该文件的名称即表明其包含拼接断裂的帧。元数据ID3 与 XingMp3Demuxer.getMetadataTagssrc/mp3/mp3-demuxer.ts会依次解析文件头的 ID3v2 标签通过 src/id3.ts 中的parseId3V2Tag若文件头没有 ID3v2则回退读取文件尾的 ID3v1 标签。而getDurationFromMetadata则利用帧序列中发现的 Xing/Info 帧XING 0x58696e67、INFO 0x496e666f见 shared/mp3-misc.ts携带的frameCount直接换算时长没有 Xing 时则按 CBR恒定码率假设用文件大小除以平均帧长估算帧数src/mp3/mp3-demuxer.ts。源码链路二从EncodedPacket到 MP3 文件写入/封装写入侧的核心是Mp3OutputFormat与Mp3Muxer。Mp3OutputFormatsrc/output-format.ts声明 MP3 容器只支持恰好 1 条音频轨、0 条视频与字幕轨getSupportedTrackCounts支持的编解码仅有[mp3]src/output-format.ts。其构造选项Mp3OutputFormatOptionssrc/output-format.ts有两个实用配置type Mp3OutputFormatOptions { /** * 是否在文件开头写入 Xing 头含附加元数据与索引。 * 关闭后写入过程变为纯追加式append-only。默认 true。 */ xingHeader?: boolean; /** * 当 Xing 元数据帧最终确定后被调用。 * param data - 原始字节 * param position - 数据在文件中的字节偏移 */ onXingFrame?: (data: Uint8Array, position: number) unknown; };Mp3Muxersrc/mp3/mp3-muxer.ts的写入流程清晰体现了MP3 没有容器级头部这一本质起始阶段start先写 ID3v2 标签若输出携带元数据随后由Mp3Writersrc/mp3/mp3-writer.ts写入一个 Xing 帧作为文件的伪容器头数据阶段addEncodedAudioPacket校验首个 packet 的帧头合法性、忽略 Xing/Info 帧、记录每帧位置、逐包追加原始帧数据由于 muxer 不做码率约束无法预知输出是 CBR 还是 VBR因此总是写入 Xing 帧见 src/mp3/mp3-muxer.ts 的注释收尾阶段finalize回填 Xing 帧的frameCount、fileSize与 100 字节 TOC 索引表。如果全程没有收到任何 packet 且xingHeader被禁用则抛出错误因为没有任何帧可写若xingHeader开启则即使没有 packet 也会写一个独立的 Xing 帧使文件仍然是合法的空的MP3。Mp3Writer.writeXingFramesrc/mp3/mp3-writer.ts展示了 Xing 帧的构造技巧先按需查找尺寸足以容纳 Xing 数据155 字节的最低码率索引再以该码率写出完整帧从而保证 Xing 帧本身是合法的 Layer III 帧。输出配置在测试中也有对应验证例如 test/node/empty-media.test.ts 覆盖了new Mp3OutputFormat({ xingHeader: false })的空文件失败路径以及Mp3OutputFormat对maximumPacketCount、primingPacket、decoderConfig等轨道元数据的依赖逻辑。MP3 编码mediabunny/mp3-encoder扩展WebCodecs 的浏览器实现普遍不支持 MP3 编码。Mediabunny 因此提供了mediabunny/mp3-encoder扩展包见 packages/mp3-encoder/README.md它基于 Mediabunny 的自定义编解码器 API内部使用 WASM 化的 LAME MP3 EncoderSIMD 优化构建完成编码。启用方式极为简单import { registerMp3Encoder } from mediabunny/mp3-encoder; registerMp3Encoder();更严谨的做法是先探测原生支持再决定是否注册import { canEncodeAudio } from mediabunny; import { registerMp3Encoder } from mediabunny/mp3-encoder; if (!(await canEncodeAudio(mp3))) { registerMp3Encoder(); }注册之后Mediabunny 会在编码流程中自动优先使用该自定义编码器。一个完整的任意输入 → MP3 文件转换示例取自扩展包 READMEimport { Input, ALL_FORMATS, BlobSource, Output, BufferTarget, Mp3OutputFormat, canEncodeAudio, Conversion, } from mediabunny; import { registerMp3Encoder } from mediabunny/mp3-encoder; if (!(await canEncodeAudio(mp3))) { // 仅在无原生支持时注册自定义编码器 registerMp3Encoder(); } const input new Input({ source: new BlobSource(file), // 例如来自文件选择器 formats: ALL_FORMATS, }); const output new Output({ format: new Mp3OutputFormat(), target: new BufferTarget(), }); const conversion await Conversion.init({ input, output }); await conversion.execute(); output.target.buffer; // 包含 MP3 文件的 ArrayBuffer该扩展的 worker 会在初始化时加载 LAME WASM主线程负责把编码输出按帧边界切分产出的包同样遵守注册表一包一帧、type 恒为 key的约定。此外test/browser/worker-error.test.ts中以{ codec: mp3, register: registerMp3Encoder, bitrate: 192000 }的形式在 192 kbps 配置下对扩展进行了集成测试。测试与验证依据仓库为 MP3 注册定义与实现提供了多层测试佐证畸形流容错test/browser/mp3.test.ts 使用test/public/malformed-join.mp3验证读取链路轨道与解码配置test/browser/mpeg-ts-muxing.test.ts 断言 MPEG-TS 输出格式支持[avc, hevc, aac, mp3, ac3, eac3, dts]并验证 MP3 轨道getCodec()返回mp3、getDecoderConfig()返回codec: mp3时长与空文件test/node/duration-from-metadata.test.ts 使用 VBR 的test/public/Toothsome-Meme.VBRv2.mp3验证 Xing 元数据时长推导test/node/empty-media.test.ts 验证Mp3OutputFormat的空文件与xingHeader: false行为。小结把注册表定义落到代码综合来看MP3 在 Mediabunny 注册表中的定义可以浓缩为一张速查表注册项规定值源码佐证Codec IDmp3src/codec.tsNON_PCM_AUDIO_CODECSEncodedPacket数据单个 MP3 帧ISO/IEC 13818-3 §2.4.2.2shared/mp3-misc.ts 帧头解析EncodedPackettype恒为keysrc/mp3/mp3-demuxer.tscodec 字符串mp3src/codec.tsdescription不使用src/mp3/mp3-demuxer.ts 仅输出三个字段正是每帧自含参数、帧间无依赖的编码特性决定了 MP3 在注册表中的全部特殊性key恒定、description省略、一包一帧。理解了这一点无论是自行实现自定义 MP3 编解码器遵循自定义编解码器 API还是阅读 Mediabunny 的 MP3 读写源码都能做到心中有数。赞分享音视频视频处理音频处理【免费下载链接】mediabunnyPure TypeScript media toolkit for reading, writing, and converting video and audio files, directly in the browser.项目地址https://gitcode.com/gh_mirrors/me/mediabunny点击查看免费下载相关推荐Mediabunny MP3 编码扩展 mediabunny/mp3-encoder基于 LAME WASM 的浏览器与服务器端 MP3 编码方案Mediabunny MP3 编码扩展 mediabunny/mp3 encoder基于 LAME WASM 的浏览器与服务器端 MP3 编码方案 Medi音视频视频处理音频处理Mediabunny AAC 编解码器注册规范codec 字符串、解码配置与数据包格式详解Mediabunny AAC 编解码器注册规范codec 字符串、解码配置与数据包格式详解 导读 AACAdvanced Audio CodingISO/音视频视频处理音频处理mediabunny/mp3-encoder 实战指南基于 LAME WASM 的浏览器端 MP3 编码扩展mediabunny/mp3 encoder 实战指南基于 LAME WASM 的浏览器端 MP3 编码扩展 本指南围绕 Mediabunny 官方扩展包音视频视频处理音频处理上一篇突破100Gbps网络测试瓶颈dperf分布式压力测试系统实战指南下一篇告别配色选择困难症telescope.nvim颜色方案管理完全指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考