OC之AVCaptureDevice

輸入設(shè)備AVCaptureDevice 繼承自NSObject:是關(guān)于相機(jī)硬件的接口,用于配置底層硬件的屬性(例如相機(jī)聚焦、白平衡、感光度ISO、曝光、幀率、閃光燈、縮放等),這些底層硬件包括前置攝像頭、后置攝像頭、麥克風(fēng)、閃光燈等。使用AVCaptureDeviceAVCaptureSession對象提供輸入數(shù)據(jù)(如音頻或視頻)。

1、驗(yàn)證授權(quán)

1.1、請求用戶授權(quán)指定的媒體類型

為保護(hù)用戶隱私,應(yīng)用在使用相機(jī)或者麥克風(fēng),總是需要用戶授權(quán)才能正常使用。當(dāng)應(yīng)用第一次為需要權(quán)限的媒體類型創(chuàng)建任何AVCaptureDeviceInput對象時(shí),系統(tǒng)會(huì)自動(dòng)顯示一個(gè)alert以請求用戶授權(quán)。

調(diào)用下述類方法,可以讓應(yīng)用直接獲取用戶授權(quán),而不是需要等到創(chuàng)建AVCaptureDeviceInput對象時(shí),系統(tǒng)自動(dòng)顯示一個(gè)alert以請求用戶授權(quán)。

+ (void)requestAccessForMediaType:(AVMediaType)mediaType completionHandler:(void (^)(BOOL granted))handler;

該方法有兩個(gè)參數(shù):

  • 第一個(gè)參數(shù):AVMediaType mediaType:媒體類型常量,可以是AVMediaTypeVideo,也可以是AVMediaTypeAudio;如果沒有提供媒體類型,將拋出異常NSInvalidArgumentException
  • 第二個(gè)參數(shù):(void (^)(BOOL granted))handler:獲得用戶響應(yīng)后將調(diào)用的塊;塊中參數(shù)BOOL granted如果用戶授予使用硬件的權(quán)限,則返回YES;否則返回NO。注意:塊回調(diào)可能在任意線程,如果要處理UI,請回歸主線程。

該方法是異步調(diào)用,被調(diào)用時(shí)允許客戶端繼續(xù)運(yùn)行,不會(huì)堵塞當(dāng)前線程。在被授予訪問權(quán)限之前,任何AVMediaType類型的AVCaptureDevice都將關(guān)閉靜默音頻樣本或黑色視頻幀。
如果調(diào)用該方法之前,已經(jīng)顯示alert以請求用戶授權(quán),不管用戶同意授權(quán)或者拒絕授權(quán),該方法的回調(diào)都會(huì)立即返回用戶曾經(jīng)的授權(quán)結(jié)果,而不會(huì)再次去顯示一個(gè)alert以請求用戶授權(quán)。

應(yīng)用程序必須在配置信息info.plist中提供使用NSCameraUsageDescriptionNSMicrophoneUsageDescription信息的解釋。iOS在最初請求用戶許可時(shí)顯示了這個(gè)解釋,然后在設(shè)置應(yīng)用程序中顯示。在沒有使用說明的情況下啟動(dòng)AVCaptureSession會(huì)引發(fā)異常。

當(dāng)應(yīng)用在手機(jī)上,沒有請求用戶授權(quán)時(shí),執(zhí)行下述代碼,將會(huì)顯示一個(gè)alert以請求用戶授權(quán)

[AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
    NSLog(@"granted --- %d currentThread : %@",granted,NSThread.currentThread);
}];
NSLog(@"currentThread : %@",NSThread.currentThread);
請求授權(quán).png

我們點(diǎn)擊同意,觀察打印結(jié)果:

14:06:49 currentThread : <NSThread: 0x17006ee80>{number = 1, name = main}
14:06:54 granted --- 1 currentThread : <NSThread: 0x174070600>{number = 3, name = (null)}

可以看到handler中返回YES,并且handler的響應(yīng)在任意線程中

注意:(void (^)(BOOL granted))handler 回調(diào)可能在任意線程,如果要處理UI,請確保在主線程。

1.2、獲取關(guān)于指定媒體類型的授權(quán)狀態(tài)

為保護(hù)用戶隱私,應(yīng)用在使用相機(jī)或者麥克風(fēng),總是需要用戶授權(quán)才能正常使用。當(dāng)應(yīng)用第一次為需要權(quán)限的媒體類型創(chuàng)建任何AVCaptureDeviceInput對象時(shí),系統(tǒng)會(huì)自動(dòng)顯示一個(gè)alert以請求用戶授權(quán)。

為獲悉應(yīng)用程序是否獲取指定媒體類型的權(quán)限,應(yīng)用可以調(diào)用下述類方法獲取授權(quán)狀態(tài):

+ (AVAuthorizationStatus)authorizationStatusForMediaType:(AVMediaType)mediaType;

該方法同步調(diào)用,會(huì)立即返回授權(quán)狀態(tài);如果此方法返回AVAuthorizationStatusNotDetermined,則可以調(diào)用+ requestAccessForMediaType:completionHandler:以提示用戶記錄權(quán)限。

我們不妨執(zhí)行下述代碼,分析打印結(jié)果:

switch ([AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo])
{
    case AVAuthorizationStatusNotDetermined:
        [AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
            NSLog(@"granted --- %d currentThread : %@",granted,NSThread.currentThread);
        }];
        NSLog(@"用戶尚未授予或拒絕該權(quán)限:AVAuthorizationStatusNotDetermined");
        break;
    case AVAuthorizationStatusRestricted:
        NSLog(@"不允許用戶訪問媒體捕獲設(shè)備:AVAuthorizationStatusRestricted");
        break;
    case AVAuthorizationStatusDenied:
        NSLog(@"用戶已經(jīng)明確拒絕了應(yīng)用訪問捕獲設(shè)備:AVAuthorizationStatusDenied");
        break;
    case AVAuthorizationStatusAuthorized:
        NSLog(@"用戶授予應(yīng)用訪問捕獲設(shè)備的權(quán)限:AVAuthorizationStatusAuthorized");
        break;
    default:
        break;
}
NSLog(@"currentThread : %@",NSThread.currentThread);

/****************** 打印結(jié)果 *************************
14:24:54 用戶已經(jīng)明確拒絕了應(yīng)用訪問捕獲設(shè)備:AVAuthorizationStatusDenied
14:24:54 currentThread : <NSThread: 0x170264e00>{number = 1, name = main}
****************************************************/

注意:使用AVMediaTypeVideoAVMediaTypeAudio以外的任何媒體類型調(diào)用此方法都會(huì)引發(fā)異常NSInvalidArgumentException。

 *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** +[AVCaptureDevice authorizationStatusForMediaType:] The passed mediaType text is not supported'
*** First throw call stack:
(0x1836e2fe0 0x182144538 0x18b1008c0 0x1000a2b04 0x18982c838 0x18982c5a8 0x1898cc09c 0x1898cb870 0x1898cb424 0x1898cb388 0x189811cc0 0x186a02274 0x1869f6de8 0x1869f6ca8 0x18697234c 0x1869993ac 0x186999e78 0x1836909a8 0x18368e630 0x1835bedc4 0x18987efc8 0x189879c9c 0x1000a62f0 0x1825cd59c)
libc++abi.dylib: terminating with uncaught exception of type NSException
不被支持的AVMediaType.png
1.3、媒體類型AVMediaType

媒體類型AVMediaType是使用typedef修飾的標(biāo)識符,提供各種媒體類型:

