首页 > 解决方案 > 电影创作后 AVAssetWriter 内存分配猛增

问题描述

我的应用程序中有一个例程,它通过 AVAssetWriter 将 MTLTextures 捕获到电影中。当我在设备上运行应用程序时,内存管理似乎保持稳定,电影被创建并写入磁盘。但是,当我在 App Store 上发布该应用程序(并下载它)时,无论电影大小如何,该应用程序都会在电影创建后不久崩溃。通过仪器运行应用程序显示在从 MTLTextures 提取 PixelBuffer 的例程中发生了巨大的内存分配。这就是崩溃发生的地方。有问题的例程在我要记录的每个 MTLTexture 的循环内运行:

func AVAssetWriterEncodeFrame(forTexture texture: MTLTexture) {

    while !assetWriterVideoInput!.isReadyForMoreMediaData  {
    } // hang out here until isReadyForMoreMediaData == true

    autoreleasepool(invoking: { () -> () in

      let fps: Int32 = Int32(Constants.movieFPS)

      guard let pixelBufferPool = assetWriterPixelBufferInput!.pixelBufferPool else {
        print("[MovieMakerVC]: Pixel buffer asset writer input did not have a pixel buffer pool available; cannot retrieve frame")
        return
      }

      var maybePixelBuffer: CVPixelBuffer? = nil
      let status  = CVPixelBufferPoolCreatePixelBuffer(nil, pixelBufferPool, &maybePixelBuffer)
      if status != kCVReturnSuccess {
        print("[MovieMakerVC]: Could not get pixel buffer from asset writer input; dropping frame...")
        return
      }
      guard let pixelBuffer = maybePixelBuffer else { return }
      CVPixelBufferLockBaseAddress(pixelBuffer, [])
      let pixelBufferBytes = CVPixelBufferGetBaseAddress(pixelBuffer)!

      // Use the bytes per row value from the pixel buffer since its stride may be rounded up to be 16-byte aligned
      let bytesPerRow = CVPixelBufferGetBytesPerRow(pixelBuffer)
      let region = MTLRegionMake2D(0, 0, texture.width, texture.height)

      texture.getBytes(pixelBufferBytes, bytesPerRow: bytesPerRow, from: region, mipmapLevel: 0)

      let presentationTime = CMTimeMake(value: Int64(frameIndexForPresentationTime), timescale: fps)

      assetWriterPixelBufferInput!.append(pixelBuffer, withPresentationTime: presentationTime)

      CVPixelBufferUnlockBaseAddress(pixelBuffer, [])

      frameIndexForPresentationTime += frameHoldLength  // set up for next frame

    }) // end of autoreleasepool

  } // end of func AVAssetWriterEncodeFrame(forTexture texture: MTLTexture)

分配崩溃

分配清单

影片写入磁盘后,图中看到的峰值无法控制地增长。分配列表显示了数以千计的条目,如下所示:

@autoreleasepool 内容。... 4 KiB AVFoundation。-[AVAssetWriter 输入助手]。(如上图所示)

有人可以指出我做错了什么,并希望如何解决它?

标签: swiftinstrumentsallocationmetalavassetwriter

解决方案


The culprit turned out to be this bit of code:

while !assetWriterVideoInput!.isReadyForMoreMediaData { } // hang out here until isReadyForMoreMediaData == true

It turns out that testing for falsehood of this condition in the AVAssetWriterInputPixelBufferAdaptor object is a really BAD idea.

Instead, wrapping the entirety of that logic bloc as follows is the way to go:

var frameIsCaptured: Bool = false

while (assetWriterVideoInput!.isReadyForMoreMediaData && frameIsCaptured == false) {     
    ... // insert all the texture processing logic
    texture.getBytes(pixelBufferBytes, bytesPerRow: bytesPerRow, from: region, mipmapLevel: 0)
    ... // insert pixelBuffer ingestion logic
    frameIsCaptured = true. // needed so we only process this texture once
}

What I was doing before achieved the same thing in the sense that the routine would loop endlessly until isReadyForMoreMediaData became true (at which point I would to all the texture work needed to append the pixelBuffer to the movie-in-progress), but the ridiculous overhead created by that empty loop is not something that seemed to be a design flaw to address. Of course, Instruments pointed to there being an issue in this loop, but it took me a while to see the light.


推荐阅读