iOS學習筆記27-攝像頭 – iPhone手機開發 iPhone軟體開發教學課程

一、攝像頭

在iOS中,手機攝像頭的使用有以下兩種方法:
1. UIImagePickerController拍照和視頻錄制
* 優點:使用方便,功能強大
* 缺點:高度封裝性,無法實現一些自定義工作
2. AVFoundation框架實現
* 優點:靈活性強,提供瞭很多現成的輸入設備和輸出設備,還有很多底層的內容可以供開發者使用
* 缺點:需要和底層打交道,學習難度大,使用復雜

我們平常使用UIImagePickerController就基本可以滿足瞭,功能確實強大,但它也有不好的一點,那就是由於它的高度封裝性,如果要進行某些自定義工作就比較復雜,例如如果要做出一款類似於美顏相機的拍照界面就比較難以實現,這個時候就要考慮AVFoundation框架實現。

二、UIImagePickerController

UIImagePickerController繼承於UINavigationController,屬於UIKit框架,可以實現圖片選取、拍照、錄制視頻等功能,使用起來十分方便。

1. 常用屬性:
@property (nonatomic) UIImagePickerControllerSourceType sourceType;/* 拾取源類型枚舉 */
typedef NS_ENUM(NSInteger, UIImagePickerControllerSourceType) {
    UIImagePickerControllerSourceTypePhotoLibrary,//照片庫
    UIImagePickerControllerSourceTypeCamera,//攝像頭
    UIImagePickerControllerSourceTypeSavedPhotosAlbum//相簿
};
/* 
  媒體類型,默認情況下此數組包含kUTTypeImage,表示拍照
  如果要錄像,必須設置為kUTTypeVideo(視頻不帶聲音)或kUTTypeMovie(視頻帶聲音)
*/
@property (nonatomic,copy) NSArray *mediaTypes;
@property (nonatomic) NSTimeInterval videoMaximumDuration;//視頻最大錄制時長,默認10s
@property (nonatomic) UIImagePickerControllerQualityType videoQuality;//視頻質量
typedef NS_ENUM(NSInteger, UIImagePickerControllerQualityType) {
    UIImagePickerControllerQualityTypeHigh = 0,  //高清
    UIImagePickerControllerQualityTypeMedium,    //中等,適合WiFi傳輸
    UIImagePickerControllerQualityTypeLow,      //低質量,適合蜂窩網傳輸
    UIImagePickerControllerQualityType640x480, //640*480
    UIImagePickerControllerQualityTypeIFrame1280x720, //1280*720
    UIImagePickerControllerQualityTypeIFrame960x540, //960*540
};

@property (nonatomic)  BOOL showsCameraControls;/* 是否顯示攝像頭控制面板,默認為YES */
@property (nonatomic,strong) UIView *cameraOverlayView;/* 攝像頭上覆蓋的視圖 */
@property (nonatomic) CGAffineTransform cameraViewTransform;/* 攝像頭形變 */

@property (nonatomic) UIImagePickerControllerCameraCaptureMode cameraCaptureMode;/* 攝像頭捕捉模式 */
typedef NS_ENUM(NSInteger, UIImagePickerControllerCameraCaptureMode) {
    UIImagePickerControllerCameraCaptureModePhoto,//拍照模式
    UIImagePickerControllerCameraCaptureModeVideo//視頻錄制模式
};
@property (nonatomic) UIImagePickerControllerCameraDevice cameraDevice;/* 攝像頭設備 */
typedef NS_ENUM(NSInteger, UIImagePickerControllerCameraDevice) {
    UIImagePickerControllerCameraDeviceRear,//前置攝像頭
    UIImagePickerControllerCameraDeviceFront//後置攝像頭
};
@property (nonatomic) UIImagePickerControllerCameraFlashMode cameraFlashMode;/* 閃光燈模式 */
typedef NS_ENUM(NSInteger, UIImagePickerControllerCameraFlashMode) {
    UIImagePickerControllerCameraFlashModeOff  = -1,//關閉閃光燈
    UIImagePickerControllerCameraFlashModeAuto = 0,//閃光燈自動,默認
    UIImagePickerControllerCameraFlashModeOn   = 1//打開閃光燈
};
2. 常用對象方法:
- (void)takePicture; //拍照                     
- (BOOL)startVideoCapture;//開始錄制視頻
- (void)stopVideoCapture;//停止錄制視頻
3. 代理方法:
/* 媒體獲取完成會調用 */
- (void)imagePickerController:(UIImagePickerController *)picker 
didFinishPickingMediaWithInfo:(NSDictionary *)info;
/* 取消獲取會調用 */
- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker;
4. 擴展函數,用於保存到相簿:
/* 保存圖片到相簿 */
void UIImageWriteToSavedPhotosAlbum(
    UIImage *image,//保存的圖片UIImage
    id completionTarget,//回調的執行者
    SEL completionSelector, //回調方法
    void *contextInfo//回調參數信息
);
//上面一般保存圖片的回調方法為:
- (void)image:(UIImage *)image 
        didFinishSavingWithError:(NSError *)error 
        contextInfo:(void *)contextInfo;

