title: iOS swift memory issue during a Images to Video Convert Using AVAssetWriter
body: <p>I already do a lot of search for that and do many experiment but I didn't get any proper solution.</p>
  <p>i try to convert UIImages to Video. i have 250+ images array and i try to convert this images to video with 60FPS.</p>
  <p>i put render code in autoreleasepool method and add some other code also add autoreleasepool but didn't effect.</p>
  <p>Code.</p>
  <pre><code>import AVFoundation
  import UIKit
  import Photos
  import AVKit
  
  var tempurl = &quot;&quot;
  
  struct RenderSettings {
      
      var width: CGFloat = UIScreen.main.bounds.width * UIScreen.main.scale
      var height: CGFloat = UIScreen.main.bounds.width * UIScreen.main.scale
      var fps: Int32 = 60   //frames per second
      var avCodecKey = AVVideoCodecType.h264
      var videoFilename = &quot;ImageToVideo&quot;
      var videoFilenameExt = &quot;mp4&quot;
      
      var size: CGSize {
          return CGSize(width: width, height: height)
      }
      
      var outputURL: URL {
          
          let fileManager = FileManager.default
          if let tmpDirURL = try? fileManager.url(for: .cachesDirectory, in: .userDomainMask, appropriateFor: nil, create: true) {
              return tmpDirURL.appendingPathComponent(videoFilename).appendingPathExtension(videoFilenameExt) as URL
          }
          fatalError(&quot;URLForDirectory() failed&quot;)
          
      }
  }
  
  class VideoWriter {
      
      let renderSettings: RenderSettings
      
      var videoWriter: AVAssetWriter!
      var videoWriterInput: AVAssetWriterInput!
      var pixelBufferAdaptor: AVAssetWriterInputPixelBufferAdaptor!
      
      var isReadyForData: Bool {
          return videoWriterInput?.isReadyForMoreMediaData ?? false
      }
      
      class func pixelBufferFromImage(image: UIImage, pixelBufferPool: CVPixelBufferPool, size: CGSize) -&gt; CVPixelBuffer {
          
          autoreleasepool {
              
              var pixelBufferOut: CVPixelBuffer?
              
              let status = CVPixelBufferPoolCreatePixelBuffer(kCFAllocatorDefault, pixelBufferPool, &amp;pixelBufferOut)
              
              if status != kCVReturnSuccess {
                  fatalError(&quot;CVPixelBufferPoolCreatePixelBuffer() failed&quot;)
              }
              
              let pixelBuffer = pixelBufferOut!
              
              CVPixelBufferLockBaseAddress(pixelBuffer, [])
              
              let data = CVPixelBufferGetBaseAddress(pixelBuffer)
              let rgbColorSpace = CGColorSpaceCreateDeviceRGB()
              let context = CGContext(data: data, width: Int(size.width), height: Int(size.height),
                                      bitsPerComponent: 8, bytesPerRow: CVPixelBufferGetBytesPerRow(pixelBuffer), space: rgbColorSpace, bitmapInfo: CGImageAlphaInfo.premultipliedFirst.rawValue)
              
              context!.clear(CGRect(x: 0, y: 0, width: size.width, height: size.height))
              
              let horizontalRatio = size.width / image.size.width
              let verticalRatio = size.height / image.size.height
              
              let aspectRatio = min(horizontalRatio, verticalRatio) // ScaleAspectFit
              
              let newSize = CGSize(width: image.size.width * aspectRatio, height: image.size.height * aspectRatio)
              
              let x = newSize.width &lt; size.width ? (size.width - newSize.width) / 2 : 0
              let y = newSize.height &lt; size.height ? (size.height - newSize.height) / 2 : 0
              
              context!.concatenate(CGAffineTransform.identity)
              context!.draw(image.cgImage!, in: CGRect(x: x, y: y, width: newSize.width, height: newSize.height))
              
              CVPixelBufferUnlockBaseAddress(pixelBuffer, [])
              
              return pixelBuffer
          }
      }
      
      init(renderSettings: RenderSettings) {
          self.renderSettings = renderSettings
      }
      
      func start() {
          
          let avOutputSettings: [String: AnyObject] = [
              AVVideoCodecKey: renderSettings.avCodecKey as AnyObject,
              AVVideoWidthKey: NSNumber(value: Float(renderSettings.width)),
              AVVideoHeightKey: NSNumber(value: Float(renderSettings.height))
          ]
          
          func createPixelBufferAdaptor() {
              let sourcePixelBufferAttributesDictionary = [
                  kCVPixelBufferPixelFormatTypeKey as String: NSNumber(value: kCVPixelFormatType_32ARGB),
                  kCVPixelBufferWidthKey as String: NSNumber(value: Float(renderSettings.width)),
                  kCVPixelBufferHeightKey as String: NSNumber(value: Float(renderSettings.height))
              ]
              pixelBufferAdaptor = AVAssetWriterInputPixelBufferAdaptor(assetWriterInput: videoWriterInput,
                                                                        sourcePixelBufferAttributes: sourcePixelBufferAttributesDictionary)
          }
          
          func createAssetWriter(outputURL: URL) -&gt; AVAssetWriter {
              guard let assetWriter = try? AVAssetWriter(outputURL: outputURL, fileType: AVFileType.mp4) else {
                  fatalError(&quot;AVAssetWriter() failed&quot;)
              }
              
              guard assetWriter.canApply(outputSettings: avOutputSettings, forMediaType: AVMediaType.video) else {
                  fatalError(&quot;canApplyOutputSettings() failed&quot;)
              }
              
              return assetWriter
          }
          
          videoWriter = createAssetWriter(outputURL: renderSettings.outputURL)
          videoWriterInput = AVAssetWriterInput(mediaType: AVMediaType.video, outputSettings: avOutputSettings)
          
          if videoWriter.canAdd(videoWriterInput) {
              videoWriter.add(videoWriterInput)
          }
          else {
              fatalError(&quot;canAddInput() returned false&quot;)
          }
          
          
          createPixelBufferAdaptor()
          
          if videoWriter.startWriting() == false {
              fatalError(&quot;startWriting() failed&quot;)
          }
          
          videoWriter.startSession(atSourceTime: CMTime.zero)
          
          precondition(pixelBufferAdaptor.pixelBufferPool != nil, &quot;nil pixelBufferPool&quot;)
      }
      
      
      func render(appendPixelBuffers: @escaping (VideoWriter)-&gt;Bool, completion: @escaping ()-&gt;Void) {
  
          autoreleasepool {
              precondition(videoWriter != nil, &quot;Call start() to initialze the writer&quot;)
  
              let queue = DispatchQueue(label: &quot;mediaInputQueue&quot;)
              videoWriterInput.requestMediaDataWhenReady(on: queue) {
                  let isFinished = appendPixelBuffers(self)
                  if isFinished {
                      self.videoWriterInput.markAsFinished()
                      self.videoWriter.finishWriting() {
                          DispatchQueue.main.async {
                              completion()
                          }
                      }
                  }
              }
          }
          
      }
      
      func addImage(image: UIImage, withPresentationTime presentationTime: CMTime) -&gt; Bool {
          
          autoreleasepool {
              precondition(pixelBufferAdaptor != nil, &quot;Call start() to initialze the writer&quot;)
              
              let pixelBuffer = VideoWriter.pixelBufferFromImage(image: image, pixelBufferPool: pixelBufferAdaptor.pixelBufferPool!, size: renderSettings.size)
              return pixelBufferAdaptor.append(pixelBuffer, withPresentationTime: presentationTime)
          }
              
      }
      
  }
  
  class ImageAnimator {
      
      static let kTimescale: Int32 = 600
      
      let settings: RenderSettings
      let videoWriter: VideoWriter
      var images: [UIImage]!
      
      var frameNum = 0
      
      class func removeFileAtURL(fileURL: URL) {
          do {
              try FileManager.default.removeItem(atPath: fileURL.path)
          }
          catch _ as NSError {
              //
          }
      }
      
      init(renderSettings: RenderSettings,imagearr: [UIImage]) {
          settings = renderSettings
          videoWriter = VideoWriter(renderSettings: settings)
          images = imagearr
      }
      
      func render(completion: @escaping ()-&gt;Void) {
          
          // The VideoWriter will fail if a file exists at the URL, so clear it out first.
          ImageAnimator.removeFileAtURL(fileURL: settings.outputURL)
          
          videoWriter.start()
          videoWriter.render(appendPixelBuffers: appendPixelBuffers) {
              let s: String = self.settings.outputURL.path
              tempurl = s
              completion()
          }
          
      }
      
      func appendPixelBuffers(writer: VideoWriter) -&gt; Bool {
          
          let frameDuration = CMTimeMake(value: Int64(ImageAnimator.kTimescale / settings.fps), timescale: ImageAnimator.kTimescale)
          
          
          while !images.isEmpty {
              
              if writer.isReadyForData == false {
                  return false
              }
              
              let image = images.removeFirst()
              let presentationTime = CMTimeMultiply(frameDuration, multiplier: Int32(frameNum))
              let success = videoWriter.addImage(image: image, withPresentationTime: presentationTime)
              if success == false {
                  fatalError(&quot;addImage() failed&quot;)
              }
              
              frameNum=frameNum+1
          }
          
          return true
      }
      
  }
  </code></pre>
  <p>Memory Usage :</p>
  <p><a href="https://i.stack.imgur.com/3LQAy.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/3LQAy.png" alt="enter image description here" /></a></p>
  <p>Get Images Using This Code :</p>
  <pre><code>   @objc public class Recorder: NSObject {
      
      public var view : UIView?
      
      var displayLink : CADisplayLink?
      var referenceDate : NSDate?
      var imageArray = [UIImage]()
      
      public func start() {
          
          self.imageArray.removeAll()
          
          if (view == nil) {
              NSException(name: NSExceptionName(rawValue: &quot;No view set&quot;), reason: &quot;You must set a view before calling start.&quot;, userInfo: nil).raise()
          }else {
              displayLink = CADisplayLink(target: self, selector: #selector(self.handleDisplayLink(displayLink:)))
              displayLink!.add(to: RunLoop.main, forMode: RunLoop.Mode.common)
              referenceDate = NSDate()
          }
          
      }
      
      @objc func handleDisplayLink(displayLink : CADisplayLink) {
          if (view != nil) {
              createImageFromView(captureView: view!)
          }
      }
  
      
      func createImageFromView(captureView : UIView) {
          
          UIGraphicsBeginImageContextWithOptions(captureView.bounds.size, false, 0)
          captureView.drawHierarchy(in: captureView.bounds, afterScreenUpdates: false)
          
          let image = UIGraphicsGetImageFromCurrentImageContext();
          
          if let img = image {
              self.imageArray.append(img)
          }
  
          UIGraphicsEndImageContext();
      }
      
      public func stop(completion: @escaping (_ saveURL: String) -&gt; Void) {
          
          displayLink?.invalidate()
          
          let seconds = referenceDate?.timeIntervalSinceNow
          if (seconds != nil) {
              
              print(&quot;Image Count : \(self.imageArray.count)&quot;)            
              DispatchQueue.main.async {
                  let settings = RenderSettings()
                  let imageAnimator = ImageAnimator(renderSettings: settings,imagearr: self.imageArray)
                  imageAnimator.render() {
                      let u: String = tempurl
                      completion(u)
                      //self.saveVideoInPhotos()
                  }
              }
              
          }
          
      }    
      
  }
  </code></pre>
  <p>Thank In Advance</p>