7

The issue that I'm running into is that when a user takes a photo with our app, using AVCaptureSession, I have no way of determining whether they took the photo in Portrait or Landscape mode. Our app only supports Portrait and I keep the Orientation Lock on when using my phone so I'm trying to build a solution assuming that others might do the same.

I looked into using [UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications] but when the Orientation Lock is on, no notifications are ever received. I know that this functionality is possible because the base Camera app and the camera in the Google Hangouts app can detect the rotation (animations on the Cancel and Flash buttons are apparent) when my phone has Orientation Lock on.

Is my best bet to use the accelerometer and detect the angle the phone is being rotated to? An old answer, Detect iPhone screen orientation, makes it very obvious that detecting the angle that way is easy to to do (obviously adapting the answer to use Core Motion instead of UIAccelerometer), but I'm curious if there is another way to do it.

Community
  • 1
  • 1
jer-k
  • 1,413
  • 2
  • 12
  • 23

2 Answers2

1

Yes you can do it by looking at the metadata for the image. Don't have time to write up a detailed answer (sorry about that), but I did it for my own project a while back through CMCopyDictionaryOfAttachments(NULL, buffer, kCMAttachmentMode_ShouldPropagate); where I passed in a CMSampleBufferRef for the buffer. I got that buffer from

captureStillImageAsynchronouslyFromConnection:stillImageConnection completionHandler: ^(CMSampleBufferRef imageDataSampleBuffer, NSError *error){},

but you can get it from

- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection as well.

You can find all the keys for that dictionary here.

Did a quick test with the default camera app with the orientation lock on, and I did get a different orientation for the two pictures. 6 for the portrait one, and 3 for the landscape one.

Again, would love to give you more details about this, but I'm sure you can figure it out by looking through the docs.

vegather
  • 470
  • 4
  • 14
  • 1
    Thanks for the heads up on CMCopyDictionaryOfAttachments, it makes reading the metadata so much easier. Using `captureStillImageAsynchronouslyFromConnection` i'm still only able to get orientation = 6. I enabled the Landscape modes on the app, but to no avail. I'm going to try it out in a brand to new app to ensure that there isn't a weird setting hidden somewhere that is causing this issue. – jer-k May 22 '14 at 17:47
  • 1
    Assuming you have `AVCaptureConnection *connection`, are you setting connection.videoOrientation anywhere? No matter what I try, the metadata returns 6 for the orientation which makes sense because the videoOrientation default is `AVCaptureVideoOrientationPortrait`. Which leads me all the way back to the original question of how do I detect the rotation to change the capture orientation. – jer-k May 22 '14 at 21:11
  • Yeah, I think I remember having some issues with that as well. Don't have time to dig deeper into it now, unfortunately . But it should be possible, because the default camera app does it, and I don't think they went through the trouble of using their own private API just for that. – vegather May 22 '14 at 21:16
  • I, too, am only able to see `orientation = 6` (portrait). I don't think this works. Here's how I solved my issue, using Core Motion: http://stackoverflow.com/a/40984953/2145198 – beebcon Dec 06 '16 at 16:36
1

You can use CoreMotion to detect the device orientation when users locks the orientation. Below is the code:

import CoreMotion
....
func initializeMotionManager() {
    motionManager = CMMotionManager()
    motionManager?.accelerometerUpdateInterval = 0.2
    motionManager?.gyroUpdateInterval = 0.2
 }

func startMotionManagerAccelertion() {
    motionManager?.startAccelerometerUpdates(to: (OperationQueue.current)!, withHandler: {
        (accelerometerData, error) -> Void in
        if error == nil {
            self.outputAccelertionData((accelerometerData?.acceleration)!)
        }
        else {
            print("\(error!)")
        }
    })
}

func stopMotionManagerAccelertion(){
    motionManager?.stopAccelerometerUpdates()
}

func outputAccelertionData(_ acceleration: CMAcceleration) {
   var orientationNew: AVCaptureVideoOrientation
   if acceleration.x >= 0.75 {
       orientationNew = .landscapeLeft
       print("landscapeLeft")
   } else if acceleration.x <= -0.75 {
       orientationNew = .landscapeRight
       print("landscapeRight")
   } else if acceleration.y <= -0.75 {
       orientationNew = .portrait
       print("portrait")
   } else if acceleration.y >= 0.75 {
       orientationNew = .portraitUpsideDown
       print("portraitUpsideDown")
   } else {
       // Consider same as last time
       return
   }

   if orientationNew == orientationLast { return }
   orientationLast = orientationNew
}
Nandish
  • 1,136
  • 9
  • 16