一、攝像頭
在iOS中,手機(jī)攝像頭的使用有以下兩種方法:
UIImagePickerController拍照和視頻錄制
- 優(yōu)點(diǎn):使用方便,功能強(qiáng)大
- 缺點(diǎn):高度封裝性,無(wú)法實(shí)現(xiàn)一些自定義工作
-
AVFoundation框架實(shí)現(xiàn)
- 優(yōu)點(diǎn):靈活性強(qiáng),提供了很多現(xiàn)成的輸入設(shè)備和輸出設(shè)備,還有很多底層的內(nèi)容可以供開(kāi)發(fā)者使用
- 缺點(diǎn):需要和底層打交道,學(xué)習(xí)難度大,使用復(fù)雜
我們平常使用UIImagePickerController就基本可以滿足了,功能確實(shí)強(qiáng)大,但它也有不好的一點(diǎn),那就是由于它的高度封裝性,如果要進(jìn)行某些自定義工作就比較復(fù)雜,例如如果要做出一款類似于美顏相機(jī)的拍照界面就比較難以實(shí)現(xiàn),這個(gè)時(shí)候就要考慮AVFoundation框架實(shí)現(xiàn)。
二、UIImagePickerController
UIImagePickerController繼承于UINavigationController,屬于UIKit框架,可以實(shí)現(xiàn)圖片選取、拍照、錄制視頻等功能,使用起來(lái)十分方便。
1. 常用屬性:
@property (nonatomic) UIImagePickerControllerSourceType sourceType;/* 拾取源類型枚舉 */
typedef NS_ENUM(NSInteger, UIImagePickerControllerSourceType) {
UIImagePickerControllerSourceTypePhotoLibrary,//照片庫(kù)
UIImagePickerControllerSourceTypeCamera,//攝像頭
UIImagePickerControllerSourceTypeSavedPhotosAlbum//相簿
};
/*
媒體類型,默認(rèn)情況下此數(shù)組包含kUTTypeImage,表示拍照
如果要錄像,必須設(shè)置為kUTTypeVideo(視頻不帶聲音)或kUTTypeMovie(視頻帶聲音)
*/
@property (nonatomic,copy) NSArray<NSString *> *mediaTypes;
@property (nonatomic) NSTimeInterval videoMaximumDuration;//視頻最大錄制時(shí)長(zhǎng),默認(rèn)10s
@property (nonatomic) UIImagePickerControllerQualityType videoQuality;//視頻質(zhì)量
typedef NS_ENUM(NSInteger, UIImagePickerControllerQualityType) {
UIImagePickerControllerQualityTypeHigh = 0, //高清
UIImagePickerControllerQualityTypeMedium, //中等,適合WiFi傳輸
UIImagePickerControllerQualityTypeLow, //低質(zhì)量,適合蜂窩網(wǎng)傳輸
UIImagePickerControllerQualityType640x480, //640*480
UIImagePickerControllerQualityTypeIFrame1280x720, //1280*720
UIImagePickerControllerQualityTypeIFrame960x540, //960*540
};
@property (nonatomic) BOOL showsCameraControls;/* 是否顯示攝像頭控制面板,默認(rèn)為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;/* 攝像頭設(shè)備 */
typedef NS_ENUM(NSInteger, UIImagePickerControllerCameraDevice) {
UIImagePickerControllerCameraDeviceRear,//前置攝像頭
UIImagePickerControllerCameraDeviceFront//后置攝像頭
};
@property (nonatomic) UIImagePickerControllerCameraFlashMode cameraFlashMode;/* 閃光燈模式 */
typedef NS_ENUM(NSInteger, UIImagePickerControllerCameraFlashMode) {
UIImagePickerControllerCameraFlashModeOff = -1,//關(guān)閉閃光燈
UIImagePickerControllerCameraFlashModeAuto = 0,//閃光燈自動(dòng),默認(rèn)
UIImagePickerControllerCameraFlashModeOn = 1//打開(kāi)閃光燈
};
2. 常用對(duì)象方法:
- (void)takePicture; //拍照
- (BOOL)startVideoCapture;//開(kāi)始錄制視頻
- (void)stopVideoCapture;//停止錄制視頻
3. 代理方法:
/* 媒體獲取完成會(huì)調(diào)用 */
- (void)imagePickerController:(UIImagePickerController *)picker
didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info;
/* 取消獲取會(huì)調(diào)用 */
- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker;
4. 擴(kuò)展函數(shù),用于保存到相簿:
/* 保存圖片到相簿 */
void UIImageWriteToSavedPhotosAlbum(
UIImage *image,//保存的圖片UIImage
id completionTarget,//回調(diào)的執(zhí)行者
SEL completionSelector, //回調(diào)方法
void *contextInfo//回調(diào)參數(shù)信息
);
//上面一般保存圖片的回調(diào)方法為:
- (void)image:(UIImage *)image
didFinishSavingWithError:(NSError *)error
contextInfo:(void *)contextInfo;
/* 判斷是否能保存視頻到相簿 */
BOOL UIVideoAtPathIsCompatibleWithSavedPhotosAlbum(NSString *videoPath);
/* 保存視頻到相簿 */
void UISaveVideoAtPathToSavedPhotosAlbum(
NSString *videoPath, //保存的視頻文件路徑
id completionTarget, //回調(diào)的執(zhí)行者
SEL completionSelector,//回調(diào)方法
void *contextInfo//回調(diào)參數(shù)信息
);
//上面一般保存視頻的回調(diào)方法為:
- (void)video:(NSString *)videoPath
didFinishSavingWithError:(NSError *)error
contextInfo:(void *)contextInfo;
5. 使用攝像頭的步驟:
- 創(chuàng)建
UIImagePickerController對(duì)象
- 指定拾取源,拍照和錄像都需要使用攝像頭
- 指定攝像頭設(shè)備,是前置的還是后置的
- 設(shè)置媒體類型,媒體類型定義在
MobileCoreServices.framework中 - 指定攝像頭捕捉模式,錄像必須先設(shè)置媒體類型再設(shè)置捕捉模式。
- 展示
UIImagePickerController,通常以模態(tài)彈出形式打開(kāi) - 拍照或錄像結(jié)束后,在代理方法中展示或者保存照片或視頻
6. 下面是具體實(shí)例代碼:
#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{
//創(chuàng)建拾取控制器
UIImagePickerController *pickerController = [[UIImagePickerController alloc] init];
//設(shè)置拾取源為攝像頭
pickerController.sourceType = UIImagePickerControllerSourceTypeCamera;
//設(shè)置攝像頭為后置
pickerController.cameraDevice = UIImagePickerControllerCameraDeviceRear;
pickerController.editing = YES;//設(shè)置運(yùn)行編輯,即可以點(diǎn)擊一些拾取控制器的控件
pickerController.delegate = self;//設(shè)置代理
self.pickerController = pickerController;
}
#pragma mark - UI點(diǎn)擊
/* 點(diǎn)擊拍照 */
- (IBAction)imagePicker:(id)sender {
//設(shè)定拍照的媒體類型
self.pickerController.mediaTypes = @[(NSString *)kUTTypeImage];
//設(shè)置攝像頭捕捉模式為捕捉圖片
self.pickerController.cameraCaptureMode = UIImagePickerControllerCameraCaptureModePhoto;
//模式彈出拾取控制器
[self presentViewController:self.pickerController animated:YES completion:nil];
}
/* 點(diǎn)擊錄像 */
- (IBAction)videoPicker:(id)sender {
//設(shè)定錄像的媒體類型
self.pickerController.mediaTypes = @[(NSString *)kUTTypeMovie];
//設(shè)置攝像頭捕捉模式為捕捉視頻
self.pickerController.cameraCaptureMode = UIImagePickerControllerCameraCaptureModeVideo;
//設(shè)置視頻質(zhì)量為高清
self.pickerController.videoQuality = UIImagePickerControllerQualityTypeHigh;
//模式彈出拾取控制器
[self presentViewController:self.pickerController animated:YES completion:nil];
}
#pragma mark - 代理方法
/* 拍照或錄像成功,都會(huì)調(diào)用 */
- (void)imagePickerController:(UIImagePickerController *)picker
didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info
{
//從info取出此時(shí)攝像頭的媒體類型
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];
}
/* 取消拍照或錄像會(huì)調(diào)用 */
- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker
{
NSLog(@"取消");
//拾取控制器彈回
[self dismissViewControllerAnimated:YES completion:nil];
}
#pragma mark - 保存圖片或視頻完成的回調(diào)
- (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



功能十分強(qiáng)大,基本滿足一般的需求,使用起來(lái)也很簡(jiǎn)單。
三、AVFoundation的拍照錄像
首先了解下AVFoundation做拍照和錄像的相關(guān)類:
AVCaptureSession:
媒體捕捉會(huì)話,負(fù)責(zé)把捕獲到的音視頻數(shù)據(jù)輸出到輸出設(shè)備上,一個(gè)會(huì)話可以有多個(gè)輸入輸出。
-
AVCaptureVideoPervieewLayer:
相機(jī)拍攝預(yù)覽圖層,是CALayer的子類,實(shí)時(shí)查看拍照或錄像效果。
-
AVCaptureDevice:
輸入設(shè)備,包括麥克風(fēng)、攝像頭等,可以設(shè)置一些物理設(shè)備的屬性 -
AVCaptureDeviceInput:
設(shè)備輸入數(shù)據(jù)管理對(duì)象,管理輸入數(shù)據(jù) -
AVCaptureOutput:
設(shè)備輸出數(shù)據(jù)管理對(duì)象,管理輸出數(shù)據(jù),通常使用它的子類:
AVCaptureAudioDataOutput//輸出音頻管理對(duì)象,輸出數(shù)據(jù)為NSData
AVCaptureStillImageDataOutput//輸出圖片管理對(duì)象,輸出數(shù)據(jù)為NSData
AVCaptureVideoDataOutput//輸出視頻管理對(duì)象,輸出數(shù)據(jù)為NSData
/* 輸出文件管理對(duì)象,輸出數(shù)據(jù)以文件形式輸出 */
AVCaptureFileOutput
{//子類
AVCaptureAudioFileOutput //輸出是音頻文件
AVCaptureMovieFileOutput //輸出是視頻文件
}

拍照或錄像的一般步驟為:
- 創(chuàng)建
AVCaptureSession對(duì)象
- 使用
AVCaptureDevice的類方法獲得要使用的設(shè)備 - 利用輸入設(shè)備
AVCaptureDevice創(chuàng)建并初始化AVCaptureDeviceInput對(duì)象 - 初始化輸出數(shù)據(jù)管理對(duì)象,看具體輸出什么數(shù)據(jù)決定使用哪個(gè)
AVCaptureOutput子類 - 將
AVCaptureDeviceInput、AVCaptureOutput添加到媒體會(huì)話管理對(duì)象AVCaptureSession中 - 創(chuàng)建視頻預(yù)覽圖層
AVCaptureVideoPreviewLayer并指定媒體會(huì)話,添加圖層到顯示容器中 - 調(diào)用媒體會(huì)話
AVCaptureSession的startRunning方法開(kāi)始捕獲,stopRunning方法停止捕捉 - 將 捕獲的音頻或視頻數(shù)據(jù)輸出到指定文件
拍照使用實(shí)例如下:
#import "ViewController.h"
#import <AVFoundation/AVFoundation.h>
@interface ViewController ()
@property (strong, nonatomic) AVCaptureSession *session;//媒體管理會(huì)話
@property (strong, nonatomic) AVCaptureDeviceInput *captureInput;//輸入數(shù)據(jù)對(duì)象
@property (strong, nonatomic) AVCaptureStillImageOutput *imageOutput;//輸出數(shù)據(jù)對(duì)象
@property (strong, nonatomic) AVCaptureVideoPreviewLayer *captureLayer;//視頻預(yù)覽圖層
@property (strong, nonatomic) IBOutlet UIButton *captureBtn;//拍照按鈕
@property (strong, nonatomic) IBOutlet UIButton *openCaptureBtn;//打開(kāi)攝像頭按鈕
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self initCapture];
self.openCaptureBtn.hidden = NO;
self.captureBtn.hidden = YES;
}
/* 初始化攝像頭 */
- (void)initCapture{
//1. 創(chuàng)建媒體管理會(huì)話
AVCaptureSession *session = [[AVCaptureSession alloc] init];
self.session = session;
//判斷分辨率是否支持1280*720,支持就設(shè)置為1280*720
if( [session canSetSessionPreset:AVCaptureSessionPreset1280x720] ) {
session.sessionPreset = AVCaptureSessionPreset1280x720;
}
//2. 獲取后置攝像頭設(shè)備對(duì)象
AVCaptureDevice *device = nil;
NSArray *cameras = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
for (AVCaptureDevice *camera in cameras) {
if (camera.position == AVCaptureDevicePositionBack) {//取得后置攝像頭
device = camera;
}
}
if(!device) {
NSLog(@"取得后置攝像頭錯(cuò)誤");
return;
}
//3. 創(chuàng)建輸入數(shù)據(jù)對(duì)象
NSError *error = nil;
AVCaptureDeviceInput *captureInput = [[AVCaptureDeviceInput alloc] initWithDevice:device
error:&error];
if (error) {
NSLog(@"創(chuàng)建輸入數(shù)據(jù)對(duì)象錯(cuò)誤");
return;
}
self.captureInput = captureInput;
//4. 創(chuàng)建輸出數(shù)據(jù)對(duì)象
AVCaptureStillImageOutput *imageOutput = [[AVCaptureStillImageOutput alloc] init];
NSDictionary *setting = @{ AVVideoCodecKey:AVVideoCodecJPEG };
[imageOutput setOutputSettings:setting];
self.imageOutput = imageOutput;
//5. 添加輸入數(shù)據(jù)對(duì)象和輸出對(duì)象到會(huì)話中
if ([session canAddInput:captureInput]) {
[session addInput:captureInput];
}
if ([session canAddOutput:imageOutput]) {
[session addOutput:imageOutput];
}
//6. 創(chuàng)建視頻預(yù)覽圖層
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點(diǎn)擊
/* 點(diǎn)擊拍照按鈕 */
- (IBAction)takeCapture:(id)sender {
//根據(jù)設(shè)備輸出獲得連接
AVCaptureConnection *connection = [self.imageOutput connectionWithMediaType:AVMediaTypeVideo];
//通過(guò)連接獲得設(shè)備輸出的數(shù)據(jù)
[self.imageOutput captureStillImageAsynchronouslyFromConnection:connection
completionHandler:^(CMSampleBufferRef imageDataSampleBuffer, NSError *error)
{
//獲取輸出的JPG圖片數(shù)據(jù)
NSData *imageData =
[AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer];
UIImage *image = [UIImage imageWithData:imageData];
UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil);//保存到相冊(cè)
self.captureLayer.hidden = YES;
self.captureBtn.hidden = YES;
self.openCaptureBtn.hidden = NO;
[self.session stopRunning];//停止捕捉
}];
}
/* 點(diǎn)擊打開(kāi)攝像頭按鈕 */
- (IBAction)openCapture:(id)sender {
self.captureLayer.hidden = NO;
self.captureBtn.hidden = NO;
self.openCaptureBtn.hidden = YES;
[self.session startRunning];//開(kāi)始捕捉
}
@end