描述
AVMediaTypeVideo 指定視頻
AVMediaTypeAudio 指定音頻
AVMediaTypeText 指定文本
AVMediaTypeClosedCaption 指定閉路內(nèi)容
AVMediaTypeSubtitle 指定字幕
AVMediaTypeTimecode 指定一個(gè)時(shí)間代碼
AVMediaTypeMetadata 指定元數(shù)據(jù)
AVMediaTypeMuxed 指定mux媒體
AVMediaTypeMetadataObject
AVMediaTypeDepthData
1.4、授權(quán)狀態(tài)AVAuthorizationStatus

授權(quán)狀態(tài)AVAuthorizationStatus是個(gè)枚舉類型,提供有關(guān)使用捕獲設(shè)備AVCaptureDevice權(quán)限信息的常量:

描述
AVAuthorizationStatusNotDetermined 用戶尚未授予或拒絕該權(quán)限,
AVAuthorizationStatusRestricted 不允許用戶訪問媒體捕獲設(shè)備。這個(gè)狀態(tài)通常是看不到的:用于發(fā)現(xiàn)設(shè)備的AVCaptureDevice類方法不會(huì)返回用戶被限制訪問的設(shè)備。
AVAuthorizationStatusDenied 用戶已經(jīng)明確拒絕了應(yīng)用訪問捕獲設(shè)備
AVAuthorizationStatusAuthorized 用戶授予應(yīng)用訪問捕獲設(shè)備的權(quán)限
1.5、使用例子
+ (void)getAuthorizationStatus:(void(^)(void))authorizedBlock
{
    switch ([AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo]){
        case AVAuthorizationStatusNotDetermined:{
            
            [AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted){
                 if (granted){
                     dispatch_async(dispatch_get_main_queue(), ^{
                         if (authorizedBlock) {
                             authorizedBlock();
                         }
                     });
                 }
                 NSLog(@"granted --- %d currentThread : %@",granted,NSThread.currentThread);
             }];
            NSLog(@"用戶尚未授予或拒絕該權(quán)限:AVAuthorizationStatusNotDetermined");
        }
            break;
        case AVAuthorizationStatusRestricted:
            NSLog(@"不允許用戶訪問媒體捕獲設(shè)備:AVAuthorizationStatusRestricted");
            break;
        case AVAuthorizationStatusDenied:
        {
            UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"沒有權(quán)限" message:@"該功能需要授權(quán)使用你的相機(jī)" preferredStyle:UIAlertControllerStyleAlert];
            UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"拒絕" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {}];
            UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"授權(quán)" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
                
                NSURL *url= [NSURL URLWithString:UIApplicationOpenSettingsURLString];
                
                if (@available(iOS 10.0, *)){
                    if( [[UIApplication sharedApplication]canOpenURL:url] ) {
                        [[UIApplication sharedApplication]openURL:url options:@{}completionHandler:^(BOOL success) {
                        }];
                    }
                }else{
                    if( [[UIApplication sharedApplication]canOpenURL:url] ) {
                        [[UIApplication sharedApplication]openURL:url];
                    }
                }
            }];
            [alertController addAction:cancelAction];
            [alertController addAction:okAction];
            [UIApplication.sharedApplication.keyWindow.rootViewController presentViewController:alertController animated:YES completion:nil];
            NSLog(@"用戶已經(jīng)明確拒絕了應(yīng)用訪問捕獲設(shè)備:AVAuthorizationStatusDenied");
        }
            break;
        case AVAuthorizationStatusAuthorized:
        {
            dispatch_async(dispatch_get_main_queue(), ^{
                if (authorizedBlock) {
                    authorizedBlock();
                }
            });
            NSLog(@"用戶授予應(yīng)用訪問捕獲設(shè)備的權(quán)限:AVAuthorizationStatusAuthorized");
        }
            break;
        default:
            break;
    }
}

在進(jìn)入相機(jī)拍攝界面之前,調(diào)用上述方法,只有授權(quán)使用相機(jī)時(shí)回調(diào)authorizedBlock 進(jìn)入相機(jī)界面:

    [AVCaptureTools getAuthorizationStatus:^{
        
        if (@available(iOS 10.2, *)) {
            //使用 AVCapturePhotoOutput 拍照
            UINavigationController *nav = [[UINavigationController alloc]initWithRootViewController:[[AVCaptureViewController alloc]init]];
            self.window.rootViewController = nav;
        } else {
            //使用 AVCaptureStillImageOutput 拍照
            UINavigationController *nav = [[UINavigationController alloc]initWithRootViewController:[[AVCaptureLowVersionViewController alloc]init]];
            self.window.rootViewController = nav;
        }
    }];

2、發(fā)現(xiàn)設(shè)備

2.1、AVCaptureDeviceDiscoverySession

在 iPhone 7plus 及以后某些 iPhone 上,采用了后置雙攝像頭配置:一個(gè)廣角鏡頭和一個(gè)長焦鏡頭,這兩個(gè)鏡頭可以合并為一個(gè)單一采集設(shè)備一起工作。在iOS 10及以后的 版本中使用 AVCaptureDeviceDiscoverySession 查詢可用AVCaptureDevice。使用這個(gè)類可以找到所有匹配特定設(shè)備類型deviceTypes(如麥克風(fēng)或廣角相機(jī))、支持用于捕獲的媒體類型(如音頻、視頻或兩者)和相機(jī)位置(前或后)的可用AVCaptureDevice。

使用下述類方法創(chuàng)建一個(gè)AVCaptureDeviceDiscoverySession,用于查找具有指定標(biāo)準(zhǔn)的設(shè)備:

+ (instancetype)discoverySessionWithDeviceTypes:(NSArray<AVCaptureDeviceType> *)deviceTypes mediaType:(AVMediaType)mediaType position:(AVCaptureDevicePosition)position;
  • 參數(shù)NSArray<AVCaptureDeviceType> *deviceTypes :要搜索的設(shè)備類型列表,例如 麥克風(fēng)AVCaptureDeviceTypeBuiltInMicrophone 和相機(jī)AVCaptureDeviceTypeBuiltInWideAngleCamera 。這個(gè)數(shù)組必須至少包含一個(gè)有效的AVCaptureDeviceType 值。

創(chuàng)建AVCaptureDeviceDiscoverySession后,讀取其設(shè)備數(shù)組devices以檢查匹配的設(shè)備并選擇一個(gè)可用的AVCaptureDevice。

@property(nonatomic, readonly) NSArray<AVCaptureDevice *> *devices;

這個(gè)數(shù)組只包含當(dāng)前可用的AVCaptureDevice(在讀取屬性時(shí)),并且滿足使用+ discoverySessionWithDeviceTypes:mediaType:position: initializer創(chuàng)建設(shè)備發(fā)現(xiàn)會(huì)話時(shí)指定的條件。

在 iOS 11.0 及更高版本中,這個(gè)數(shù)組的順序與用于創(chuàng)建發(fā)現(xiàn)會(huì)話的deviceTypes參數(shù)的順序相匹配,因此可以快速選擇匹配首選類型的設(shè)備(請參閱帶有發(fā)現(xiàn)會(huì)話的排序和篩選設(shè)備)。在較老的iOS版本中,搜索整個(gè)數(shù)組以找到首選設(shè)備。

2.1.1、 AVCaptureDeviceType

設(shè)備類型AVCaptureDeviceType是使用typedef修飾的標(biāo)識符,提供各種設(shè)備類型,與+ defaultDeviceWithDeviceType:mediaType:position:方法和AVCaptureDeviceDiscoverySession類一起使用。

