Search code examples
iosswiftvideo-processingavassetwriter

How to limit AVAssetWriter from using too much memory when encoding a video?


I use AVAssetWriter to generate a video with frame images (UIImage array). When it is doing the encoding work, memory usage sometimes spikes to more than 700MB. On my iPhone (with 3GB MEM), this doesn't cause OOM issue, but I am afraid it would cause trouble in older iPhones.

enter image description here

Can I somehow limit AVAssetWriter memory usage? I guess internally it may use some queue to cache the frame bytes, if I can specify the size of the cache, then I can set an upper limit of memory. But I didn't find such options/settings in the documentation.

Here is my code:

    let settings:[String:Any] = [
        AVVideoCodecKey: AVVideoCodecH264,
        AVVideoWidthKey: FRAME_SIZE,
        AVVideoHeightKey: FRAME_SIZE
    ]
    let assetWriter = AVAssetWriter(url: self.url!, fileType: AVFileType.mov)        
    let assetWriterInput = AVAssetWriterInput(mediaType: AVMediaType.video, outputSettings: settings)
    self.assetWriter?.add(assetWriterInput)

    let bufferAttributes:[String: Any] = [kCVPixelBufferPixelFormatTypeKey as String: Int(kCVPixelFormatType_32ARGB)]
    let bufferAdapter = AVAssetWriterInputPixelBufferAdaptor(assetWriterInput: assetWriterInput, sourcePixelBufferAttributes: bufferAttributes)
    let frameTime = CMTimeMake(1, 30)

    self.assetWriter?.startWriting()
    self.assetWriter?.startSession(atSourceTime: kCMTimeZero)

    let mediaInputQueue = DispatchQueue(label: "mediaInputQueue")

    var i = 0

    assetWriterInput.requestMediaDataWhenReady(on: mediaInputQueue){
        var localframes = frames
        while(true){
            if(localframes.isEmpty){
                break
            }
            if (assetWriterInput.isReadyForMoreMediaData){
                let image = localframes.removeFirst()
                let pixelBuffer = self.newPixelBufferFrom(cgImage: image.cgImage!)

                var time:CMTime
                if i == 0 {
                    time = kCMTimeZero
                } else {
                    time = CMTimeMake(Int64(i), frameTime.timescale)
                }

                bufferAdapter.append(pixelBuffer!, withPresentationTime: time)

                // TODO: how to release memory?
                i += 1
            }
        }
        assetWriterInput.markAsFinished()
        self.assetWriter?.finishWriting(completionHandler: {
                finishedHandler()
        })
    }

Solution

  • Not sure if it will help, but I had a similar problem inside a while loop. You can check this answer: Is the ios iPhone simulator causing memory usage analysis to swell?

    You can give it a try, and see if it improves:

    let pixelBuffer = autoreleasepool { return self.newPixelBufferFrom(cgImage: image.cgImage!) }