/* 判斷是否能保存視頻到相簿 */
BOOL UIVideoAtPathIsCompatibleWithSavedPhotosAlbum(NSString *videoPath);
/* 保存視頻到相簿 */
void UISaveVideoAtPathToSavedPhotosAlbum(
    NSString *videoPath, //保存的視頻文件路徑
    id completionTarget, //回調的執行者
    SEL completionSelector,//回調方法
    void *contextInfo//回調參數信息
);
//上面一般保存視頻的回調方法為:
- (void)video:(NSString *)videoPath 
        didFinishSavingWithError:(NSError *)error 
        contextInfo:(void *)contextInfo;
5. 使用攝像頭的步驟:

創建UIImagePickerController對象 指定拾取源,拍照和錄像都需要使用攝像頭 指定攝像頭設備,是前置的還是後置的 設置媒體類型,媒體類型定義在MobileCoreServices.framework中 指定攝像頭捕捉模式,錄像必須先設置媒體類型再設置捕捉模式。 展示UIImagePickerController,通常以模態彈出形式打開 拍照或錄像結束後,在代理方法中展示或者保存照片或視頻

6. 下面是具體實例代碼:
#import "ViewController.h"
#import 

@interface ViewController () 
@property (strong, nonatomic) UIImagePickerController *pickerController;//拾取控制器
@property (strong, nonatomic) IBOutlet UIImageView *showImageView;//顯示圖片
@end

@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    //初始化拾取控制器
    [self initPickerController];
}
/* 初始化拾取控制器 */
- (void)initPickerController{
    //創建拾取控制器
    UIImagePickerController *pickerController = [[UIImagePickerController alloc] init];
    //設置拾取源為攝像頭
    pickerController.sourceType = UIImagePickerControllerSourceTypeCamera;
    //設置攝像頭為後置
    pickerController.cameraDevice = UIImagePickerControllerCameraDeviceRear;
    pickerController.editing = YES;//設置運行編輯,即可以點擊一些拾取控制器的控件
    pickerController.delegate = self;//設置代理
    self.pickerController = pickerController;
}
#pragma mark - UI點擊
/* 點擊拍照 */
- (IBAction)imagePicker:(id)sender {
    //設定拍照的媒體類型
    self.pickerController.mediaTypes = @[(NSString *)kUTTypeImage];
    //設置攝像頭捕捉模式為捕捉圖片
    self.pickerController.cameraCaptureMode = UIImagePickerControllerCameraCaptureModePhoto;
    //模式彈出拾取控制器
    [self presentViewController:self.pickerController animated:YES completion:nil];
}
/* 點擊錄像 */
- (IBAction)videoPicker:(id)sender {
    //設定錄像的媒體類型
    self.pickerController.mediaTypes = @[(NSString *)kUTTypeMovie];
    //設置攝像頭捕捉模式為捕捉視頻
    self.pickerController.cameraCaptureMode = UIImagePickerControllerCameraCaptureModeVideo;
    //設置視頻質量為高清
    self.pickerController.videoQuality = UIImagePickerControllerQualityTypeHigh;
    //模式彈出拾取控制器
    [self presentViewController:self.pickerController animated:YES completion:nil];
}

