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