設(shè)備類型AVCaptureDeviceType 描述
AVCaptureDeviceTypeBuiltInMicrophone 一個(gè)內(nèi)置的麥克風(fēng)
AVCaptureDeviceTypeBuiltInWideAngleCamera 內(nèi)置廣角相機(jī),這些裝置適用于一般用途。
AVCaptureDeviceTypeBuiltInTelephotoCamera 內(nèi)置長焦相機(jī),比廣角相機(jī)的焦距長。這種類型只是將窄角設(shè)備與配備兩種類型的攝像機(jī)的硬件上的寬角設(shè)備區(qū)分開來。要確定攝像機(jī)設(shè)備的實(shí)際焦距,可以檢查AVCaptureDeviceformat數(shù)組中的AVCaptureDeviceFormat對象。
AVCaptureDeviceTypeBuiltInDualCamera 廣角相機(jī)和長焦相機(jī)的組合,創(chuàng)建了一個(gè)拍照,錄像的AVCaptureDevice。具有和深度捕捉,增強(qiáng)變焦和雙圖像捕捉功能。
AVCaptureDeviceTypeBuiltInTrueDepthCamera 相機(jī)和其他傳感器的組合,創(chuàng)建了一個(gè)捕捉設(shè)備,能夠拍照、視頻和深度捕捉。
AVCaptureDeviceTypeBuiltInDuoCamera iOS 10.2 之后添加自動(dòng)變焦功能,該值功能被AVCaptureDeviceTypeBuiltInDualCamera替代
2.1.2、 AVCaptureDevicePosition

相機(jī)鏡頭位置AVCaptureDevicePosition是個(gè)枚舉類型:

描述
AVCaptureDevicePositionUnspecified AVCaptureDevice相對于系統(tǒng)硬件的位置未指定。
AVCaptureDevicePositionBack 后置鏡頭
AVCaptureDevicePositionFront 前置鏡頭
2.2、獲取指定設(shè)備
2.2.1、多個(gè)篩選條件獲取一個(gè)默認(rèn)設(shè)備
+ (AVCaptureDevice *)defaultDeviceWithDeviceType:(AVCaptureDeviceType)deviceType mediaType:(AVMediaType)mediaType position:(AVCaptureDevicePosition)position;

返回指定設(shè)備類型、媒體類型和位置的默認(rèn)設(shè)備;如果當(dāng)前沒有可用設(shè)備滿足指定條件,則為nil。使用該方法可以輕松地為指定場景選擇系統(tǒng)默認(rèn)AVCaptureDevice,例如,要在支持的硬件上獲得雙攝像頭,并返回到標(biāo)準(zhǔn)廣角攝像頭;

- (AVCaptureDevice *)defaultCamera {
    AVCaptureDevice *device;
    if (@available(iOS 10.0, *)) {
        device = [AVCaptureDevice defaultDeviceWithDeviceType: AVCaptureDeviceTypeBuiltInDuoCamera
                                                    mediaType: AVMediaTypeVideo
                                                     position: AVCaptureDevicePositionBack];
    } else {
        // Fallback on earlier versions
    }
    if (device != nil) {
        return device;
    }
    if (@available(iOS 10.0, *)) {
        device = [AVCaptureDevice defaultDeviceWithDeviceType: AVCaptureDeviceTypeBuiltInWideAngleCamera
                                                    mediaType: AVMediaTypeVideo
                                                     position: AVCaptureDevicePositionBack];
    } else {
        // Fallback on earlier versions
    }
    if (device != nil) {
        return device;
    }
    return nil;
}
2.2.2、給定ID的一個(gè)設(shè)備
+ (AVCaptureDevice *)deviceWithUniqueID:(NSString *)deviceUniqueID;

返回具有給定ID的設(shè)備。

2.2.3、給定媒體類型AVMediaType的一個(gè)設(shè)備
+ (AVCaptureDevice *)defaultDeviceWithMediaType:(AVMediaType)mediaType;

返回指定媒體類型AVMediaType的默認(rèn)設(shè)備。

注意:使用該方法請求攝像機(jī)AVMediaTypeVideo時(shí),返回的總是AVCaptureDeviceTypeBuiltInWideAngleCamera設(shè)備類型。要使用其他設(shè)備類型,使用+ defaultDeviceWithDeviceType:mediaType:position:方法。

2.2.4、返回一組設(shè)備
//返回系統(tǒng)上可用捕獲設(shè)備的數(shù)組。已經(jīng)被蘋果使用AVCaptureDeviceDiscoverySession替代
+ (NSArray<AVCaptureDevice *> *)devices;
//返回給定媒體類型的設(shè)備數(shù)組。已經(jīng)被蘋果使用AVCaptureDeviceDiscoverySession替代
+ (NSArray<AVCaptureDevice *> *)devicesWithMediaType:(AVMediaType)mediaType;
2.3、通知
通知 描述
AVCaptureDeviceWasConnectedNotification 當(dāng)新設(shè)備可用時(shí)發(fā)送一個(gè)通知,通知對象是AVCaptureDevice實(shí)例,表示已可用的設(shè)備。
AVCaptureDeviceWasDisconnectedNotification 當(dāng)現(xiàn)有設(shè)備不可用時(shí)發(fā)送一個(gè)通知,通知對象是AVCaptureDevice實(shí)例,表示不可用的設(shè)備。
2.4、例子

我們以獲取mediaTypeAVMediaTypeVideo的后置鏡頭為例:

- (void)getTheAVCaptureDevice
{
    if (@available(iOS 10.2, *)) {
        
        NSArray<AVCaptureDeviceType> *deviceTypes = @[AVCaptureDeviceTypeBuiltInWideAngleCamera, AVCaptureDeviceTypeBuiltInDualCamera];//設(shè)備類型:廣角鏡頭、雙鏡頭
        AVCaptureDeviceDiscoverySession *sessionDiscovery = [AVCaptureDeviceDiscoverySession discoverySessionWithDeviceTypes:deviceTypes mediaType:AVMediaTypeVideo position:AVCaptureDevicePositionBack];
        
        NSArray<AVCaptureDevice *> *devices = sessionDiscovery.devices;//當(dāng)前可用的AVCaptureDevice集合
        __block AVCaptureDevice *newVideoDevice = nil;
        
        //遍歷所有可用的AVCaptureDevice,獲取 后置雙鏡頭
        [devices enumerateObjectsUsingBlock:^(AVCaptureDevice * _Nonnull device, NSUInteger idx, BOOL * _Nonnull stop) {
            if ( device.position == AVCaptureDevicePositionBack && [device.deviceType isEqualToString:AVCaptureDeviceTypeBuiltInDualCamera] ) {
                newVideoDevice = device;
                * stop = YES;
            }
        }];
        
        if (!newVideoDevice){
            //如果后置雙鏡頭獲取失敗,則獲取廣角鏡頭
            [devices enumerateObjectsUsingBlock:^(AVCaptureDevice * _Nonnull device, NSUInteger idx, BOOL * _Nonnull stop) {
                if ( device.position == AVCaptureDevicePositionBack) {
                    newVideoDevice = device;
                    * stop = YES;
                }
            }];
        }
        
    } else {
        
        //獲取指定mediaType類型的AVCaptureDevice集合
        NSArray<AVCaptureDevice *> *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
        
        __block AVCaptureDevice *newVideoDevice = nil;
        //遍歷所有可用的AVCaptureDevice,獲取后置鏡頭
        [devices enumerateObjectsUsingBlock:^(AVCaptureDevice * _Nonnull device, NSUInteger idx, BOOL * _Nonnull stop) {
            if ( device.position == AVCaptureDevicePositionBack) {
                newVideoDevice = device;
                * stop = YES;
            }
        }];
    }
}

我們可以看到,針對不同版本的 iOS 系統(tǒng),蘋果提供了兩種方法獲取指定的AVCaptureDevice:在適配 iOS 10 以后的版本中,我們可以使用AVCaptureDeviceDiscoverySession 獲取后置雙鏡頭!但是在另一種方法中,我們無法使用雙鏡頭,只能獲取一個(gè)默認(rèn)的廣角鏡頭。

