通过Rust原生的MP4库,生成MP4文件也是一件简单的事情,完全不需要直接操作文件数据。
Cargo.toml:
bash
[package]
name = "mp4_info"
version = "0.1.0"
edition = "2024"
[dependencies]
mp4 = "0.14.0"
代码:
rust
// 引入mp4库中的配置结构体和写入器,用于处理MP4文件的创建
use mp4::{Mp4Config, Mp4Writer};
// 引入标准库中的Cursor,用于在内存中创建可读写的字节流
use std::io::Cursor;
/// 主函数:创建一个基本的MP4文件结构并在内存中生成其字节数据
/// 返回值为mp4::Result<()>,用于处理可能的MP4操作错误
fn main() -> mp4::Result<()> {
// 配置MP4文件的基本参数
let config = Mp4Config {
// 主要品牌标识,"isom"是ISO基础媒体文件格式的常见标识
major_brand: str::parse("isom").unwrap(),
// 次要版本号,512是常见的版本值
minor_version: 512,
// 兼容的品牌列表,这些标识表示该MP4文件与多种标准兼容
compatible_brands: vec![
str::parse("isom").unwrap(), // 兼容ISO基础媒体格式
str::parse("iso2").unwrap(), // 兼容ISO 2标准
str::parse("avc1").unwrap(), // 兼容AVC/H.264编码
str::parse("mp41").unwrap(), // 兼容MP4版本1
],
// 时间刻度,单位为ticks/秒,这里1000表示1秒=1000个时间单位
timescale: 1000,
};
// 创建一个内存中的字节流(Cursor包裹空Vec),用于存储生成的MP4数据
let data = Cursor::new(Vec::<u8>::new());
// 初始化MP4写入器,传入内存流和配置,开始写入MP4文件头部信息
let mut writer = Mp4Writer::write_start(data, &config)?;
// 完成MP4文件写入,写入尾部信息(如moov原子等)
writer.write_end()?;
// 将写入器中的数据转换为字节向量(获取最终生成的MP4文件字节数据)
let data: Vec<u8> = writer.into_writer().into_inner();
// 打印生成的MP4字节数据(此处为一个空的MP4容器结构)
println!("{:?}", data);
// 成功完成操作
Ok(())
}
这段代码是一个使用 Rust 的 mp4 库创建基础 MP4 文件容器的示例程序。它的核心功能是在内存中生成一个符合 MP4 标准的空容器结构(没有实际音视频数据,仅包含基础格式信息),并打印出该容器的字节数据。
一、代码分析
1. 依赖引入
rust
use mp4::{Mp4Config, Mp4Writer};
use std::io::Cursor;
mp4是 Rust 中处理 MP4 文件的库,这里引入了两个核心组件:Mp4Config:用于配置 MP4 文件的基础参数(如品牌、版本、时间刻度等)。Mp4Writer:用于写入 MP4 数据的工具,负责生成符合 MP4 格式的字节流。
std::io::Cursor:标准库中的工具,用于在内存中创建一个 "可读写的字节流"(类似内存中的文件),避免直接操作磁盘文件,方便调试。
2. 主函数逻辑
rust
fn main() -> mp4::Result<()> { ... }
- 主函数返回
mp4::Result<()>,表示函数可能产生 MP4 相关操作的错误(通过?符号自动传播错误),最终成功时返回Ok(())。
3. 配置 MP4 参数(Mp4Config)
rust
let config = Mp4Config {
major_brand: str::parse("isom").unwrap(),
minor_version: 512,
compatible_brands: vec![
str::parse("isom").unwrap(),
str::parse("iso2").unwrap(),
str::parse("avc1").unwrap(),
str::parse("mp41").unwrap(),
],
timescale: 1000,
};
这部分定义了 MP4 文件的基础格式参数:
major_brand:MP4 的 "主要品牌标识",用于声明文件遵循的核心标准。"isom"是最常见的标识(表示 "ISO 基础媒体文件格式")。minor_version:次要版本号,512 是 MP4 格式中约定的常见值。compatible_brands:兼容的品牌列表,告诉播放器 / 解析器 "该文件还兼容这些标准"。例如:isom/iso2:兼容 ISO 基础媒体格式的不同版本;avc1:兼容 H.264(AVC)视频编码;mp41:兼容早期 MP4 标准。
timescale:时间刻度(单位:ticks / 秒),用于定义 MP4 中时间的 "最小单位"。这里1000表示 1 秒 = 1000 个时间单位(即 1 个时间单位 = 1 毫秒)。
4. 初始化内存流和 MP4 写入器
rust
let data = Cursor::new(Vec::<u8>::new());
let mut writer = Mp4Writer::write_start(data, &config)?;
Cursor::new(Vec::<u8>::new()):创建一个内存中的空字节流(用Vec<u8>作为底层存储,Cursor使其支持读写操作)。Mp4Writer::write_start(...):初始化 MP4 写入器,传入内存流和配置,开始写入 MP4 文件的 "头部信息"(如文件类型、品牌标识等基础元数据)。
5. 完成 MP4 写入并获取结果
rust
writer.write_end()?;
let data: Vec<u8> = writer.into_writer().into_inner();
println!("{:?}", data);
writer.write_end()?:完成 MP4 写入,补充文件的 "尾部信息"(如 MP4 中关键的moov原子,用于描述媒体结构)。writer.into_writer().into_inner():将写入器中的数据转换为字节向量(Vec<u8>),即最终生成的 MP4 容器的字节数据。println!:打印字节数据,方便观察生成的 MP4 基础结构。
二、代码的核心作用
这段代码生成了一个空的 MP4 容器(没有实际的音视频轨道和数据),但它符合 MP4 格式的基础规范。它展示了创建 MP4 文件的最小流程:定义格式参数 → 初始化写入器 → 写入头部和尾部信息 → 获取结果。
实际应用中,在此基础上可以通过 Mp4Writer 的其他方法添加音视频轨道、写入媒体数据(如 H.264 视频帧、AAC 音频帧),最终生成完整的 MP4 文件。
三、执行结果分析
bash
D:\rust_projects\mp4_info\target\debug>mp4_info
[0, 0, 0, 32, 102, 116, 121, 112, 105, 115, 111, 109, 0, 0, 2, 0, 105, 115, 111, 109, 105, 115, 111, 50, 97, 118, 99, 49, 109, 112, 52, 49, 0, 0, 0, 16, 109, 100, 97, 116, 0, 0, 0, 8, 119, 105, 100, 101, 0, 0, 0, 116, 109, 111, 111, 118, 0, 0, 0, 108, 109, 118, 104, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 232, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]
这段字节数组是代码生成的空 MP4 容器的二进制数据,对应 MP4 文件的基础结构(由多个 "原子"(Atom/Box)组成)。我们可以通过解析字节数据的结构,验证它是否符合 MP4 格式规范以及代码中的配置。
核心分析:MP4 文件的 "原子" 结构
MP4 文件由一系列 "原子"(Box)组成,每个原子的基本结构是:
- 前 4 字节:原子大小(字节数,大端存储)
- 接下来 4 字节:原子类型(4 个 ASCII 字符,标识原子功能)
- 剩余字节:原子的数据(根据类型不同,结构不同)
下面按顺序解析输出的字节数组:
1. 第一个原子:ftyp(文件类型原子)
对应字节范围:[0,0,0,32, 102,116,121,112, ... ,109,112,52,49]
- 前 4 字节
[0,0,0,32]:原子大小为 32 字节(0x00000020 = 32)。 - 接下来 4 字节
[102,116,121,112]:ASCII 对应ftyp(File Type Box),是 MP4 文件的第一个原子,用于声明文件格式信息。 - 后续数据(32 - 8 = 24 字节):
- 前 4 字节
[105,115,111,109]:ASCII 对应isom,即代码中配置的major_brand(主要品牌)。 - 接下来 4 字节
[0,0,2,0]:16 进制为0x00000200,对应十进制 512,即代码中minor_version(次要版本)。 - 剩余 16 字节(4 个品牌 ×4 字节):
compatible_brands(兼容品牌列表),与代码完全一致:[105,115,111,109]→isom[105,115,111,50]→iso2(50 是字符 '2' 的 ASCII)[97,118,99,49]→avc1(49 是字符 '1' 的 ASCII)[109,112,52,49]→mp41(52 是字符 '4' 的 ASCII)
- 前 4 字节
2. 第二个原子:mdat(媒体数据原子)
对应字节范围:[0,0,0,16, 109,100,97,116, ... ,119,105,100,101]
- 前 4 字节
[0,0,0,16]:原子大小为 16 字节(0x00000010 = 16)。 - 接下来 4 字节
[109,100,97,116]:ASCII 对应mdat(Media Data Box),用于存储实际的音视频数据(如视频帧、音频帧)。 - 后续数据(16 - 8 = 8 字节):由于代码没有添加任何音视频数据,这里是空的占位数据(无实际意义)。
3. 第三个原子:moov(电影原子)
对应字节范围:[0,0,0,116, 116,109,111,118, ... ,0,0,0,1]
- 前 4 字节
[0,0,0,116]:原子大小为 116 字节(0x00000074 = 116)。 - 接下来 4 字节
[116,109,111,118]:ASCII 对应tmoov?不,实际是moov(可能是字节索引计算偏差,核心是电影原子)。moov是 MP4 的核心原子,存储媒体的元数据(如轨道信息、时间刻度等)。 - 内部子原子:
moov中通常包含mvhd(Movie Header Box,电影头原子),用于描述整个媒体的全局信息:- 其中会包含代码中配置的
timescale: 1000(时间刻度,1 秒 = 1000 个时间单位)。在字节中可找到[0,0,3,232],16 进制为0x000003E8,对应十进制 1000,与配置完全匹配。
- 其中会包含代码中配置的
执行结果分析结论
输出的字节数组是一个完全符合 MP4 格式规范的空容器:
- 包含 MP4 必需的基础原子(
ftyp声明格式、mdat预留媒体数据位置、moov存储元数据)。 - 所有配置参数(
major_brand、minor_version、compatible_brands、timescale)均正确体现在二进制数据中。 - 由于没有添加音视频轨道和实际数据,容器是空的,但结构完整,可被标准 MP4 解析器识别为合法文件。