FFMPEG mp4录像
author:lyn
date:2022.09.28
version: ffmpeg4.1.3
1.mp4数据结构
2.ffmpeg mp4封装实现
3.mp4函数调用关系
4.参考资料
1.mp4数据结构
1.1mp4简介
MP4或称MPEG-4第14部分(英语:MPEG-4 Part 14)是一种标准的数字多媒体容器格式。MPEG-4第14部分的扩展名为.mp4
,以存储数字音频及数字视频为主,但也可以存储字幕和静止图像。因其可容纳支持比特流的视频流(如高级视频编码),为流媒体。
扩展名为.flv
或.f4v
并同样基于ISO基础媒体文件格式(MPEG-4第12部分)的Flash视频格式与MPEG-4第14部分极为相似,多数情况下直接变更扩展名为.mp4
也能够正常播放
1.2mp4名词和文件结构
mp4
采用box
的方式封装,可以理解为俄罗斯套娃一样一层嵌套一层的结构,但是mp4 box
的顺序和结构并不是固定的,除了部分的box
是固定的,有些box
的位置和顺序是可以更改的,mp4
读的过程是根据box
的type区分不同box
处理;
mp4
box的类型有很多,具体可以查看标准文件,这里只说一些常用的box
-
ftyp
File Type Box 一般在文件的开头,用来指示该mp4
文件使用的标准规范。为了早期规范版本兼容,允许不包含ftyp box -
moov
Movie Box包含了文件媒体的metadata信息,可以包含mvhd
、trak
、mvex
等box
-
mvhd
Movie Header Box 包含创建时间,推荐播放速度,推荐播放音量等参数;
-
trak
Track Box
一个mp4
文件可以包含多个track
,且至少有一个track
,track
之间是独立,有自己的时间和空间信息。trak
必须包含一个tkhd
和一个mdia
-
tkhd
Track Header Box 包含宽高,音量等信息
-
mdia
Media Box 一个
track
只有一个,mdia
定义了track
媒体类型以及sample数据,描述sample信息,包含mdhd
hdlr
minf
和user data -
mdhd
Media Header Box
mdhd
和tkhd
,内容大致都是一样的。不过tkhd
通常是对指定的track
设定相关属性和内容。而mdhd
是针对于独立的media
来设置的。不过两者一般都是一样的 -
hdlr
Handler Reference Box
hdlr
解释了媒体的播放过程信息,该box也可以被包含在meta box(meta)
中 -
minf
Media Information Box 重要的容器 box,
minf
存储了解释track
媒体数据的handler-specific信息,media handler用这些信息将媒体时间映射到媒体数据并进行处理。minf
是一个container box,其实际内容由子box说明 -
(vmhd、smhd、hmhd、nmhd)
Media Information Header Box vmhd、smhd这两个box在解析时,非不可或缺的(有时候得看播放器),缺了的话,有可能会被认为格式不正确。
-
dinf
Data Information Box
dinf
解释如何定位媒体信息dinf
一般包含一个dref
(data reference box)。dref
下会包含若干个url
或urn”
,这些box组成一个表,用来定位track
数据。简单的说,track
可以被分成若干段,每一段都可以根据url
或urn
指向的地址来获取数据,sample描述中会用这些片段的序号将这些片段组成一个完整的track
。一般情况下,当数据被完全包含在文件中时,url
或urn
中的定位字符串是空的 -
stbl
Sample Table Box 包含track中的媒体样本中所有的时间和数据索引,使用采样表可以及时定位样本,确定类型(比如是否为I帧)还有确定大小,位置偏移等
-
mvex
Movie Extends Box 这个box告知读取设备文件里面可能存在片段的媒体数据,必须要查找和扫描完这些片段的媒体数据;
-
moof
Movie Fragment Box 是分段形式解析,如同hls中的ts分片一样。
moof
如同moov
一样,但moov
一定要有,但moof
可能没有。每个moof
后边都会接一个mdat
数据段 -
mfhd
Movie Fragment Header Box ,被
moof
包含,包含序列号,从1开始,每个片段递增 -
mdat
Media Data Box 实际的媒体数据,在视频
track
里,包含的是视频帧,可以是0个或多个; -
sidx
Segment Index Box 0个或多个,提供媒体流的索引,它的设计不仅可以用于
mp4
的规范,也可用于其他的媒体格式上;
普通格式mp4、分段mp4文件、前移moov mp4文件格式如下所示
2.ffmpeg mp4封装实现
视频和音频封装为avi格式调用接口顺序如上所示,avio_open
avio_close
接口取代旧的 url_fopen
和url_fclose
然后直接调用mov_write_header
写入mp4
文件的头部,数据包通过mov_write_packet
一帧一帧的将数据写入,不断调用这个接口,最后调用mov_write_trailer
将文件尾部写入;这样就将编码数据封装为mov
格式的文件;
fragment mp4二进制文件如上所示,mp4文件是由不同的box构成;
3.mp4函数调用关系
函数调用关系分析了常用和比较重要的一些函数调用关系,指出整个调用的流程,具体的可以查看相关源码;
avio_open
{
avio_open2
{
ffurl_open_whitelist //初始化URLContext
{
ffurl_alloc
{
url_find_protocol //查找合适的URLProtocol,并创建,file也是一种协议
url_alloc_for_protocol
}
ffurl_connect //打开获得的URLProtocol
}
ffio_fdopen //URLContext初始化AVIOContext
{
avio_alloc_context
{
ffio_init_context
}
}
}
}
具体的avio函数分析可以参考雷神博客;链接详见参考资料;
mov_write_header
{
mov_write_identification
{
mov_write_ftyp_tag //写ftyp box
mov_write_mdat_tag(pb, mov); //如果不是片段的mp4就直接开始写入mdat
ff_parse_creation_time_metadata //解析创建时间
mov_write_isml_manifest //ism1流媒体方式
mov_write_moov_tag // 片段的mp4写moov box
{
mov_write_mvhd_tag //写moov容器内的一系列box
mov_write_iods_tag
mov_write_trak_tag
mov_write_mvex_tag
mov_write_udta_tag
update_size
}
}
}
mov_write_packet
{
mov_flush_fragment
{
/* 如果moov没写就将其写入,否则就跳过写入moov过程 */
get_moov_size
mov_write_identification // FF_MOV_FLAG_DELAY_MOOV 写ftye等信息
mov_write_moov_tag // 如果moov没写则写入
avio_wb32(s->pb, buf_size + 8);
ffio_wfourcc(s->pb, "mdat");
avio_write(s->pb, buf, buf_size); // 写入媒体数据
av_free(buf);
mov_write_moof_tag //片段类型mp4写moof
{
mov_write_moof_tag_internal
{
ffio_wfourcc(pb, "moof"); // 写 moof
mov_write_mfhd_tag; // 写 mfhd
{
ffio_wfourcc(pb, "mfhd");
}
mov_write_traf_tag // 写traf
{
mov_write_tfhd_tag
mov_write_tfdt_tag
mov_write_trun_tag
}
}
mov_write_sidx_tags //写索引
{
mov_write_sidx_tag
{
ffio_wfourcc(pb, "sidx");
}
}
mov_write_moof_tag_internal
}
ffio_wfourcc(s->pb, "mdat");
avio_write(s->pb, buf, buf_size); // 写入媒体数据
av_free(buf);
}
/* 一般情况下会直接来到这里写包 */
mov_write_single_packet
{
check_pkt // 检查数据包
mov_auto_flush_fragment // 有关键的帧会调用这个接口,fragment mp4写入sidx sidx moof
{
mov_flush_fragment // 写入一个包
}
ff_mov_write_packet
{
check_pkt // 检查数据包
mov_flush_fragment_interleaving//写入一帧的数据
{
offset = avio_tell(mov->mdat_buf);
avio_write(mov->mdat_buf, buf, buf_size);
av_free(buf);
}
}
}
}
mov_write_trailer
{
mov_write_subtitle_end_packet
mov_write_moov_tag // 普通mp4在尾部写入moov,fragment mp4会跳过这个阶段
mov_auto_flush_fragment
mov_write_mfra_tag
}
avio_close // 释放资源
{
avio_flush
av_opt_free
avio_context_free
ffurl_close
}
4.参考资料
mp4简介
mp4结构文章来源:https://www.toymoban.com/news/detail-404073.html
avio函数分析文章来源地址https://www.toymoban.com/news/detail-404073.html
到了这里,关于FFMPEG mp4封装实现的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!