3、配置設(shè)備

3.1、鎖定設(shè)備

AVCaptureDevice在改變某些參數(shù)前必須先鎖定,直到改變結(jié)束才能解鎖:

//在修改AVCaptureDevice相關(guān)屬性之前,必須調(diào)用下述方法上鎖;當(dāng)這個(gè)方法成功地鎖定設(shè)備進(jìn)行配置時(shí),它返回YES;如果沒有獲得鎖,則為NO。
- (BOOL)lockForConfiguration:(NSError * _Nullable *)outError;
//在修改AVCaptureDevice相關(guān)屬性完成后,還需要調(diào)用下述方法解鎖,允許其他應(yīng)用程序進(jìn)行更改。
- (void)unlockForConfiguration;

如果要求設(shè)備屬性保持不變,可以持有一個(gè)鎖而不釋放它。然而,不必要地持有設(shè)備鎖可能會(huì)降低共享設(shè)備的其他應(yīng)用程序的捕獲質(zhì)量。

在iOS中,直接設(shè)置AVCaptureDeviceactiveFormat屬性,會(huì)導(dǎo)致AVCaptureSession的預(yù)設(shè)值sessionPreset更改為AVCaptureSessionPresetInputPriority。更改AVCaptureSession時(shí),當(dāng)調(diào)用它的startRunning方法 或 更改它的結(jié)構(gòu)(如添加、刪除輸入和輸出)后調(diào)用-commitConfiguration方法時(shí),AVCaptureSession不再自動(dòng)配置捕獲格式。但是在 Mac OS 中,在進(jìn)行更改之后,AVCaptureSession仍然可以自動(dòng)配置捕獲格式activeFormat

為了防止 Mac OS 中捕捉格式activeFormat的自動(dòng)更改,需要執(zhí)行以下步驟:

  • 1、調(diào)用AVCaptureDevice- lockForConfiguration:方法鎖定設(shè)備。
  • 2、更改AVCaptureDeviceactiveFormat屬性。
  • 3、調(diào)用AVCaptureSessionstartRunning方法
  • 4、調(diào)用AVCaptureDevice- unlockForConfiguration解鎖設(shè)備。

為了防止 在修改 Mac OS 中的AVCaptureSession的結(jié)構(gòu)后自動(dòng)更改activeFormat:

  • 1、調(diào)用AVCaptureDevice- lockForConfiguration:方法鎖定設(shè)備。
  • 2、調(diào)用AVCaptureSession- beginConfiguration方法,修改它的結(jié)構(gòu);
  • 3、調(diào)用AVCaptureSession- commitConfiguration方法,提交修改;
  • 4、調(diào)用AVCaptureDevice- unlockForConfiguration解鎖設(shè)備。
3.2、一些設(shè)備參數(shù)
屬性 類型 描述
inUseByAnotherApplication BOOL 指示設(shè)備是否被其他應(yīng)用程序占用。
suspended BOOL 指示設(shè)備是否掛起。一些設(shè)備由于參數(shù)設(shè)置不允許數(shù)據(jù)捕獲;例如,當(dāng)iSight的隱私虹膜關(guān)閉時(shí),isSuspended會(huì)返回YES,對于筆記本上的iSight攝像機(jī),或者對于筆記本顯示關(guān)閉時(shí)的內(nèi)部iSight攝像機(jī),issuspend會(huì)返回YES。
linkedDevices NSArray<AVCaptureDevice *> 一組AVCaptureDevice對象,表示外接設(shè)備。例如,對于外部iSight攝像機(jī),數(shù)組包含一個(gè)AVCaptureDevice實(shí)例,表示外部iSight麥克風(fēng)。
transportType int32_t AVCaptureDevice的傳輸類型(USB、PCI等)。
inputSources NSArray<AVCaptureDeviceInputSource *> AVCaptureDevice支持的輸入源的AVCaptureDeviceInputSource對象數(shù)組。 有些設(shè)備可以從多個(gè)數(shù)據(jù)源之一(例如,同一音頻設(shè)備上的不同輸入插孔)捕獲數(shù)據(jù)。
activeInputSource AVCaptureDeviceInputSource 當(dāng)前活躍的輸入源。要設(shè)置該屬性,需要先調(diào)用- lockForConfiguration:方法鎖定設(shè)備;否則將拋出異常NSGenericException;如果傳遞的formatformats中不存在,調(diào)用-setActiveInputSource:拋出異常NSInvalidArgumentException。
3.3、輸入源AVCaptureDeviceInputSource

AVCaptureDeviceInputSource是捕獲設(shè)備AVCaptureDevice上的輸入源。

AVCaptureDevice對象可以從inputSources數(shù)組中選擇一個(gè)作為輸入源,表示設(shè)備的不同的互斥輸入。例如,音頻捕獲設(shè)備可能具有ADAT光學(xué)和模擬輸入源;視頻捕獲設(shè)備可能有HDMI輸入源或組件輸入源。

屬性 類型 描述
inputSourceID NSString 輸入源ID;在指定AVCaptureDevice對象的輸入源中,ID是唯一的。
localizedName NSString 輸入源的本地化名稱;可以使用此屬性在用戶界面中顯示AVCaptureDevice輸入源的名稱。

4、檢查設(shè)備特征

屬性 類型 描述
connected BOOL 表示AVCaptureDevice實(shí)例當(dāng)前是否已連接,是否可用。但是,當(dāng)這個(gè)屬性的值對于給定的AVCaptureDevice實(shí)例變成NO時(shí),它就不會(huì)再變成YES。如果相同的物理設(shè)備再次對系統(tǒng)可用,它將使用AVCaptureDevice的新實(shí)例表示。
position AVCaptureDevicePosition 表示設(shè)備AVCaptureDevice的物理位置。
modelID NSString 設(shè)備的型號ID。該值是同一型號的所有設(shè)備的唯一標(biāo)識符,該值是跨設(shè)備連接和斷開連接以及跨不同系統(tǒng)的持久值。例如,內(nèi)置在兩個(gè)相同iPhone機(jī)型上的攝像頭的型號ID將是相同的,盡管它們是不同的物理設(shè)備。
localizedName NSString 一個(gè)本地可讀的AVCaptureDevice設(shè)備名稱;可以使用該值在用戶界面中顯示AVCaptureDevice設(shè)備設(shè)備的名稱。
uniqueID NSString AVCaptureDevice唯一ID,它可以跨設(shè)備連接和斷開連接、應(yīng)用程序重新啟動(dòng)和系統(tǒng)重新啟動(dòng)在一個(gè)系統(tǒng)上持續(xù)存在??梢源鎯υ撝?,以便將來收回或跟蹤特定設(shè)備的狀態(tài)。
lensAperture float 鏡片隔膜的尺寸,表示透鏡光圈的大小(f號)。是個(gè)只讀屬性,該值不變。
deviceType AVCaptureDeviceType 設(shè)備類型,如內(nèi)置麥克風(fēng)或廣角攝像機(jī)。
manufacturer NSString 設(shè)備制造商。對于所有蘋果設(shè)備來說,這一屬性值都是“Apple Inc.”;來自第三方制造商的設(shè)備可能不被識別,此時(shí)該值值為空字符串。
//返回一個(gè)布爾值,表示AVCaptureDevice是否為指定AVMediaType提供媒體。
- (BOOL)hasMediaType:(AVMediaType)mediaType;
//返回一個(gè)布爾值,表示AVCaptureDevice是否可以使用 指定sessionPreset的會(huì)話AVCaptureSession。
- (BOOL)supportsAVCaptureSessionPreset:(AVCaptureSessionPreset)preset;

5、管理格式

