最近为了更加深入了解音视频demux这块的功能,准备着手写个demuxer,提取视频流。
MP4简介 MP4的定义MP4是一种常用的视音频流封装格式,按照指定的协议来存放媒体数据;因为mp4是基于苹果QuickTime文件格式,所以与mov有很多相同之处,在苹果开发者平台可以看到详细的有关封装文档(https://developer.apple.com/library/archive/documentation/QuickTime/QTFF/QTFFChap2/qtff2.html#//apple_ref/doc/uid/TP40000939-CH204-25615)
MP4的封装格式MP4格式预览—mp4是由多个box嵌套组成的
MP4主要的顶部box
ftyp box:描述MP4锁遵循的规范和版本
mdat box :存放媒体数据
moov box:存放媒体参数(pps、sps等)相关信息和用于索引媒体数据存储位置的信息
MP4常用box
字段分布图
字段解析
major_brand:比如常见的 isom、mp41、mp42、avc1、qt等。它表示“最好”基于哪种格式来解析当前的文件。举例,major_brand 是 A,compatible_brands 是 A1,当解码器同时支持 A、A1 规范时,最好使用A规范来解码当前媒体文件,如果不支持A规范,但支持A1规范,那么,可以使用A1规范来解码;
minor_version:提供 major_brand 的说明信息,比如版本号,不得用来判断媒体文件是否符合某个标准/规范;
compatible_brands:文件兼容的brand列表。比如 mp41 的兼容 brand 为 isom。通过兼容列表里的 brand 规范,可以将文件 部分(或全部)解码出来;
字段分布图
字段解析
version :一字节用于指定mvhd的版本
flags:3字节,预留
Create time:媒体创建时间,与UTC时间不同的是,此时间是从1904年1月1日0:0:0开始计算的,而utc是从1970年1月1日0:0:0开始计算,故Create time须要减去时间差换算成utc时间
// 注:66年时间差不是66*365*24*3600来计算
creation_time_utc = creation_time - (66年时间差) = creation_time - 2082844800
Modification time:媒体最后被修改的时间,计算方式同Create time
Timescale:一秒包含的时间单位(整数)。举个例子,如果timescale等于1000,那么,一秒包含1000个时间单位(后面track等的时间,都要用这个来换算,比如track的duration为10,000,那么,track的实际时长为10,000/1000=10s);
Duration:影片时长(整数),根据文件中的track的信息推导出来,等于时间最长的track的duration;
Preferred rate:推荐的播放速率,32位整数,高16位、低16位分别代表整数部分、小数部分([16.16]),举例 0x0001 0000 代表1.0,正常播放速度;
Preferred volume:播放音量,16位整数,高8位、低8位分别代表整数部分、小数部分([8.8]),举例 0x01 00 表示 1.0,即大音量;
Matrix struct:视频的转换矩阵,详情看
Basic Data Types
Next_track_ID:32位整数,非0,一般可以忽略不计。当要添加一个新的track到这个影片时,可以使用的track id,必须比当前已经使用的track id要大。也就是说,添加新的track时,需要遍历所有track,确认可用的track id;
字段分布图
字段解析
字段分布图
字段解析
数据结构分布图
注意:取到的frame前四个字节为frame数据的长度字节,须要偏移去掉
主要存放了媒体参数(pps、sps、vps等)相关信息和用于解析mdat中视音频数据的关键信息
字段分布图
字段解析
avc1 box
字段分布图
字段解析
这个在https://developer.apple.com/library/archive/documentation/QuickTime/QTFF/QTFFChap2/qtff2.html的“Sample Description Atoms”一节可以研究下
avcC box(包含了视频关键参数,在ISO/IEC 14496-15中定义)
字段分布图
字段解析
其他字段可以自行在ISO/IEC 14496-15中查到
字段分布图
字段解析
字段分布图
字段解析
Sample-to-chunk table示意图
字段分布图
字段解析
字段分布图
字段解析
Time-to-sample table:示意图
sample1 - sample4的sample duration是4
字段分布图
字段解析
获取sps pps参数
解析stsd box,其中contain avc1 box和avcC box(此步骤详解见上文)
解析avcC box可以获取到sps和pps
以下为ISO/IEC 14496-15中解析avcC的伪代码
aligned(8) class AVCDecoderConfigurationRecord { unsigned int(8) configurationVersion = 1;
unsigned int(8) AVCProfileIndication;
unsigned int(8) profile_compatibility;
unsigned int(8) AVCLevelIndication;
bit(6) reserved = ‘111111’b;
unsigned int(2) lengthSizeMinusOne;
bit(3) reserved = ‘111’b;
unsigned int(5) numOfSequenceParameterSets;
for (i=0; i< numOfSequenceParameterSets; i++) { unsigned int(16) sequenceParameterSetLength ;
bit(8*sequenceParameterSetLength) sequenceParameterSetNALUnit;
}
unsigned int(8) numOfPictureParameterSets;
for (i=0; i< numOfPictureParameterSets; i++) { unsigned int(16) pictureParameterSetLength;
bit(8*pictureParameterSetLength) pictureParameterSetNALUnit;
}
if( profile_idc == 100 || profile_idc == 110 ||
profile_idc == 122 || profile_idc == 144 )
{ bit(6) reserved = ‘111111’b;
unsigned int(2) chroma_format;
bit(5) reserved = ‘11111’b;
unsigned int(3) bit_depth_luma_minus8;
bit(5) reserved = ‘11111’b;
unsigned int(3) bit_depth_chroma_minus8;
unsigned int(8) numOfSequenceParameterSetExt;
for (i=0; i< numOfSequenceParameterSetExt; i++) { unsigned int(16) sequenceParameterSetExtLength;
bit(8*sequenceParameterSetExtLength) sequenceParameterSetExtNALUnit;
}
}
}
获取关键帧位置
解析stss box可以知道哪一个sample中包含关键帧
获取chunk位置
解析stco box可以获取到每个chunk在视频文件中的索引
获取每个chunk中sample个数
解析stsc box可以获取到每个chunk包含多少个sample
获取sample大小
解析stsz box可以获取到每个sample的大小
获取frame位置(demo视频文件一个sample只包含一个frame,所以sample的位置和大小就是frame的位置和大小)
获取到一帧数据后
保存成h264文件,可使用ffplay和potplay播放
注意:有些非字串的字段为大端字节序,须要转换
总结:mp4info—可以看到相关box的字节信息,但发现对avcC的解析漏掉了几个字节
mp4 exploer—可以更加直观的看到视音频数据信息
https://github.com/TaoChou/demuxer-c
参考你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