mp4ff

mp4ff

Go语言实现的MP4文件解析与生成库

mp4ff是一个Go语言实现的MP4文件解析和生成库。它主要用于处理DASH、MSS和HLS fMP4等流媒体格式的分片MP4文件,支持AVC和HEVC视频、AAC和AC-3音频以及stpp和wvtt字幕。库提供API和命令行工具,可用于分析MP4结构、提取编解码器信息、重新分段和加解密。mp4ff优化了内存管理和I/O处理,能高效处理大型MP4文件。

MP4解析视频处理音频处理流媒体DASHGithub开源项目

Logo

测试 golangci-lint GoDoc Go 报告卡 许可证

mp4ff 包实现了 MP4 媒体文件的解析和写入,支持 AVC 和 HEVC 视频、AAC 和 AC-3 音频以及 stpp 和 wvtt 字幕。它主要针对 DASH、MSS 和 HLS fMP4 流媒体所使用的分段文件,但也可以解码和编码渐进式 MP4 文件所需的所有盒子。特别是,mp4ff-crop 工具可用于裁剪渐进式文件。

命令行工具

cmd 目录中提供了一些有用的命令行工具。

  1. mp4ff-info 打印 mp4 文件的盒子层级树结构,并提供盒子信息。可以使用 -l 选项增加详细程度,如 -l all:1 适用于所有盒子,或 -l trun:1,stss:1 适用于特定盒子。
  2. mp4ff-pslister 从 mp4 或字节流(Annex B)文件中提取并显示 AVC 或 HEVC 的 SPS 和 PPS。对于 HEVC,会打印部分信息。
  3. mp4ff-nallister 列出渐进式或分段文件中视频的 NALU 和图像类型。
  4. mp4ff-subslister 列出 wvtt 或 stpp(ISOBMFF 中的 WebVTT 或 TTML)字幕样本的详细信息。
  5. mp4ff-crop 将渐进式 mp4 文件裁剪到指定时长。
  6. mp4ff-encrypt 使用 cenc 或 cbcs 通用加密方案加密分段文件。
  7. mp4ff-decrypt 解密使用 cenc 或 cbcs 通用加密方案加密的分段文件。

你可以通过进入各个工具的目录并运行 go install . 来安装这些工具,或直接从仓库安装:

go install github.com/Eyevinn/mp4ff/cmd/mp4ff-info@latest

示例代码

examples 目录中提供了示例代码。 这些示例及其功能如下:

  1. initcreator 为视频和音频创建典型的初始化段(ftyp + moov)。
  2. resegmenter 读取分段文件(CMAF 轨道)并使用 fullSample 以其他段持续时间重新分段。
  3. segmenter 接受渐进式 mp4 文件并从中创建初始化段和媒体段。该工具已扩展以支持生成多轨道段,以及以惰性模式读写 mdat
  4. multitrack 解析具有多个轨道的分段文件。
  5. combine-segs 将单轨道初始化段和媒体段合并为多轨道段。

该库在 mp4ff/mp4 包中提供了解析(称为 Decode)和写入(Encode)的功能。它还包含 mp4ff.avc 包中的 AVC/H.264 编解码器特定解析,包括完整的 SPS 和 PPS 解析。HEVC/H.265 解析不太完整,可在 mp4ff.hevc 中使用。补充增强信息可以使用 mp4ff.sei 包进行解析和写入。

可以解析和解码传统的非分段多路复用 mp4 文件,但重点是 DASH、HLS 和 CMAF 中使用的分段 mp4 文件。

除了单轨道分段文件外,还增加了对解析和生成多轨道分段文件的支持,可以在 examples/segmentexamples/multitrack 中看到。

非分段和分段 mp4 文件的顶层结构都是 mp4.File

在渐进式(非分段)mp4.File 中,顶层属性 Ftyp、Moov 和 Mdat 指向相应的盒子。

分段 mp4.File 可以或多或少地完整,如单个初始化段、一个或多个媒体段,或两者的组合,如可渲染成可播放单轨道资产的 CMAF 轨道。它也可以有多个轨道。 对于分段文件,使用以下高级属性:

  • Init 包含 ftypmoov 盒子,提供分段文件的一般元数据。它对应于 CMAF 头部。它还可以包含一个或多个 sidx 盒子。
  • SegmentsMediaSegment 的切片,以可选的 styp 盒子开始,可能有一个或多个 sidx 盒子,然后是一个或多个 Fragment
  • Fragment 是一个 mp4 片段,恰好有一个 moof 盒子,后跟一个包含媒体数据的 mdat 盒子。它可以有一个或多个 trun 盒子,包含样本的元数据。

容器盒子(如 MoovBox)的所有子盒子都列在 Children 属性中,但最重要的子盒子有直接链接的名称,这使得可以写出如下路径:

fragment.Moof.Traf.Trun

以访问只有一个 traf 盒子的片段中的(唯一)trun 盒子,或

fragment.Moof.Trafs[1].Trun[1]

以获取第二个 traf 盒子的第二个 trun(如果存在)。必须注意确保中间指针都不为 nil,以避免 panic

创建新的分段文件

一个典型的用例是创建一个由初始化段后跟一系列媒体段组成的片段。

第一步是创建初始化段。这可以通过三个步骤完成,如 examples/initcreator 中所示:

init := mp4.CreateEmptyInit() init.AddEmptyTrack(timescale, mediatype, language) init.Moov.Trak.SetHEVCDescriptor("hvc1", vpsNALUs, spsNALUs, ppsNALUs)

这里第三步将编解码器特定参数填入单个轨道的样本描述符中。多个轨道也可以通过 Traks 切片属性而不是 Trak 来使用。

第二步是开始生成媒体段。它们应该使用创建初始化段时设置的时间刻度。通常,应选择时间刻度使样本持续时间具有精确值,无需四舍五入。

媒体段包含一个或多个片段,每个片段都有一个 moof 和一个 mdat 盒子。如果在创建段之前所有样本都可用,可以在每个段中使用单个片段。这方面的示例代码可以在 examples/segmenter 中找到。

创建媒体段的一种简单但不优化的方法是首先创建一个包含所需数据的 FullSample 切片。mp4.FullSample 的定义如下:

