从CMFormatDescription计算视频帧率得到异常值的解决方法
你直接读取CMFormatDescription.frameDuration得到的异常值是无效脏数据,不是真实的单帧时长。
你观察到的「timescale取值始终等于视频高度」的现象已经能直接佐证这一点:你读到的根本不是被正确赋值的CMTime结构,只是该内存偏移位置上存储的其他格式描述字段(比如视频维度的高度参数),和帧时长没有任何关系。
关键规则提醒:CMTime的value/timescale = seconds计算规则仅对标记为有效的CMTime实例生效,如果对应CMTime的flags字段为kCMTimeFlags_Invalid,说明这个时间值本身没有被初始化赋值,强行计算出来的结果没有任何参考意义。CMFormatDescription的frameDuration字段只会在解析本地固定帧率封装媒体(比如本地存储的MP4、MOV视频文件的轨道格式描述)时被填充有效值,对于实时摄像头采集流的格式描述,苹果没有对这个字段做赋值,直接读取必然拿到无意义的内存垃圾值。
不要从formatDescription读取帧时长,AVCaptureDevice.Format专门提供了官方的帧率查询属性videoSupportedFrameRateRanges,这是获取设备格式支持帧率的唯一可靠入口:
let currentActiveFormat = device.activeFormat // 遍历当前格式支持的所有帧率区间 for frameRateRange in currentActiveFormat.videoSupportedFrameRateRanges { print("支持的最小帧率: \(frameRateRange.minFrameRate)") print("支持的最大帧率: \(frameRateRange.maxFrameRate)") // 如需对应单帧时长,可通过帧率反向计算,例如30fps对应单帧时长为1/30秒 let minSingleFrameDuration = CMTime( value: 1, timescale: CMTimeScale(frameRateRange.maxFrameRate) ) }
如果你需要获取采集过程中的实时实际帧率,不要依赖静态格式描述计算,直接在AVCaptureVideoDataOutput的采集回调中,通过相邻CMSampleBuffer的输出展示时间戳差值做统计,结果是最准确的。
内容的提问来源于stack exchange,提问作者nmnir

