How to detect if a video file was recorded in portrait orientation, or landscape in iOS
Asked Answered
R

9

33

I am using AlAssetsGroup enumerateAssetsAtIndexes to list the assets in the Photos (Camera) app. For a given video asset I want to determine whether it was shot in portrait or landscape mode.

In the following code, asset is an AlAsset and I have tested to see if it is a video asset [asset valueForProperty:ALAssetPropertyType] is AlAssetTypeVideo, then:

int orientation = [[asset valueForProperty:ALAssetPropertyOrientation] intValue];

In this case orientation is always 0 which is ALAssetOrientationUp. Maybe this is to be expected, all videos are up right, but a portrait video is represented in MPEG-4 as a landscape video turned 90 degrees (i.e. all videos are actually landscape, try the MediaInfo app on the mac if you don't believe me).

Where within the file and/or how do I access the information that tells me it was actually recorded while holding the phone in portrait orientation?

I have also tried this, given the url of the asset:

AVURLAsset *avAsset = [[AVURLAsset alloc] initWithURL:url options:nil];
CGSize size = [avAsset naturalSize];
NSLog(@"size.width = %f size.height = %f", size.width, size.height);
CGAffineTransform txf = [avAsset preferredTransform];
NSLog(@"txf.a = %f txf.b = %f  txf.c = %f  txf.d = %f  txf.tx = %f  txf.ty = %f",
            txf.a, txf.b, txf.c, txf.d, txf.tx, txf.ty);

Which always yields a width > height so for iPhone 4, width=1280 height=720 and the transform a and d values are 1.0, the others are 0.0, regardless of the capture orientation.

I have looked at the meta data using MediaInfo app on the Mac, I have done a Hexdump and so far have not found any difference between a landscape and portrait video. But QuickTime knows and displays portrait videos vertically, and the phone knows by rotating a portrait video if you are holding the phone in landscape orientation on playback and correctly displaying it if holding it in portrait.

BTW I can't use ffmpeg (can't live with the license restrictions). Is there an iPhone SDK native way to do this?

Ramshackle answered 7/1, 2011 at 16:46 Comment(0)
R
21

Somebody on apple dev forums suggested getting the transform of the video track, this does the job. You can see from the logs below that for these orientations the results make sense and our web developer is now able to rotate a variety of vids so they all match and composite them into one video.

