ios 手机相机调用使用(媒体捕捉)
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 手机相机调用使用(媒体捕捉)相关推荐
- IOS开发调用系统相机和打开闪光灯
IOS开发调用系统相机和打开闪光灯 今天给大家分享一下如何调用iphone的拍照功能和打开闪光灯,有些代码我也不太理解,很多是在网上借鉴其他人的.IOS有两种的拍照和视频的方式:1.直接使用 ...
- web dialog 内嵌 图片_Unity游戏如何在iOS上调用Facebook原生对话框分享图片
原文发表于Unity中国论坛 ,如果对你有帮助请关注我! Unity游戏如何在iOS上调用Facebook原生对话框分享图片 - Unityunity.cn 手头上的一个游戏项目需要实现截屏并分享到 ...
- 在iOS中调用C语言的国密算法SM2以替换RSA
相对于另一种更好的加密实现,本文方法容易受干扰 尽量使用我另一篇博客介绍的https://blog.csdn.net/qq_15509071/article/details/107832587 这个开 ...
- 苹果手机 微信PHP问题,安卓、ios APP调用微信支付时,页面出现空白 。(php微信支付,app发起微信支付白屏)?...
安卓.ios APP调用微信支付时,页面出现空白 我的后端是用php开发的.我是先调用微信的统一下单生成,生成prepay_id预支付订单号,代码如下: public function send_pa ...
- 【iOS】—— 调用手机相机和相册
在app开发过程中,会有很多需要调取摄像头的地方,例如上传头像,上传图片等,并且又的是会包含直接拍照或者是从相册取图片来完成上传的,这里我就大概给大家说说如何调用手机的相机和相册. 需要用到的库: # ...
- html 调用ios手机相机 demo,H5网页调用Android和iOS手机相机功能
下面是完整的一个HTML页面内容,放在服务器上然后浏览就可以了,只支持Chrome和Safari核的浏览器,QQ浏览器,Chrome,Safari浏览器都可以.在不同的手机和浏览器上面展现的方式不一样 ...
- iOS -OC调用js页面
我们这边和h5商量好传递一个结构体参数过去,然后由于解析的问题,导致我这里传递的参数,到h5那边不执行那边的方法 -(void)loginCallBack { NSDictionary *dic; u ...
- ios UIWebView调用本地html和javascript,并且和ios通讯
ios和android都提供了有关webview和javascript通讯的功能,这就使开发者根据手机的系统展示适合手机的界面,是界面开发更加简单. 我的原型主要实现通过UIWebView展示本地的h ...
- 谈谈iOS获取调用链
本文由云+社区发表 iOS开发过程中难免会遇到卡顿等性能问题或者死锁之类的问题,此时如果有调用堆栈将对解决问题很有帮助.那么在应用中如何来实时获取函数的调用堆栈呢?本文参考了网上的一些博文,讲述了使用 ...
最新文章
- javascript常用工具类封装
- 阿里一道Java并发面试题 (详细分析篇)
- Java面试题15牛客 以下关于Integer与int的区别错误的是
- 一次难忘的 MTS 故障的排除过程
- mysql循环遍历获取_MySQL 全表遍历
- 源码编译安装keepalived
- matlab 集合操作
- MongoDB聚合运算之mapReduce函数的使用(11)
- [推荐算法]基于用户的协同过滤算法
- 仿陶小铺S2B2C商城源码产品分析
- 算法设计与分析(第二版)上机实验题——Java实现
- python制作表情,使用Python制作滑稽表情
- python期货程序化交易高手心得_10分钟打造WonderTrader上的期货日内交易策略
- java指定小数点位数_Java指定保留小数位数的方法
- html5移动开发是什么意思,移动端什么意思?
- python求平均值_如何用python求平均值
- 程序员武器之键盘,适用于其他文字录入者
- 将N阶矩阵M置成单位阵
- 更改iTunes(macOS下)备份路径至移动硬盘
- PWM波控制舵机总结