如何检测在iOS中视频文件是以纵向还是横向录制的?

33

我正在使用AlAssetsGroup enumerateAssetsAtIndexes列出照片(相机)应用程序中的资源。 对于给定的视频资源,我想确定它是以纵向还是横向模式拍摄的。

在以下代码中,asset是一个AlAsset,我已经测试过它是否为视频资源[asset valueForProperty:ALAssetPropertyType]AlAssetTypeVideo,然后:

int orientation = [[asset valueForProperty:ALAssetPropertyOrientation] intValue];
在这种情况下,orientation始终为0,即ALAssetOrientationUp。也许这是可以预见的,所有视频都是正立的,但纵向视频在MPEG-4中表示为横向视频旋转90度(即所有视频实际上都是横向的,如果不相信,请尝试在mac上使用MediaInfo应用程序)。我该在文件的哪个位置或者如何访问信息,以便告诉我它实际上是在竖直方向拍摄的? 我还尝试过这个,给出了资源的URL:
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);

对于iPhone 4而言,宽度始终大于高度,因此宽度为1280,高度为720,转换的“a”和“d”值为1.0,其他值为0.0,不考虑拍摄方向。

我使用Mac上的MediaInfo应用程序查看了元数据,也进行了Hexdump,到目前为止没有发现横向和纵向视频之间的任何区别。但是QuickTime知道并垂直显示竖向视频,并且如果在播放时将手机保持为横向方向,则通过旋转纵向视频来正确地显示它。

顺便说一句,我无法使用ffmpeg(无法接受许可限制)。是否有iPhone SDK本地方法可以实现这一点?

9个回答

42

根据之前的答案,您可以使用以下内容确定视频方向:

+ (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;
}

3
以上评论在横屏模式下返回了错误的右/左UIInterfaceOrientation值。也许George P是在考虑UIDeviceOrientationLandscape*,或者是基于第一个答案中含糊不清的文本“(1)横屏摄像头在右侧(Home按钮在左侧)(2)左横屏”。正确的返回值为:如果(size.width == txf.tx && size.height == txf.ty),则返回UIInterfaceOrientationLandscapeLeft; 否则,如果(txf.tx == 0 && txf.ty == 0),则返回UIInterfaceOrientationLandscapeRight。 - Tom Pace
据我所见,原帖作者(George P)的代码是正确的。 - Simon
支持@Simon。唯一不转换视频的时候是当原始代码返回UIInterfaceOrientationLandscapeRight方向时,这时视频是正常的,也就是Home键在右边拍摄的视频。根据文档:“UIInterfaceOrientationLandscapeRight设备处于横屏模式,设备直立并且Home键在右侧。” - kball
1
在我的测试中,如果视频是用后置摄像头拍摄的,则返回正确的右/左方向;如果视频是用前置摄像头拍摄的,则返回错误的方向。在 iPhone 4S 上进行了测试。 - jDutton

21

在苹果开发者论坛上,有人建议获取视频轨道的变换,这可以完成任务。从下面的日志中可以看出,对于这些方向,结果是有意义的,我们的网络开发人员现在能够旋转各种视频,使它们匹配并合成为一个视频。

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);

使用4个iPhone 4普通相机记录的日志: (1)设备横向,摄像头在右侧 (Home键在左侧) (2)设备横向,摄像头在左侧 (3)设备竖向倒置 (4)设备竖向正常,摄像头在右下方 (Home键在底部)

  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


2
是的,AVAsset的naturalSize已经被弃用了。然而,AVAssetTrack的naturalSize并没有被弃用。由于上述代码正在使用AVAssetTrack,所以应该没问题。 - Ray Fix
在iOS 8.1中,我在初始化AVURLAsset *avAsset = [[AVURLAsset alloc] initWithURL:disUrl options:nil];后,在AVAssetTrack上遇到了sigAbrt错误,但没有任何原因。还有其他人遇到过这个问题吗? - Deepak Thakur
2
无论视频开始录制时的方向如何,对我来说,这总是返回相同的结果。可能出了什么问题? - Tom

17

在我的使用场景中,我只需要知道视频是竖直还是水平的。

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)

此已使用前置和后置摄像头进行测试,验证了所有可能的方向。


终于有一个解决方案可以与前置摄像头一起使用了。这里的被接受的答案和其他一些流行的答案返回的数值对于使用前置摄像头拍摄的视频是错误的。谢谢! - Georgios
我们有没有任何解决方案来修复它?比如在纵向模式下具有实际高度和宽度的视频。 - Aiyub Munshi

10

AVAssetImageGenerator

如果您正在使用AVAssetImageGeneratorAVAssets生成图像,则可以将AVAssetImageGenerator.appliesPreferredTrackTransform属性设置为true,它将以正确的方向返回资产中的图像! :)


Swift 3

但是在Swift 3中,要扩展@onmyway133的答案:

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
        }
    }
}

2

虽然这里的一些答案是正确的,但它们并不全面。例如,您可能还需要知道使用了哪个相机设备来应用适当的转换。我创建了一个要点来完成这件事;提取UIInterfaceOrientation和AVCaptureDevicePosition。

提取AVAsset方向和相机位置


2
如果您不想使用AVFoundation框架来获取录制视频的方向,那么可以尝试这个方法。
-(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];

}

4
给MoviePlayer分配空间以获取视频方向是不好的。将AVAsset上的CGAffineTransform应用于视频轨道的naturalSize,然后比较宽度>高度(使用fabs!)会更容易些。 - Augunrik

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;
}

0

在 Swift 2 中扩展 George 的答案

UIInterfaceOrientationUIImageOrientation 相同

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
    }
  }
}

0

在 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"
    }
    
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接