錄像的操作差不多,下面代碼是以上面代碼為基礎(chǔ)進(jìn)行修改:
- 比拍照多了一個(gè)音頻輸入,改變輸出數(shù)據(jù)對(duì)象的類
- 需要處理視頻輸出代理方法
- 錄制的方法是在輸出數(shù)據(jù)對(duì)象上
1. 獲取音頻輸入數(shù)據(jù)對(duì)象以及視頻輸出數(shù)據(jù)對(duì)象
//獲取麥克風(fēng)設(shè)備對(duì)象
AVCaptureDevice *device = [AVCaptureDevice devicesWithMediaType:AVMediaTypeAudio].firstObject;
if(!device) {
NSLog(@"取得麥克風(fēng)錯(cuò)誤");
return;
}
//創(chuàng)建輸入數(shù)據(jù)對(duì)象
NSError *error = nil;
AVCaptureDeviceInput *audioInput = [[AVCaptureDeviceInput alloc] initWithDevice:device
error:&error];
if (error) {
NSLog(@"創(chuàng)建輸入數(shù)據(jù)對(duì)象錯(cuò)誤");
return;
}
//創(chuàng)建視頻文件輸出對(duì)象
AVCaptureMovieFileOutput *movieOutput = [[AVCaptureMovieFileOutput alloc] init];
self.movieOutput = movieOutput;
2. 添加進(jìn)媒體管理會(huì)話中
if([session canAddInput:captureInput]) {
[session addInput:captureInput];
[session addInput:audioInput];
//添加防抖動(dòng)功能
AVCaptureConnection *connection = [movieOutput connectionWithMediaType:AVMediaTypeVideo];
if ([connection isVideoStabilizationSupported]) {
connection.preferredVideoStabilizationMode = AVCaptureVideoStabilizationModeAuto;
}
}
if ([session canAddOutput:movieOutput]) {
[session addOutput:movieOutput];
}
3. 點(diǎn)擊錄像按鈕
if (!self.movieOutput.isRecording) {
NSString *outputPath = [NSTemporaryDirectory() stringByAppendingString:@"myMovie.mov"];
NSURL *url = [NSURL fileURLWithPath:outputPath];//記住是文件URL,不是普通URL
//開(kāi)始錄制并設(shè)置代理監(jiān)控錄制過(guò)程,錄制文件會(huì)存放到指定URL路徑下
[self.movieOutput startRecordingToOutputFileURL:url recordingDelegate:self];
} else {
[self.movieOutput stopRecording];//結(jié)束錄制
}
4. 處理錄制代理AVCaptureFileOutputRecordingDelegate
/* 開(kāi)始錄制會(huì)調(diào)用 */
- (void)captureOutput:(AVCaptureFileOutput *)captureOutput
didStartRecordingToOutputFileAtURL:(NSURL *)fileURL
fromConnections:(NSArray *)connections
{
NSLog(@"開(kāi)始錄制");
}
/* 錄制完成會(huì)調(diào)用 */
- (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音頻視頻使用總結(jié)

以上的表格中,我沒(méi)有全部都講,我主要講
AVFoundation框架,里面還有非常多的內(nèi)容可以學(xué)習(xí),這個(gè)框架是非常強(qiáng)大,有時(shí)間也可以再深入去學(xué)習(xí)。iOS對(duì)于多媒體支持相當(dāng)靈活和完善,具體開(kāi)發(fā)過(guò)程到底如何選擇,以上的表格僅供參考。
代碼Demo點(diǎn)這里:LearnDemo里面的CaptureDemo
