iOSswift 自定义相机拍照自动对焦拍照求助

在 SegmentFault,学习技能、解决问题
每个月,我们帮助 1000 万的开发者解决各种各样的技术问题。并助力他们在技术能力、职业生涯、影响力上获得提升。
问题对人有帮助,内容完整,我也想知道答案
问题没有实际价值,缺少关键内容,没有改进余地
自定义相机存入相册旋转了90度 如何加解决 用网上的方法 判断图片是向上的,我改了代码,但是存入图片变成黑色的了,求解
答案对人有帮助,有参考价值
答案没帮助,是错误的答案,答非所问
ios相机默认是向左旋转90度,所以判断照片的imageOriention显示为up 但是就是已经旋转了90度的,所以
** 此方法只在照片存入相册是调用
- (UIImage *)fixOrientation
CGAffineTransform transform = CGAffineTransformI
switch (self.imageOrientation) {
case UIImageOrientationDown:
case UIImageOrientationDownMirrored:
transform = CGAffineTransformTranslate(transform, self.size.width, self.size.height);
transform = CGAffineTransformRotate(transform, M_PI);
case UIImageOrientationLeft:
case UIImageOrientationLeftMirrored:
transform = CGAffineTransformTranslate(transform, self.size.width, 0);
transform = CGAffineTransformRotate(transform, M_PI_2);
case UIImageOrientationRight:
case UIImageOrientationRightMirrored:
transform = CGAffineTransformTranslate(transform, 0, self.size.height);
transform = CGAffineTransformRotate(transform, -M_PI_2);
case UIImageOrientationUp:
case UIImageOrientationUpMirrored:
transform = CGAffineTransformTranslate(transform, 0, self.size.height);
transform = CGAffineTransformRotate(transform, -M_PI_2);
switch (self.imageOrientation) {
case UIImageOrientationUpMirrored:
transform = CGAffineTransformTranslate(transform, self.size.height, 0);
transform = CGAffineTransformScale(transform, -1, 1);
case UIImageOrientationDownMirrored:
transform = CGAffineTransformTranslate(transform, self.size.width, 0);
transform = CGAffineTransformScale(transform, -1, 1);
case UIImageOrientationLeftMirrored:
case UIImageOrientationRightMirrored:
transform = CGAffineTransformTranslate(transform, self.size.height, 0);
transform = CGAffineTransformScale(transform, -1, 1);
CGContextRef ctx = CGBitmapContextCreate(NULL, self.size.width, self.size.height,
CGImageGetBitsPerComponent(self.CGImage), 0,
CGImageGetColorSpace(self.CGImage),
CGImageGetBitmapInfo(self.CGImage));
CGContextConcatCTM(ctx, transform);
switch (self.imageOrientation) {
case UIImageOrientationLeft:
case UIImageOrientationLeftMirrored:
case UIImageOrientationUpMirrored:
case UIImageOrientationUp:
case UIImageOrientationRight:
case UIImageOrientationRightMirrored:
CGContextDrawImage(ctx, CGRectMake(0,0,self.size.height,self.size.width), self.CGImage);
CGContextDrawImage(ctx, CGRectMake(0,0,self.size.width,self.size.height), self.CGImage);
CGImageRef cgimg = CGBitmapContextCreateImage(ctx);
UIImage *img = [UIImage imageWithCGImage:cgimg];
CGContextRelease(ctx);
CGImageRelease(cgimg);
答案对人有帮助,有参考价值
答案没帮助,是错误的答案,答非所问
冲你这个id 我也得给你点个赞!
答案对人有帮助,有参考价值
答案没帮助,是错误的答案,答非所问
不知道你那有没有小demo
这个我写成自定义的,但是一直转不过来。
该答案已被忽略,原因:
同步到新浪微博
分享到微博?
关闭理由:
删除理由:
忽略理由:
推广(招聘、广告、SEO 等)方面的内容
与已有问题重复(请编辑该提问指向已有相同问题)
答非所问,不符合答题要求
宜作评论而非答案
带有人身攻击、辱骂、仇恨等违反条款的内容
无法获得确切结果的问题
非开发直接相关的问题
非技术提问的讨论型问题
其他原因(请补充说明)
我要该,理由是:
在 SegmentFault,学习技能、解决问题
每个月,我们帮助 1000 万的开发者解决各种各样的技术问题。并助力他们在技术能力、职业生涯、影响力上获得提升。ios能实现相机自动对焦,对焦成功后自动拍照吗_百度知道
ios能实现相机自动对焦,对焦成功后自动拍照吗
我有更好的答案
相机自带延迟摄影技术哦~~
为您推荐:
其他类似问题
换一换
回答问题,赢新手礼包
个人、企业类
违法有害信息,请在下方选择后提交
色情、暴力
我们会通过消息、邮箱等方式尽快将举报结果通知您。Pages: 1/2
主题 : iPAD自定义相机问题
级别: 新手上路
可可豆: 14 CB
威望: 14 点
在线时间: 12(时)
发自: Web Page
来源于&&分类
iPAD自定义相机问题&&&
公司iPAD项目,用的是AV框架自定义的相机, 现在出了个问题就是横屏拍照, 拍出来的是旋转过的.网上找了个改imageOrientation的例子,但还是不行,有没有哪位大神遇到同样的问题求解,后面还要在图片上做一系列的算法跟操作,&&求帮忙.
级别: 骑士
UID: 534703
可可豆: 578 CB
威望: 543 点
在线时间: 687(时)
发自: Web Page
旋转和拍照的角度有关系
级别: 骑士
UID: 534703
可可豆: 578 CB
威望: 543 点
在线时间: 687(时)
发自: Web Page
我刚刚试了我自定义的相机,和角度并没有一毛钱的关系
级别: 新手上路
可可豆: 14 CB
威望: 14 点
在线时间: 12(时)
发自: Web Page
回 2楼(只有我和我的瑶瑶先上) 的帖子
是没有任何问题啊 .. 而且我把相关空气器朝向都改成跟iPad一样的了 该转的还是转过去了.... 我快疯掉了... 兄弟 你自定义的相机是iPAD的么&&有demo么....
级别: 精灵王
UID: 504932
可可豆: 2233 CB
威望: 2194 点
在线时间: 1128(时)
发自: Web Page
是预览的时候是旋转的,还是拍照成功后图片是旋转的???
级别: 新手上路
可可豆: 14 CB
威望: 14 点
在线时间: 12(时)
发自: Web Page
回 4楼(霸海飘) 的帖子
当然是拍完之后啦,
级别: 精灵王
UID: 504932
可可豆: 2233 CB
威望: 2194 点
在线时间: 1128(时)
发自: Web Page
回 5楼(cxr1991) 的帖子
- (UIImage *)fixOrientation:(UIImage *)aImage {&&&&&&&&if (aImage.imageOrientation == UIImageOrientationUp)&&&&&&&&return aI&&&&&&&&CGAffineTransform transform = CGAffineTransformI&&&&&&&&switch (aImage.imageOrientation) {&&&&&&&&case UIImageOrientationDown:&&&&&&&&case UIImageOrientationDownMirrored:&&&&&&&&&&&&transform = CGAffineTransformTranslate(transform, aImage.size.width, aImage.size.height);&&&&&&&&&&&&transform = CGAffineTransformRotate(transform, M_PI);&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&case UIImageOrientationLeft:&&&&&&&&case UIImageOrientationLeftMirrored:&&&&&&&&&&&&transform = CGAffineTransformTranslate(transform, aImage.size.width, 0);&&&&&&&&&&&&transform = CGAffineTransformRotate(transform, M_PI_2);&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&case UIImageOrientationRight:&&&&&&&&case UIImageOrientationRightMirrored:&&&&&&&&&&&&transform = CGAffineTransformTranslate(transform, 0, aImage.size.height);&&&&&&&&&&&&transform = CGAffineTransformRotate(transform, -M_PI_2);&&&&&&&&&&&&&&&&&&&&default:&&&&&&&&&&&&&&&&}&&&&&&&&switch (aImage.imageOrientation) {&&&&&&&&case UIImageOrientationUpMirrored:&&&&&&&&case UIImageOrientationDownMirrored:&&&&&&&&&&&&transform = CGAffineTransformTranslate(transform, aImage.size.width, 0);&&&&&&&&&&&&transform = CGAffineTransformScale(transform, -1, 1);&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&case UIImageOrientationLeftMirrored:&&&&&&&&case UIImageOrientationRightMirrored:&&&&&&&&&&&&transform = CGAffineTransformTranslate(transform, aImage.size.height, 0);&&&&&&&&&&&&transform = CGAffineTransformScale(transform, -1, 1);&&&&&&&&&&&&&&&&&&&&default:&&&&&&&&&&&&&&&&}&&&&&&&&CGContextRef ctx = CGBitmapContextCreate(NULL, aImage.size.width, aImage.size.height,&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& CGImageGetBitsPerComponent(aImage.CGImage), 0,&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& CGImageGetColorSpace(aImage.CGImage),&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& CGImageGetBitmapInfo(aImage.CGImage));&&&&CGContextConcatCTM(ctx, transform);&&&&switch (aImage.imageOrientation) {&&&&&&&&case UIImageOrientationLeft:&&&&&&&&case UIImageOrientationLeftMirrored:&&&&&&&&case UIImageOrientationRight:&&&&&&&&case UIImageOrientationRightMirrored:&&&&&&&&&&&&CGContextDrawImage(ctx, CGRectMake(0,0,aImage.size.height,aImage.size.width), aImage.CGImage);&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&default:&&&&&&&&&&&&CGContextDrawImage(ctx, CGRectMake(0,0,aImage.size.width,aImage.size.height), aImage.CGImage);&&&&&&&&&&&&&&&&}&&&&&&&&CGImageRef cgimg = CGBitmapContextCreateImage(ctx);&&&&UIImage *img = [UIImage imageWithCGImage:cgimg];&&&&CGContextRelease(ctx);&&&&CGImageRelease(cgimg);&&&&//NSLog(@&%ld&,(long)img.imageOrientation);&&&&}拍照得到的图片&&试试看看
级别: 骑士
UID: 534703
可可豆: 578 CB
威望: 543 点
在线时间: 687(时)
发自: Web Page
回 3楼(cxr1991) 的帖子
恩,我之前写过这个,我发给你吧
级别: 新手上路
可可豆: 14 CB
威望: 14 点
在线时间: 12(时)
发自: Web Page
回 6楼(霸海飘) 的帖子
用的就是这个方法...更改照片的朝向,,, 但是还是没用.... 谢啦
级别: 新手上路
可可豆: 14 CB
威望: 14 点
在线时间: 12(时)
发自: Web Page
回 7楼(只有我和我的瑶瑶先上) 的帖子
拜谢, 私你了 可以直接发我qq邮箱 不胜感激啊..
Pages: 1/2
关注本帖(如果有新回复会站内信通知您)
发帖、回帖都会得到可观的积分奖励。
按"Ctrl+Enter"直接提交
关注CocoaChina
关注微信 每日推荐
扫一扫 关注CVP公众号
扫一扫 浏览移动版只要出发,就会到达
之前用AVFoundation自定义相机做了拍照与视频相关的东西,为什么要自定义呢?主要是提供更个性化的交互设计,符合app主题,对于视频来说,也便于提供更多丰富有趣的功能。前段时间整理了下拍照部分的功能,主要分为以下五个部分
PS:吐槽一下博客园,图床也太那啥了,回头来看图片丢了好几张,还要重新补上。。。
1.初始化,建立会话,获取摄像头
使用AVCaptureSessionPresetPhoto模式,输出的图片分辨率与系统相机输出的分辨率保持一致
添加后置摄像头与图片输出(默认采用后置摄像头拍摄)
2.嵌入实时预览层
获取实时预览画面,添加手势,初始化时默认在画面中心点对焦一次并显示对焦框,提示用户可点击画面进行对焦及自动曝光调节。
3.相关设置
对焦与自动曝光调节
闪光灯设置
切换前后摄像头,_isFrontCameraInput这个标识在最后输出处理拍摄得到的照片时会用到,前置摄像头拍摄的照片需要旋转180度
4.拍摄获取照片
有以下几点需要注意下:
(1)view层点击拍照按钮时需要把拍摄时设备的方向信息传过来,最后输出照片时,会根据设备方向对图片进行旋转处理,使得最后输出的结果与系统相机拍摄结果一致。
(注:目前是通过系统自带传感器CMMotionManager获取到设备方向,其实也可以通过照片元数据获取到方向信息,进而做旋转处理)
(2)加入防抖模式,iOS8及以上系统支持。
(3)获取拍摄照片的元数据信息
(4)确定照片裁剪范围,裁剪范围即拍摄预览区域。系统拍摄获取到照片默认是一张全屏照片,但是UI设计上拍摄预览区域非全屏,为了保证所见即所得,需要对拍摄得到的照片进行裁剪,
在iPhone4上拍摄预览区域是一个屏幕等宽区域(考虑到设备尺寸,iPhone4单独设计的尺寸),其他机型上,预览区域是一个宽高比3:4(宽度为屏幕等宽)的区域。
(5)对图片进行裁剪及旋转处理,裁剪方法参考:
根据拍摄时设备方向信息对照片进行旋转,前置摄像头拍摄照片再单独旋转180度
5.View层UI及交互
具体的交互设计随实际情况而定。
有以下几点需要注意下:
(1)进入拍照页面即启动传感器监听,方便获取拍摄时设备方向信息,销毁需要关闭。
(2)切换闪光灯效果(打开/关闭/自动)时,记得保存用户选择,下一次拍照时恢复该设置(与系统相机行为一致)
阅读(...) 评论()iOS-AVFoundation自定义相机详解 - 简书
iOS-AVFoundation自定义相机详解
AVFoundation 中关于视频主要的类
相机基本实现步骤
捕捉会话——AVCaptureSession
捕捉输入——AVCaptureDeviceInput
捕捉预览——AVCaptureVideoPreviewLayer/OpenGL ES
捕捉连接——AVCaptureConnection
拍照——AVCaptureStillImageOutput
音频——AVCaptureAudioDataOutput
视频——AVCaptureVideoDataOutput
生成视频文件——AVAssetWriter、AVAssetWriterInput
写入相册——ALAssetsLibrary、PHPhotoLibrary
转换摄像头
自动聚焦曝光
视频重力——Video gravity
方向问题——Orientation
相机实现步骤,下面对每一会对每一步需要做的事情详解
1.创建session(捕捉会话)
2.创建device input(捕捉设备输入)
3.预览view
4.创建capture output(捕捉的输出)
5.拍照、录视频(元数据转成图片或文件)
捕捉会话——AVCaptureSession
AVCaptureSession(捕捉会话管理):它从物理设备得到数据流(比如摄像头和麦克风),输出到一个或多个目的地,它可以通过会话预设值(session preset),来控制捕捉数据的格式和质量
下面是创建一个 session 的代码:
AVCaptureSession *captureSession = [[AVCaptureSession alloc]init];
[captureSession setSessionPreset:AVCaptureSessionPresetPhoto];
SessionPreset在iOS中大概有11个
NSString *const AVCaptureSessionPresetP
NSString *const AVCaptureSessionPresetH
NSString *const AVCaptureSessionPresetM
NSString *const AVCaptureSessionPresetL
NSString *const AVCaptureSessionPreset352x288;
NSString *const AVCaptureSessionPreset640x480;
NSString *const AVCaptureSessionPreset;
NSString *const AVCaptureSessionPreset;
NSString *const AVCaptureSessionPresetiFrame960x540;
NSString *const AVCaptureSessionPresetiFrame;
NSString *const AVCaptureSessionPresetInputP
第一个代表高像素图片输出;接下来三种为相对预设(low, medium, high),这些预设的编码配置会因设备不同而不同,如果选择high,那么你选定的相机会提供给你该设备所能支持的最高画质;再后面就是特定分辨率的预设(352x288 VGA,
VGA, 640x480 VGA, 960x540 iFrame,
iFrame);最后一个代表 capture session 不去控制音频与视频输出设置,而是通过已连接的捕获设备的 activeFormat 来反过来控制 capture session 的输出质量等级
注意:所有对 capture session 的调用都是阻塞的,因此建议将它们分配到后台串行队列中,不过这里为了简单,不考虑性能,所以省略了dispatch queue
捕捉输入——AVCaptureDeviceInput
AVCaptureDeviceInput(捕捉设备):它实际上是为摄像头和麦克风等物理设备定义的接口,我们可以通过它来访问或控制这些硬件设备。比如控制摄像头的对焦、曝光等。
该方法会返回当前能够输入视频的全部设备,包括前后摄像头和外接设备
NSArray *videoDevices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
该方法会返回当前能够输入音频的全部设备
NSArray *videoDevices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeAudio];
// 获取视频输入设备,该方法默认返回iPhone的后置摄像头
AVCaptureDevice *videoDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
// 将捕捉设备加入到捕捉会话中
AVCaptureDeviceInput *videoInput = [AVCaptureDeviceInput deviceInputWithDevice:videoDevice error:error];
if (videoInput) {
if ([_captureSession canAddInput:videoInput]){
[_captureSession addInput:videoInput];
// 音频输入
AVCaptureDevice *audioDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeAudio];
AVCaptureDeviceInput *audioIn = [[AVCaptureDeviceInput alloc] initWithDevice:audioDevice error:error];
if ([_captureSession canAddInput:audioIn]){
[_captureSession addInput:audioIn];
捕捉预览——AVCaptureVideoPreviewLayer/OpenGL ES
AVCaptureVideoPreviewLayer(捕捉预览):它是CALayer的子类,可被用于自动显示相机产生的实时图像。previewLayer支持视频重力概念,可以控制视频内容渲染的缩放和拉效果(关于视频重力,将在后面进行详解)
// 创建一个previewLayer
AVCaptureVideoPreviewLayer
*previewLayer = [[AVCaptureVideoPreviewLayer alloc] initpWithFrame:self.view.bounds]
[previewLayer.layer setVideoGravity:AVLayerVideoGravityResizeAspect];
[previewLayer.layer setSession:session];
// 将屏幕坐标系的点转换为previewLayer坐标系的点
- (CGPoint)captureDevicePointForPoint:(CGPoint)point {
return [previewLayer.layer captureDevicePointOfInterestForPoint:point];
它看起来有点像输出,但其实不是,它仅用来预览摄像头捕捉的画面。真正用于输出的是AVCaptureSession(previewLayer拥有session,session拥有outputs);
它的坐标系和屏幕的坐标系不同,如果点击某区域实现对焦时,我们需要将设备的坐标系转换为实时预览图的坐标;
它的坐标原点永远都在右上角,这和我们手机的坐标系不同,手机坐标系的原点是不变的。因此拍照或录制视频时,要先得到设备方向(关于方向问题,后面会详解),计算输出的旋转角度。
捕捉预览除了用AVCaptureVideoPreviewLayer外,还可以用OpenGL ES绘制,我们可以从输出数据流捕捉单一的图像帧,并使用 OpenGL ES手动地把它们显示在 view 上。如果我们想对预览视图进行操作,如使用滤镜,我们就必须这样做。这里不做深入研究,下面给出一段简单的实现代码:
// 创建glview
EAGLContext *context = [[EAGLContext alloc]initWithAPI:kEAGLRenderingAPIOpenGLES2];
GLKView *glView = [[GLKView alloc]initWithFrame:self.view.bounds context:context];
[EAGLContext setCurrentContext:context];
[self.view addSubview:glView];
glView.transform = CGAffineTransformMakeRotation(M_PI_2);
glView.frame = [UIApplication sharedApplication].keyWindow.
// 在视频输出函数中绘制出来
-(void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection{
if (_glview.context != [EAGLContext currentContext]) {
[EAGLContext setCurrentContext:_glview.context];
CVImageBufferRef imageRef = CMSampleBufferGetImageBuffer(sampleBuffer);
CIImage *image = [CIImage imageWithCVImageBuffer:imageRef];
[_glview bindDrawable];
[_cicontext drawImage:image inRect:image.extent fromRect:image.extent];
[_glview display];
捕捉连接——AVCaptureConnection
捕捉连接负责将捕捉会话接收的媒体类型和输出连接起来,比如AVCaptureAudioDataOutput可以接受音频数据,AVCaptureVideoDataOutput可以接受视频数据。会话通过捕捉连接,确定哪些输入视频,那些输入音频。通过对捕捉连接的访问,可以对信号流进行底层控制,比如禁用某些特定的连接。
// 设置视频捕捉连接
_videoConnection = [videoOutput connectionWithMediaType:AVMediaTypeVideo];
_videoConnection.videoOrientation = self.referenceO
// 在视频元数据的输出函数中,如果捕捉连接是视频连接,则写入视频数据
if (connection == _videoConnection){
if ([self inputsReadyToRecord]){
[self writeSampleBuffer:sampleBuffer ofType:AVMediaTypeVideo];
// 设置音频捕捉连接
_audioConnection = [audioOut connectionWithMediaType:AVMediaTypeAudio];
// 在视频元数据的输出函数中,如果捕捉连接是音频连接,则写入音频数据
if (connection == _audioConnection){
if (_readyToRecordVideo && _readyToRecordAudio){
[self writeSampleBuffer:sampleBuffer ofType:AVMediaTypeAudio];
拍照——AVCaptureStillImageOutput
AVCaptureStillImageOutput会为我们捕捉高分辨率的图像,起设置如下:
// 创建image output 代码
AVCaptureStillImageOutput *imageOutput = [[AVCaptureStillImageOutput alloc] init];
imageOutput.outputSettings = @{AVVideoCodecKey:AVVideoCodecJPEG};
if ([_captureSession canAddOutput:imageOutput]) {
[_captureSession addOutput:imageOutput];
_imageOutput = imageO
// 输出图片
AVCaptureConnection *connection = [_imageOutput connectionWithMediaType:AVMediaTypeVideo];
if (connection.isVideoOrientationSupported) {
connection.videoOrientation = [self currentVideoOrientation];
id takePictureSuccess = ^(CMSampleBufferRef sampleBuffer,NSError *error){
if (sampleBuffer == NULL) {
[self showError:error];
NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:sampleBuffer];
UIImage *image = [[UIImage alloc]initWithData:imageData];
[_imageOutput captureStillImageAsynchronouslyFromConnection:connection completionHandler:takePictureSuccess];
音频——AVCaptureAudioDataOutput
AVCaptureAudioDataOutput(音频数据输出):它输出硬件实时捕捉的音频数字样本,还有一个音频输出类是AVCaptureAudioFileOutput,不过它只能在录制完成后输出完整的音频文件。
// 音频输出
AVCaptureAudioDataOutput *audioOut = [[AVCaptureAudioDataOutput alloc] init];
[audioOut setSampleBufferDelegate:self queue:captureQueue];
if ([_captureSession canAddOutput:audioOut]){
[_captureSession addOutput:audioOut];
视频——AVCaptureVideoDataOutput
AVCaptureVideoDataOutput(视频数据输出):它输出硬件实时捕捉的视频数字样本,还有一个音频和视频输出类是AVCaptureMovieFileOutput,不过它只能在录制完成后输出完整的视频和音频文件。
// 视频输出
AVCaptureVideoDataOutput *videoOut = [[AVCaptureVideoDataOutput alloc] init];
[videoOut setAlwaysDiscardsLateVideoFrames:YES];
[videoOut setVideoSettings:@{(id)kCVPixelBufferPixelFormatTypeKey : [NSNumber numberWithInt:kCVPixelFormatType_32BGRA]}];
[videoOut setSampleBufferDelegate:self queue:captureQueue];
if ([_captureSession canAddOutput:videoOut]){
[_captureSession addOutput:videoOut];
_videoOutput = videoO
生成视频文件——AVAssetWriter、AVAssetWriterInput
AVAssetWriter:用于对媒体资源进行编码并讲其写入到容器文件中,比如一个QuickTime文件。
AVAssetWriterInput:用于处理指定的媒体类型,比如音频和视频。
AVAssetWriterInputPixelBufferAdaptor:这个类在生成视频文件时提供最优性能,不过Demo没有使用该类,有兴趣的可以去研究一下
// 初始化一个assetWriter
_assetWriter = [[AVAssetWriter alloc] initWithURL:_movieURL fileType:AVFileTypeQuickTimeMovie error:&error];
if (error){
[self showError:error];
// 配置视频源数据输入
- (BOOL)setupAssetWriterVideoInput:(CMFormatDescriptionRef)currentFormatDescription
CGFloat bitsPerP
CMVideoDimensions dimensions = CMVideoFormatDescriptionGetDimensions(currentFormatDescription);
NSUInteger numPixels = dimensions.width * dimensions.
NSUInteger bitsPerS
if (numPixels & (640 * 480)){
bitsPerPixel = 4.05;
bitsPerPixel = 11.4;
bitsPerSecond = numPixels * bitsPerP
NSDictionary *videoCompressionSettings = @{AVVideoCodecKey
: AVVideoCodecH264,
AVVideoWidthKey
: [NSNumber numberWithInteger:dimensions.width],
AVVideoHeightKey : [NSNumber numberWithInteger:dimensions.height],
AVVideoCompressionPropertiesKey:@{AVVideoAverageBitRateKey:[NSNumber numberWithInteger:bitsPerSecond],
AVVideoMaxKeyFrameIntervalKey:[NSNumber numberWithInteger:30]}
if ([_assetWriter canApplyOutputSettings:videoCompressionSettings forMediaType:AVMediaTypeVideo])
_assetVideoInput = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeVideo outputSettings:videoCompressionSettings];
_assetVideoInput.expectsMediaDataInRealTime = YES;
_assetVideoInput.transform = [self transformFromCurrentVideoOrientationToOrientation:self.referenceOrientation];
if ([_assetWriter canAddInput:_assetVideoInput]){
[_assetWriter addInput:_assetVideoInput];
[self showError:_assetWriter.error];
return NO;
[self showError:_assetWriter.error];
return NO;
return YES;
// 配置音频源数据输入
- (BOOL)setupAssetWriterAudioInput:(CMFormatDescriptionRef)currentFormatDescription
size_t aclSize = 0;
const AudioStreamBasicDescription *currentASBD = CMAudioFormatDescriptionGetStreamBasicDescription(currentFormatDescription);
const AudioChannelLayout *currentChannelLayout = CMAudioFormatDescriptionGetChannelLayout(currentFormatDescription, &aclSize);
NSData *currentChannelLayoutData =
if (currentChannelLayout && aclSize & 0 ){
currentChannelLayoutData = [NSData dataWithBytes:currentChannelLayout length:aclSize];
currentChannelLayoutData = [NSData data];
NSDictionary *audioCompressionSettings = @{AVFormatIDKey : [NSNumber numberWithInteger:kAudioFormatMPEG4AAC],
AVSampleRateKey : [NSNumber numberWithFloat:currentASBD-&mSampleRate],
AVEncoderBitRatePerChannelKey : [NSNumber numberWithInt:64000],
AVNumberOfChannelsKey : [NSNumber numberWithInteger:currentASBD-&mChannelsPerFrame],
AVChannelLayoutKey : currentChannelLayoutData};
if ([_assetWriter canApplyOutputSettings:audioCompressionSettings forMediaType:AVMediaTypeAudio])
_assetAudioInput = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeAudio outputSettings:audioCompressionSettings];
_assetAudioInput.expectsMediaDataInRealTime = YES;
if ([_assetWriter canAddInput:_assetAudioInput]){
[_assetWriter addInput:_assetAudioInput];
[self showError:_assetWriter.error];
return NO;
[self showError:_assetWriter.error];
return NO;
return YES;
通过上面的代码,我们就准备好了一个AVAssetWriter了,就可以用它来生产视频文件,我们可以在视频源数据输出函数中写入
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection{
if (_recording) {
CFRetain(sampleBuffer);
dispatch_async(_movieWritingQueue, ^{
if (_assetWriter)
if (connection == _videoConnection)
if (!_readyToRecordVideo){
_readyToRecordVideo = [self setupAssetWriterVideoInput:CMSampleBufferGetFormatDescription(sampleBuffer)];
if ([self inputsReadyToRecord]){
[self writeSampleBuffer:sampleBuffer ofType:AVMediaTypeVideo];
else if (connection == _audioConnection){
if (!_readyToRecordAudio){
_readyToRecordAudio = [self setupAssetWriterAudioInput:CMSampleBufferGetFormatDescription(sampleBuffer)];
if ([self inputsReadyToRecord]){
[self writeSampleBuffer:sampleBuffer ofType:AVMediaTypeAudio];
CFRelease(sampleBuffer);
写入相册——ALAssetsLibrary、PHPhotoLibrary
iOS9.0以前:
ALAssetsLibrary *lab = [[ALAssetsLibrary alloc]init];
// 保存视频
[lab writeVideoAtPathToSavedPhotosAlbum:_movieURL completionBlock:^(NSURL *assetURL, NSError *error) {
if (error) {
[self showError:error];
iOS9.0以后
[PHPhotoLibrary requestAuthorization:^( PHAuthorizationStatus status ) {
if (status == PHAuthorizationStatusAuthorized) {
[[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
// 保存视频
PHAssetCreationRequest *videoRequest = [PHAssetCreationRequest creationRequestForAsset];
[videoRequest addResourceWithType:PHAssetResourceTypeVideo fileURL:_movieURL options:nil];
} completionHandler:^( BOOL success, NSError * _Nullable error ) {
if (!success) {
[self showError:error];
相机的操作都是一些固定的代码,我就不多讲了,我们只需要注意以下几点:
1.闪光灯和手电筒不能同时开启
2.在前置摄像头时不能开启手电筒,所有在转换时,会被强制关闭
3.前后摄像头需要分别设置闪光灯的开关,所以我们必须记录当前闪光灯的设置状态,在转换完成之后,还需要重新设置一次
4.在转换摄像头时,你之前设置的视频输出就无效了,你需要删除原来的视频输出,再重新添加一个新的视频输出(我也不知道为什么会有这种情况,但是音频源数据是一直都有的,视频源数据每次转换摄像头都需要重新设置视频输出)
转换摄像头
- (BOOL)switchCameras{
if (![self canSwitchCameras]) {
return NO;
AVCaptureDevice *videoDevice = [self inactiveCamera];
AVCaptureDeviceInput *videoInput = [AVCaptureDeviceInput deviceInputWithDevice:videoDevice error:&error];
if (videoInput) {
[_captureSession beginConfiguration];
[_captureSession removeInput:_deviceInput];
if ([_captureSession canAddInput:videoInput]) {
[_captureSession addInput:videoInput];
_deviceInput = videoI
[_captureSession addInput:_deviceInput];
[_captureSession commitConfiguration];
// 如果从后置转前置,会关闭手电筒,如果之前打开的,需要通知camera更新UI
if (videoDevice.position == AVCaptureDevicePositionFront) {
[self.cameraView changeTorch:NO];
// 闪关灯,前后摄像头的闪光灯是不一样的,所以在转换摄像头后需要重新设置闪光灯
[self changeFlash:_currentflashMode];
// 转换摄像头时,视频输出就无效了,所以在转换回来时,需要把原来的删除了,在重新加一个新的进去
[self resetupVideoOutput];
[self showError:error];
return NO;
return YES;
-(void)resetupVideoOutput{
[_captureSession beginConfiguration];
[_captureSession removeOutput:_videoOutput];
AVCaptureVideoDataOutput *videoOut = [[AVCaptureVideoDataOutput alloc] init];
[videoOut setAlwaysDiscardsLateVideoFrames:YES];
[videoOut setVideoSettings:@{(id)kCVPixelBufferPixelFormatTypeKey : [NSNumber numberWithInt:kCVPixelFormatType_32BGRA]}];
dispatch_queue_t videoCaptureQueue = dispatch_queue_create("Video Capture Queue", DISPATCH_QUEUE_SERIAL);
[videoOut setSampleBufferDelegate:self queue:videoCaptureQueue];
if ([_captureSession canAddOutput:videoOut]) {
[_captureSession addOutput:videoOut];
_videoOutput = videoO
_videoConnection = [videoOut connectionWithMediaType:AVMediaTypeVideo];
[_captureSession commitConfiguration];
AVCaptureDevice *device = [self activeCamera];
if (device.torchMode != torchMode && [device isTorchModeSupported:torchMode]) {
if ([device lockForConfiguration:&error]) {
device.torchMode = torchM
[device unlockForConfiguration];
[self showError:error];
AVCaptureDevice *device = [self activeCamera];
if (device.flashMode != flashMode && [device isFlashModeSupported:flashMode]) {
if ([device lockForConfiguration:&error]) {
device.flashMode = flashM
[device unlockForConfiguration];
[self showError:error];
- (void)focusAtPoint:(CGPoint)point {
AVCaptureDevice *device = [self activeCamera];
if ([self cameraSupportsTapToFocus] && [device isFocusModeSupported:AVCaptureFocusModeAutoFocus]) {
if ([device lockForConfiguration:&error]) {
device.focusPointOfInterest =
device.focusMode = AVCaptureFocusModeAutoF
[device unlockForConfiguration];
[self showError:error];
static const NSString *CameraAdjustingExposureC
- (void)exposeAtPoint:(CGPoint)point{
AVCaptureDevice *device = [self activeCamera];
if ([self cameraSupportsTapToExpose] && [device isExposureModeSupported:AVCaptureExposureModeContinuousAutoExposure]) {
if ([device lockForConfiguration:&error]) {
device.exposurePointOfInterest =
device.exposureMode = AVCaptureExposureModeContinuousAutoE
if ([device isExposureModeSupported:AVCaptureExposureModeLocked]) {
[device addObserver:self
forKeyPath:@"adjustingExposure"
options:NSKeyValueObservingOptionNew
context:&CameraAdjustingExposureContext];
[device unlockForConfiguration];
[self showError:error];
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
if (context == &CameraAdjustingExposureContext) {
AVCaptureDevice *device = (AVCaptureDevice *)
if (!device.isAdjustingExposure && [device isExposureModeSupported:AVCaptureExposureModeLocked]) {
[object removeObserver:self
forKeyPath:@"adjustingExposure"
context:&CameraAdjustingExposureContext];
dispatch_async(dispatch_get_main_queue(), ^{
if ([device lockForConfiguration:&error]) {
device.exposureMode = AVCaptureExposureModeL
[device unlockForConfiguration];
[self showError:error];
[super observeValueForKeyPath:keyPath
ofObject:object
change:change
context:context];
自动聚焦/曝光
- (BOOL)resetFocusAndExposureModes{
AVCaptureDevice *device = [self activeCamera];
AVCaptureExposureMode exposureMode = AVCaptureExposureModeContinuousAutoE
AVCaptureFocusMode focusMode = AVCaptureFocusModeContinuousAutoF
BOOL canResetFocus = [device isFocusPointOfInterestSupported] && [device isFocusModeSupported:focusMode];
BOOL canResetExposure = [device isExposurePointOfInterestSupported] && [device isExposureModeSupported:exposureMode];
CGPoint centerPoint = CGPointMake(0.5f, 0.5f);
if ([device lockForConfiguration:&error]) {
if (canResetFocus) {
device.focusMode = focusM
device.focusPointOfInterest = centerP
if (canResetExposure) {
device.exposureMode = exposureM
device.exposurePointOfInterest = centerP
[device unlockForConfiguration];
return YES;
[self showError:error];
return NO;
视频重力——Video gravity
视频重力:控制视频内容渲染的缩放和拉伸效果。
举个例子,在我们设置会话时有一个参数session preset,它是用来控制捕捉数据格式和质量了。我的测试机是6s,当我选择参数AVCaptureSessionPresetPhoto时,输出图片大小如下:
Printing description of image:
&CIImage: 0x12c7bdad0 extent [0 0 750 1000]&
affine [1 0 0 -1 0 1000] extent=[0 0 750 1000]
colormatch "QuickTime 'nclc' Video (1,1,6)"-to-workingspace extent=[0 0 750 1000]
IOSurface 0x12da00008 BGRA8 extent=[0 0 750 1000]
当我选择参数AVCaptureSessionPresetHigh时,输出图片大小如下:
Printing description of image:
&CIImage: 0x15f851680 extent [0 0 ]&
affine [1 0 0 -1 0 1920] extent=[0 0 ]
colormatch "QuickTime 'nclc' Video (1,1,6)"-to-workingspace extent=[0 0 ]
IOSurface 0x15f900008 BGRA8 extent=[0 0 ]
可以看出选择不同的session preset,会输出不同大小的图片,但是这些图片都是很大的,这么大的图片要显示在手机预览层,必须要缩放,而视频重力其实就是缩放参数。
AVLayerVideoGravityResizeAspect:在预览层区域内缩放视频,保持视频原始宽高比。这是默认值,同时适用大多数情况。使用该参数预览时,有可能不能铺满整个预览视图
AVLayerVideoGravityResizeAspectFill:按照视频的宽高比将视频拉伸填满整个图层。使用该参数时,很可能造成视频预览图片被裁剪,而拍摄输出没有被裁剪,这样就会使预览图和最终拍摄的图不一致。
AVLayerVideoGravityResize:拉伸视频内容以匹配预览层大小,这个是最不常用的,可能造成视频扭曲。
方向问题——Orientation
设备方向device orientation
// 设备方向
UIDevice *device = [UIDevice currentDevice] ;
switch (device.orientation) {
case UIDeviceOrientationFaceUp:
NSLog(@"屏幕朝上平躺");
case UIDeviceOrientationFaceDown:
NSLog(@"屏幕朝下平躺");
case UIDeviceOrientationUnknown:
NSLog(@"未知方向");
case UIDeviceOrientationLandscapeLeft:
NSLog(@"屏幕向左橫置");
case UIDeviceOrientationLandscapeRight:
NSLog(@"屏幕向右橫置");
case UIDeviceOrientationPortrait:
NSLog(@"屏幕直立");
case UIDeviceOrientationPortraitUpsideDown:
NSLog(@"屏幕直立,上下顛倒");
从上面可以看到所有的设备方向,而视频方向videoOrientation没有那么多分类,它分为:
AVCaptureVideoOrientationPortrait
home健在下
AVCaptureVideoOrientationPortraitUpsideDown
home健在上
AVCaptureVideoOrientationLandscapeRight
home健在右
AVCaptureVideoOrientationLandscapeLeft
home健在左
这些视频方向,是视频或拍照时的输入方向,而我们的数据输出时会跟具这些输入方向自动对图片或视频进行矩阵变换,以达到最佳的用户体验。
这里以拍照举个例子(视频同理):
假如你横着手机拍了一张照片,第一次你在拍照前不传入视频方向,它默认为AVCaptureVideoOrientationPortrait,这是正常手机拿着的姿势,所以到输出时不会对图片进行矩阵变换,当你把图片存入相册时,你会发现,你要正确查看这张图,你也需要横着手机看。如果你是倒着手机拍的,就需要倒着手机看。但是如果你在拍照前传入视频方向,比如你横着手机拍,并且home健在右,就传入参数AVCaptureVideoOrientationLandscapeRight,这时你存入相册的照片就可以以正常拿手机的姿势查看它了。
// 在拍照前通过会话连接,传入当前输入视频方向(视频同理也可以这样做)
AVCaptureConnection *connection = [_imageOutput connectionWithMediaType:AVMediaTypeVideo];
if (connection.isVideoOrientationSupported) {
connection.videoOrientation = [self currentVideoOrientation];
苹果给出的类处理后都是默认正常拿手机的姿势观看,不管是图片还是视频,如果我们想拍出的所有图片或视频都需要横着手机看,我们这时可以不传入视频方向,这样视频到输出时就不会被变换,我们在视频输入类中,手动对视频进行transform变换,这样就可以实现我们想要的查看方式,在本例中,视频就是用的这种处理方式。
// 视频的播放方向,后面计算视频旋转角度使用
_referenceOrientation = AVCaptureVideoOrientationP
// 这行代码在设置视频输入方向为默认输入方向
_videoConnection.videoOrientation =
AVCaptureVideoOrientationP
// 视频输入类中手动旋转视频方向
_assetVideoInput.transform = [self transformFromCurrentVideoOrientationToOrientation:self.referenceOrientation];
// 旋转视频方向函数实现
(CGAffineTransform)transformFromCurrentVideoOrientationToOrientation:(AVCaptureVideoOrientation)orientation
CGFloat orientationAngleOffset = [self angleOffsetFromPortraitOrientationToOrientation:orientation];
CGFloat videoOrientationAngleOffset = [self angleOffsetFromPortraitOrientationToOrientation:self.motionManager.videoOrientation];
CGFloat angleO
if ([self activeCamera].position == AVCaptureDevicePositionBack) {
angleOffset = orientationAngleOffset - videoOrientationAngleO
angleOffset = videoOrientationAngleOffset - orientationAngleOffset + M_PI_2;
CGAffineTransform transform = CGAffineTransformMakeRotation(angleOffset);
- (CGFloat)angleOffsetFromPortraitOrientationToOrientation:(AVCaptureVideoOrientation)orientation
CGFloat angle = 0.0;
switch (orientation)
case AVCaptureVideoOrientationPortrait:
angle = 0.0;
case AVCaptureVideoOrientationPortraitUpsideDown:
angle = M_PI;
case AVCaptureVideoOrientationLandscapeRight:
angle = -M_PI_2;
case AVCaptureVideoOrientationLandscapeLeft:
angle = M_PI_2;
个人主页:https://cdcyd.github.io/
原文链接http://www.cnblogs.com/kenshincui/p/4186022.html 音频在iOS中音频播放从形式上可以分为音效播放和音乐播放。前者主要指的是一些短音频播放,通常作为点缀音频,对于这类音频不需要进行进度、循环等控制。后者指的是一些较长的音...
链接:http://www.cnblogs.com/liangzhimy/archive//2740905.html [IOS开发]拍照,摄像,载图总结 1 建立Session 2 添加 input 3 添加output 4 开始捕捉 5 为用户显示当前...
最终诉求? 拍摄、保存、播放、上传。就这四个步骤,当然首先拍摄就有许许多多的优化小功能,切换摄像头、单击跳帧焦距、双击远近距离切换、上移取消拍摄。 保存功能就只有一个保存到本地沙盒的功能。 播放自然就涉及到原生或是三方库来播放的问题,用什么第三方库来播放。 上传就有几种情况...
前段时间遇到一个需求,需要实时扫描图像,并且设定摄像头的尺寸为,然后从中间截取出512x512的图片传给第三方SDK做进一步业务处理,直到SDK返回正确的处理结果。 一顿Google,发现图像预览、人脸识别、二维码识别这些苹果都帮我们做好了,而且它们都是基...
由于我们公司不是专门做直播的, 所以研究直播开发完全处于兴趣爱好,可能很多地方用处理的不是很周到, 所以, 希望大家多提提意见, 互相学习一下哈! 这里附上我写的第一篇直播开发的文章传送门iOS-直播开发(开发从底层做起) 好啦, 废话不多说, 直奔主题! 本篇文章是针对直...
来猜猜哪些菜是我做的吧~?!
作为一个领导,你必须能容纳你身边人的种种想法和行为。你不能容纳,他们就会一个一个地走掉。但是,领导往往把面子和尊严看得比什么都重要。 有一类领导,宁可员工走光,宁可事业不做,公司倒闭,也不能丢了自己的尊严。还有一类领导,只要员工能推动他的企业,他的尊严不重要。后者是成熟理智...
导语: 我们一直在谈论读书,那么怎么样读书才能让我们收获更多的知识,让我们在职场、在生活中成为一个“成功”的人,小编今天带大家走进韩国作家二志成和郑会一的读书世界,看他们是如何通过读书成功的。 笔记: 1.如何实现真正意义上的读书 阅读——思考——实践 2.读书的三个阶段 ...
《官员生活》又一个李达康书记----吕展 书看到第21章,觉得真的出来一个李达康书记,那就是吕展。从开始读,就觉得他可能是这部书里的一个正面角色,可是没想到他是如此刚正不阿,而且立足于发展科学经济。 对于一个能耗型的城市,想要转型谈何容易!已经依赖慣了重工业,尤其是钢铁矿产...

我要回帖

更多关于 相机不能自动对焦 的文章

 

随机推荐