ios - AVMutableComposition 旋转视频文件

标签 ios swift avassetexportsession avmutablecomposition trim

我正在使用 PryntTrimmerView 来修剪视频文件。有我的导出修剪视频文件和生成视频缩略图的代码:

func prepareAssetComposition() throws {

    guard let asset = trimmerView.asset, let videoTrack = asset.tracks(withMediaType: AVMediaTypeVideo).last else {
      return
    }
    let size = videoTrack.naturalSize.applying(videoTrack.preferredTransform)
    print(CGSize(width: fabs(size.width), height: fabs(size.height)))

    let assetComposition = AVMutableComposition()
    let start = trimmerView.startTime?.seconds
    let end = trimmerView.endTime?.seconds
    let startTime = CMTime(seconds: Double(start ?? 0), preferredTimescale: 1000)
    let endTime = CMTime(seconds: Double(end ?? 0), preferredTimescale: 1000)
    let trackTimeRange = CMTimeRange(start: startTime, end: endTime)


    let compositionTrack = assetComposition.addMutableTrack(withMediaType: AVMediaTypeVideo, preferredTrackID: kCMPersistentTrackID_Invalid)
    try compositionTrack.insertTimeRange(trackTimeRange, of: videoTrack, at: kCMTimeZero)

    var url: URL!
    if self.state == .Left {
     url = URL(fileURLWithPath: "\(NSTemporaryDirectory())TrimmedMovie1.mp4")
    }else if state == .Right {
      url = URL(fileURLWithPath: "\(NSTemporaryDirectory())TrimmedMovie3.mp4")

    }else if state == .Center {
      url = URL(fileURLWithPath: "\(NSTemporaryDirectory())TrimmedMovie2.mp4")
    }

    try? FileManager.default.removeItem(at: url)

    let exportSession = AVAssetExportSession(asset: assetComposition, presetName: AVAssetExportPresetHighestQuality)
    if UIDevice.current.userInterfaceIdiom == .phone {
    exportSession?.outputFileType = AVFileTypeQuickTimeMovie
    }else {
      exportSession?.outputFileType = AVFileTypeQuickTimeMovie
    }
    exportSession?.shouldOptimizeForNetworkUse = true
    exportSession?.outputURL = url
    exportSession?.exportAsynchronously(completionHandler: {

      DispatchQueue.main.async {

        if let url = exportSession?.outputURL, exportSession?.status == .completed {
          var thump: UIImage?
          var vData: Data?
          if let asset = self.trimmerView.asset {
          if let img = asset.videoThumbnail {
            thump = img
          }
          }
          if  let  videoData = NSData(contentsOf: url) {
            vData = videoData as Data
          }
          if let delegate = self.delegate {
            delegate.setVideoFromPath(path: url.path, thump: thump, videoData: vData)
            self.dismiss(animated: true, completion: nil)
          }
        } else {
          let error = exportSession?.error
          print("error exporting video \(String(describing: error))")
        }
      }
    })
  }

extension AVAsset{
  var videoThumbnail:UIImage?{
    let assetImageGenerator = AVAssetImageGenerator(asset: self)
    var time = self.duration
    time.value = min(time.value, 2)
    do {
      let imageRef = try assetImageGenerator.copyCGImage(at: time, actualTime: nil)
      let thumbNail = UIImage.init(cgImage: imageRef)
      print("Video Thumbnail genertated successfuly")
      return thumbNail

    } catch {
      print("error getting thumbnail video")
      return nil
    }

  }
}

有时导出的视频和缩略图会旋转为横向旋转,这种情况最有可能发生在用移动相机录制视频时。 如何解决这个问题。并强制 Assets 方向为肖像? 感谢所有回复。

最佳答案

