【问题标题】:iOS swift memory issue during a while-loop inside a closure: AVAssetWriter闭包内的while循环期间的iOS swift内存问题:AVAssetWriter
【发布时间】:2016-09-21 13:04:58
【问题描述】:

我的应用程序用于从由代码生成的许多图像制作视频文件。

当我的代码完成制作图像并将其放入 myImage 时,它​​会将 isImageReady 切换为“true”。当 self.i 被 Property Observer 设置(或更改)时,它开始制作另一个图像。最后,当没有更多图像要生成时,self.iReset 设置为 'true'。

但应用程序在 while 循环期间由于内存问题而终止。我已经注释掉了实际组装视频帧的 if 语句。它仍然存在内存问题。所以我认为问题存在于 requestMediaDataWhenReadyOnQueue:usingBlock 闭包中的 while 循环中。

我不知道如何解决这个问题。请帮我。

    if videoWriter.startWriting() {
        videoWriter.startSessionAtSourceTime(kCMTimeZero)
        assert(pixelBufferAdaptor.pixelBufferPool != nil)

        let media_queue = dispatch_queue_create("mediaInputQueue", nil)
        videoWriterInput.requestMediaDataWhenReadyOnQueue(media_queue, usingBlock: { () -> Void in
            let fps: Int32 = 30
            let frameDuration = CMTimeMake(1, fps)
            var lastFrameTime:CMTime = CMTime()
            var presentationTime:CMTime = CMTime()

            while (self.iReset != true) {
                if videoWriterInput.readyForMoreMediaData && self.isImageReady {
                    lastFrameTime = CMTimeMake(Int64(self.i), fps)
                    presentationTime = self.i == 1 ? lastFrameTime : CMTimeAdd(lastFrameTime, frameDuration)

                    //commented out for tracking
                    /* if !self.appendPixelBufferForImage(self.myImage, pixelBufferAdaptor: pixelBufferAdaptor, presentationTime: presentationTime) {
                    error = NSError(
                    domain: kErrorDomain,
                    code: kFailedToAppendPixelBufferError,
                    userInfo: [
                    "description": "AVAssetWriterInputPixelBufferAdapter failed to append pixel buffer",
                    "rawError": videoWriter.error ?? "(none)"])
                    break
                    } */

                    self.isImageReady = false
                    self.i++
                }// if ..&&..
            } //while loop ends


            // Finish writing
            videoWriterInput.markAsFinished()
            videoWriter.finishWritingWithCompletionHandler { () -> Void in
                if error == nil {
                    print("Finished Making a Movie !!")
                    success(videoOutputURL)
                }
                self.videoWriter = nil
            }
        }) // requestMediaDataWhenReadyOnQueue ends
    }

