AVCaptureDevice.RotationCoordinator.videoRotationAngleForHorizonLevelCapture: behavior is different with iPhone 17

The front facing camera on iPhone 16 (and every model previous) gives the following values for AVCaptureDevice.RotationCoordinator.videoRotationAngleForHorizonLevelCapture:

  • 90 degrees portrait
  • 180 degrees landscape left
  • 270 degrees for upside-down
  • 0 degrees for landscape right

Using these values a transform is calculated:

var transform: CGAffineTransform { 
    let degrees = rotationCoordinator.videoRotationAngleForHorizonLevelCapture
    let radians = degrees * .pi / 180.0
    return CGAffineTransform(rotationAngle: radians)
}

And then applied to the AVAssetInput:

videoInput = AVAssetWriterInput(mediaType: .video,
outputSettings: videoSettings,
sourceFormatHint: videoFormatDescription)

videoInput.transform = transform 

And this ensures the correct transform is added to the metadata so that the recorded video plays in the correct orientation.

However, with the iPhone 17 Pro and iPhone 17 Pro Max front facing cameras, AVCaptureDevice.RotationCoordinator.videoRotationAngleForHorizonLevelCapture return the different values:

  • 0 degrees portrait
  • 90 degrees landscape left
  • 180 degrees for upside-down
  • 270 degrees for landscape right

So this approach breaks down, and the video orientation is incorrect. How is this intended to be handled?

Answered by Media Engineer in 873598022

Hi there shimanopower, The difference in rotation angle is expected on iPhone 17 due to a different sensor rotation. Looks like your 2nd line in the first code block is not taking into consideration any video rotation already being applied in the AVCaptureConnection to your output.

Here's some sample code:

let videoRotationAngle = self.videoRotationAngle
let videoConnectionVideoRotationAngle = self.videoOutputConnection!.videoRotationAngle

// Subtract how much we should apply (videoRotationAngle) by how much is already applied (videoConnectionVideoRotationAngle)
let rotationAngleToApply = videoRotationAngle - videoConnectionVideoRotationAngle

var videoRotationTransform = CGAffineTransform(rotationAngle: rotationAngleToApply * .pi / 180.0) // Convert from degrees to radians

To achieve best performance, set your video rotation angle on your video data output's connection to 0 — which will result in unrotated video from the source. But be aware that the source might come from a different sensor rotation with respect to the phone.

Accepted Answer

Hi there shimanopower, The difference in rotation angle is expected on iPhone 17 due to a different sensor rotation. Looks like your 2nd line in the first code block is not taking into consideration any video rotation already being applied in the AVCaptureConnection to your output.

Here's some sample code:

let videoRotationAngle = self.videoRotationAngle
let videoConnectionVideoRotationAngle = self.videoOutputConnection!.videoRotationAngle

// Subtract how much we should apply (videoRotationAngle) by how much is already applied (videoConnectionVideoRotationAngle)
let rotationAngleToApply = videoRotationAngle - videoConnectionVideoRotationAngle

var videoRotationTransform = CGAffineTransform(rotationAngle: rotationAngleToApply * .pi / 180.0) // Convert from degrees to radians

To achieve best performance, set your video rotation angle on your video data output's connection to 0 — which will result in unrotated video from the source. But be aware that the source might come from a different sensor rotation with respect to the phone.

Hi, that fixed it - thanks for the explanation and the sample code! I appreciate it

AVCaptureDevice.RotationCoordinator.videoRotationAngleForHorizonLevelCapture: behavior is different with iPhone 17
 
 
Q