我找到了解决方案。有我的 swift 3.2 代码

 func prepareAssetComposition() throws {


    guard let asset = trimmerView.asset, let videoTrack = asset.tracks(withMediaType: AVMediaTypeVideo).first else {
      return
    }

    let assetComposition = AVMutableComposition()
    let start = trimmerView.startTime?.seconds
    let end = trimmerView.endTime?.seconds
    let startTime = CMTime(seconds: Double(start ?? 0), preferredTimescale: 1000)
    let endTime = CMTime(seconds: Double(end ?? 0), preferredTimescale: 1000)
    let trackTimeRange = CMTimeRange(start: startTime, end: endTime)

    let videoCompositionTrack = assetComposition.addMutableTrack(withMediaType: AVMediaTypeVideo, preferredTrackID: kCMPersistentTrackID_Invalid)

    try videoCompositionTrack.insertTimeRange(trackTimeRange, of: videoTrack, at: kCMTimeZero)

    if let audioTrack = asset.tracks(withMediaType: AVMediaTypeAudio).first {
      let audioCompositionTrack = assetComposition.addMutableTrack(withMediaType: AVMediaTypeAudio, preferredTrackID: kCMPersistentTrackID_Invalid)

      try audioCompositionTrack.insertTimeRange(trackTimeRange, of: audioTrack, at: kCMTimeZero)
    }

    let size = videoTrack.naturalSize
    let txf = videoTrack.preferredTransform

    var recordType = ""
    if (size.width == txf.tx && size.height == txf.ty){
     recordType = "UIInterfaceOrientationLandscapeRight"
    }else if (txf.tx == 0 && txf.ty == 0){
     recordType = "UIInterfaceOrientationLandscapeLeft"
    }else if (txf.tx == 0 && txf.ty == size.width){
     recordType = "UIInterfaceOrientationPortraitUpsideDown"
    }else{
    recordType = "UIInterfaceOrientationPortrait"
  }

    if recordType == "UIInterfaceOrientationPortrait" {
      let t1: CGAffineTransform = CGAffineTransform(translationX: videoTrack.naturalSize.height, y: -(videoTrack.naturalSize.width - videoTrack.naturalSize.height)/2)
      let t2: CGAffineTransform = t1.rotated(by: CGFloat(Double.pi / 2))
      let finalTransform: CGAffineTransform = t2
      videoCompositionTrack.preferredTransform = finalTransform
    }else if recordType == "UIInterfaceOrientationLandscapeRight" {
      let t1: CGAffineTransform = CGAffineTransform(translationX: videoTrack.naturalSize.height, y: -(videoTrack.naturalSize.width - videoTrack.naturalSize.height)/2)
      let t2: CGAffineTransform = t1.rotated(by: -CGFloat(Double.pi))
      let finalTransform: CGAffineTransform = t2
      videoCompositionTrack.preferredTransform = finalTransform
    }else if recordType == "UIInterfaceOrientationPortraitUpsideDown" {
      let t1: CGAffineTransform = CGAffineTransform(translationX: videoTrack.naturalSize.height, y: -(videoTrack.naturalSize.width - videoTrack.naturalSize.height)/2)
      let t2: CGAffineTransform = t1.rotated(by: -CGFloat(Double.pi/2))
      let finalTransform: CGAffineTransform = t2
      videoCompositionTrack.preferredTransform = finalTransform
    }

    var url: URL!
    if self.state == .Left {
      url = URL(fileURLWithPath: "\(NSTemporaryDirectory())TrimmedMovie1.mp4")
    }else if state == .Right {
      url = URL(fileURLWithPath: "\(NSTemporaryDirectory())TrimmedMovie3.mp4")

    }else if state == .Center {
      url = URL(fileURLWithPath: "\(NSTemporaryDirectory())TrimmedMovie2.mp4")
    }

    try? FileManager.default.removeItem(at: url)

    let exportSession = AVAssetExportSession(asset: assetComposition, presetName: AVAssetExportPresetHighestQuality)
    if UIDevice.current.userInterfaceIdiom == .phone {
      exportSession?.outputFileType = AVFileTypeQuickTimeMovie
    }else {
      exportSession?.outputFileType = AVFileTypeQuickTimeMovie
    }
    exportSession?.shouldOptimizeForNetworkUse = true
    exportSession?.outputURL = url
    exportSession?.exportAsynchronously(completionHandler: {

      DispatchQueue.main.async {

        if let url = exportSession?.outputURL, exportSession?.status == .completed {
          let asset = AVAsset(url: url)
          var thump: UIImage?
          var vData: Data?
          if let img = asset.videoThumbnail {
            thump = img
            if recordType == "UIInterfaceOrientationPortrait" {
              if  thump != nil {
                let img = UIImage(cgImage: thump!.cgImage!, scale: CGFloat(1.0), orientation: .right)
                thump = img
                thump = thump?.fixedOrientation()
              }
            }else if recordType == "UIInterfaceOrientationLandscapeRight" {
              if  thump != nil {
                let img = UIImage(cgImage: thump!.cgImage!, scale: CGFloat(1.0), orientation: .down)
                thump = img
                thump = thump?.fixedOrientation()
              }

            }else if recordType == "UIInterfaceOrientationPortraitUpsideDown" {
              if  thump != nil {
                let img = UIImage(cgImage: thump!.cgImage!, scale: CGFloat(1.0), orientation: .left)
                thump = img
                thump = thump?.fixedOrientation()
              }
            }
          }
          if  let  videoData = NSData(contentsOf: url) {
            vData = videoData as Data
          }
          if let delegate = self.delegate {
            delegate.setVideoFromPath(path: url.path, thump: thump, videoData: vData)
            self.dismiss(animated: true, completion: nil)
          }
        } else {
          let error = exportSession?.error
          print("error exporting video \(String(describing: error))")
        }
      }
    })

  }

关于ios - AVMutableComposition 旋转视频文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51112858/

相关文章:

ios - 在 AVExportSession 中暂停视频

ios - MonoTouch.Dialog 与 UISplitViewController

ios - 如何从 UI 标签(Swift 2 IOS 9)在应用程序中发送电子邮件?

swift - 将 Swift 字符串编码为转义的 unicode?

ios - 如何在预览 View 层上设置相机大小

swift - 减小使用 AVAssetExportSession 导出的视频的大小 - iOS Swift

ios - 无法从我的项目中分配任何内容 - 静态库(使用 XCTests)

iphone - Imageview 不显示正确的 Iphone 5 Retina

ios - Core Data 拒绝从内存中清除外部数据引用

ios - 如何正确使用 AVAssetExportSession 为音频 Assets 设置元数据?