5.1、捕獲格式AVCaptureDeviceFormat

AVCaptureDeviceFormat 對象詳細(xì)描述了特定捕獲模式的視頻、圖像或音頻參數(shù)等,提供一組可用于配置AVCaptureDevice的媒體格式與設(shè)置,如視頻分辨率和幀速率。

5.2、設(shè)備支持的捕獲格式
@property(nonatomic, readonly) NSArray<AVCaptureDeviceFormat *> *formats;

該屬性提供了AVCaptureDevice支持的捕獲格式,如果需要訪問AVCaptureSession預(yù)設(shè)中未包含的設(shè)置,可以將activeFormat屬性設(shè)置為該數(shù)組中的任何格式。

5.3、當(dāng)前捕獲格式
@property(nonatomic, retain) AVCaptureDeviceFormat *activeFormat;

該屬性提供了AVCaptureDevice的當(dāng)前活動(dòng)媒體數(shù)據(jù)格式;可以使用此屬性獲取或設(shè)置當(dāng)前活動(dòng)的設(shè)備格式。

在iOS中,通常在AVCaptureSession對象上設(shè)置sessionPreset來配置圖像或視頻捕獲,并使用共享的AVAudioSession對象來配置音頻捕獲;在使用sessionPreset時(shí),AVCaptureSession會(huì)自動(dòng)控制AVCaptureDevice的活動(dòng)格式activeFormat。然而,在AVCaptureSession的設(shè)置中,有些專門的捕獲選項(xiàng)(比如高幀率)是不可用的;對于這些選項(xiàng),需要設(shè)置AVCaptureDevice的活動(dòng)格式activeFormat,這樣做將關(guān)聯(lián)的AVCaptureSessionsessionPreset更改為AVCaptureSessionPresetInputPriority。

注意:試圖將activeFormat設(shè)置為formats數(shù)組中不存在的格式,會(huì)拋出NSInvalidArgumentException。

在更改此屬性的值之前,必須調(diào)用-lockForConfiguration:以獲得對設(shè)備配置屬性的訪問權(quán),否則,設(shè)置此屬性的值將引發(fā)異常;完成設(shè)備配置后,調(diào)用-unlockForConfiguration釋放鎖并允許其他設(shè)備配置設(shè)置。還必須在調(diào)用AVCaptureSession-startRunning方法之前調(diào)用-lockForConfiguration:,或AVCaptureSessionsessionPreset將覆蓋AVCaptureDevice的活動(dòng)格式activeFormat

5.4、當(dāng)前深度捕獲格式
@property(nonatomic, retain) AVCaptureDeviceFormat *activeDepthDataFormat;

該屬性提供了AVCaptureDevice的當(dāng)前活動(dòng)深度數(shù)據(jù)格式。

當(dāng)使用AVCaptureDepthDataOutput類捕獲深度信息時(shí),或者使用AVCapturePhotoOutput類在照片旁邊啟用捕獲深度映射時(shí),捕獲輸出自動(dòng)使用與其活動(dòng)照片/視頻捕獲格式相關(guān)聯(lián)的深度數(shù)據(jù)格式。如果需要更改深度捕獲格式,需要設(shè)置此屬性的值。

深度數(shù)據(jù)捕獲要求視頻/照片數(shù)據(jù)的兼容捕獲格式,如果將此屬性設(shè)置為當(dāng)前activeFormat對象AVCaptureDeviceFormatsupporteddepthdataformat數(shù)組中沒有列出的捕獲格式,將會(huì)引發(fā)異常。

不能直接設(shè)置深度數(shù)據(jù)捕獲的幀速率:深度數(shù)據(jù)幀速率與設(shè)備的activeVideoMinFrameDurationactiveVideoMaxFrameDuration值是同步的,可以等于設(shè)備當(dāng)前的幀速率,如果設(shè)備不能以足夠快的速度生成深度數(shù)據(jù),則可以降低。深度數(shù)據(jù)捕獲可能會(huì)增加系統(tǒng)負(fù)載,從而降低視頻幀速率以實(shí)現(xiàn)熱可持續(xù)性。

在更改此屬性的值之前,必須調(diào)用-lockForConfiguration:以獲得對設(shè)備配置屬性的訪問權(quán),否則,設(shè)置此屬性的值將引發(fā)異常;完成設(shè)備配置后,調(diào)用-unlockForConfiguration釋放鎖并允許其他設(shè)備配置設(shè)置。還必須在調(diào)用AVCaptureSession-startRunning方法之前調(diào)用-lockForConfiguration:,或AVCaptureSessionsessionPreset將覆蓋AVCaptureDevice的活動(dòng)格式activeFormat。

6、焦點(diǎn)設(shè)置

鏡頭焦點(diǎn)與焦距的區(qū)別:
焦點(diǎn) 是透鏡(或曲面鏡)將光線會(huì)聚后所形成的點(diǎn),因光線會(huì)聚成一點(diǎn)可將物燒焦。
焦距 是焦點(diǎn)到面鏡的中心點(diǎn)之間的距離。鏡頭焦距的長短決定著拍攝的成像大小,視場角大小,景深大小和畫面的透視強(qiáng)弱。鏡頭的焦距是鏡頭的一個(gè)非常重要的指標(biāo)。鏡頭焦距的長短決定了被攝物在成像介質(zhì)(膠片或CCD等)上成像的大小,也就是相當(dāng)于物和象的比例尺。當(dāng)對同一距離遠(yuǎn)的同一個(gè)被攝目標(biāo)拍攝時(shí),鏡頭焦距長的所成的象大,鏡頭焦距短的所成的象小。根據(jù)用途的不同,照相機(jī)鏡頭的焦距相差非常大,有短到幾毫米,十幾毫米的,也有長達(dá)幾米的。

6.1、對焦模式
屬性 類型 描述
focusMode AVCaptureFocusMode 設(shè)備的對焦模式。在更改此屬性的值之前,必須調(diào)用-lockForConfiguration:以鎖定AVCaptureDevice;否則,設(shè)置此屬性的值會(huì)引發(fā)異常。完成配置設(shè)備后,請調(diào)用-unlockForConfiguration以釋放鎖定并允許其他設(shè)備配置設(shè)置。
focusPointOfInterestSupported BOOL 指示設(shè)備是否支持焦點(diǎn)。
adjustingFocus BOOL 指示設(shè)備當(dāng)前是否正在調(diào)整其焦點(diǎn)設(shè)置。

AVCaptureFocusMode 用于指定捕獲設(shè)備的焦點(diǎn)模式的枚舉。

枚舉值 描述
AVCaptureFocusModeLocked 焦點(diǎn)被鎖定。
AVCaptureFocusModeAutoFocus 設(shè)備會(huì)自動(dòng)調(diào)整焦距一次,然后將焦點(diǎn)模式更改為AVCaptureFocusModeLocked。
AVCaptureFocusModeContinuousAutoFocus AVCaptureDevice持續(xù)監(jiān)視焦點(diǎn)并在必要時(shí)自動(dòng)聚焦。

當(dāng)設(shè)備支持焦點(diǎn),設(shè)置對焦模式時(shí),我們需要調(diào)用- (BOOL)isFocusModeSupported:(AVCaptureFocusMode)focusMode方法判斷設(shè)備是否支持給定的焦點(diǎn)模式。如果支持focusMode則為YES,否則為NO。

6.2、焦點(diǎn)位置設(shè)置
@property(nonatomic) CGPoint focusPointOfInterest;

聚焦的中心點(diǎn); {0,0}是圖片區(qū)域的左上角,{1,1}是右下角。無論實(shí)際的設(shè)備方向如何,此坐標(biāo)系始終相對于橫向設(shè)備方向,主頁按鈕位于右側(cè)??梢允褂?code>AVCaptureVideoPreviewLayer方法在此坐標(biāo)系和視圖坐標(biāo)之間進(jìn)行轉(zhuǎn)換。

