iOS-攝像頭

1、攝像頭

在iOS中,手機攝像頭的使用有如下兩種方法:git

  1. UIImagePickerController拍照和視頻錄製
  • 優勢:使用方便,功能強大
  • 缺點:高度封裝性,沒法實現一些自定義工做
  1. AVFoundation框架實現
  • 優勢:靈活性強,提供了不少現成的輸入設備和輸出設備,還有不少底層的內容能夠供開發者使用
  • 缺點:須要和底層打交道,學習難度大,使用複雜

咱們日常使用UIImagePickerController就基本能夠知足了,功能確實強大,但它也有很差的一點,那就是因爲它的高度封裝性,若是要進行某些自定義工做就比較複雜,例如若是要作出一款相似於美顏相機的拍照界面就比較難以實現,這個時候就要考慮AVFoundation框架實現。github

2、UIImagePickerController

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

1. 經常使用屬性:session

@property (nonatomic) UIImagePickerControllerSourceType sourceType;/* 拾取源類型枚舉 */
typedef NS_ENUM(NSInteger, UIImagePickerControllerSourceType) {
    UIImagePickerControllerSourceTypePhotoLibrary,//照片庫
    UIImagePickerControllerSourceTypeCamera,//攝像頭
    UIImagePickerControllerSourceTypeSavedPhotosAlbum//相簿
};
/* 
  媒體類型,默認狀況下此數組包含kUTTypeImage,表示拍照
  若是要錄像,必須設置爲kUTTypeVideo(視頻不帶聲音)或kUTTypeMovie(視頻帶聲音)
*/
@property (nonatomic,copy) NSArray<NSString *> *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. 代理方法:ide

/* 媒體獲取完成會調用 */
- (void)imagePickerController:(UIImagePickerController *)picker 
didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)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. 使用攝像頭的步驟:學習

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

6. 下面是具體實例代碼:atom

#import "ViewController.h"
#import <MobileCoreServices/MobileCoreServices.h>

@interface ViewController () <UIImagePickerControllerDelegate,UINavigationControllerDelegate>
@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<NSString *,id> *)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

 

 

功能十分強大,基本知足通常的需求,使用起來也很簡單。url

3、AVFoundation的拍照錄像

首先了解下AVFoundation作拍照和錄像的相關類:

  1. AVCaptureSession
    媒體捕捉會話,負責把捕獲到的音視頻數據輸出到輸出設備上,一個會話能夠有多個輸入輸出。
  1. AVCaptureVideoPervieewLayer
    相機拍攝預覽圖層,是CALayer的子類,實時查看拍照或錄像效果。

  2. AVCaptureDevice
    輸入設備,包括麥克風、攝像頭等,能夠設置一些物理設備的屬性
  3. AVCaptureDeviceInput
    設備輸入數據管理對象,管理輸入數據
  4. AVCaptureOutput
    設備輸出數據管理對象,管理輸出數據,一般使用它的子類:
AVCaptureAudioDataOutput//輸出音頻管理對象,輸出數據爲NSData
AVCaptureStillImageDataOutput//輸出圖片管理對象,輸出數據爲NSData
AVCaptureVideoDataOutput//輸出視頻管理對象,輸出數據爲NSData
/* 輸出文件管理對象,輸出數據以文件形式輸出 */
AVCaptureFileOutput  
{//子類    
       AVCaptureAudioFileOutput   //輸出是音頻文件
       AVCaptureMovieFileOutput   //輸出是視頻文件
}

拍照或錄像的通常步驟爲:

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

拍照使用實例以下:

#import "ViewController.h"
#import <AVFoundation/AVFoundation.h>

@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. 比拍照多了一個音頻輸入,改變輸出數據對象的類
  1. 須要處理視頻輸出代理方法
  2. 錄製的方法是在輸出數據對象上

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);
    }
}

4、iOS音頻視頻使用總結

 


以上的表格中,我沒有所有都講,我主要講AVFoundation框架,裏面還有很是多的內容能夠學習,這個框架是很是強大,有時間也能夠再深刻去學習。
iOS對於多媒體支持至關靈活和完善,具體開發過程到底如何選擇,以上的表格僅供參考。

 

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

做者:執着丶執念 連接:https://www.jianshu.com/p/f00c5031dccd 來源:簡書 簡書著做權歸做者全部,任何形式的轉載都請聯繫做者得到受權並註明出處。