#pragma mark - 代理方法
/* 拍照或錄像成功,都會調用 */
- (void)imagePickerController:(UIImagePickerController *)picker
didFinishPickingMediaWithInfo:(NSDictionary *)info
{
    //從info取出此時攝像頭的媒體類型
    NSString *mediaType = [info objectForKey:UIImagePickerControllerMediaType];

    if ([mediaType isEqualToString:(NSString *)kUTTypeImage]) {//如果是拍照
        //獲取拍照的圖像
        UIImage *image = [info objectForKey:UIImagePickerControllerOriginalImage];
        //保存圖像到相簿
        UIImageWriteToSavedPhotosAlbum(image, self, 
                  @selector(image:didFinishSavingWithError:contextInfo:), nil);

    } else if ([mediaType isEqualToString:(NSString *)kUTTypeMovie]) {//如果是錄像
        //獲取錄像文件路徑URL
        NSURL *url = [info objectForKey:UIImagePickerControllerMediaURL];
        NSString *path = url.path;
        //判斷能不能保存到相簿
        if (UIVideoAtPathIsCompatibleWithSavedPhotosAlbum(path)) {
            //保存視頻到相簿
            UISaveVideoAtPathToSavedPhotosAlbum(path, self, 
                   @selector(video:didFinishSavingWithError:contextInfo:), nil);
        }

    }
    //拾取控制器彈回
    [self dismissViewControllerAnimated:YES completion:nil];
}
/* 取消拍照或錄像會調用 */
- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker
{
    NSLog(@"取消");
    //拾取控制器彈回
    [self dismissViewControllerAnimated:YES completion:nil];
}

#pragma mark - 保存圖片或視頻完成的回調
- (void)image:(UIImage *)image didFinishSavingWithError:(NSError *)error
                                            contextInfo:(void *)contextInfo {
    NSLog(@"保存圖片完成");
    self.showImageView.image = image;
    self.showImageView.contentMode = UIViewContentModeScaleToFill;
}

- (void)video:(NSString *)videoPath didFinishSavingWithError:(NSError *)error
                                                 contextInfo:(void *)contextInfo {
    NSLog(@"保存視頻完成");
}
@end



功能十分強大,基本滿足一般的需求,使用起來也很簡單。

三、AVFoundation的拍照錄像

首先瞭解下AVFoundation做拍照和錄像的相關類:

AVCaptureSession
媒體捕捉會話,負責把捕獲到的音視頻數據輸出到輸出設備上,一個會話可以有多個輸入輸出。 AVCaptureVideoPervieewLayer
相機拍攝預覽圖層,是CALayer的子類,實時查看拍照或錄像效果。
AVCaptureDevice
輸入設備,包括麥克風、攝像頭等,可以設置一些物理設備的屬性 AVCaptureDeviceInput
設備輸入數據管理對象,管理輸入數據 AVCaptureOutput
設備輸出數據管理對象,管理輸出數據,通常使用它的子類:

AVCaptureAudioDataOutput//輸出音頻管理對象,輸出數據為NSData
AVCaptureStillImageDataOutput//輸出圖片管理對象,輸出數據為NSData
AVCaptureVideoDataOutput//輸出視頻管理對象,輸出數據為NSData
/* 輸出文件管理對象,輸出數據以文件形式輸出 */
AVCaptureFileOutput  
{//子類    
        AVCaptureAudioFileOutput   //輸出是音頻文件
        AVCaptureMovieFileOutput   //輸出是視頻文件
}

拍照或錄像的一般步驟為:

創建AVCaptureSession對象 使用AVCaptureDevice的類方法獲得要使用的設備 利用輸入設備AVCaptureDevice創建並初始化AVCaptureDeviceInput對象 初始化輸出數據管理對象,看具體輸出什麼數據決定使用哪個AVCaptureOutput子類 將AVCaptureDeviceInputAVCaptureOutput添加到媒體會話管理對象AVCaptureSession中 創建視頻預覽圖層AVCaptureVideoPreviewLayer並指定媒體會話,添加圖層到顯示容器中 調用媒體會話AVCaptureSessionstartRunning方法開始捕獲,stopRunning方法停止捕捉 將 捕獲的音頻或視頻數據輸出到指定文件

拍照使用實例如下:

#import "ViewController.h"
#import 

@interface ViewController ()
@property (strong, nonatomic) AVCaptureSession *session;//媒體管理會話
@property (strong, nonatomic) AVCaptureDeviceInput *captureInput;//輸入數據對象
@property (strong, nonatomic) AVCaptureStillImageOutput *imageOutput;//輸出數據對象
@property (strong, nonatomic) AVCaptureVideoPreviewLayer *captureLayer;//視頻預覽圖層
@property (strong, nonatomic) IBOutlet UIButton *captureBtn;//拍照按鈕
@property (strong, nonatomic) IBOutlet UIButton *openCaptureBtn;//打開攝像頭按鈕

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    [self initCapture];
    self.openCaptureBtn.hidden = NO;
    self.captureBtn.hidden = YES;
}
/* 初始化攝像頭 */
- (void)initCapture{
    //1. 創建媒體管理會話
    AVCaptureSession *session = [[AVCaptureSession alloc] init];
    self.session = session;
    //判斷分辨率是否支持1280*720,支持就設置為1280*720
    if( [session canSetSessionPreset:AVCaptureSessionPreset1280x720] ) {
        session.sessionPreset = AVCaptureSessionPreset1280x720;
    }
    //2. 獲取後置攝像頭設備對象
    AVCaptureDevice *device = nil;
    NSArray *cameras = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
    for (AVCaptureDevice *camera in cameras) {
        if (camera.position == AVCaptureDevicePositionBack) {//取得後置攝像頭
            device = camera;
        }
    }
    if(!device) {
        NSLog(@"取得後置攝像頭錯誤");
        return;
    }
    //3. 創建輸入數據對象
    NSError *error = nil;
    AVCaptureDeviceInput *captureInput = [[AVCaptureDeviceInput alloc] initWithDevice:device
                                                                                error:&error];
    if (error) {
        NSLog(@"創建輸入數據對象錯誤");
        return;
    }
    self.captureInput = captureInput;
    //4. 創建輸出數據對象
    AVCaptureStillImageOutput *imageOutput = [[AVCaptureStillImageOutput alloc] init];
    NSDictionary *setting = @{ AVVideoCodecKey:AVVideoCodecJPEG };
    [imageOutput setOutputSettings:setting];
    self.imageOutput = imageOutput;
    //5. 添加輸入數據對象和輸出對象到會話中
    if ([session canAddInput:captureInput]) {
        [session addInput:captureInput];
    }
    if ([session canAddOutput:imageOutput]) {
        [session addOutput:imageOutput];
    }
    //6. 創建視頻預覽圖層
    AVCaptureVideoPreviewLayer *videoLayer = 
              [[AVCaptureVideoPreviewLayer alloc] initWithSession:session];
    self.view.layer.masksToBounds = YES;
    videoLayer.frame = self.view.bounds;
    videoLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
    //插入圖層在拍照按鈕的下方
    [self.view.layer insertSublayer:videoLayer below:self.captureBtn.layer];
    self.captureLayer = videoLayer;
}
#pragma mark - UI點擊
/* 點擊拍照按鈕 */
- (IBAction)takeCapture:(id)sender {
    //根據設備輸出獲得連接
    AVCaptureConnection *connection = [self.imageOutput connectionWithMediaType:AVMediaTypeVideo];
    //通過連接獲得設備輸出的數據
    [self.imageOutput captureStillImageAsynchronouslyFromConnection:connection
                      completionHandler:^(CMSampleBufferRef imageDataSampleBuffer, NSError *error)
    {
        //獲取輸出的JPG圖片數據
        NSData *imageData = 
              [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer];
        UIImage *image = [UIImage imageWithData:imageData];
        UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil);//保存到相冊
        self.captureLayer.hidden = YES;
        self.captureBtn.hidden = YES;
        self.openCaptureBtn.hidden = NO;
        [self.session stopRunning];//停止捕捉
    }];
}
/* 點擊打開攝像頭按鈕 */
- (IBAction)openCapture:(id)sender {
    self.captureLayer.hidden = NO;
    self.captureBtn.hidden = NO;
    self.openCaptureBtn.hidden = YES;
    [self.session startRunning];//開始捕捉
}
@end