設(shè)置此屬性不會(huì)啟動(dòng)聚焦操作。要將相機(jī)聚焦在中心點(diǎn),首先設(shè)置此屬性的值,然后將focusMode屬性設(shè)置為AVCaptureFocusModeAutoFocusAVCaptureFocusModeContinuousAutoFocus。

在更改此屬性的值之前,必須調(diào)用-lockForConfiguration: 以鎖定AVCaptureDevice;否則,設(shè)置此屬性的值會(huì)引發(fā)異常。完成配置設(shè)備后,請調(diào)用-unlockForConfiguration以釋放鎖定并允許其他設(shè)備配置設(shè)置。

6.3、自動(dòng)對焦
屬性 類型 描述
smoothAutoFocusSupported BOOL 指示設(shè)備是否支持平滑自動(dòng)對焦。自動(dòng)對焦模式僅適用于兼容設(shè)備,如果此屬性的值為NO,則將smoothAutoFocusEnabled的值設(shè)置為YES會(huì)引發(fā)異常。
smoothAutoFocusEnabled BOOL 用于確定是否啟用了平滑自動(dòng)對焦;在兼容設(shè)備上,可以啟用自動(dòng)對焦模式,使鏡頭移動(dòng)速度更慢。在更改此屬性的值之前,必須調(diào)用-lockForConfiguration:以鎖定AVCaptureDevice;否則,設(shè)置此屬性的值會(huì)引發(fā)異常。完成配置設(shè)備后,請調(diào)用-unlockForConfiguration以釋放鎖定并允許其他設(shè)備配置設(shè)置。
6.4、自動(dòng)對焦范圍
  • 屬性AVCaptureAutoFocusRangeRestriction autoFocusRangeRestriction
    控制自動(dòng)對焦允許范圍;默認(rèn)情況下,能夠進(jìn)行硬件聚焦的設(shè)備會(huì)嘗試聚焦在任何距離的對象上。如果希望主要關(guān)注近物體或遠(yuǎn)物體,請?jiān)O(shè)置范圍限制以提高速度并降低自動(dòng)聚焦的功耗,并減少聚焦模糊的可能性。
    在更改此屬性的值之前,必須調(diào)用-lockForConfiguration:以鎖定AVCaptureDevice;否則,設(shè)置此屬性的值會(huì)引發(fā)異常。完成配置設(shè)備后,請調(diào)用-unlockForConfiguration以釋放鎖定并允許其他設(shè)備配置設(shè)置。

  • 屬性 BOOL autoFocusRangeRestrictionSupported:一個(gè)布爾值,指示設(shè)備是否支持焦距范圍限制。對焦范圍限制僅適用于兼容設(shè)備。 如果此屬性的值為NO,則設(shè)置autoFocusRangeRestriction會(huì)引發(fā)異常。

AVCaptureAutoFocusRangeRestriction
用于指定捕獲設(shè)備的自動(dòng)聚焦范圍的枚舉:如果您希望主要關(guān)注近物體或遠(yuǎn)物體,可以使用autoFocusRangeRestriction屬性為聚焦系統(tǒng)提供提示。 這種方法使自動(dòng)對焦更快,更節(jié)能,并且更不容易出錯(cuò)。 限制優(yōu)先于指定范圍內(nèi)的距離聚焦,但如果設(shè)備在該范圍內(nèi)沒有找到焦點(diǎn),則不會(huì)阻止聚焦在別處。

枚舉值 描述
AVCaptureAutoFocusRangeRestrictionNone 設(shè)備會(huì)嘗試關(guān)注任何范圍內(nèi)的對象。此值是默認(rèn)值,是不支持焦點(diǎn)范圍限制的設(shè)備上允許的唯一值。
AVCaptureAutoFocusRangeRestrictionNear 該設(shè)備主要嘗試聚焦在相機(jī)附近的拍攝對象上。對于使用AVCaptureMetadataOutput識別機(jī)器可讀代碼的應(yīng)用程序,建議使用此值。
AVCaptureAutoFocusRangeRestrictionFar 該設(shè)備主要嘗試對焦于遠(yuǎn)離相機(jī)的拍攝對象。

7、鏡頭焦距設(shè)置

7.1、獲取鏡頭焦距
@property(nonatomic, readonly) float lensPosition;

表示鏡頭的焦距;取值范圍是 0.0~1.0 :0.0是鏡頭可以聚焦的最短距離,1.0是最遠(yuǎn)的距離;默認(rèn)值是1.0。
該值只能通過-setFocusModeLockedWithLensPosition:completionHandler:來設(shè)置。

const float AVCaptureLensPositionCurrent表示當(dāng)前鏡頭位置的特殊常數(shù)。將此值傳遞給-setFocusModeLockedWithLensPosition:completionHandler:在不更改鏡頭當(dāng)前位置的情況下鎖定焦點(diǎn)(即禁用自動(dòng)對焦)。

7.2、焦距是否支持修改
@property(nonatomic, readonly, getter=isLockingFocusWithCustomLensPositionSupported) BOOL lockingFocusWithCustomLensPositionSupported;

指示設(shè)備是否支持將焦點(diǎn)鎖定到特定的鏡頭位置的布爾值;如果此屬性的值為NO,則使用除AVCaptureLensPositionCurrent之外的鏡頭位置值調(diào)用-setFocusModeLockedWithLensPosition:completionHandler:方法會(huì)引發(fā)異常。

7.3、設(shè)置鏡頭焦距
- (void)setFocusModeLockedWithLensPosition:(float)lensPosition completionHandler:(void (^)(CMTime syncTime))handler;

修改鏡頭焦距為指定值;該方法是設(shè)置鏡頭焦距的唯一路徑。如果lensPosition被設(shè)為不支持的值,這個(gè)方法會(huì)拋出NSInvalidArgumentException異常。如果調(diào)用該方法前沒有使用lockForConfiguration鎖定AVCaptureDevice,則會(huì)引發(fā)NSGenericException異常。

  • 第一個(gè)參數(shù) lensPosition:鏡頭的焦距;
  • 第二個(gè)參數(shù) (void (^)(CMTime syncTime))handler: 當(dāng)lensPosition設(shè)置為指定值且屬性focusModeAVCaptureFocusModeLocked時(shí)調(diào)用的塊。 該塊接收與已應(yīng)用所有設(shè)置的第一個(gè)緩沖區(qū)匹配的時(shí)間戳;時(shí)間戳與設(shè)備時(shí)鐘同步,因此必須先轉(zhuǎn)換為主時(shí)鐘,然后再與AVCaptureVideoDataOutput實(shí)例傳送的緩沖區(qū)的時(shí)間戳進(jìn)行比較。如果不需要知道操作的完成情況,可以將參數(shù)傳遞nil。

8、閃光燈設(shè)置

屬性 類型 描述
hasFlash BOOL 指示AVCaptureDevice是否有閃光燈。
flashMode AVCaptureFlashMode 當(dāng)前的閃光燈模式。在更改此屬性的值之前,必須調(diào)用-lockForConfiguration:以鎖定AVCaptureDevice;否則,設(shè)置此屬性的值會(huì)引發(fā)異常。完成配置設(shè)備后,請調(diào)用-unlockForConfiguration以釋放鎖定并允許其他設(shè)備配置設(shè)置。
flashAvailable BOOL 指示閃光燈當(dāng)前是否可用;例如,如果設(shè)備過熱并需要冷卻,閃光燈可能會(huì)變得不可用。

AVCaptureFlashMode 是指定AVCaptureDevice的閃光燈模式的枚舉:

枚舉值 描述
AVCaptureFlashModeOff 捕獲設(shè)備閃光燈始終處于關(guān)閉狀態(tài)。
AVCaptureFlashModeOn 捕獲設(shè)備閃存始終打開。
AVCaptureFlashModeAuto 捕獲設(shè)備持續(xù)監(jiān)控光照水平,并在必要時(shí)使用閃光燈。

我們可以調(diào)用 - (BOOL)isFlashModeSupported:(AVCaptureFlashMode)flashMode;方法判斷是否支持指定的閃光模式。

- (void)flashModelButtonClick:(UIButton *)sender
{
    dispatch_async( self.sessionQueue, ^{
        AVCaptureDevice *device = self.videoDeviceInput.device;
        
        //是否有閃光燈,閃光燈當(dāng)前是否可用:如果設(shè)備過熱并需要冷卻,閃光燈可能會(huì)變得不可用
        if (device.hasFlash == NO || device.flashAvailable == NO) {
            return ;
        }
        
        NSError *error = nil;
        if ([device lockForConfiguration:&error] ) {
            
            switch (device.flashMode){
                case AVCaptureFlashModeAuto:{
                    if ([device isFlashModeSupported:AVCaptureFlashModeOn]){
                        [device setFlashMode:AVCaptureFlashModeOn];
                        dispatch_async(dispatch_get_main_queue(), ^{
                            [sender setImage:resourceImage(@"cameraFlash_On") forState:UIControlStateNormal];
                        });
                    }
                }
                    break;
                case AVCaptureFlashModeOff:{
                    if ([device isFlashModeSupported:AVCaptureFlashModeAuto]){
                        [device setFlashMode:AVCaptureFlashModeAuto];
                        dispatch_async(dispatch_get_main_queue(), ^{
                            [sender setImage:resourceImage(@"cameraFlash_Auto") forState:UIControlStateNormal];
                        });
                    }

                }
                    break;
                case AVCaptureFlashModeOn: {
                    if ([device isFlashModeSupported:AVCaptureFlashModeOff]){
                        [device setFlashMode:AVCaptureFlashModeOff];
                        dispatch_async(dispatch_get_main_queue(), ^{
                            [sender setImage:resourceImage(@"cameraFlash_Off") forState:UIControlStateNormal];
                        });
                    }
                }
                    break;
                default:
                    break;
            }
            
            [device unlockForConfiguration];
        }
        else {
            NSLog( @"Could not lock device for configuration: %@", error );
        }
    } );
}

9、手電筒設(shè)置

手電筒是一種光源,例如LED閃光燈,可在設(shè)備上使用,用于照亮捕獲的內(nèi)容或提供一般照明。

屬性 類型 描述
hasTorch BOOL 反映當(dāng)前設(shè)備是否具有內(nèi)置的手電筒。即使設(shè)備有手電筒,也可能無法使用。 因此,在使用之前檢查torchAvailable屬性的值。
torchAvailable BOOL 指示手電筒當(dāng)前是否可供使用。例如,如果設(shè)備過熱并需要冷卻,則手電筒可能無法使用。
torchActive BOOL 指示設(shè)備的手電筒當(dāng)前是否打開。必須在設(shè)備上有一個(gè)手電筒,并且當(dāng)前可用,然后才能激活它。
torchLevel float 獲取當(dāng)前手電筒亮度級別;其值在 0.0 到 1.0 的范圍內(nèi):0.0 表示手電筒已關(guān)閉,1.0 表示理論最大值,如果設(shè)備當(dāng)前過熱,則實(shí)際最大值可能更低。
torchMode AVCaptureTorchMode 目前的手電筒模式;設(shè)置此屬性的值還會(huì)將手電筒級別設(shè)置為其最大當(dāng)前值。在更改此屬性的值之前,必須調(diào)用-lockForConfiguration:以鎖定AVCaptureDevice;否則,設(shè)置此屬性的值會(huì)引發(fā)異常。完成配置設(shè)備后,請調(diào)用-unlockForConfiguration以釋放鎖定并允許其他設(shè)備配置設(shè)置。
9.1、手電筒模式

AVCaptureTorchMode 指定AVCaptureDevice的手電筒模式的枚舉:

枚舉值 描述
AVCaptureTorchModeOff 設(shè)備手電筒始終關(guān)閉。
AVCaptureTorchModeOn 設(shè)備手電筒始終打開。
AVCaptureTorchModeAuto 設(shè)備持續(xù)監(jiān)控光照水平,并在必要時(shí)使用手電筒。
- (BOOL)isTorchModeSupported:(AVCaptureTorchMode)torchMode;

我們必須在修改torchMode之前調(diào)用上述方法判斷設(shè)備是否支持指定的手電筒模式。

9.2、設(shè)置手電筒照明級別
- (BOOL)setTorchModeOnWithLevel:(float)torchLevel error:(NSError * _Nullable *)outError;

我們設(shè)置手電筒照明級別,需要調(diào)用上述方法。要將割炬模式級別torchLevel設(shè)置為當(dāng)前可用的最大值,需要使用常量AVCaptureMaxAvailableTorchLevel。

設(shè)置手電筒照明級別之前需要將手電筒模式torchMode設(shè)置為AVCaptureTorchModeOn并將級別設(shè)置為指定值。 如果設(shè)備不支持AVCaptureTorchModeOn手電筒模式,或者如果為torchLevel指定的值超出了可接受的范圍,則此方法會(huì)引發(fā)異常。 如果割炬值在可接受范圍內(nèi)但大于當(dāng)前支持的最大值 - 可能是因?yàn)樵O(shè)備過熱 - 此方法只返回NO。

在更改此屬性的值之前,必須調(diào)用-lockForConfiguration:以鎖定AVCaptureDevice;否則,設(shè)置此屬性的值會(huì)引發(fā)異常。完成配置設(shè)備后,請調(diào)用-unlockForConfiguration以釋放鎖定并允許其他設(shè)備配置設(shè)置。

10、縮放設(shè)置

10.1、用于控制圖像的縮放值
@property(nonatomic) CGFloat videoZoomFactor;

該屬性是一個(gè)用于控制AVCaptureDevice的圖像的裁剪和放大的值;它是一個(gè)乘數(shù):例如,值 2.0 會(huì)使圖像主體的大小加倍(并使視野減半)。允許的值范圍從 1.0(完整視野)到活動(dòng)格式AVCaptureDeviceFormat的屬性videoMaxZoomFactor值。

AVCaptureDevice通過圍繞傳感器捕獲的圖像的中心進(jìn)行裁剪來實(shí)現(xiàn)縮放效果。在低縮放系數(shù)下,裁剪的圖像等于或大于輸出大小。在較高的縮放系數(shù)下,設(shè)備必須將裁剪后的圖像縮放到輸出尺寸,從而導(dǎo)致圖像質(zhì)量下降。活動(dòng)格式AVCaptureDeviceFormat的videoZoomFactorUpscaleThreshold屬性指示將進(jìn)行放大的因素。

在更改此屬性的值之前,必須調(diào)用-lockForConfiguration:以鎖定AVCaptureDevice;否則,設(shè)置此屬性的值會(huì)引發(fā)異常。完成配置設(shè)備后,請調(diào)用-unlockForConfiguration以釋放鎖定并允許其他設(shè)備配置設(shè)置。

設(shè)置此屬性的值會(huì)立即跳轉(zhuǎn)到新的縮放系數(shù);要流暢過渡,需要使用-rampToVideoZoomFactor:withRate:方法。

最小縮放值
@property(nonatomic, readonly) CGFloat minAvailableVideoZoomFactor;

