减少 AVAssetWriter 使用 CVPixelBufferPool 的注意事项

问题描述

正如标题所说,我在使用 AVAssetWriter 和内存时遇到了一些问题。

关于我的环境/要求的一些说明:

  • 我没有使用 ARC,但如果有一种方法可以简单地使用它并使其全部工作,我完全支持它。不过,我的尝试并没有产生任何影响。我将在其中使用它的环境需要尽快最小化/释放内存。
  • Objective-C 是一项要求
  • 内存使用量必须尽可能低,在我的设备 (iPhone X) 上测试时,它现在占用的 300 mb 不稳定

代码

这是截取下面截图时使用的代码https://gist.github.com/jontelang/8f01b895321d761cbb8cda9d7a5be3bd

内存中保留的问题/项目

在整个处理过程中似乎占用大量内存的大部分东西似乎都是在开始时分配的。

enter image description here

所以在这一点上,我认为问题不在于我的代码。我个人控制的代码似乎不是问题,即加载图像、创建缓冲区、释放它们似乎都不是内存有问题的地方。例如,如果我在上面的大部分时间之后在 Instruments 中标记,则内存是稳定的,没有任何内存被保留。

enter image description here

持续 5mb 的唯一原因是它在标记期结束后被释放。

现在怎么办?

我实际上开始写这个问题的重点是我的代码是否正确地发布了东西,但现在看来这很好。那么我现在有哪些选择?

  • 是否可以在当前代码配置一些东西来减少内存需求?
  • 我的编写器/输入设置有问题吗?
  • 我是否需要使用完全不同的视频制作方式才能完成这项工作?

使用 CVPixelBufferPool 的注意事项

在 CVPixelBufferCreate Apple 的文档中说:

如果您需要创建和释放多个像素缓冲区,则应改为使用像素缓冲池(请参阅 CVPixelBufferPool)以有效重用像素缓冲内存。

我也试过这个,但我发现内存使用没有变化。更改池的属性似乎也没有任何影响,因此我实际上没有 100% 正确使用它的可能性很小,尽管与在线代码相比似乎我是,至少。并且输出文件有效。

代码在这里https://gist.github.com/jontelang/41a702d831afd9f9ceeb0f9f5365de03

这是一个略有不同的版本,我以略有不同的方式设置池https://gist.github.com/jontelang/c0351337bd496a6c7e0c94293adf881f

更新 1

所以我更深入地研究了一个跟踪,以确定大部分分配的时间/地点。这是一张带注释的图片

enter image description here

要点是:

  1. 空间不是“使用”AVAssetWriter 分配的
  2. 在处理开始后 500ms 内分配直到结束的 500mb
  3. 好像是在 AVAssetWriter 内部完成的

我在此处上传了 .trace 文件https://www.dropbox.com/sh/f3tf0gw8gamu924/AAACrAbleYzbyeoCbC9FQLR6a?dl=0

解决方法

  1. 在创建 Dispatch Queue 时,请确保使用 Autorlease Pool 创建一个队列。将 DISPATCH_QUEUE_SERIAL 替换为 DISPATCH_QUEUE_SERIAL_WITH_AUTORELEASE_POOL

  2. for 循环的每次迭代也包装到自动释放池中

像这样:

[assetWriterInput requestMediaDataWhenReadyOnQueue:recordingQueue usingBlock:^{
        for (int i = 1; i < 200; ++i) {
            @autoreleasepool {
                while (![assetWriterInput isReadyForMoreMediaData]) {
                    [NSThread sleepForTimeInterval:0.01]; 
                }
                NSString *path = [NSString stringWithFormat:@"/Users/jontelang/Desktop/SnapperVideoDump/frames/frame_%i.jpg",i];
                UIImage *image = [UIImage imageWithContentsOfFile:path];
                CGImageRef ref = [image CGImage];
                CVPixelBufferRef buffer = [self pixelBufferFromCGImage:ref pool:writerAdaptor.pixelBufferPool];
                CMTime presentTime = CMTimeAdd(CMTimeMake(i,60),CMTimeMake(1,60));
                [writerAdaptor appendPixelBuffer:buffer withPresentationTime:presentTime];
                CVPixelBufferRelease(buffer);
            }
        }
        [assetWriterInput markAsFinished];
        [assetWriter finishWritingWithCompletionHandler:^{}];
    }];
,

不,我看到它在应用程序中达到峰值约为 240 mb。这是我第一次使用这种分配方式 - 很有趣。

allocation assetwriter allocation

我正在使用 AssetWriter 通过流式传输 cmSampleBuffer 来编写视频文件:CMSampleBuffer。它通过 Camera CaptureOutput Realtime 从 AVCaptureVideoDataOutputSampleBufferDelegate 获取。

,

虽然我还没有找到实际的问题,但我在这个问题中描述的内存问题通过简单地在实际设备上而不是在模拟器上进行就解决了。