ios - 视频大小调整不符合请求的大小

ios - video resizing not respecting requested size

我正在尝试在我的 ios 应用中调整视频的大小。我的代码如下:

fileprivate func resize(url: URL, height: CGFloat, completion: @escaping ((URL) -> Void)) {
  let video = AVAsset(url: url)
  guard let track = video.tracks(withMediaType: AVMediaTypeVideo).first
    else { return }
  let size = track.naturalSize

  // detect video rotation
  let txf = track.preferredTransform
  let videoAngle = atan2(txf.b, txf.a)
  let isRotated = videoAngle == CGFloat(M_PI_2) || videoAngle == CGFloat(M_PI_2 * 3)
  let videoW = isRotated ? size.height : size.width
  let videoH = isRotated ? size.width : size.height
  // get output width that keeps aspect ratio
  let width = height / videoH * videoW

  print("desired width \(width) height \(height)")
  print("original width \(size.width) height \(size.height)")

  // resize the video
  let videoComposition = AVMutableVideoComposition()
  videoComposition.renderSize = CGSize(width: width, height: height)
  videoComposition.frameDuration = CMTimeMake(1, 30)
  let instruction = AVMutableVideoCompositionInstruction()
  instruction.timeRange = CMTimeRangeMake(kCMTimeZero, video.duration)
  videoComposition.instructions = [instruction]
  let outputURL = Constants.cacheDirectory.appendingPathComponent(
    "\(String.random(ofLength: 10)).mp4")

  guard let exporter = AVAssetExportSession(
    asset: video,
    presetName: AVAssetExportPresetMediumQuality
  ) else { return }
  exporter.videoComposition = videoComposition
  exporter.outputURL = outputURL
  exporter.outputFileType = AVFileTypeQuickTimeMovie

  exporter.exportAsynchronously(completionHandler: { () -> Void in
    DispatchQueue.main.async {
      let asset = AVAsset(url: outputURL)
      let track = asset.tracks(withMediaType: AVMediaTypeVideo).first!
      print("result size \(track.naturalSize)")
      completion(outputURL)
    }
  })
}

当我在 1280.0 x 720.0 且旋转 90 度的视频上将 resize 调用到 720 的高度时,我得到的结果是 320.0 x 568.0 并且我的日志是:

desired width 405.0 height 720.0
original width 1280.0 height 720.0
result size (320.0, 568.0)

我什至找不到这些数字来自何处的关系。唯一的问题是宽高比保持不变。

我最终采取了另一种方式,因为视频调整大小似乎比 ios 上应该复杂得多。 SDAVAssetExportSession makes a great job at handling all that, and if you look at the code还有很多事要做。

无论如何,我的最终代码如下:

import SDAVAssetExportSession

fileprivate func resize(url: URL, height: CGFloat, completion: @escaping ((Error?) -> Void)) {
  let video = AVAsset(url: url)

  guard let track = video.tracks(withMediaType: AVMediaTypeVideo).first
    else { return }
  let size = track.naturalSize

  let txf = track.preferredTransform
  let videoAngle = atan2(txf.b, txf.a)
  let isRotated = videoAngle == CGFloat(M_PI_2) || videoAngle == CGFloat(M_PI_2 * 3)
  let videoW = isRotated ? size.height : size.width
  let videoH = isRotated ? size.width : size.height
  let width = height / videoH * videoW

  let outputURL = Constants.cacheDirectory.appendingPathComponent(
    "\(String.random(ofLength: 10)).mp4")

  if let encoder = SDAVAssetExportSession(asset: AVAsset(url: url)) {
    encoder.outputFileType = AVFileTypeMPEG4
    encoder.outputURL = outputURL
    encoder.videoSettings = [
      AVVideoCodecKey: AVVideoCodecH264,
      AVVideoWidthKey: width,
      AVVideoHeightKey: height,
      AVVideoCompressionPropertiesKey: [
        AVVideoAverageBitRateKey: 1000000,
        AVVideoProfileLevelKey: AVVideoProfileLevelH264High40,
        AVVideoMaxKeyFrameIntervalKey: 60,
      ],
    ]

    encoder.exportAsynchronously(completionHandler: {
      if encoder.status == AVAssetExportSessionStatus.completed {
        DispatchQueue.main.async {
          self.url = outputURL
          let asset = AVAsset(url: outputURL)
          let track = asset.tracks(withMediaType: AVMediaTypeVideo).first!
          completion(nil)
        }
      } else if encoder.status == AVAssetExportSessionStatus.cancelled {
        completion(nil)
      } else {
        completion(encoder.error)
      }
    })
  }
}