AVFoundation 中关于高帧率的支持
在 高帧率捕捉是开发者在一些场景中希望使用的技术。苹果公司并没有单独推出这一个功能,而是通过 AV Foundation 框架为这个功能给出了强大的支撑。
- 捕捉: 支持每秒 60 帧 (fps) 的完整 720p(1280 x 720 像素)分辨率,包括视频稳定和支持启动 droppable P-frames (H.264 编码的特性 ),即使在较慢和较旧的硬件上也可以流畅地播放电影。
- 播放:
AVPlayer
已经支持以多种播放帧率播放资源内容,增强了对慢速和快速播放的音频支持,AVPlayerItem
有一个audioTimePitchAlgorithm
可以允许以较慢或较快的速度设置算法。 - 编辑:全面支持可变组合成中执行缩放编辑。
- 导出:AV Foundation 提供了保存原始帧率的功能,所以 60 fps 影片可以被导出,或者将电影转换为任意较慢的帧速率,例如每秒 30 帧。
高帧率捕捉概述
使用高帧率捕捉的首先要获取到设备的最高质量格式,找到它相关的帧时长,之后手动设置捕捉设备的格式和帧时长。AVCaptureDeviceFormat
类确定设备的捕获能力。此类具有返回支持的媒体类型、帧速率、视野、最大缩放系数、是否支持视频稳定等的方法,其具有一个 videoSupportedFrameRateRanges
属性,包含了一个 AVFrameRateRange
对象数组,其中带有格式所支持的最小帧率、最大帧率和时长信息。
支持高帧率捕捉
由于查找设备最大帧率的过程较为麻烦,通过给 AVCaptureDevice
添加分类的方法来实现,开启高帧率率捕捉的功能:
- 新增分类声明两个方法
- (BOOL)supportsHighFrameRateCapture
判断当前设备是否支持高帧率捕捉和- (BOOL)enableMaxFrameRateCapture;
开启高帧率捕捉 - 实现
findHighestQualityOfService
遍历所有的捕捉设备的支持formats
,并对每一个元素获取相应的codeType
,这里筛选出kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange
格式,接着遍历AVFrameRateRange
对象数组,获取最大的AVFrameRateRange
,最终找到提供的最高的format
和帧率并将其存储起来。 - 实现
supportsHighFrameRateCapture
,若最大帧率大于 30 fps,则支持高帧率; - 实现
enableMaxFrameRateCapture
,修改捕捉设备的format
、最小帧时长activeVideoMinFrameDuration
和最大帧时长activeVideoMaxFrameDuration
@interface AVCaptureDevice (QualityOfService)
- (BOOL)supportsHighFrameRateCapture;
- (BOOL)enableMaxFrameRateCapture;
@end
const void* kMaxFormat = &kMaxFormat;
const void* kMaxFrameRateRange = &kMaxFrameRateRange;
@implementation AVCaptureDevice (QualityOfService)
- (void)setMaxFormat:(AVCaptureDeviceFormat *)maxFormat{
objc_setAssociatedObject(self, kMaxFormat, maxFormat, OBJC_ASSOCIATION_RETAIN);
}
- (AVCaptureDeviceFormat *)maxFormat{
return objc_getAssociatedObject(self, &kMaxFormat);
}
- (void)setMaxFrameRateRange:(AVFrameRateRange*)maxFrameRateRange{
objc_setAssociatedObject(self, kMaxFrameRateRange, maxFrameRateRange, OBJC_ASSOCIATION_RETAIN);
}
- (AVFrameRateRange *)maxFrameRateRange{
return objc_getAssociatedObject(self, &kMaxFrameRateRange);
}
- (BOOL)supportsHighFrameRateCapture {
if (![self hasMediaType:AVMediaTypeVideo]) { // 1
return NO;
}
return [self isHighFrameRate]; // 2
}
- (BOOL)enableMaxFrameRateCapture{
if (![self isHighFrameRate]) { // 1
return NO;
}
if ([self lockForConfiguration:nil]) { // 2
CMTime minFrameDuration = [self maxFrameRateRange].minFrameDuration;
self.activeFormat = [self maxFormat]; // 3
self.activeVideoMinFrameDuration = minFrameDuration; // 4
self.activeVideoMaxFrameDuration = minFrameDuration;
[self unlockForConfiguration];
return YES;
}
return NO;
}
- (void)findHighestQualityOfService {
AVCaptureDeviceFormat *maxFormat = nil;
AVFrameRateRange *maxFrameRateRange = nil;
for (AVCaptureDeviceFormat *format in self.formats) {
FourCharCode codecType =
CMVideoFormatDescriptionGetCodecType(format.formatDescription);
if (codecType == kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange) {
NSArray *frameRateRanges = format.videoSupportedFrameRateRanges;
for (AVFrameRateRange *range in frameRateRanges) {
if (range.maxFrameRate > maxFrameRateRange.maxFrameRate) {
maxFormat = format;
maxFrameRateRange = range;
}
}
}
}
[self setMaxFormat:maxFormat];
[self setMaxFrameRateRange:maxFrameRateRange];
}
- (BOOL)isHighFrameRate {
AVFrameRateRange *frameRateRange = [self maxFrameRateRange]
if(!frameRateRange){
[self findHighestQualityOfService];
}
return [self maxFrameRateRange].maxFrameRate > 30.0f;
}
@end