AVFoundation的适用范围

AVFoundation是苹果在iOS和OS X系统中用于处理基于时间的媒体数据的Objective-C框架. 供使用者来开发媒体类型的应用程序.

虽然现在你可以在iOS和 OS X系统中通过WebView或HTML标签直接来播放音视频内容,但是如果想要加入额外媒体相关功能如:录制,音视频流控制,添加额外动画和图片效果等.则需要专门的音视频框架进行控制.

  • AVCaptureSession 捕捉会话

AVCaptureSession相当于一个虚拟的插座,连接了输入和输出资源.它管理着从物理设备(摄像头和麦克风)得到的数据流,然后输出到其他地方.
可以额外配置一个会话预设值,用来控制捕捉数据的格式和质量,默认是AVCaptureSessionPresetHigh

  • AVCaptureDevice 捕捉设备

AVCaptureDevice为物理设备定义一个接口和大量控制方法,例如对焦、曝光、白平衡和闪光等

  • 捕捉设备输入 AVCaptureDeviceInput

在使用AVCaptureDevice进行处理前,需要将它封装到AVCaptureDeviceInput实例中.因为一个捕捉设备不能直接添加到AVCaptureSession中

  • 捕捉的输出 AVCaptureOutput

AVCaptureOutput有许多扩展类,它本身只是一个抽象基类,用于为从Session得到的数据寻找输出目的地. 看上面结构图就可以看出各扩展类功能方向, 这里单独说一下,AVCaptureAudioDataOutput和AVCaptureVideoDataOutput可以直接访问硬件扑捉到的数字样本,可以用于音视频流进行实施处理

  • 捕捉连接 AVCaptureConnection

这个类其实就是上图中连接不同组件的连接箭头所表示. 对这些连接的访问可以让开发者对信号流就行底层控制,比如禁用某些特定的连接,或者音频连接中限制单独的音频轨道

  • 捕捉预览 AVCaptureVideoPreviewLayer

这个类不在上图中, 它是对捕捉视频数据进行实时预览.在视频角色中类似于AVPlayerLayer

捕捉会话应用流程

a.用之前先判断相机权限:

    // 检查相机app是否有相机权限AVAuthorizationStatus authStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];if (authStatus == AVAuthorizationStatusDenied){UIAlertController *alertC = [UIAlertController alertControllerWithTitle:@"提示" message:@"请在iPhone的\"设置-隐私-相机\"选项中,允许云眸访问你的相机" preferredStyle:UIAlertControllerStyleAlert];UIAlertAction *alertA = [UIAlertAction actionWithTitle:@"确定" style:(UIAlertActionStyleDefault) handler:^(UIAlertAction * _Nonnull action) {}];[alertC addAction:alertA];[currentController presentViewController:alertC animated:YES completion:nil];return;}

b. 创建会话

@property (nonatomic,strong) AVCaptureDevice *cam_back;

@property (nonatomic, strong) AVCaptureSession *session;

@property (nonatomic, strong) AVCaptureVideoDataOutput *videoDataOutput;

@property (nonatomic, strong) AVCaptureVideoPreviewLayer *videoPreviewLayer;

