=================================================================
音视频入门基础:H.264专题系列文章:
音视频入门基础:H.264专题(1)——H.264官方文档下载
音视频入门基础:H.264专题(2)——使用FFmpeg命令生成H.264裸流文件
音视频入门基础:H.264专题(3)——EBSP, RBSP和SODB
音视频入门基础:H.264专题(4)——NALU Header:forbidden_zero_bit、nal_ref_idc、nal_unit_type简介
音视频入门基础:H.264专题(5)——FFmpeg源码中 解析NALU Header的函数分析
音视频入门基础:H.264专题(6)——FFmpeg源码:从H.264码流中提取NALU Header、EBSP、RBSP和SODB
音视频入门基础:H.264专题(7)——FFmpeg源码中 指数哥伦布编码的解码实现
音视频入门基础:H.264专题(8)——H.264官方文档的描述符
音视频入门基础:H.264专题(10)——FFmpeg源码中,存放SPS属性的结构体和解码SPS的函数分析
音视频入门基础:H.264专题(11)——计算视频分辨率的公式
音视频入门基础:H.264专题(12)——FFmpeg源码中通过SPS属性计算视频分辨率的实现
音视频入门基础:H.264专题(13)——FFmpeg源码中通过SPS属性获取视频色彩格式的实现
音视频入门基础:H.264专题(14)——计算视频帧率的公式
音视频入门基础:H.264专题(15)——FFmpeg源码中通过SPS属性获取视频帧率的实现
音视频入门基础:H.264专题(16)——FFmpeg源码中,判断某文件是否为H.264裸流文件的实现
音视频入门基础:H.264专题(17)——FFmpeg源码获取H.264裸流文件信息(视频压缩编码格式、色彩格式、视频分辨率、帧率)的总流程
=================================================================
一、引言
通过FFmpeg命令可以获取到H.264裸流文件的视频帧率:
在vlc中也可以获取到视频帧率(vlc底层也使用了FFmpeg进行解码):
所以FFmpeg和vlc是怎样获取到H.264编码的视频的帧率呢?它们其实是通过SPS中的VUI parameters中的属性timing_info_present_flag、num_units_in_tick、time_scale、fixed_frame_rate_flag(划掉,我测过实际不需要fixed_frame_rate_flag这个属性)获取的。
二、H.264官方文档对视频帧率相关属性的描述
根据H.264官方文档《T-REC-H.264-202108-I!!PDF-E.pdf》第45页,当vui_parameters_present_flag值为1时, SPS中存在vui_parameters( ),也就是视频可用参数:
《T-REC-H.264-202108-I!!PDF-E.pdf》第422页到第423页定义了VUI parameters syntax(视频可用参数语法):
根据第436页,timing_info_present_flag等于1表示num_units_in_tick,time_scale和fixed_frame_rate_flag在比特流中存在。timing_info_present_flag等于0表示num_units_in_tick,time_scale和fixed_frame_rate_flag在比特流中不存在。
num_units_in_tick是运行在time_scale Hz的频率(相应地时钟跳变计数器加一,称作一个时钟跳变)下的时钟的时间单元的数量。num_units_in_tick应大于0。一个时钟跳变是在编码数据中可以再现的最小时间间隔。
time_scale是在一秒钟内的时间单元的数量。
fixed_frame_rate_flag等于1表示是在输出顺序上相连的任意两幅图像的HRD输出时间的暂时间隔受下述限制。fixed_frame_rate_flag等于0表示对输出顺序上相连的任意两幅图像的HRD输出时间的暂时时间间隔没有该限制:
也就是说:只有当vui_prameters_present_flag和timing_info_present_flag都等于1的情况下,才能从H.264码流的SPS中获取到视频帧率。
根据第355页,视频帧率 = time_scale / (2 * num_units_in_tick):
网上某些文章会写到:fixed_frame_rate_flag的值不同时,计算视频帧率的公式也会不一样,分别为:“视频帧率 = time_scale / (2 * num_units_in_tick)”和“视频帧率 = time_scale / num_units_in_tick”。
但我阅读FFmpeg源码时发现:FFmepg判断视频帧率时似乎没用到fixed_frame_rate_flag这个属性。H.264官方文档对fixed_frame_rate_flag属性也没有作过多的说明。
根据文章《Find frame rate SPS》里面的说法:
“当fixed_frame_rate_flag为1时,那么你的图像速率(以fps计算)是:time_scale / num_units_in_tick。如果你使用的是基于场的视频,那么这将是一个场速率,所以你必须将其减半才能得到帧速率。”
所以到底哪种说法是正确的呢?计算视频帧率到底要不要考虑fixed_frame_rate_flag?下面我们分别以fixed_frame_rate_flag的值为0和值为1的两个视频为例子,实际看一下。
三、计算视频帧率的例子
(一)fixed_frame_rate_flag值为0的视频
用Elecard Stream Analyzer工具打开一个用H.264编码的视频文件,其vui_prameters_present_flag值为1,timing_info_present_flag值为1,num_units_in_tick值为1,time_scale值为60,fixed_frame_rate_flag值为0:
用Elecard StreamEye工具可以看到其帧率为30。符合公式:视频帧率 = time_scale / (2 * num_units_in_tick) = 60 / (2 * 1) = 30:
所以fixed_frame_rate_flag值为0时,视频帧率 = time_scale / (2 * num_units_in_tick)
(二)fixed_frame_rate_flag值为1的视频
用Elecard Stream Analyzer工具打开一个用H.264编码的视频文件,其vui_prameters_present_flag值为1,timing_info_present_flag值为1,num_units_in_tick值为125,time_scale值为5994,fixed_frame_rate_flag值为1:
用Elecard StreamEye、vlc等工具可以看到其帧率为23.98。也符合公式:视频帧率 = time_scale / (2 * num_units_in_tick) = 5994 / (2 * 125) = 23.976 ≈ 23.98:
所以fixed_frame_rate_flag值为1时,视频帧率 = time_scale / (2 * num_units_in_tick)
综上所述,我们可以得出一个结论:
H.264编码的视频,无论其SPS中的fixed_frame_rate_flag值为多少,视频帧率的计算公式都是:视频帧率 = time_scale / (2 * num_units_in_tick) !!!