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