压缩视频错误:由于内存问题而终止

时间:2018-07-15 11:03:03

标签: ios swift avassetwriter avassetreader

我想先修剪从photoLibrary中选择的视频,然后压缩视频文件以自定义大小和比特率。我使用PryntTrimmerView修剪视频,然后使用修剪过的视频压缩视频文件。 我的代码用于修剪和压缩视频文件。 我成功导出修剪资产,然后成功获取压缩文件。当我从图库中选择短视频时没有问题,但是当压缩后选择大尺寸视频时,控制台中出现此错误: 来自调试器的消息:由于内存问题而终止 有我用于修剪和压缩视频文件的代码。

func prepareAssetComposition() throws {
    topActivity.isHidden = false
    topActivity.startAnimating()
    confirmButton.isUserInteractionEnabled = false
    //get asset and track
    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)
    }
    //set video oriention to portrati
    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
    }
    //start exporting video
    var name = ""
    var url: URL!
    if self.state == .Left {
      url = URL(fileURLWithPath: "\(NSTemporaryDirectory())TrimmedMovie1.mp4")
      name = "TrimmedMovie1.mp4"
    }else if state == .Right {
      url = URL(fileURLWithPath: "\(NSTemporaryDirectory())TrimmedMovie3.mp4")
      name = "TrimmedMovie3.mp4"
    }else if state == .Center {
      url = URL(fileURLWithPath: "\(NSTemporaryDirectory())TrimmedMovie2.mp4")
      name = "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)
          print(asset.duration)
          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 {
            self.playbackTimeCheckerTimer?.invalidate()
            self.playButton.setImage(#imageLiteral(resourceName: "play"), for: .normal)
            self.playbackTimeCheckerTimer = nil
            let size = CGSize(width: 1280, height: 720)

            if  let  videoData = NSData(contentsOf: url) {
              vData = videoData as Data
            }
            let directoryURL: URL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
            let folderPath: URL = directoryURL.appendingPathComponent(name, isDirectory: true)
            do {
              try vData?.write(to: folderPath, options: [])
            }
            catch {
              print(error.localizedDescription)
            }

            self.compress(fileName:name,videoPath: folderPath.path, exportVideoPath: folderPath.path, renderSize: size, completion: {res in
              if res {
                OperationQueue.main.addOperation {
                self.topActivity.isHidden = true
                self.topActivity.stopAnimating()
                self.confirmButton.isUserInteractionEnabled = true
                delegate.setVideoFromPath(path: folderPath.path, thump: thump, videoData: vData)
                self.dismiss(animated: true, completion: nil)
                  return
                }
              }else {
                print("can not compress")
              }
            })
          }
        } else {
          self.topActivity.isHidden = true
          self.topActivity.stopAnimating()
          self.confirmButton.isUserInteractionEnabled = true
          let error = exportSession?.error
          print("error exporting video \(String(describing: error))")
        }
      }
    })
  }
  private func existsFileAtUrl(url:String,name:String) -> Bool {

    let path = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as String
    let url = URL(fileURLWithPath: path)
    let filePath = url.appendingPathComponent(name).path
    let fileManager = FileManager.default
    if fileManager.fileExists(atPath: filePath) {
      return true
    } else {
      return false
    }
  }
  //MARK: Compress
  func compress(fileName:String,videoPath : String, exportVideoPath : String, renderSize : CGSize, completion : @escaping (Bool) -> ()) {

    let videoUrl = URL(fileURLWithPath: videoPath)
    if (!existsFileAtUrl(url: videoUrl.absoluteString,name:fileName)) {
      completion(false)
      return
    }

    let videoAssetUrl = AVURLAsset(url: videoUrl)
    let videoTrackArray = videoAssetUrl.tracks(withMediaType: AVMediaTypeVideo)

    if videoTrackArray.count < 1 {
      completion(false)
      return
    }
    let videoAssetTrack = videoTrackArray[0]
    let audioTrackArray = videoAssetUrl.tracks(withMediaType: AVMediaTypeAudio)

    if audioTrackArray.count < 1 {
      completion(false)
      return
    }
    let audioAssetTrack = audioTrackArray[0]
    let outputUrl = URL(fileURLWithPath: exportVideoPath)
    var videoWriter = try? AVAssetWriter(url: outputUrl, fileType: AVFileTypeQuickTimeMovie)
    videoWriter?.shouldOptimizeForNetworkUse = true

    let vSetting = videoSettings(size: renderSize)
    let videoWriterInput = AVAssetWriterInput(mediaType: AVMediaTypeVideo, outputSettings: vSetting)
    videoWriterInput.expectsMediaDataInRealTime = false
    videoWriterInput.transform = videoAssetTrack.preferredTransform
    videoWriter?.add(videoWriterInput)

    // output readers

    let videoReaderSettings : [String : Int] = [kCVPixelBufferPixelFormatTypeKey as String : Int(kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange)]
    let videoReaderOutput = AVAssetReaderTrackOutput(track: videoAssetTrack, outputSettings: videoReaderSettings)
    let videoReader = try! AVAssetReader(asset: videoAssetUrl)
    videoReader.add(videoReaderOutput)
    videoWriter?.startWriting()
    videoReader.startReading()
    videoWriter?.startSession(atSourceTime: kCMTimeZero)

    let processingVideoQueue = DispatchQueue(label: "processingVideoCompressionQueue")
    videoWriterInput.requestMediaDataWhenReady(on: processingVideoQueue, using: {
      while(videoWriterInput.isReadyForMoreMediaData){
        let sampleVideoBuffer = videoReaderOutput.copyNextSampleBuffer()
        if (videoReader.status == .reading && sampleVideoBuffer != nil) {
          videoWriterInput.append(sampleVideoBuffer!)
        }else {
          videoWriterInput.markAsFinished()
          if (videoReader.status == .completed) {
                    videoWriter?.finishWriting(completionHandler: {
                      videoWriter = nil
                      completion(true)
                    })
          }
        }
      }
    })
  }

  //MARK: Setting
  func videoSettings(size : CGSize) -> [String : AnyObject] {

    var compressionSettings = [String : AnyObject]()
    compressionSettings[AVVideoAverageBitRateKey] = 5 as AnyObject

    var settings = [String : AnyObject]()
    settings[AVVideoCompressionPropertiesKey] = compressionSettings as AnyObject
    settings[AVVideoCodecKey] = AVVideoCodecH264 as AnyObject?
    settings[AVVideoHeightKey] = size.height as AnyObject?
    settings[AVVideoWidthKey] = size.width as AnyObject?

    return settings
  }

1 个答案:

答案 0 :(得分:0)

我发现了问题,问题在于while语句。当我关闭视图控制器时,此语句反复调用,并且出现此错误。现在,当我想关闭视图控制器时,在带有break的循环中停止,一切正常。