iOS學習筆記27-網路攝影機
一、網路攝影機
在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 *)pickerdidFinishPickingMediaWithInfo:(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//輸出音頻管理對象,輸出資料為NSDataAVCaptureStillImageDataOutput//輸出圖片管理對象,輸出資料為NSDataAVCaptureVideoDataOutput//輸出視頻管理對象,輸出資料為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
錄影的操作差不多,下面代碼是以上面代碼為基礎進行修改:比拍照多了一個音頻輸入,改變輸出資料對象的類 需要處理視頻輸出代理方法 錄製的方法是在輸出資料對象上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