在iOS中,手機攝像頭的使用有以下兩種方法:
1. UIImagePickerController
拍照和視頻錄制
* 優點:使用方便,功能強大
* 缺點:高度封裝性,無法實現一些自定義工作
2. AVFoundation
框架實現
* 優點:靈活性強,提供了很多現成的輸入設備和輸出設備,還有很多底層的內容可以供開發者使用
* 缺點:需要和底層打交道,學習難度大,使用復雜
我們平常使用UIImagePickerController
就基本可以滿足了,功能確實強大,但它也有不好的一點,那就是由於它的高度封裝性,如果要進行某些自定義工作就比較復雜,例如如果要做出一款類似於美顏相機的拍照界面就比較難以實現,這個時候就要考慮AVFoundation
框架實現。
UIImagePickerController
繼承於UINavigationController
,屬於UIKit
框架,可以實現圖片選取、拍照、錄制視頻等功能,使用起來十分方便。
@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//打開閃光燈
};
- (void)takePicture; //拍照
- (BOOL)startVideoCapture;//開始錄制視頻
- (void)stopVideoCapture;//停止錄制視頻
/* 媒體獲取完成會調用 */
- (void)imagePickerController:(UIImagePickerController *)picker
didFinishPickingMediaWithInfo:(NSDictionary *)info;
/* 取消獲取會調用 */
- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker;
/* 保存圖片到相簿 */
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;
UIImagePickerController
對象 指定拾取源,拍照和錄像都需要使用攝像頭 指定攝像頭設備,是前置的還是後置的 設置媒體類型,媒體類型定義在MobileCoreServices.framework
中 指定攝像頭捕捉模式,錄像必須先設置媒體類型再設置捕捉模式。 展示UIImagePickerController
,通常以模態彈出形式打開 拍照或錄像結束後,在代理方法中展示或者保存照片或視頻
#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
功能十分強大,基本滿足一般的需求,使用起來也很簡單。
AVCaptureSession
:AVCaptureVideoPervieewLayer
:CALayer
的子類,實時查看拍照或錄像效果。AVCaptureDevice
:AVCaptureDeviceInput
:AVCaptureOutput
:
AVCaptureAudioDataOutput//輸出音頻管理對象,輸出數據為NSData
AVCaptureStillImageDataOutput//輸出圖片管理對象,輸出數據為NSData
AVCaptureVideoDataOutput//輸出視頻管理對象,輸出數據為NSData
/* 輸出文件管理對象,輸出數據以文件形式輸出 */
AVCaptureFileOutput
{//子類
AVCaptureAudioFileOutput //輸出是音頻文件
AVCaptureMovieFileOutput //輸出是視頻文件
}
AVCaptureSession
對象 使用AVCaptureDevice
的類方法獲得要使用的設備 利用輸入設備AVCaptureDevice
創建並初始化AVCaptureDeviceInput
對象 初始化輸出數據管理對象,看具體輸出什麼數據決定使用哪個AVCaptureOutput
子類 將AVCaptureDeviceInput
、AVCaptureOutput
添加到媒體會話管理對象AVCaptureSession
中 創建視頻預覽圖層AVCaptureVideoPreviewLayer
並指定媒體會話,添加圖層到顯示容器中 調用媒體會話AVCaptureSession
的startRunning
方法開始捕獲,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
//獲取麥克風設備對象
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;
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];
}
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];//結束錄制
}
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);
}
}
以上的表格中,我沒有全部都講,我主要講AVFoundation
框架,裡面還有非常多的內容可以學習,這個框架是非常強大,有時間也可以再深入去學習。
iOS對於多媒體支持相當靈活和完善,具體開發過程到底如何選擇,以上的表格僅供參考。
代碼Demo點這裡:LearnDemo裡面的CaptureDemo