对mp4文件解析成264+AAC的过程总结(帧率计算)

 

MP4文件提取video,audio的过程,网上有大量的示例。无外乎参考ffmpeg, live555, mp4v2库。git

因项目须要,这周基于mp4v2完成了一个功能性的示例,在这过程当中,对于视频帧率的计算,遇到了一些有意思的事情。github


 

首先,mp4v2库直接提供了帧率计算的方法:MP4GetTrackVideoFrameRate(),很简单。算法

这个函数跟下去,能发现是经过整个mp4文件的帧数/时长得出来的:数组

double MP4File::GetTrackVideoFrameRate(MP4TrackId trackId) { MP4SampleId numSamples = GetTrackNumberOfSamples(trackId); uint64_t msDuration = ConvertFromTrackDuration(trackId, GetTrackDuration(trackId), MP4_MSECS_TIME_SCALE); if (msDuration == 0) { return 0.0; } return ((double)numSamples / double(msDuration)) * MP4_MSECS_TIME_SCALE; }

 没毛病。ide

帧率为30的mp4,提取完264帧存文件后,用Elecard StreamEye Tools验证,也播放显示正常。函数

  

可是,忽然好奇一点,这是264裸流。帧率确定不能像mp4v2库同样,经过时长来计算,一样也没有头信息存储它。工具

那Elecard怎么知道的?ui

确定是在264帧里有。spa

 

是的,在SPS NAL单元里。3d

那就得对SPS解析了,这个嘛,本身写是不可能的,这辈子都不会本身写这种解析的。

找了网上的代码,也不难。

bool h264_decode_sps(BYTE * buf, unsigned int nLen, int &width, int &height, int &fps) { ... if (timing_info_present_flag) { int num_units_in_tick = u(32, buf, StartBit); int time_scale = u(32, buf, StartBit); fps = time_scale / num_units_in_tick; int fixed_frame_rate_flag = u(1, buf, StartBit); if (fixed_frame_rate_flag) { fps = fps / 2; } } ... }

 

语法清晰易懂,语义彻底蒙圈。

若是这段代码work正常,也就没有后面的折腾了。

我试过多个文件,这样计算出来的fps会有很高几率是错的,并且确定是翻倍。即30帧的视频计算出60帧,24帧的为48帧。

缘由嘛,用Elecard看也很清楚,就是出错的264文件SPS,其 fixed_frame_rate_flag 不为1。因此没有执行fps/2那个逻辑,就错了

 

那看来这个算法有问题,须要再深刻解读了,看看264规范吧,这个 fixed_frame_rate_flag 是啥意思。

好家伙,这一看,可复杂了,还涉及一个因子: DeltaTfiDivisor

它又是根据另外几个参数组合约定的。

 

从上表来看,里面的几个参数,不是在SPS里的,因此也不知道该如何改进上面那段计算fps的代码。

因而,继续看源码吧,再结合264标准啃。

找到live555看是怎么整的,人家能直接对264裸流进行实时rtsp传输,确定也要计算这个帧率。

在 liveMedia\H264or5VideoStreamFramer.cpp 里找到了答案。

原来live555还计算了SEI这个NAL单元,在函数analyze_sei_payload():

 

DeltaTfiDivisor = pic_struct == 0 ? 2.0 : pic_struct <= 2 ? 1.0 : pic_struct <= 4 ? 2.0 : pic_struct <= 6 ? 3.0 : pic_struct == 7 ? 4.0 : pic_struct == 8 ? 6.0 : 2.0; } 

 额外话,这里把C语言三元运算符用到了新境界。

 

这个实现和上面的表彻底对上。但这个SEI (Supplemental enhancement information)看全称就知道,它不是必须存在的,若是没有SEI呢?

看看double DeltaTfiDivisor的初值吧,原来live555代码写死是2.0

那好吧,这貌似就能够参照权威了。若是解析SPS得到视频帧率,能够粗暴的直接:

        if(num_units_in_tick > 0) fps = time_scale / num_units_in_tick / 2;

 

好了,先这样。节后再看看 ffmpeg,看这个重量级工具是怎么整这个帧率的。

待续。

 


 

完整工程代码:
相关文章
相关标签/搜索