//获取设备摄像头列表NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];AVCaptureDevice *deviceF;for (AVCaptureDevice *device in devices ){//后置摄像头if (self.isPositionBack) {if (device.position == AVCaptureDevicePositionBack) {deviceF = device;break;}}//后置摄像头else{if (device.position == AVCaptureDevicePositionFront) {deviceF = device;break;}}}self.cam_back = deviceF;//摄像头的拍摄数据对象AVCaptureDeviceInput *input = [[AVCaptureDeviceInput alloc] initWithDevice:deviceF error:nil];//实例生成适用于使用其他媒体API进行处理的视频帧,可以使用captureOutput:didOutputSampleBuffer:FromConnection:Delegate方法访问帧// 创建设备输出流AVCaptureVideoDataOutput *output = [AVCaptureVideoDataOutput new];//设置接收程序的委托,该委托将接受捕获的缓冲区和将调用委托的调度队列[output setSampleBufferDelegate:self queue:dispatch_get_main_queue()];// 创建数据输出流AVCaptureMetadataOutput *metaout = [[AVCaptureMetadataOutput alloc] init];[metaout setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];//创建捕捉会话self.session = [[AVCaptureSession alloc] init];// beginConfiguration 和 commitConfiguration 包住设置过程[self.session beginConfiguration];if ([self.session canAddInput:input]) {[self.session addInput:input];}if (self.isPositionBack) {//设置捕捉像素 AVCaptureSessionPresetHigh AVCaptureSessionPresetMedium AVCaptureSessionPresetLowif ([self.session canSetSessionPreset:AVCaptureSessionPresetHigh]) {[self.session setSessionPreset:AVCaptureSessionPresetHigh];}else{[self.session setSessionPreset:AVCaptureSessionPreset640x480];}}else{if ([self.session canSetSessionPreset:AVCaptureSessionPreset640x480]) {[self.session setSessionPreset:AVCaptureSessionPreset640x480];}}//添加点击时获取CGpoint的事件UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(focusGesture:)];[currentController.view addGestureRecognizer:tapGesture];//注意改变设备属性前一定要首先调用lockForConfiguration:调用完之后使用unlockForConfiguration方法解锁,不然app崩溃NSError *error;if ([self.cam_back lockForConfiguration:&error]) {self.cam_back.subjectAreaChangeMonitoringEnabled = YES;//闪光灯自动调整if ([self.cam_back isFlashModeSupported:AVCaptureFlashModeAuto]) {[self.cam_back setFlashMode:AVCaptureFlashModeAuto];}//自动白平衡自动调整if ([self.cam_back isWhiteBalanceModeSupported:AVCaptureWhiteBalanceModeAutoWhiteBalance]) {[self.cam_back setWhiteBalanceMode:AVCaptureWhiteBalanceModeAutoWhiteBalance];}[self.cam_back unlockForConfiguration];}else{NSLog(@"设置设备属性过程发生错误,错误信息:%@",error.localizedDescription);}// 设置捕捉会话的输出部分// image输出if ([self.session canAddOutput:output]) {[self.session addOutput:output];}if ([self.session canAddOutput:metaout]) {[self.session addOutput:metaout];}[self.session commitConfiguration];// 设置视频输出编码格式NSString     *key           = (NSString *)kCVPixelBufferPixelFormatTypeKey;NSNumber     *value         = [NSNumber numberWithUnsignedInt:kCVPixelFormatType_32BGRA];NSDictionary *videoSettings = [NSDictionary dictionaryWithObject:value forKey:key];[output setVideoSettings:videoSettings];self.videoDataOutput = output;if (!self.isPositionBack) {//这里 我们告诉要检测到人脸 就给我一些反应,里面还有QRCode 等 都可以放进去,就是 如果视频流检测到了你要的 就会出发didOutputSampleBuffer代理方法[metaout setMetadataObjectTypes:@[AVMetadataObjectTypeFace]];AVCaptureSession* session = (AVCaptureSession *)self.session;//前置摄像头一定要设置一下 要不然画面是镜像for (AVCaptureVideoDataOutput* output in session.outputs) {for (AVCaptureConnection * av in output.connections) {//判断是否是前置摄像头状态if (av.supportsVideoMirroring) {//镜像设置av.videoOrientation = AVCaptureVideoOrientationPortrait;av.videoMirrored = YES;}}}}else{AVCaptureSession* session = (AVCaptureSession *)self.session;//前置摄像头一定要设置一下 要不然画面是镜像for (AVCaptureVideoDataOutput* output in session.outputs) {for (AVCaptureConnection * av in output.connections) {//判断是否是前置摄像头状态if (av.supportsVideoMirroring) {//镜像设置av.videoOrientation = AVCaptureVideoOrientationPortrait;}}}}// 实例化预览图层, 传递_session是为了告诉图层将来显示什么内容_videoPreviewLayer = [AVCaptureVideoPreviewLayer layerWithSession:_session];// 保持纵横比;填充层边界_videoPreviewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;CGFloat x = 0;CGFloat y = 0;CGFloat w = [UIScreen mainScreen].bounds.size.width;CGFloat h = [UIScreen mainScreen].bounds.size.height;_videoPreviewLayer.frame = CGRectMake(x, y, w, h);[currentController.view.layer insertSublayer:_videoPreviewLayer atIndex:0];// 9、启动会话[_session startRunning];