【问题讨论】:

    标签: ios memory swift2 avassetwriter


    【解决方案1】:

    可能为时已晚,但我遇到了与此类似的问题(循环中的图像),这让我非常头疼。我的解决方案是在解决问题的循环中放置一个自动释放池。

    【讨论】:

      【解决方案2】:

      根据@C。 Carter 建议您可以使用autoreleasepool 来释放已使用的内存,如下所示:

      autoreleasepool {
        /* your code */ 
      }
      

      除此之外,这是我使用UIImagesAudio 制作电影的代码,效果很好。

      func build(chosenPhotos: [UIImage], audioURL: NSURL, completion: (NSURL) -> ()) {
              showLoadingIndicator(appDel.window!.rootViewController!.view)
              let outputSize = CGSizeMake(640, 480)
              var choosenPhotos = chosenPhotos
              let fileManager = NSFileManager.defaultManager()
              let urls = fileManager.URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)
              guard let documentDirectory: NSURL = urls.first else {
                  fatalError("documentDir Error")
              }
      
          let videoOutputURL = documentDirectory.URLByAppendingPathComponent("OutputVideo.mp4")
          print("Video Output URL \(videoOutputURL)")
          if NSFileManager.defaultManager().fileExistsAtPath(videoOutputURL.path!) {
              do {
                  try NSFileManager.defaultManager().removeItemAtPath(videoOutputURL.path!)
              } catch {
                  fatalError("Unable to delete file: \(error) : \(#function).")
              }
          }
      
          guard let videoWriter = try? AVAssetWriter(URL: videoOutputURL, fileType: AVFileTypeMPEG4) else {
              fatalError("AVAssetWriter error")
          }
      
          let outputSettings = [AVVideoCodecKey : AVVideoCodecH264, AVVideoWidthKey : NSNumber(float: Float(outputSize.width)), AVVideoHeightKey : NSNumber(float: Float(outputSize.height))]
      
          guard videoWriter.canApplyOutputSettings(outputSettings, forMediaType: AVMediaTypeVideo) else {
              fatalError("Negative : Can't apply the Output settings...")
          }
      
          let videoWriterInput = AVAssetWriterInput(mediaType: AVMediaTypeVideo, outputSettings: outputSettings)
          let sourcePixelBufferAttributesDictionary = [kCVPixelBufferPixelFormatTypeKey as String : NSNumber(unsignedInt: kCVPixelFormatType_32ARGB), kCVPixelBufferWidthKey as String: NSNumber(float: Float(outputSize.width)), kCVPixelBufferHeightKey as String: NSNumber(float: Float(outputSize.height))]
          let pixelBufferAdaptor = AVAssetWriterInputPixelBufferAdaptor(assetWriterInput: videoWriterInput, sourcePixelBufferAttributes: sourcePixelBufferAttributesDictionary)
      
          if videoWriter.canAddInput(videoWriterInput) {
              videoWriter.addInput(videoWriterInput)
          }
      
          if videoWriter.startWriting() {
              videoWriter.startSessionAtSourceTime(kCMTimeZero)
              assert(pixelBufferAdaptor.pixelBufferPool != nil)
      
              let media_queue = dispatch_queue_create("mediaInputQueue", nil)
      
              videoWriterInput.requestMediaDataWhenReadyOnQueue(media_queue, usingBlock: { () -> Void in
                  let fps: Int32 = 1
                  let frameDuration = CMTimeMake(1, fps)
      
                  var frameCount: Int64 = 0
                  var appendSucceeded = true
                  while (!choosenPhotos.isEmpty) {
                      if (videoWriterInput.readyForMoreMediaData) {
                          let nextPhoto = choosenPhotos.removeAtIndex(0)
                          let lastFrameTime = CMTimeMake(frameCount, fps)
                          let presentationTime = frameCount == 0 ? lastFrameTime : CMTimeAdd(lastFrameTime, frameDuration)
      
                          var pixelBuffer: CVPixelBuffer? = nil
                          let status: CVReturn = CVPixelBufferPoolCreatePixelBuffer(kCFAllocatorDefault, pixelBufferAdaptor.pixelBufferPool!, &pixelBuffer)
      
                          if let pixelBuffer = pixelBuffer where status == 0 {
                              let managedPixelBuffer = pixelBuffer
      
                              CVPixelBufferLockBaseAddress(managedPixelBuffer, 0)
      
                              let data = CVPixelBufferGetBaseAddress(managedPixelBuffer)
                              let rgbColorSpace = CGColorSpaceCreateDeviceRGB()
                              let context = CGBitmapContextCreate(data, Int(outputSize.width), Int(outputSize.height), 8, CVPixelBufferGetBytesPerRow(managedPixelBuffer), rgbColorSpace, CGImageAlphaInfo.PremultipliedFirst.rawValue)
      
                              CGContextClearRect(context, CGRectMake(0, 0, CGFloat(outputSize.width), CGFloat(outputSize.height)))
      
                              let horizontalRatio = CGFloat(outputSize.width) / nextPhoto.size.width
                              let verticalRatio = CGFloat(outputSize.height) / nextPhoto.size.height
                              //aspectRatio = max(horizontalRatio, verticalRatio) // ScaleAspectFill
                              let aspectRatio = min(horizontalRatio, verticalRatio) // ScaleAspectFit
      
                              let newSize:CGSize = CGSizeMake(nextPhoto.size.width * aspectRatio, nextPhoto.size.height * aspectRatio)
      
                              let x = newSize.width < outputSize.width ? (outputSize.width - newSize.width) / 2 : 0
                              let y = newSize.height < outputSize.height ? (outputSize.height - newSize.height) / 2 : 0
      
                              CGContextDrawImage(context, CGRectMake(x, y, newSize.width, newSize.height), nextPhoto.CGImage)
      
                              CVPixelBufferUnlockBaseAddress(managedPixelBuffer, 0)
      
                              appendSucceeded = pixelBufferAdaptor.appendPixelBuffer(pixelBuffer, withPresentationTime: presentationTime)
                          } else {
                              print("Failed to allocate pixel buffer")
                              appendSucceeded = false
                          }
                      }
                      if !appendSucceeded {
                          break
                      }
                      frameCount += 1
                  }
                  videoWriterInput.markAsFinished()
                  videoWriter.finishWritingWithCompletionHandler { () -> Void in
                      print("FINISHED!!!!!")
                      self.compileToMakeMovie(videoOutputURL, audioOutPutURL: audioURL, completion: { url in
                          completion(url)
                      })
                  }
              })
          }
      }
      
      func compileToMakeMovie(videoOutputURL: NSURL, audioOutPutURL: NSURL, completion: (NSURL) -> ()){
      
          let mixComposition = AVMutableComposition()
          let fileManager = NSFileManager.defaultManager()
          let urls = fileManager.URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)
          guard let documentDirectory: NSURL = urls.first else {
              fatalError("documentDir Error")
          }
      
          let actualVideoURl = documentDirectory.URLByAppendingPathComponent("OutputVideoMusic.mp4")
          print("Video Output URL \(actualVideoURl)")
          if NSFileManager.defaultManager().fileExistsAtPath(actualVideoURl.path!) {
              do {
                  try NSFileManager.defaultManager().removeItemAtPath(actualVideoURl.path!)
              } catch {
                  fatalError("Unable to delete file: \(error) : \(#function).")
              }
          }
      
          let nextClipStartTime = kCMTimeZero
          let videoAsset = AVURLAsset(URL: videoOutputURL)
          let video_timeRange = CMTimeRangeMake(kCMTimeZero,videoAsset.duration)
          let a_compositionVideoTrack = mixComposition.addMutableTrackWithMediaType(AVMediaTypeVideo, preferredTrackID: kCMPersistentTrackID_Invalid)
          try! a_compositionVideoTrack.insertTimeRange(video_timeRange, ofTrack: videoAsset.tracksWithMediaType(AVMediaTypeVideo)[0], atTime: nextClipStartTime)
      
          let audioAsset = AVURLAsset(URL: audioOutPutURL)
          let audio_timeRange = CMTimeRangeMake(kCMTimeZero,audioAsset.duration)
          let b_compositionAudioTrack = mixComposition.addMutableTrackWithMediaType(AVMediaTypeAudio, preferredTrackID: kCMPersistentTrackID_Invalid)
          do {
              try b_compositionAudioTrack.insertTimeRange(audio_timeRange, ofTrack: audioAsset.tracksWithMediaType(AVMediaTypeAudio)[0], atTime: nextClipStartTime)
          }catch _ {}
      
      
          let assetExport = AVAssetExportSession.init(asset: mixComposition, presetName: AVAssetExportPresetHighestQuality)
          assetExport?.outputFileType = "com.apple.quicktime-movie"
          assetExport?.outputURL = actualVideoURl
          assetExport?.exportAsynchronouslyWithCompletionHandler({
              completion(actualVideoURl)
          })
      }
      

      如果您仍然遇到此问题,请告诉我。

      【讨论】:

        猜你喜欢
        • 2011-06-19
        • 2016-03-14
        • 2020-09-17
        • 1970-01-01
        • 2011-08-09
        • 2014-11-08
        • 1970-01-01
        • 2014-03-22
        • 1970-01-01
        相关资源
        最近更新 更多