當(dāng)前AVCaptureDevice配置中允許的最小縮放系數(shù)。在單攝像頭設(shè)備上,此值始終為 1.0 ;在雙攝像頭設(shè)備上,如果設(shè)備將深度數(shù)據(jù)傳送到一個(gè)或多個(gè)捕獲輸出,則允許的視頻縮放系數(shù)范圍可能會(huì)發(fā)生變化。

設(shè)置屬性videoZoomFactor或調(diào)用-rampToVideoZoomFactor:withRate:方法使值小于 1.0 會(huì)引發(fā)異常。 將視頻縮放系數(shù)設(shè)置為介于 1.0 和最小可用縮放系數(shù)之間的值會(huì)將縮放設(shè)置限制為最小值。

最大縮放值
@property(nonatomic, readonly) CGFloat maxAvailableVideoZoomFactor;

當(dāng)前AVCaptureDevice配置中允許的最大縮放系數(shù)。在單攝像頭設(shè)備上,此值始終等于AVCaptureDeviceFormat的屬性videoMaxZoomFactor ;在雙攝像頭設(shè)備上,如果設(shè)備將深度數(shù)據(jù)傳送到一個(gè)或多個(gè)捕獲輸出,則允許的視頻縮放系數(shù)范圍可能會(huì)發(fā)生變化。

設(shè)置屬性videoZoomFactor或調(diào)用-rampToVideoZoomFactor:withRate:方法使其值大于AVCaptureDeviceFormat的屬性videoMaxZoomFactor,會(huì)引發(fā)異常。將視頻縮放系數(shù)設(shè)置為最大可用縮放系數(shù)與設(shè)備格式最大值之間的值會(huì)將縮放設(shè)置限制為最大可用值。

10.2、流暢過渡到新的縮放值

我們可以使用下述方法實(shí)現(xiàn)從當(dāng)前縮放因子到另一個(gè)縮放因子的流暢過渡:

- (void)rampToVideoZoomFactor:(CGFloat)factor withRate:(float)rate;

在該方法中有兩個(gè)參數(shù):

  • CGFloat factor: 新的放大系數(shù);
  • float rate:轉(zhuǎn)換到新放大系數(shù)的速率,以每秒 2 的冪表示。

縮放值factor允許范圍從1.0(完整視野)到活動(dòng)捕獲格式AVCaptureDeviceFormat指定的屬性videoMaxZoomFactor值。

在變化期間,縮放系數(shù)factor以指數(shù)速率變化,但這會(huì)產(chǎn)生視覺線性過渡。rate參數(shù)控制此轉(zhuǎn)換的速度,與方向無關(guān); 例如,值 1.0 會(huì)導(dǎo)致縮放因子在放大時(shí)每秒加倍(即,如果指定的因子大于當(dāng)前的videoZoomFactor),或者如果縮小則每秒減半。

在更改此屬性的值之前,必須調(diào)用-lockForConfiguration:以鎖定AVCaptureDevice;否則,設(shè)置此屬性的值會(huì)引發(fā)異常。完成配置設(shè)備后,請調(diào)用-unlockForConfiguration以釋放鎖定并允許其他設(shè)備配置設(shè)置。

10.3、結(jié)束縮放

流暢的進(jìn)行縮放需要一定的過渡時(shí)間,我們可能需要停止該緩慢的過渡,需要調(diào)用下述方法:

- (void)cancelVideoZoomRamp;

該方法能流暢地結(jié)束正在進(jìn)行的縮放而不是突然停止;調(diào)用此方法相當(dāng)于調(diào)用 -rampToVideoZoomFactor:withRate: 的速率rate為零。 如果正在進(jìn)行縮放轉(zhuǎn)換,則轉(zhuǎn)換將減慢為停止。

在更改此屬性的值之前,必須調(diào)用-lockForConfiguration:以鎖定AVCaptureDevice;否則,設(shè)置此屬性的值會(huì)引發(fā)異常。完成配置設(shè)備后,請調(diào)用-unlockForConfiguration以釋放鎖定并允許其他設(shè)備配置設(shè)置。

10.4、其它指示參數(shù)
屬性 類型 描述
rampingVideoZoom BOOL 指示是否正在進(jìn)行縮放轉(zhuǎn)換的布爾值。使用KVO觀察對此屬性值的更改,以便在縮放過渡開始或結(jié)束時(shí)得到通知。
dualCameraSwitchOverVideoZoomFactor CGFloat 雙攝像頭設(shè)備可以在攝像頭之間自動(dòng)切換的視頻縮放系數(shù);在該系數(shù)下,來自廣角攝像機(jī)的縮放視野與遠(yuǎn)攝相機(jī)的全視野相匹配。 當(dāng)videoZoomFactor設(shè)置達(dá)到或超過此值時(shí),設(shè)備可以根據(jù)場景條件自動(dòng)選擇哪個(gè)攝像機(jī)提供輸出圖像(或自動(dòng)組合兩者的圖像以創(chuàng)建最終輸出)。對于低于此值的縮放系數(shù),設(shè)備始終使用來自廣角攝像機(jī)的圖像。在單攝像頭設(shè)備上,此值始終為1.0。
10.5、縮放示例
//最小縮放值
- (CGFloat)minZoomFactor
{
    CGFloat minZoomFactor = 1.0;
    if (@available(iOS 11.0, *)) {
        minZoomFactor = self.device.minAvailableVideoZoomFactor;
    }
    return minZoomFactor;
}

//最大縮放值
- (CGFloat)maxZoomFactor
{
    CGFloat maxZoomFactor = self.device.activeFormat.videoMaxZoomFactor;
    if (@available(iOS 11.0, *)) {
        maxZoomFactor = self.device.maxAvailableVideoZoomFactor;
    }
    
    if (maxZoomFactor > 6.0) {
        maxZoomFactor = 6.0;
    }
    return maxZoomFactor;
}

- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
    if ([gestureRecognizer isKindOfClass:[UIPinchGestureRecognizer class]]){
        self.slider.minimumValue = self.minZoomFactor;
        self.slider.maximumValue = self.maxZoomFactor;
        self.currentZoomFactor = self.device.videoZoomFactor;
    }
    return YES;
}

//縮放手勢
- (void)zoomChangePinchGestureRecognizerClick:(UIPinchGestureRecognizer *)pinchGestureRecognizer
{
    if (pinchGestureRecognizer.state == UIGestureRecognizerStateBegan ||
        pinchGestureRecognizer.state == UIGestureRecognizerStateChanged)
    {
        CGFloat currentZoomFactor = self.currentZoomFactor * pinchGestureRecognizer.scale;
        self.slider.hidden = NO;
        
        if (currentZoomFactor < self.maxZoomFactor &&
            currentZoomFactor > self.minZoomFactor){
            
            NSError *error = nil;
            if ([self.device lockForConfiguration:&error] ) {
                self.device.videoZoomFactor = currentZoomFactor;
                self.slider.value = self.device.videoZoomFactor;
                [self.device unlockForConfiguration];
            }
            else {
                NSLog( @"Could not lock device for configuration: %@", error );
            }
        }
    }
    else
    {
        self.slider.hidden = YES;
    }
}

- (void)sliderValueChangeClick:(UISlider *)sender
{
    self.slider.hidden = NO;
    
    if (sender.value < self.maxZoomFactor &&
        sender.value > self.minZoomFactor){
        
        NSError *error = nil;
        if ([self.device lockForConfiguration:&error] ) {
            self.device.videoZoomFactor = sender.value;
            [self.device unlockForConfiguration];
        }
        else {
            NSLog( @"Could not lock device for configuration: %@", error );
        }
    }
}

由于篇幅限制,關(guān)于 AVCaptureDevice 的更多知識在 OC之AVCaptureDevice續(xù)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容