AVFoundation 中關(guān)于高幀率的支持
????在 高幀率捕捉是開發(fā)者在一些場(chǎng)景中希望使用的技術(shù)。蘋果公司并沒有單獨(dú)推出這一個(gè)功能,而是通過 AV Foundation 框架為這個(gè)功能給出了強(qiáng)大的支撐。
- 捕捉: 支持每秒 60 幀 (fps) 的完整 720p(1280 x 720 像素)分辨率,包括視頻穩(wěn)定和支持啟動(dòng) droppable P-frames (H.264 編碼的特性 ),即使在較慢和較舊的硬件上也可以流暢地播放電影。
- 播放:
AVPlayer已經(jīng)支持以多種播放幀率播放資源內(nèi)容,增強(qiáng)了對(duì)慢速和快速播放的音頻支持,AVPlayerItem有一個(gè)audioTimePitchAlgorithm可以允許以較慢或較快的速度設(shè)置算法。 - 編輯:全面支持可變組合成中執(zhí)行縮放編輯。
- 導(dǎo)出:AV Foundation 提供了保存原始幀率的功能,所以 60 fps 影片可以被導(dǎo)出,或者將電影轉(zhuǎn)換為任意較慢的幀速率,例如每秒 30 幀。
高幀率捕捉概述
????使用高幀率捕捉的首先要獲取到設(shè)備的最高質(zhì)量格式,找到它相關(guān)的幀時(shí)長(zhǎng),之后手動(dòng)設(shè)置捕捉設(shè)備的格式和幀時(shí)長(zhǎng)。AVCaptureDeviceFormat 類確定設(shè)備的捕獲能力。此類具有返回支持的媒體類型、幀速率、視野、最大縮放系數(shù)、是否支持視頻穩(wěn)定等的方法,其具有一個(gè) videoSupportedFrameRateRanges 屬性,包含了一個(gè) AVFrameRateRange 對(duì)象數(shù)組,其中帶有格式所支持的最小幀率、最大幀率和時(shí)長(zhǎng)信息。
支持高幀率捕捉
由于查找設(shè)備最大幀率的過程較為麻煩,通過給 AVCaptureDevice 添加分類的方法來實(shí)現(xiàn),開啟高幀率率捕捉的功能:
- 新增分類聲明兩個(gè)方法
- (BOOL)supportsHighFrameRateCapture判斷當(dāng)前設(shè)備是否支持高幀率捕捉和- (BOOL)enableMaxFrameRateCapture;開啟高幀率捕捉 - 實(shí)現(xiàn)
findHighestQualityOfService遍歷所有的捕捉設(shè)備的支持formats,并對(duì)每一個(gè)元素獲取相應(yīng)的codeType,這里篩選出kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange格式,接著遍歷AVFrameRateRange對(duì)象數(shù)組,獲取最大的AVFrameRateRange,最終找到提供的最高的format和幀率并將其存儲(chǔ)起來。 - 實(shí)現(xiàn)
supportsHighFrameRateCapture,若最大幀率大于 30 fps,則支持高幀率; - 實(shí)現(xiàn)
enableMaxFrameRateCapture,修改捕捉設(shè)備的format、最小幀時(shí)長(zhǎng)activeVideoMinFrameDuration和最大幀時(shí)長(zhǎng)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