c . 停止,启动,检查会话

    - (void)startRunning {[_session startRunning];}- (void)stopRunning {[_session stopRunning];}- (BOOL)isScanRunning {return [_session isRunning];}

其他细节(切换摄像头,聚焦)

a.切换摄像头

// 当前捕捉会话对应的摄像头.
- (AVCaptureDevice *)activeCamera {                                         return self.activeVideoInput.device;
}
// 当前未激活的摄像头
- (AVCaptureDevice *)inactiveCamera {                                      AVCaptureDevice *device = nil;if (self.cameraCount > 1) {if ([self activeCamera].position == AVCaptureDevicePositionBack) {  device = [self cameraWithPosition:AVCaptureDevicePositionFront];} else {device = [self cameraWithPosition:AVCaptureDevicePositionBack];}}return device;
}
// 切换摄像头
- (BOOL)switchCameras {if (![self canSwitchCameras]) {                                         return NO;}// 1. 获取当前未使用的摄像头,并为他创建一个新的Input.NSError *error;AVCaptureDevice *videoDevice = [self inactiveCamera];                   AVCaptureDeviceInput *videoInput =[AVCaptureDeviceInput deviceInputWithDevice:videoDevice error:&error];// 2. 用新的Input替换掉正在激活的Inputif (videoInput) {[self.captureSession beginConfiguration];    // 原子性配置开始.保证线程安全[self.captureSession removeInput:self.activeVideoInput];            if ([self.captureSession canAddInput:videoInput]) {                 [self.captureSession addInput:videoInput];self.activeVideoInput = videoInput;} else { // 确保安全,如果新的Input不能添加,继续使用旧的[self.captureSession addInput:self.activeVideoInput];}[self.captureSession commitConfiguration];    // 原子性配置完成} else {    // 错误处理[self.delegate deviceConfigurationFailedWithError:error];           return NO;}return YES;
}- (BOOL)canSwitchCameras {                                                  return self.cameraCount > 1;
}- (NSUInteger)cameraCount {                                                 return [[AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo] count];
}// 获取指定位置的AVCaptureDevice
- (AVCaptureDevice *)cameraWithPosition:(AVCaptureDevicePosition)position { NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];for (AVCaptureDevice *device in devices) {  if (device.position == position) {return device;}}return nil;
}

b.代理处理数据(拍照)

@property (nonatomic,copy) NSArray *currentMetadata;

typedef void (^ShutterImageBlock)(UIImage *image);

@property (nonatomic,copy) ShutterImageBlock callBack;