mp4.FullSample{ Sample: mp4.Sample{ Flags uint32 // 标记同步样本等 Dur uint32 // 样本持续时间(以 mdhd 时间刻度为单位) Size uint32 // 样本数据大小 Cto int32 // 有符号合成时间偏移 }, DecodeTime uint64 // 绝对解码时间(偏移量 + 累积样本 Dur) Data []byte // 样本数据 }

mp4.Sample 部分是将写入 trun 盒子的内容。DecodeTime 是累积的媒体时间线时间。片段第一个样本的 DecodeTime 值将被设置为 tfdt 盒子中的 BaseMediaDecodeTime

一旦有了一定数量的这样的完整样本,就可以将它们添加到媒体段中

seg := mp4.NewMediaSegment() frag := mp4.CreateFragment(uint32(segNr), mp4.DefaultTrakID) seg.AddFragment(frag) for _, sample := range samples { frag.AddFullSample(sample) }

最后可以将该片段输出到一个 w io.Writer 中:

err := seg.Encode(w)

对于多轨道片段,代码会稍微复杂一些。请查看 examples/segmenter 了解具体实现。处理媒体样本的更优方式是延迟处理,下面将对此进行解释。

mdat 数据的延迟解码和写入

对于视频和音频,mp4 文件的主要部分是存储在一个或多个 mdat box 中的媒体数据。在某些情况下,例如对大型渐进式文件进行分段时,只读取 moovmoof box 中的电影或片段数据,并将 mdat box 中的媒体数据读取推迟到后面,这种方式在内存使用上会更加高效。

要以延迟模式进行解码,可以如下运行 mp4.DecodeFile():

parsedMp4, err = mp4.DecodeFile(ifd, mp4.WithDecodeMode(mp4.DecModeLazyMdat))

在这种情况下,不会读取 mdat box 的媒体数据,只会设置其大小。要读取或复制与样本对应的实际数据,必须计算相应的字节范围,然后调用:

func (m *MdatBox) ReadData(start, size int64, rs io.ReadSeeker) ([]byte, error)

func (m *MdatBox) CopyData(start, size int64, rs io.ReadSeeker, w io.Writer) (nrWritten int64, err error)

包括延迟写入 mdat 在内的示例代码可以在 examples/segmenter 中找到,将 lazy 模式设置为开启。

使用 SliceReader 和 SliceWriter 实现更高效的 I/O

使用 io.Readerio.Writer 接口来读写 box 提供了很大的灵活性,但在内存分配方面并不是最佳选择。特别是 Read(p []byte) 方法需要一个适当大小的切片 p 来读取数据,这会导致大量的内存分配和数据复制。 为了获得更好的性能,将顶层 box 完整读入一个或几个切片中进行解码是更有利的。

为了启用该模式,代码的 0.27 版本为每个 box X 引入了 DecodeX(sr bits.SliceReader) 方法,其中 mp4ff.bits.SliceReader 是一个接口。 例如,TrunBox 除了原有的 DecodeTrun(r io.Reader) 方法外,还新增了 DecodeTrunSR(sr bits.SliceReader) 方法。bits.SliceReader 接口提供了从底层字节切片中读取各种数据结构的方法。它有一个实现 bits.FixedSliceReader,使用固定大小的切片作为底层切片,但也可以考虑实现一个可增长的版本,从某个外部源获取数据。

通过这种方式实现的内存分配和速度改进可能会有所不同,但应该是显著的,特别是与 0.27 版本之前使用额外的 io.LimitReader 层相比。

为了进一步减少读取渐进式文件的 mdat 数据时的内存分配,应该使用某种缓冲读取器。

基准测试

为了研究新的 SliceReader 和 SliceWriter 方法的效率,进行了基准测试。 基准测试定义在 mp4/benchmarks_test.gomp4/benchmarks_srw_test.go 文件中。 对于 DecodeFile,可以看到从 0.26 版本到 0.27 版本有很大的改进,这两个版本都使用 io.Reader 接口, 但使用 SliceReader 源又有了很大的提升。 后者的基准测试称为 BenchmarkDecodeFileSR,但在这里为了便于比较,使用了相同的名称。 请注意,这里的分配是指在基准测试循环内进行的堆分配。在循环外, 会分配一个切片来保存输入数据。

对于 EncodeFile,可以看到使用 io.Writer 接口时,v0.27 实际上比 v0.26 更差。 这是因为代码被重构,所有写入都通过 SliceWriter 层进行,以减少代码重复。 然而,如果直接使用 SliceWriter 方法,在分配方面可以看到很大的相对增益, 如最后一列所示。

名称 \ 操作时间v0.26v0.27v0.27-srw
DecodeFile/1.m4s-1621.9µs6.7µs2.6µs
DecodeFile/prog_8s.mp4-16143µs48µs16µs
EncodeFile/1.m4s-161.70µs2.14µs1.50µs
EncodeFile/prog_8s.mp4-1615.7µs18.4µs12.9µs
名称 \ 分配操作v0.26v0.27v0.27-srw
DecodeFile/1.m4s-16120kB28kB2kB
DecodeFile/prog_8s.mp4-16906kB207kB12kB
EncodeFile/1.m4s-161.16kB1.39kB0.08kB
EncodeFile/prog_8s.mp4-166.84kB8.30kB0.05kB
名称 \ 分配次数v0.26v0.27v0.27-srw
DecodeFile/1.m4s-1698.042.034.0
DecodeFile/prog_8s.mp4-16454180169
EncodeFile/1.m4s-1615.015.03.0
EncodeFile/prog_8s.mp4-16101861

Box 结构和接口

大多数 box 都有自己的以 box 命名的文件,但在某些情况下,可能有多个 box 具有相同的内容,这时代码文件会有一个通用名称,如 mp4/visualsampleentry.go

Box 接口在 mp4/box.go 中指定。它不包含解码(解析)方法,这些方法对每种 box 类型都有不同的名称并被分发。

解码分发的映射在 mp4.decoders 表中给出,用于 io.Reader 方法,在 mp4.decodersSR 中给出,用于 mp4ff.bits.SliceReader 方法。

如何实现新的 box

要实现一个新的 box fooo,需要以下步骤:

创建一个文件 fooo.go 并创建一个结构体类型 FoooBox

FoooBox 必须实现 Box 接口方法:

Type() Size() Encode(w io.Writer) EncodeSW(sw bits.SliceWriter) // v0.27.0 新增 Info()

它还需要自己的解码方法 DecodeFooo,必须将其添加到 box.go 中的 decoders 映射中, 以及 v0.27.0 新增的 DecodeFoooSR 方法添加到 decodersSR 中。 一个简单的例子,可以查看 prft.go 中的 PrftBox

还应该有一个测试文件 fooo_test.go,其中包含使用 boxDiffAfterEncodeAndDecode 方法的测试,以检查编码和解码后 box 信息是否相同。

直接更改属性

许多属性是公开的,因此可以自由更改。 这种方式的优点是可以编写代码以多种方式操作 box, 但必须谨慎,避免破坏与子 box 的链接或在 box 中创建不一致的状态。 例如,容器盒子如TrafBox有一个AddChild方法,该方法不仅将盒子添加到其子盒子切片Children中,还会设置一个特定的成员引用(如Tfdt)指向该盒子。如果直接操作Children,该链接可能会失效。

编码模式和优化

对于分片文件,可以选择编码mp4.File中的所有盒子,或只编码包含在初始化段和媒体段中的盒子。控制这一行为的属性称为FragEncMode。 另一个属性EncOptimize控制文件编码过程的可能优化。 目前,只有一种可能的优化,称为OptimizeTrun。 它可以通过在TfhdBox中查找并写入默认值,并从TrunBox中省略相应的值来减小TrunBox的大小。 请注意,这可能会改变trun所有祖先盒子的大小。

样本编号偏移

按照ISOBMFF标准,样本编号和其他编号从1开始(基于1)。 这适用于函数和方法的参数。 在切片中的实际存储是基于0的,所以 样本编号1在相应的切片中索引为0。

稳定性

API应该相当稳定,但在版本1之前可能会发生轻微的不向后兼容的变化。

规范

MP4文件格式的主要规范是ISO基本媒体文件格式(ISOBMFF)标准 ISO/IEC 14496-12第6版2020。一些盒子在其他标准中有规定,应在代码中注释说明。

许可证

MIT,请参见LICENSE

pkg/mp4中的一些代码来自或基于https://github.com/jfbus/mp4,其版权为 Copyright (c) 2015 Jean-François Bustarret

pkg/bits中的一些代码来自或基于https://github.com/tcnksm/go-casper/tree/master/internal/bits Copyright (c) 2017 Taichi Nakashima

更新日志和版本

请参见CHANGELOG.md

支持

加入我们的Slack社区,您可以在那里提出任何关于我们开源项目的问题。Eyevinn的咨询业务还可以为您提供:

  • 该组件的进一步开发
  • 该组件在您平台上的定制和集成
  • 支持和维护协议

如果您感兴趣,请联系sales@eyevinn.se

关于Eyevinn Technology

Eyevinn Technology是一家独立的咨询公司,专门从事视频和流媒体领域。我们独立的方式是不与任何平台或技术供应商有商业关系。作为推动行业前进的创新方式,我们开发概念验证和工具。我们学到的知识和编写的代码,我们通过博客与行业分享,并开源我们编写的代码。

想了解更多关于Eyevinn的信息以及在这里工作的感受吗?请联系我们:work@eyevinn.se

编辑推荐精选

Vora

Vora

免费创建高清无水印Sora视频

Vora是一个免费创建高清无水印Sora视频的AI工具

Refly.AI

Refly.AI

最适合小白的AI自动化工作流平台

无需编码,轻松生成可复用、可变现的AI自动化工作流

酷表ChatExcel

酷表ChatExcel

大模型驱动的Excel数据处理工具

基于大模型交互的表格处理系统,允许用户通过对话方式完成数据整理和可视化分析。系统采用机器学习算法解析用户指令,自动执行排序、公式计算和数据透视等操作,支持多种文件格式导入导出。数据处理响应速度保持在0.8秒以内,支持超过100万行数据的即时分析。

AI工具酷表ChatExcelAI智能客服AI营销产品使用教程
TRAE编程

TRAE编程

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

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

AI工具TraeAI IDE协作生产力转型热门
AIWritePaper论文写作

AIWritePaper论文写作

AI论文写作指导平台

AIWritePaper论文写作是一站式AI论文写作辅助工具,简化了选题、文献检索至论文撰写的整个过程。通过简单设定,平台可快速生成高质量论文大纲和全文,配合图表、参考文献等一应俱全,同时提供开题报告和答辩PPT等增值服务,保障数据安全,有效提升写作效率和论文质量。

AI辅助写作AI工具AI论文工具论文写作智能生成大纲数据安全AI助手热门
博思AIPPT

博思AIPPT

AI一键生成PPT,就用博思AIPPT!

博思AIPPT,新一代的AI生成PPT平台,支持智能生成PPT、AI美化PPT、文本&链接生成PPT、导入Word/PDF/Markdown文档生成PPT等,内置海量精美PPT模板,涵盖商务、教育、科技等不同风格,同时针对每个页面提供多种版式,一键自适应切换,完美适配各种办公场景。

AI办公办公工具AI工具博思AIPPTAI生成PPT智能排版海量精品模板AI创作热门
潮际好麦

潮际好麦

AI赋能电商视觉革命,一站式智能商拍平台

潮际好麦深耕服装行业,是国内AI试衣效果最好的软件。使用先进AIGC能力为电商卖家批量提供优质的、低成本的商拍图。合作品牌有Shein、Lazada、安踏、百丽等65个国内外头部品牌,以及国内10万+淘宝、天猫、京东等主流平台的品牌商家,为卖家节省将近85%的出图成本,提升约3倍出图效率,让品牌能够快速上架。

iTerms

iTerms

企业专属的AI法律顾问

iTerms是法大大集团旗下法律子品牌,基于最先进的大语言模型(LLM)、专业的法律知识库和强大的智能体架构,帮助企业扫清合规障碍,筑牢风控防线,成为您企业专属的AI法律顾问。

SimilarWeb流量提升

SimilarWeb流量提升

稳定高效的流量提升解决方案,助力品牌曝光

稳定高效的流量提升解决方案,助力品牌曝光

Sora2视频免费生成

Sora2视频免费生成

最新版Sora2模型免费使用,一键生成无水印视频

最新版Sora2模型免费使用,一键生成无水印视频

下拉加载更多