AVAssetTrack 的preferredTransform 有时似乎是错误的。如何解决这个问题?

问题描述 投票:0回答:3

我正在使用

AVAssetExportSession
在 iOS 应用程序中导出视频。为了以正确的方向渲染视频,我使用
AVAssetTrack
preferredTransform
。对于某些源视频,此属性似乎具有错误的值,并且视频在结果中显示为偏移或全黑。我该如何解决这个问题?

ios avfoundation avassetexportsession
3个回答
12
投票

preferredTransform
CGAffineTransform
。属性
a
b
c
d
是反射矩阵和旋转矩阵的串联,属性
tx
ty
描述平移。在我观察到的所有不正确的
preferredTransform
情况下,反射/旋转部分似乎是正确的,只有平移部分包含错误的值。一个可靠的修复似乎是检查
a
b
c
d
(总共八个案例,每个案例对应于
UIImageOrientation
中的一个案例)并相应地更新
tx
ty

extension AVAssetTrack {
  var fixedPreferredTransform: CGAffineTransform {
    var t = preferredTransform
    switch(t.a, t.b, t.c, t.d) {
    case (1, 0, 0, 1):
      t.tx = 0
      t.ty = 0
    case (1, 0, 0, -1):
      t.tx = 0
      t.ty = naturalSize.height
    case (-1, 0, 0, 1):
      t.tx = naturalSize.width
      t.ty = 0
    case (-1, 0, 0, -1):
      t.tx = naturalSize.width
      t.ty = naturalSize.height
    case (0, -1, 1, 0):
      t.tx = 0
      t.ty = naturalSize.width
    case (0, 1, -1, 0):
      t.tx = naturalSize.height
      t.ty = 0
    case (0, 1, 1, 0):
      t.tx = 0
      t.ty = 0
    case (0, -1, -1, 0):
      t.tx = naturalSize.height
      t.ty = naturalSize.width
    default:
      break
    }
    return t
  }
}

0
投票

我最终做了一些稍微更稳健的事情,我认为,我根据最终的结果取消了转换:

auto naturalFrame = CGRectMake(0, 0, naturalSize.width, naturalSize.height);
auto preferredFrame = CGRectApplyAffineTransform(naturalFrame, preferredTransform);
preferredTransform.tx -= preferredFrame.origin.x;
preferredTransform.ty -= preferredFrame.origin.y;

请注意,您不能只对

(0, 0)
应用变换,因为
CGRect.origin
会考虑翻转等因素。


0
投票

在 Theo 的出色回答的基础上,我将分享一些其他有用的“固定”变量,这些变量是我以与他的示例相同的风格创建的,我相信您将需要这些变量才能正确处理数据。

import AVFoundation
import UIKit

extension AVAssetTrack {


// This is the current orientation of the buffer which sits in positive X and Y space.  eg:  For a leftMirrored representation of a portrait 1080w by 1920h image, the buffer has had its X-axis reversed and is rotated to the left, but is still sitting in the top right quadrant in the space of X(0->1920) and Y(0->1080).  This variable is used to calculate the required fixedPreferredTransform and fixedNaturalSize required to handle the buffer appropriately.
var fixedOrientation: UIImage.Orientation {
    
    /*
    // Here is a list of debug transforms showing what each of these terms means.  Useful to check the logic below.
    let id = CGAffineTransform.identity
    let mirror = CGAffineTransform(scaleX: -1.0, y: 1.0)
    let up = id
    let left = id.concatenating(.init(rotationAngle: -1.0 * .pi / 2.0))
    let right = id.concatenating(.init(rotationAngle: 1.0 * .pi / 2.0))
    let down = id.concatenating(.init(rotationAngle: .pi))
    let upMirror = up.concatenating(mirror)
    let leftMirror = left.concatenating(mirror)
    let rightMirror = right.concatenating(mirror)
    let downMirror = down.concatenating(mirror)
     */
    
    switch (preferredTransform.a, preferredTransform.b, preferredTransform.c, preferredTransform.d) {
    case (0.0, 1.0, -1.0, 0.0): return .right
    case (0.0, -1.0, 1.0, 0.0): return .left
    case (1.0, 0.0, 0.0, 1.0): return .up
    case (-1.0, 0.0, 0.0, -1.0): return .down
    case (0.0, 1.0, 1.0, 0.0): return .rightMirrored
    case (0.0, -1.0, -1.0, 0.0): return .leftMirrored
    case (-1.0, 0.0, 0.0, 1.0): return .upMirrored
    case (1.0, 0.0, 0.0, -1.0): return .downMirrored
    default: return .up
    }
}


// When handling the buffer, after it has been unmirrored and set upright using the fixedPreferredTransform variable, it is important to note that the size of the upright buffer might be the transpose (swap X and Y) than the original unmodified buffer, so that subsequent code knows the proper size to deal with.
var fixedNaturalSize: CGSize {
    let naturalSize = naturalSize
    switch fixedOrientation {
    case .up: return naturalSize
    case .down: return naturalSize
    case .left: return .init(width: naturalSize.height, height: naturalSize.width)
    case .right: return .init(width: naturalSize.height, height: naturalSize.width)
    case .upMirrored: return naturalSize
    case .downMirrored: return naturalSize
    case .leftMirrored: return .init(width: naturalSize.height, height: naturalSize.width)
    case .rightMirrored: return .init(width: naturalSize.height, height: naturalSize.width)
    default: return naturalSize
    }
}

// This is the required transform to change the stored buffer (located as defined by the fixedOrientation variable) such that it is upright and un-mirrored, with origin at X,Y = 0,0 and extending into the positive X and Y space (ie: in the upper quadrant).
// Note that the built-in preferredTransform variable does not include shifting of the buffer into the top right quadrant, which is required for subsequent processing.
var fixedPreferredTransform: CGAffineTransform {
    var t = preferredTransform
    switch fixedOrientation {
    case .up: // .up
        t.tx = 0
        t.ty = 0
    case .downMirrored: // .downMirrored
        t.tx = 0
        t.ty = naturalSize.height
    case .upMirrored: // .upMirrored
        t.tx = naturalSize.width
        t.ty = 0
    case .down: // .down
        t.tx = naturalSize.width
        t.ty = naturalSize.height
    case .left: // .left
        t.tx = 0
        t.ty = naturalSize.width
    case .right: // .right
        t.tx = naturalSize.height
        t.ty = 0
    case .rightMirrored: // .rightMirrored
        t.tx = 0
        t.ty = 0
    case .leftMirrored:  // .leftMirrored
        t.tx = naturalSize.height
        t.ty = naturalSize.width
    default:
        break
    }
return t
}
}
© www.soinside.com 2019 - 2024. All rights reserved.