@interface VideoScanManager()
<
AVCaptureMetadataOutputObjectsDelegate,
AVCaptureVideoDataOutputSampleBufferDelegate
>
#pragma mark - - - AVCaptureMetadataOutputObjectsDelegate
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection {_currentMetadata = metadataObjects;
}#pragma mark - - - AVCaptureVideoDataOutputSampleBufferDelegate的方法
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection {if (self.callBack) {self.callBack([self imageFromSampleBuffer:sampleBuffer]);self.callBack = nil;}if (self.currentMetadata.count > 0) {// face表示当前获取到的 人脸对象AVMetadataObject<AVMetadataFaceObject>AVMetadataFaceObject *face = (AVMetadataFaceObject *)[_videoPreviewLayer transformedMetadataObjectForMetadataObject:self.currentMetadata.lastObject];// 此处对进行处理// 。。。。。。。}
}

从获取的sampleBuffer中获取图片数据

// 通过抽样缓存数据创建一个UIImage对象
- (UIImage *) imageFromSampleBuffer:(CMSampleBufferRef) sampleBuffer
{// 为媒体数据设置一个CMSampleBuffer的Core Video图像缓存对象CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);// 锁定pixel buffer的基地址CVPixelBufferLockBaseAddress(imageBuffer, 0);// 得到pixel buffer的基地址void *baseAddress = CVPixelBufferGetBaseAddress(imageBuffer);// 得到pixel buffer的行字节数size_t bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer);// 得到pixel buffer的宽和高size_t width = CVPixelBufferGetWidth(imageBuffer);size_t height = CVPixelBufferGetHeight(imageBuffer);// 创建一个依赖于设备的RGB颜色空间CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();// 用抽样缓存的数据创建一个位图格式的图形上下文(graphics context)对象CGContextRef context = CGBitmapContextCreate(baseAddress, width, height, 8,bytesPerRow, colorSpace, kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst);// 根据这个位图context中的像素数据创建一个Quartz image对象CGImageRef quartzImage = CGBitmapContextCreateImage(context);// 解锁pixel bufferCVPixelBufferUnlockBaseAddress(imageBuffer,0);// 释放context和颜色空间CGContextRelease(context);CGColorSpaceRelease(colorSpace);// 用Quartz image创建一个UIImage对象imageUIImage *image = [UIImage imageWithCGImage:quartzImage];// 释放Quartz image对象CGImageRelease(quartzImage);return (image);
}

c.聚焦

手动聚焦方式:点击屏幕,获取point数据,调用捕捉的设备 setFocusPointOfInterest和setFocusMode , 两者调用顺序不能变。

_focusView是UIView类型,显示点击的区域。

注意⚠️:给设备配置属性时需要用lockForConfiguration ,unlockForConfiguration 包住,不然会使APP崩溃。

在初始化session时添加点击事件,用来获取点击的CGPoint

//添加点击时获取CGpoint的事件UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(focusGesture:)];[currentController.view addGestureRecognizer:tapGesture];
- (void)focusGesture:(UITapGestureRecognizer*)gesture{CGPoint point = [gesture locationInView:gesture.view];[self focusAtPoint:point];
}- (void)focusAtPoint:(CGPoint)point{CGSize size = _videoPreviewLayer.bounds.size;// focusPoint 函数后面Point取值范围是取景框左上角(0,0)到取景框右下角(1,1)之间,按这个来但位置就是不对,只能按上面的写法才可以。前面是点击位置的y/PreviewLayer的高度,后面是1-点击位置的x/PreviewLayer的宽度CGPoint focusPoint = CGPointMake( point.y /size.height ,1 - point.x/size.width );if ([self.cam_back lockForConfiguration:nil]) {if ([self.cam_back isFocusModeSupported:AVCaptureFocusModeAutoFocus]) {[self.cam_back setFocusPointOfInterest:focusPoint];[self.cam_back setFocusMode:AVCaptureFocusModeAutoFocus];}if ([self.cam_back isExposureModeSupported:AVCaptureExposureModeAutoExpose ]) {[self.cam_back setExposurePointOfInterest:focusPoint];//曝光量调节[self.cam_back setExposureMode:AVCaptureExposureModeAutoExpose];}[self.cam_back unlockForConfiguration];self.focusView.center = point;self.focusView.hidden = NO;[UIView animateWithDuration:0.2 animations:^{self.focusView.transform = CGAffineTransformMakeScale(1.25, 1.25);}completion:^(BOOL finished) {[UIView animateWithDuration:0.3 animations:^{self.focusView.transform = CGAffineTransformIdentity;} completion:^(BOOL finished) {self.focusView.hidden = YES;}];}];}
}- (UIView *)focusView{if (!_focusView) {_focusView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, 60, 60)];_focusView.layer.borderWidth = 1.0;_focusView.layer.borderColor = [UIColor yellowColor].CGColor;_focusView.hidden = YES;}return _focusView;
}

最后不要忘记在Plist里面添加相机权限获取的请求,不然app崩溃

学习记录完毕。