AVAssetTrack* videoTrack = [[avAsset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0];
CGSize size = [videoTrack naturalSize];
NSLog(@"size.width = %f size.height = %f", size.width, size.height);
CGAffineTransform txf = [videoTrack preferredTransform];
NSLog(@"txf.a = %f txf.b = %f txf.c = %f txf.d = %f txf.tx = %f txf.ty = %f", txf.a, txf.b, txf.c, txf.d, txf.tx, txf.ty);

Logs using 4 iPhone 4 videos with the normal cam: (1) landscape cam on right side (home button on left) (2) landscape left (3) portrait upside-down (4) portrait up-right (home button at bottom)

  1. 2011-01-07 20:07:30.024 MySecretApp[1442:307] size.width = 1280.000000 size.height = 720.000000 2011-01-07 20:07:30.027 MySecretApp[1442:307] txf.a = -1.000000 txf.b = 0.000000 txf.c = 0.000000 txf.d = -1.000000 txf.tx = 1280.000000 txf.ty = 720.000000

  2. 2011-01-07 20:07:45.052 MySecretApp[1442:307] size.width = 1280.000000 size.height = 720.000000 2011-01-07 20:07:45.056 MySecretApp[1442:307] txf.a = 1.000000 txf.b = 0.000000 txf.c = 0.000000
    txf.d = 1.000000 txf.tx = 0.000000
    txf.ty = 0.000000

  3. 2011-01-07 20:07:53.763 MySecretApp[1442:307] size.width = 1280.000000 size.height = 720.000000 2011-01-07 20:07:53.766 MySecretApp[1442:307] txf.a = 0.000000 txf.b = -1.000000 txf.c = 1.000000
    txf.d = 0.000000 txf.tx = 0.000000 txf.ty = 1280.000000

  4. 2011-01-07 20:08:03.490 MySecretApp[1442:307] size.width = 1280.000000 size.height = 720.000000 2011-01-07 20:08:03.493 MySecretApp[1442:307] txf.a = 0.000000 txf.b = 1.000000 txf.c = -1.000000
    txf.d = 0.000000 txf.tx = 720.000000 txf.ty = 0.000000

Ramshackle answered 13/1, 2011 at 19:3 Comment(3)
Yes, naturalSize of AVAsset is deprecated. However, naturalSize of AVAssetTrack is not deprecated. Since the above code is using an AVAssetTrack it should be fine.Zwick
I get sigAbrt on AVAssetTrack after I initial AVURLAsset *avAsset = [[AVURLAsset alloc] initWithURL:disUrl options:nil]; in iOS 8.1 with no reason. Does anyone else face this issue ?Attalanta
For me this returns the same result for me no matter on which orientation the video started being recorded. What could be wrong?Intonate
Y
42

Based on the previous answer, you can use the following to determine the video orientation:

+ (UIInterfaceOrientation)orientationForTrack:(AVAsset *)asset
{
    AVAssetTrack *videoTrack = [[asset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0];
    CGSize size = [videoTrack naturalSize];
    CGAffineTransform txf = [videoTrack preferredTransform];

    if (size.width == txf.tx && size.height == txf.ty)
        return UIInterfaceOrientationLandscapeRight;
    else if (txf.tx == 0 && txf.ty == 0)
        return UIInterfaceOrientationLandscapeLeft;
    else if (txf.tx == 0 && txf.ty == size.width)
        return UIInterfaceOrientationPortraitUpsideDown;
    else
        return UIInterfaceOrientationPortrait;
}
Yeo answered 18/5, 2011 at 14:32 Comment(4)
This comment above returns the WRONG right/left UIInterfaceOrientation value for landscape. Perhaps George P was thinking of UIDeviceOrientationLandscape* or was responding based on an ambiguous bit of text " (1) landscape cam on right side (home button on left) (2) landscape left" from the first answer. The proper return is: if (size.width == txf.tx && size.height == txf.ty) return UIInterfaceOrientationLandscapeLeft; else if (txf.tx == 0 && txf.ty == 0) return UIInterfaceOrientationLandscapeRight;Sutter
As far as I can see the original poster (George P's) code is correct.Calcutta
Throwing my vote behind @Simon. The only time I don't transform a video is when its orientation is returned as UIInterfaceOrientationLandscapeRight by the original code, which is when the video is right side up, which is when it's taken with the home button on the right. And according to the docs: "UIInterfaceOrientationLandscapeRight The device is in landscape mode, with the device held upright and the home button on the right side."Abeyant
In my testing, it returns the correct right/left orientation if the video was taken with the back camera and the wrong orientation if the video was taken with the front camera. Tested on an iPhone 4S.Groundsheet
R
21

Somebody on apple dev forums suggested getting the transform of the video track, this does the job. You can see from the logs below that for these orientations the results make sense and our web developer is now able to rotate a variety of vids so they all match and composite them into one video.

AVAssetTrack* videoTrack = [[avAsset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0];
CGSize size = [videoTrack naturalSize];
NSLog(@"size.width = %f size.height = %f", size.width, size.height);
CGAffineTransform txf = [videoTrack preferredTransform];
NSLog(@"txf.a = %f txf.b = %f txf.c = %f txf.d = %f txf.tx = %f txf.ty = %f", txf.a, txf.b, txf.c, txf.d, txf.tx, txf.ty);

Logs using 4 iPhone 4 videos with the normal cam: (1) landscape cam on right side (home button on left) (2) landscape left (3) portrait upside-down (4) portrait up-right (home button at bottom)

  1. 2011-01-07 20:07:30.024 MySecretApp[1442:307] size.width = 1280.000000 size.height = 720.000000 2011-01-07 20:07:30.027 MySecretApp[1442:307] txf.a = -1.000000 txf.b = 0.000000 txf.c = 0.000000 txf.d = -1.000000 txf.tx = 1280.000000 txf.ty = 720.000000

  2. 2011-01-07 20:07:45.052 MySecretApp[1442:307] size.width = 1280.000000 size.height = 720.000000 2011-01-07 20:07:45.056 MySecretApp[1442:307] txf.a = 1.000000 txf.b = 0.000000 txf.c = 0.000000
    txf.d = 1.000000 txf.tx = 0.000000
    txf.ty = 0.000000

  3. 2011-01-07 20:07:53.763 MySecretApp[1442:307] size.width = 1280.000000 size.height = 720.000000 2011-01-07 20:07:53.766 MySecretApp[1442:307] txf.a = 0.000000 txf.b = -1.000000 txf.c = 1.000000
    txf.d = 0.000000 txf.tx = 0.000000 txf.ty = 1280.000000

  4. 2011-01-07 20:08:03.490 MySecretApp[1442:307] size.width = 1280.000000 size.height = 720.000000 2011-01-07 20:08:03.493 MySecretApp[1442:307] txf.a = 0.000000 txf.b = 1.000000 txf.c = -1.000000
    txf.d = 0.000000 txf.tx = 720.000000 txf.ty = 0.000000

Ramshackle answered 13/1, 2011 at 19:3 Comment(3)
Yes, naturalSize of AVAsset is deprecated. However, naturalSize of AVAssetTrack is not deprecated. Since the above code is using an AVAssetTrack it should be fine.Zwick
I get sigAbrt on AVAssetTrack after I initial AVURLAsset *avAsset = [[AVURLAsset alloc] initWithURL:disUrl options:nil]; in iOS 8.1 with no reason. Does anyone else face this issue ?Attalanta
For me this returns the same result for me no matter on which orientation the video started being recorded. What could be wrong?Intonate
C
17

In my use case I only needed to know if a video is in portrait or not (landscape).

guard let videoTrack = AVAsset(url: videoURL).tracks(withMediaType: AVMediaTypeVideo).first else {
    return ...
}

let transformedVideoSize = videoTrack.naturalSize.applying(videoTrack.preferredTransform)
let videoIsPortrait = abs(transformedVideoSize.width) < abs(transformedVideoSize.height)

This has been tested with both front and rear cameras for all orientation possibilities.

Czernowitz answered 2/3, 2017 at 16:1 Comment(2)
Finally, a solution that works with the front camera, too. The accepted answer and some other popular ones here return wrong values for videos captured with the front camera. Thank you!Goingover
and do we have any solution to fix it . Like the video with the actual height and width in portraitDeland
Z
10

AVAssetImageGenerator

If you are using AVAssetImageGenerator to generate images from AVAssets, you can simply set the .appliesPreferredTrackTransform property of AVAssetImageGenerator to true and it will return you images from your asset in the correct orientation! :)


Swift 3

But to extend on @onmyway133's answer in Swift 3:

import UIKit
import AVFoundation

extension AVAsset {


    var g_size: CGSize {
        return tracks(withMediaType: AVMediaTypeVideo).first?.naturalSize ?? .zero
    }


    var g_orientation: UIInterfaceOrientation {

        guard let transform = tracks(withMediaType: AVMediaTypeVideo).first?.preferredTransform else {
            return .portrait
        }

        switch (transform.tx, transform.ty) {
        case (0, 0):
            return .landscapeRight
        case (g_size.width, g_size.height):
            return .landscapeLeft
        case (0, g_size.width):
            return .portraitUpsideDown
        default:
            return .portrait
        }
    }
}
Zoroaster answered 20/9, 2016 at 14:36 Comment(0)
I
2

If you do not want to use AVFoundation Framework just to get the orientation of the recorded video then try it out

-(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo: (NSDictionary *)info {

   NSString *orientation;

   NSString *videoPath = [[info objectForKey:UIImagePickerControllerMediaURL] path];
   NSURL *myURL = [[NSURL alloc] initFileURLWithPath:videoPath];

   self.movieController = [[MPMoviePlayerController alloc] initWithContentURL:myURL];       
   UIImage *thumbImage = [movieController thumbnailImageAtTime:1.0 timeOption:MPMovieTimeOptionNearestKeyFrame];

   float width = thumbImage.size.width;
   float height = thumbImage.size.height;

   if (width > height){

        orientation  = @"Landscape";

      }else{

         orientation  = @"Portrait";
      }

 [self dismissViewControllerAnimated:YES completion:nil];

}
Improvement answered 14/2, 2013 at 13:23 Comment(1)
Allocating a MoviePlayer to get the video orientation is bad. It would be easier to apply the CGAffineTransform from the AVAsset on the naturalSize of the video track and then compare width > height (with fabs!).Harding
G
2

While several of the answers here are correct, they are not comprehensive. For instance, you may also need to know which camera device was used to apply the proper transforms. I created a gist to do this very thing; extract the UIInterfaceOrientation and the AVCaptureDevicePosition.

Extract AVAsset Orientation and Camera Position

Gag answered 29/10, 2015 at 19:14 Comment(0)
T
1
- (UIImageOrientation)getImageOrientationWithVideoOrientation:(UIInterfaceOrientation)videoOrientation {

    UIImageOrientation imageOrientation;
    switch (videoOrientation) {
    case UIInterfaceOrientationLandscapeLeft:
        imageOrientation = UIImageOrientationUp;
        break;
    case UIInterfaceOrientationLandscapeRight:
        imageOrientation = UIImageOrientationDown;
        break;
    case UIInterfaceOrientationPortrait:
        imageOrientation = UIImageOrientationRight;
        break;
    case UIInterfaceOrientationPortraitUpsideDown:
        imageOrientation = UIImageOrientationLeft;
        break;
    }
    return imageOrientation;
}
Tawanda answered 13/11, 2013 at 13:42 Comment(0)
C
0

Extending on George's answer in Swift 2

UIInterfaceOrientation is the same with UIImageOrientation

extension AVAsset {

  var g_size: CGSize {
    return tracksWithMediaType(AVMediaTypeVideo).first?.naturalSize ?? .zero
  }

  var g_orientation: UIInterfaceOrientation {
    guard let transform = tracksWithMediaType(AVMediaTypeVideo).first?.preferredTransform else {
      return .Portrait
    }

    switch (transform.tx, transform.ty) {
    case (0, 0):
      return .LandscapeRight
    case (g_size.width, g_size.height):
      return .LandscapeLeft
    case (0, g_size.width):
      return .PortraitUpsideDown
    default:
      return .Portrait
    }
  }
}
Cyanite answered 8/9, 2016 at 13:31 Comment(0)
R
0

Try this in swift 5

func checkVideoOrientation(url: URL) {
    let asset = AVAsset(url: url)
    let track = asset.tracks(withMediaType: .video).first
    let size = track?.naturalSize ?? .zero
    let transform = track?.preferredTransform ?? .identity
    
    if size.width == transform.tx && size.height == transform.ty {
        self.postVideoOrientation = false //Portrait
    } else if size.width == transform.ty && size.height == transform.tx {
        self.postVideoOrientation = true //"Landscape"
    } else {
        self.postVideoOrientation = false //"Unknown"
    }
    
}
Ralston answered 26/2, 2023 at 10:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.