I\'ve been tearing my hair out trying to get the AVFoundation camera to capture a picture in the correct orientation (i.e. the device orientation) but I can\'t get it to wor
Well, it's taken me fracking forever but I've done it!
The bit of code I was looking for is
[UIDevice currentDevice].orientation;
This goes in as so
AVCaptureConnection *videoConnection = [CameraVC connectionWithMediaType:AVMediaTypeVideo fromConnections:[imageCaptureOutput connections]];
if ([videoConnection isVideoOrientationSupported])
{
[videoConnection setVideoOrientation:[UIDevice currentDevice].orientation];
}
And it works perfectly :D
Woop woop!
How to use with AVCaptureFileOutput ?
- (void)detectVideoOrientation:(AVCaptureFileOutput *)captureOutput {
for(int i = 0; i < [[captureOutput connections] count]; i++) {
AVCaptureConnection *captureConnection = [[captureOutput connections] objectAtIndex:i];
if([captureConnection isVideoOrientationSupported]) {
[captureConnection setVideoOrientation:[[UIDevice currentDevice] orientation]];
}
}
}
The following is from AVCam, I added too it:
- (void)deviceOrientationDidChange{
UIDeviceOrientation deviceOrientation = [[UIDevice currentDevice] orientation];
AVCaptureVideoOrientation newOrientation;
if (deviceOrientation == UIDeviceOrientationPortrait){
NSLog(@"deviceOrientationDidChange - Portrait");
newOrientation = AVCaptureVideoOrientationPortrait;
}
else if (deviceOrientation == UIDeviceOrientationPortraitUpsideDown){
NSLog(@"deviceOrientationDidChange - UpsideDown");
newOrientation = AVCaptureVideoOrientationPortraitUpsideDown;
}
// AVCapture and UIDevice have opposite meanings for landscape left and right (AVCapture orientation is the same as UIInterfaceOrientation)
else if (deviceOrientation == UIDeviceOrientationLandscapeLeft){
NSLog(@"deviceOrientationDidChange - LandscapeLeft");
newOrientation = AVCaptureVideoOrientationLandscapeRight;
}
else if (deviceOrientation == UIDeviceOrientationLandscapeRight){
NSLog(@"deviceOrientationDidChange - LandscapeRight");
newOrientation = AVCaptureVideoOrientationLandscapeLeft;
}
else if (deviceOrientation == UIDeviceOrientationUnknown){
NSLog(@"deviceOrientationDidChange - Unknown ");
newOrientation = AVCaptureVideoOrientationPortrait;
}
else{
NSLog(@"deviceOrientationDidChange - Face Up or Down");
newOrientation = AVCaptureVideoOrientationPortrait;
}
[self setOrientation:newOrientation];
}
And be sure to add this to your init method:
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
[notificationCenter addObserver:self
selector:@selector(deviceOrientationDidChange)
name:UIDeviceOrientationDidChangeNotification object:nil];
[self setOrientation:AVCaptureVideoOrientationPortrait];
This uses the view controller's orientation method. This works for me, hopefully works for you.
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
[super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];
AVCaptureConnection *videoConnection = self.prevLayer.connection;
[videoConnection setVideoOrientation:(AVCaptureVideoOrientation)toInterfaceOrientation];
}
Isn't this a little cleaner?
AVCaptureVideoOrientation newOrientation;
switch ([[UIDevice currentDevice] orientation]) {
case UIDeviceOrientationPortrait:
newOrientation = AVCaptureVideoOrientationPortrait;
break;
case UIDeviceOrientationPortraitUpsideDown:
newOrientation = AVCaptureVideoOrientationPortraitUpsideDown;
break;
case UIDeviceOrientationLandscapeLeft:
newOrientation = AVCaptureVideoOrientationLandscapeRight;
break;
case UIDeviceOrientationLandscapeRight:
newOrientation = AVCaptureVideoOrientationLandscapeLeft;
break;
default:
newOrientation = AVCaptureVideoOrientationPortrait;
}
[stillConnection setVideoOrientation: newOrientation];
I am writing this code in Swift in case may be needful for someone.
Step-1: Generate Orientation notifications (in yourviewDidLoad
)
UIDevice.currentDevice().beginGeneratingDeviceOrientationNotifications()
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("deviceOrientationDidChange:"), name: UIDeviceOrientationDidChangeNotification, object: nil)
Step-2: Take picture. Here we will interchange orientation of videoConnection
. In AVFoundation there is a minor change in orientation especially for landscape orientation. So we will just interchange it. For example we will change from LandscapeRight
to LandscapeLeft
and viceversa
func takePicture() {
if let videoConnection = stillImageOutput!.connectionWithMediaType(AVMediaTypeVideo) {
var newOrientation: AVCaptureVideoOrientation?
switch (UIDevice.currentDevice().orientation) {
case .Portrait:
newOrientation = .Portrait
break
case .PortraitUpsideDown:
newOrientation = .PortraitUpsideDown
break
case .LandscapeLeft:
newOrientation = .LandscapeRight
break
case .LandscapeRight:
newOrientation = .LandscapeLeft
break
default :
newOrientation = .Portrait
break
}
videoConnection.videoOrientation = newOrientation!
stillImageOutput!.captureStillImageAsynchronouslyFromConnection(videoConnection) {
(imageDataSampleBuffer, error) -> Void in
let imageData = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(imageDataSampleBuffer)
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)) {
dispatch_async(dispatch_get_main_queue()) {
let image = UIImage(data: imageData!)!
let portraitImage = image.fixOrientation()
}
}
}
}
}
NOTE: Please notice the new orientation value for Landscape orientations. Its just the opposite. (This is the culprit ::UHHHH)
Step-3: Fix orientation (UIImage extension)
extension UIImage {
func fixOrientation() -> UIImage {
if imageOrientation == UIImageOrientation.Up {
return self
}
var transform: CGAffineTransform = CGAffineTransformIdentity
switch imageOrientation {
case UIImageOrientation.Down, UIImageOrientation.DownMirrored:
transform = CGAffineTransformTranslate(transform, size.width, size.height)
transform = CGAffineTransformRotate(transform, CGFloat(M_PI))
break
case UIImageOrientation.Left, UIImageOrientation.LeftMirrored:
transform = CGAffineTransformTranslate(transform, size.width, 0)
transform = CGAffineTransformRotate(transform, CGFloat(M_PI_2))
break
case UIImageOrientation.Right, UIImageOrientation.RightMirrored:
transform = CGAffineTransformTranslate(transform, 0, size.height)
transform = CGAffineTransformRotate(transform, CGFloat(-M_PI_2))
break
case UIImageOrientation.Up, UIImageOrientation.UpMirrored:
break
}
switch imageOrientation {
case UIImageOrientation.UpMirrored, UIImageOrientation.DownMirrored:
CGAffineTransformTranslate(transform, size.width, 0)
CGAffineTransformScale(transform, -1, 1)
break
case UIImageOrientation.LeftMirrored, UIImageOrientation.RightMirrored:
CGAffineTransformTranslate(transform, size.height, 0)
CGAffineTransformScale(transform, -1, 1)
case UIImageOrientation.Up, UIImageOrientation.Down, UIImageOrientation.Left, UIImageOrientation.Right:
break
}
let ctx: CGContextRef = CGBitmapContextCreate(nil, Int(size.width), Int(size.height), CGImageGetBitsPerComponent(CGImage), 0, CGImageGetColorSpace(CGImage), CGImageAlphaInfo.PremultipliedLast.rawValue)!
CGContextConcatCTM(ctx, transform)
switch imageOrientation {
case UIImageOrientation.Left, UIImageOrientation.LeftMirrored, UIImageOrientation.Right, UIImageOrientation.RightMirrored:
CGContextDrawImage(ctx, CGRectMake(0, 0, size.height, size.width), CGImage)
break
default:
CGContextDrawImage(ctx, CGRectMake(0, 0, size.width, size.height), CGImage)
break
}
let cgImage: CGImageRef = CGBitmapContextCreateImage(ctx)!
return UIImage(CGImage: cgImage)
}
}