ios 手机相机调用使用(媒体捕捉)相关推荐

  1. IOS开发调用系统相机和打开闪光灯

    IOS开发调用系统相机和打开闪光灯      今天给大家分享一下如何调用iphone的拍照功能和打开闪光灯,有些代码我也不太理解,很多是在网上借鉴其他人的.IOS有两种的拍照和视频的方式:1.直接使用 ...

  2. web dialog 内嵌 图片_Unity游戏如何在iOS上调用Facebook原生对话框分享图片

    原文发表于Unity中国论坛 ,如果对你有帮助请关注我! Unity游戏如何在iOS上调用Facebook原生对话框分享图片 - Unity​unity.cn 手头上的一个游戏项目需要实现截屏并分享到 ...

  3. 在iOS中调用C语言的国密算法SM2以替换RSA

    相对于另一种更好的加密实现,本文方法容易受干扰 尽量使用我另一篇博客介绍的https://blog.csdn.net/qq_15509071/article/details/107832587 这个开 ...

  4. 苹果手机 微信PHP问题,安卓、ios APP调用微信支付时,页面出现空白 。(php微信支付,app发起微信支付白屏)?...

    安卓.ios APP调用微信支付时,页面出现空白 我的后端是用php开发的.我是先调用微信的统一下单生成,生成prepay_id预支付订单号,代码如下: public function send_pa ...

  5. 【iOS】—— 调用手机相机和相册

    在app开发过程中,会有很多需要调取摄像头的地方,例如上传头像,上传图片等,并且又的是会包含直接拍照或者是从相册取图片来完成上传的,这里我就大概给大家说说如何调用手机的相机和相册. 需要用到的库: # ...

  6. html 调用ios手机相机 demo,H5网页调用Android和iOS手机相机功能

    下面是完整的一个HTML页面内容,放在服务器上然后浏览就可以了,只支持Chrome和Safari核的浏览器,QQ浏览器,Chrome,Safari浏览器都可以.在不同的手机和浏览器上面展现的方式不一样 ...

  7. iOS -OC调用js页面

    我们这边和h5商量好传递一个结构体参数过去,然后由于解析的问题,导致我这里传递的参数,到h5那边不执行那边的方法 -(void)loginCallBack { NSDictionary *dic; u ...

  8. ios UIWebView调用本地html和javascript,并且和ios通讯

    ios和android都提供了有关webview和javascript通讯的功能,这就使开发者根据手机的系统展示适合手机的界面,是界面开发更加简单. 我的原型主要实现通过UIWebView展示本地的h ...

  9. 谈谈iOS获取调用链

    本文由云+社区发表 iOS开发过程中难免会遇到卡顿等性能问题或者死锁之类的问题,此时如果有调用堆栈将对解决问题很有帮助.那么在应用中如何来实时获取函数的调用堆栈呢?本文参考了网上的一些博文,讲述了使用 ...

最新文章

  1. javascript常用工具类封装
  2. 阿里一道Java并发面试题 (详细分析篇)
  3. Java面试题15牛客 以下关于Integer与int的区别错误的是
  4. 一次难忘的 MTS 故障的排除过程
  5. mysql循环遍历获取_MySQL 全表遍历
  6. 源码编译安装keepalived
  7. matlab 集合操作
  8. MongoDB聚合运算之mapReduce函数的使用(11)
  9. [推荐算法]基于用户的协同过滤算法
  10. 仿陶小铺S2B2C商城源码产品分析
  11. 算法设计与分析(第二版)上机实验题——Java实现
  12. python制作表情,使用Python制作滑稽表情
  13. python期货程序化交易高手心得_10分钟打造WonderTrader上的期货日内交易策略
  14. java指定小数点位数_Java指定保留小数位数的方法
  15. html5移动开发是什么意思,移动端什么意思?
  16. python求平均值_如何用python求平均值
  17. 程序员武器之键盘,适用于其他文字录入者
  18. 将N阶矩阵M置成单位阵
  19. 更改iTunes(macOS下)备份路径至移动硬盘
  20. PWM波控制舵机总结

热门文章

  1. win10如何安装虚拟机VM10
  2. 业务中台 全渠道一盘货 基于微服务的订单管理系统OMS 开源
  3. 华三交换机ping不通用户但用户_用H3C模拟器做交换机实验,但是终端总是PING不通交换机,求各位高手帮忙!...
  4. 大数据处理 - Bitmap Bloom Filter
  5. 创建型模式-抽象工厂模式
  6. 昨天在poco面试的一些回顾
  7. lightroom 闪退_子意老师Lightroom从入门到精通系统课程
  8. PERT图(工程网络图)
  9. 性能工具之Taurus
  10. MDS3400调度指挥系统