一、摄像头

在iOS中,手机摄像头的使用有以下两种方法:

  1. UIImagePickerController拍照和视频录制
  • 优点:使用方便,功能强大
  • 缺点:高度封装性,无法实现一些自定义工作
  1. 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<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. 代理方法:
/* 媒体获取完成会调用 */
- (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对象
  2. 指定拾取源,拍照和录像都需要使用摄像头
  3. 指定摄像头设备,是前置的还是后置的
  4. 设置媒体类型,媒体类型定义在MobileCoreServices.framework
  5. 指定摄像头捕捉模式,录像必须先设置媒体类型再设置捕捉模式。
  6. 展示UIImagePickerController,通常以模态弹出形式打开
  7. 拍照或录像结束后,在代理方法中展示或者保存照片或视频
6. 下面是具体实例代码:
#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

功能十分强大,基本满足一般的需求,使用起来也很简单。

三、AVFoundation的拍照录像

首先了解下AVFoundation做拍照和录像的相关类:
  1. AVCaptureSession
    媒体捕捉会话,负责把捕获到的音视频数据输出到输出设备上,一个会话可以有多个输入输出。
  2. AVCaptureVideoPervieewLayer
    相机拍摄预览图层,是CALayer的子类,实时查看拍照或录像效果。
  3. AVCaptureDevice
    输入设备,包括麦克风、摄像头等,可以设置一些物理设备的属性
  4. AVCaptureDeviceInput
    设备输入数据管理对象,管理输入数据
  5. AVCaptureOutput
    设备输出数据管理对象,管理输出数据,通常使用它的子类:

    AVCaptureAudioDataOutput//输出音频管理对象,输出数据为NSData
    AVCaptureStillImageDataOutput//输出图片管理对象,输出数据为NSData
    AVCaptureVideoDataOutput//输出视频管理对象,输出数据为NSData
    /* 输出文件管理对象,输出数据以文件形式输出 */
    AVCaptureFileOutput
    {//子类
    AVCaptureAudioFileOutput //输出是音频文件
    AVCaptureMovieFileOutput //输出是视频文件
    }

拍照或录像的一般步骤为:
  1. 创建AVCaptureSession对象
  2. 使用AVCaptureDevice的类方法获得要使用的设备
  3. 利用输入设备AVCaptureDevice创建并初始化AVCaptureDeviceInput对象
  4. 初始化输出数据管理对象,看具体输出什么数据决定使用哪个AVCaptureOutput子类
  5. AVCaptureDeviceInputAVCaptureOutput添加到媒体会话管理对象AVCaptureSession
  6. 创建视频预览图层AVCaptureVideoPreviewLayer并指定媒体会话,添加图层到显示容器中
  7. 调用媒体会话AVCaptureSessionstartRunning方法开始捕获,stopRunning方法停止捕捉
  8. 将 捕获的音频或视频数据输出到指定文件

拍照使用实例如下:

#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. 比拍照多了一个音频输入,改变输出数据对象的类
  2. 需要处理视频输出代理方法
  3. 录制的方法是在输出数据对象上
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对于多媒体支持相当灵活和完善,具体开发过程到底如何选择,以上的表格仅供参考。

iOS学习笔记27-摄像头的更多相关文章

  1. IOS学习笔记27—使用GDataXML解析XML文档

    http://blog.csdn.net/ryantang03/article/details/7868246

  2. IOS学习笔记07---C语言函数-printf函数

    IOS学习笔记07---C语言函数-printf函数 0 7.C语言5-printf函数 ------------------------- ----------------------------- ...

  3. IOS学习笔记48--一些常见的IOS知识点+面试题

      IOS学习笔记48--一些常见的IOS知识点+面试题   1.堆和栈什么区别? 答:管理方式:对于栈来讲,是由编译器自动管理,无需我们手工控制:对于堆来说,释放工作由程序员控制,容易产生memor ...

  4. iOS学习笔记31-从图册获取图片和视频

    一.从图册中获取本地图片和视频 从图册中获取文件,我们使用的是UIImagePickerController,这个类我们在之前的摄像头中使用过,这里是链接:iOS学习笔记27-摄像头,这里我们使用的是 ...

  5. iOS学习笔记——AutoLayout的约束

    iOS学习笔记——AutoLayout约束 之前在开发iOS app时一直以为苹果的布局是绝对布局,在IB中拖拉控件运行或者直接使用代码去调整控件都会发上一些不尽人意的结果,后来发现iOS在引入了Au ...

  6. IOS学习笔记25—HTTP操作之ASIHTTPRequest

    IOS学习笔记25—HTTP操作之ASIHTTPRequest 分类: iOS2012-08-12 10:04 7734人阅读 评论(3) 收藏 举报 iosios5网络wrapper框架新浪微博 A ...

  7. IOS学习笔记之关键词@dynamic

    IOS学习笔记之关键词@dynamic @dynamic这个关键词,通常是用不到的. 它与@synthesize的区别在于: 使用@synthesize编译器会确实的产生getter和setter方法 ...

  8. iOS学习笔记-精华整理

    iOS学习笔记总结整理 一.内存管理情况 1- autorelease,当用户的代码在持续运行时,自动释放池是不会被销毁的,这段时间内用户可以安全地使用自动释放的对象.当用户的代码运行告一段 落,开始 ...

  9. iOS学习笔记10-UIView动画

    上次学习了iOS学习笔记09-核心动画CoreAnimation,这次继续学习动画,上次使用的CoreAnimation很多人感觉使用起来很繁琐,有没有更加方便的动画效果实现呢?答案是有的,那就是UI ...

  10. iOS学习笔记总结整理

    来源:http://mobile.51cto.com/iphone-386851_all.htm 学习IOS开发这对于一个初学者来说,是一件非常挠头的事情.其实学习IOS开发无外乎平时的积累与总结.下 ...

随机推荐

  1. LibreOJ #2037. 「SHOI2015」脑洞治疗仪

    线段树区间合并问题 恶心... 屠龙宝刀点击就送 #include <cstdio> #define N 200005 struct Segment { int l,r,mid,sum,l ...

  2. 香港城大:首创3D打印磁控微型机器人技术推动人体送药研究发展

    香港城市大学研究团队全球首创由磁力推动.3D打印的微型机器人技术,能于生物体内精确地运载细胞到指定位置,预料可用作人体送药,为癌症治疗.细胞层面的治疗.再生医学等方面的应用,带来革命性改变. 近年,再 ...

  3. Connectivity

    6492: Connectivity 时间限制: 1 Sec  内存限制: 128 MB提交: 118  解决: 28[提交][状态][讨论版][命题人:admin] 题目描述 There are N ...

  4. Python01 VSCode开发环境和入门程序

    1.Python的下载和安装 最新版本python3.7.3 https://www.python.org/downloads/release/python-373/ web-based: 在线安装包 ...

  5. Java基础面试操作题: File IO 文件过滤器FileFilter 练习 把一个文件夹下的.java文件复制到另一个文件夹下的.txt文件

    package com.swift; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File ...

  6. iOS 资源大全整理

    这是个精心编排的列表,它包含了优秀的 iOS 框架.库.教程.XCode 插件.组件等等. 这个列表分为以下几个部分:框架( Frameworks ).组件( Components ).测试( Tes ...

  7. NOIP模拟赛 czy的后宫

    [题目描述] czy要妥善安排他的后宫,他想在机房摆一群妹子,一共有n个位置排成一排,每个位置可以摆妹子也可以不摆妹子.有些类型妹子如果摆在相邻的位置(隔着一个空的位置不算相邻),就不好看了.假定每种 ...

  8. pandas中数据聚合【重点】

    数据聚合 数据聚合是数据处理的最后一步,通常是要使每一个数组生成一个单一的数值. 数据分类处理: 分组:先把数据分为几组 用函数处理:为不同组的数据应用不同的函数以转换数据 合并:把不同组得到的结果合 ...

  9. k8s调度的预选策略及优选函数

    scheduler调度过程:    Predicate(预选)-->Priority(优选)-->Select(选定)调度方式:    1.节点亲和性调度(NodeAffinity)使用n ...

  10. 在物理机上,用U盘安装esxi虚拟化环境

    一般使用U盘安装centos镜像,可使用镜像刻录工具UltraISO,详细方法参照如下链接: https://jingyan.baidu.com/article/647f0115ee55ba7f214 ...