錄像的操作差不多,下面代碼是以上面代碼為基礎進行修改:

比拍照多瞭一個音頻輸入,改變輸出數據對象的類 需要處理視頻輸出代理方法 錄制的方法是在輸出數據對象上

1. 獲取音頻輸入數據對象以及視頻輸出數據對象
//獲取麥克風設備對象
AVCaptureDevice *device = [AVCaptureDevice devicesWithMediaType:AVMediaTypeAudio].firstObject;
if(!device) {
    NSLog(@"取得麥克風錯誤");
    return;
}
//創建輸入數據對象
NSError *error = nil;
AVCaptureDeviceInput *audioInput = [[AVCaptureDeviceInput alloc] initWithDevice:device
                                                                          error:&error];
if (error) {
    NSLog(@"創建輸入數據對象錯誤");
    return;
}
//創建視頻文件輸出對象
AVCaptureMovieFileOutput *movieOutput = [[AVCaptureMovieFileOutput alloc] init];
self.movieOutput = movieOutput;
2. 添加進媒體管理會話中
if([session canAddInput:captureInput]) {
    [session addInput:captureInput];
    [session addInput:audioInput];
    //添加防抖動功能
    AVCaptureConnection *connection = [movieOutput connectionWithMediaType:AVMediaTypeVideo];
    if ([connection isVideoStabilizationSupported]) {
        connection.preferredVideoStabilizationMode = AVCaptureVideoStabilizationModeAuto;
    }
}
if ([session canAddOutput:movieOutput]) {
    [session addOutput:movieOutput];
}
3. 點擊錄像按鈕
if (!self.movieOutput.isRecording) {
    NSString *outputPath = [NSTemporaryDirectory() stringByAppendingString:@"myMovie.mov"];
    NSURL *url = [NSURL fileURLWithPath:outputPath];//記住是文件URL,不是普通URL
    //開始錄制並設置代理監控錄制過程,錄制文件會存放到指定URL路徑下
    [self.movieOutput startRecordingToOutputFileURL:url recordingDelegate:self];
} else {
    [self.movieOutput stopRecording];//結束錄制
}
4. 處理錄制代理AVCaptureFileOutputRecordingDelegate
/* 開始錄制會調用 */
- (void)captureOutput:(AVCaptureFileOutput *)captureOutput
        didStartRecordingToOutputFileAtURL:(NSURL *)fileURL
                           fromConnections:(NSArray *)connections
{
    NSLog(@"開始錄制");
}
/* 錄制完成會調用 */
- (void)captureOutput:(AVCaptureFileOutput *)captureOutput
        didFinishRecordingToOutputFileAtURL:(NSURL *)outputFileURL
                            fromConnections:(NSArray *)connections
                                      error:(NSError *)error
{
    NSLog(@"完成錄制");
    NSString *path = outputFileURL.path;
    //保存錄制視頻到相簿
    if (UIVideoAtPathIsCompatibleWithSavedPhotosAlbum(path)) {
        UISaveVideoAtPathToSavedPhotosAlbum(path, nil, nil, nil);
    }
}

四、iOS音頻視頻使用總結


以上的表格中,我沒有全部都講,我主要講AVFoundation框架,裡面還有非常多的內容可以學習,這個框架是非常強大,有時間也可以再深入去學習。
iOS對於多媒體支持相當靈活和完善,具體開發過程到底如何選擇,以上的表格僅供參考。

代碼Demo點這裡:LearnDemo裡面的CaptureDemo

發佈留言