iOS开发了好几年了,自定义相机都碰到过很多次,每次都是从网上copy代码使用,但是很多时候都会有方向等问题,从来没有真正研究过,现在在这里记录一下自定义相机碰到的问题,以防忘记
问题一:横向拍照/录制视频,得到的视频也需要横屏。
要实现这个功能,就需要获取到设备的方向,这里有两种方法获取方向
方法一:根据设备方向开关来获取方向,[UIDevice currentDevice].orientation
方法二:根据重力感应获取方向
DN_WEAK_SELF- if([self.cmmotionManager isDeviceMotionAvailable]) {
- [self.cmmotionManager startAccelerometerUpdatesToQueue:[NSOperationQueue currentQueue] withHandler:^(CMAccelerometerData * _Nullable accelerometerData, NSError * _Nullable error) {
- DN_STRONG_SELF
- double x = accelerometerData.acceleration.x;
- double y = accelerometerData.acceleration.y;
- if (fabs(y) >= fabs(x)) {
- if (y >= 0) {
- //Down
- self.deviceOrientation = UIDeviceOrientationPortraitUpsideDown;
- } else {
- //Portrait
- self.deviceOrientation = UIDeviceOrientationPortrait;
- }
- } else {
- if (x >= 0) {
- //Righ
- self.deviceOrientation = UIDeviceOrientationLandscapeRight;
- } else {
- //Left
- self.deviceOrientation = UIDeviceOrientationLandscapeLeft;
- }
- }
- }];
- }
获取到方向后,在拍照前设置方向
AVCaptureConnection *myVideoConnection = [self.ImageOutPut connectionWithMediaType:AVMediaTypeVideo];- if ([myVideoConnection isVideoOrientationSupported]) {
- myVideoConnection.videoOrientation = [self getCaptureVideoOrientation:self.deviceOrientation];
- }
在录制视频前设置方向
AVCaptureConnection *connection = [self.captureMovieFileOutput connectionWithMediaType:AVMediaTypeVideo];- if ([connection isVideoOrientationSupported]) {
- connection.videoOrientation = [self getCaptureVideoOrientation:self.deviceOrientation];
- }
问题二: 前置摄像头拍照时,照片和视频左右颠倒(类似微信)
这个问题其实很简单,只要设置一下输出对象的镜像开关就可以了
AVCaptureSession *session = (AVCaptureSession *)self.session;- for (AVCaptureVideoDataOutput* output in session.outputs) {
- for (AVCaptureConnection * av in output.connections) {
- //判断是否是前置摄像头状态
-
- if ([self.input device].position == AVCaptureDevicePositionFront) {
- if (av.supportsVideoMirroring) {
- //镜像设置
- av.videoMirrored = YES;
- }
- }
- }
- }
录制视频的话,解决左右颠倒 只能用以上的方法
而拍照的话,解决左右颠倒,还有一种方法,在拍完照片后,设置图片方向
UIImageOrientation imgOrientation; //拍摄后获取的的图像方向-
- if ([self.input device].position == AVCaptureDevicePositionFront &&
- self.isFixLeftRightProblem) {
- NSLog(@"前置摄像头");
- // 前置摄像头图像方向 UIImageOrientationLeftMirrored
- // IOS前置摄像头左右成像
- imgOrientation = UIImageOrientationLeftMirrored;
- if (image.imageOrientation == UIImageOrientationRight) {
- imgOrientation = UIImageOrientationLeftMirrored;
- }else if (image.imageOrientation == UIImageOrientationLeft) {
- imgOrientation = UIImageOrientationRightMirrored;
- }else if (image.imageOrientation == UIImageOrientationDown) {
- imgOrientation = UIImageOrientationDownMirrored;
- }else if (image.imageOrientation == UIImageOrientationUp) {
- imgOrientation = UIImageOrientationUpMirrored;
- }
- image = [[UIImage alloc]initWithCGImage:image.CGImage scale:1.0f orientation:imgOrientation];
- }
问题三:自定义相机拍照后,在其他端查看时,方向不正确
这时候就需要矫正方向了
/*!- ** 矫正图片方向 参考 http://www.cocoachina.com/articles/12021
-
- */
-
- - (UIImage *)fixOrientation {
-
- // No-op if the orientation is already correct
-
- if (self.imageOrientation == UIImageOrientationUp) return self;
-
- // We need to calculate the proper transformation to make the image upright.
- // We do it in 2 steps: Rotate if Left/Right/Down, and then flip if Mirrored.
- CGAffineTransform transform = CGAffineTransformIdentity;
-
- switch (self.imageOrientation) {
- case UIImageOrientationDown:
- case UIImageOrientationDownMirrored:
- transform = CGAffineTransformTranslate(transform, self.size.width, self.size.height);
- transform = CGAffineTransformRotate(transform, M_PI);
- break;
-
- case UIImageOrientationLeft:
- case UIImageOrientationLeftMirrored:
- transform = CGAffineTransformTranslate(transform, self.size.width, 0);
- transform = CGAffineTransformRotate(transform, M_PI_2);
- break;
-
- case UIImageOrientationRight:
- case UIImageOrientationRightMirrored:
- transform = CGAffineTransformTranslate(transform, 0, self.size.height);
- transform = CGAffineTransformRotate(transform, -M_PI_2);
- break;
- case UIImageOrientationUp:
- case UIImageOrientationUpMirrored:
- break;
- }
-
- switch (self.imageOrientation) {
- case UIImageOrientationUpMirrored:
- case UIImageOrientationDownMirrored:
- transform = CGAffineTransformTranslate(transform, self.size.width, 0);
- transform = CGAffineTransformScale(transform, -1, 1);
- break;
-
- case UIImageOrientationLeftMirrored:
- case UIImageOrientationRightMirrored:
- transform = CGAffineTransformTranslate(transform, self.size.height, 0);
- transform = CGAffineTransformScale(transform, -1, 1);
- break;
- case UIImageOrientationUp:
- case UIImageOrientationDown:
- case UIImageOrientationLeft:
- case UIImageOrientationRight:
- break;
- }
-
- // Now we draw the underlying CGImage into a new context, applying the transform
- // calculated above.
- 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 UIImageOrientationRight:
- case UIImageOrientationRightMirrored:
- // Grr...
- CGContextDrawImage(ctx, CGRectMake(0,0,self.size.height,self.size.width), self.CGImage);
- break;
-
- default:
- CGContextDrawImage(ctx, CGRectMake(0,0,self.size.width,self.size.height), self.CGImage);
- break;
- }
-
- // And now we just create a new UIImage from the drawing context
- CGImageRef cgimg = CGBitmapContextCreateImage(ctx);
- UIImage *img = [UIImage imageWithCGImage:cgimg];
- CGContextRelease(ctx);
- CGImageRelease(cgimg);
- return img;
- }
问题四:自定义相机打开后,这时候来电话了,再切回到相机不能拍照了。
这里就需要监听音频中断的通知AVCaptureSessionWasInterruptedNotification,中断结束的通知AVCaptureSessionInterruptionEndedNotification
还要监听系统电话来电 CTCallCenter callEventHandler,在中断通知AVCaptureSessionWasInterruptedNotification中,将音频输入设备从session中移除,等中断通知结束后,判断是否时系统电话造成的,如果是则将音频输入设备加入session
监听来电的代码 <CoreTelephony/CTCallCenter.h> <CoreTelephony/CTCall.h>
DN_WEAK_SELF- self.callCenter = [[CTCallCenter alloc] init];
- self.callCenter.callEventHandler = ^(CTCall * call) {
- DN_STRONG_SELF
- if([call.callState isEqualToString:CTCallStateDisconnected]) {
- NSLog(@"Call has been disconnected");//电话被挂断(我们用的这个)
- self.isSystemCall = NO;
- } else if([call.callState isEqualToString:CTCallStateConnected]) {
- NSLog(@"Call has been connected");//电话被接听
- } else if([call.callState isEqualToString:CTCallStateIncoming]) {
- NSLog(@"Call is incoming");//来电话了
- self.isSystemCall = YES;
- [self endCall];
- } else if([call.callState isEqualToString:CTCallStateDialing]) {
- NSLog(@"Call is Dialing");//拨号
- self.isSystemCall = YES;
- [self endCall];
- } else {
- NSLog(@"Nothing is done");
- }
- };
中断音频代码
- - (void)initNotification {
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(AVCaptureSessionWasInterruptedNotification:) name:AVCaptureSessionWasInterruptedNotification object:self.session];
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(AVCaptureSessionInterruptionEndedNotification:) name:AVCaptureSessionInterruptionEndedNotification object:self.session];
- }
- #pragma mark - 通知
-
- - (void)AVCaptureSessionWasInterruptedNotification:(NSNotification *)notification {
- AVCaptureSessionInterruptionReason reason = [notification.userInfo[AVCaptureSessionInterruptionReasonKey] integerValue];
- NSLog(@"fanshijian 中断 : %d reason : %ld",self.session.interrupted,(long)reason);
- if (reason == AVCaptureSessionInterruptionReasonAudioDeviceInUseByAnotherClient) {
- [self removeAudioInput];
- }
- }
-
- - (void)AVCaptureSessionInterruptionEndedNotification:(NSNotification *)notification {
- NSLog(@"fanshijian 中断结束 : %d",self.session.interrupted);
- if (![DNVideoChatConfig sharedConfig].isSystemCall) {
- [self addAudioInput];
- }
- }
- - (void)addAudioInput {
- if (!_audioCaptureDeviceInput) {
- //添加一个音频输入设备
- AVCaptureDevice *audioCaptureDevice = [[AVCaptureDevice devicesWithMediaType:AVMediaTypeAudio] firstObject];
- //添加音频
- NSError *error = nil;
- AVCaptureDeviceInput *audioCaptureDeviceInput = [[AVCaptureDeviceInput alloc]initWithDevice:audioCaptureDevice error:&error];
- if (error) {
- NSLog(@"取得设备输入对象时出错,错误原因:%@",error.localizedDescription);
- return;
- }
- self.audioCaptureDeviceInput = audioCaptureDeviceInput;
- }
-
- if ([self.session canAddInput:self.audioCaptureDeviceInput]) {
- [self.session addInput:self.audioCaptureDeviceInput];
- }
- }
-
- - (void)removeAudioInput {
- [self.session removeInput:self.audioCaptureDeviceInput];
- }
可以参考demo https://github.com/smallMas/FSEditVideo.git 类FSLiveWindow