【问题标题】:Reduce memory usage of AVAssetWriter减少 AVAssetWriter 的内存使用
【发布时间】:2021-06-30 12:55:30
【问题描述】:

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

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

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

代码

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

记忆中的问题/项目

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

因此,在我看来,问题出在我的代码上。我个人可以控制的代码 似乎 不是问题,即加载图像、创建缓冲区、释放它似乎不是内存有问题的地方。例如,如果我在 Instruments 中的大部分时间上面进行标记,则内存是稳定的,并且没有任何内存被保留。

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

现在呢?

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

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

使用 CVPixelBufferPool 的注意事项

在 CVPixelBufferCreate Apple 的文档中指出:

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

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

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

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

更新 1

所以我对跟踪进行了更深入的研究,以找出大部分分配的时间/地点。这是一个带注释的图像:

要点是:

  1. 空间未与 AVAssetWriter“一起”分配
  2. 在处理开始后 500ms 内分配 500mb,直到结束为止
  3. 好像是在 AVAssetWriter 内部完成的

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

【问题讨论】:

标签: ios objective-c avassetwriter cvpixelbuffer


【解决方案1】:
  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:^{}];
    }];

【讨论】:

  • 感谢您的回复,很遗憾,通过这些更改我没有看到任何不同的结果。
  • 为了更好地衡量,我还尝试使用 DISPATCH_AUTORELEASE_FREQUENCY_WORK_ITEM 调整队列的自动释放频率。没有变化。
  • 在这种情况下,最好在 Xcode 中检查内存图,看看谁拥有对这些对象的引用
  • 我不确定问题是否在 for 循环中,因为在第二个屏幕截图中,分配的内存似乎是及时释放的。不过我会检查内存图,我也忘记了。谢谢。
  • 在这里抛出一些想法,看看从零到300兆的内存大小,你可能需要释放其中的一些进程?或者使用assetwriter为特定工作创建一个视频,然后编写另一个视频然后合并在一起。我在创建具有可变轨道的资产编写器时遇到了一些问题,所以我前一段时间考虑过这个问题。 AssetWriter 需要是单个实例,所以需要是线性的。
【解决方案2】:

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

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

【讨论】:

  • 您介意上传 .trace 文件吗?
  • 我有一些更新。看来,当我在实际设备上运行它时,它没有同样的大内存问题。我还没有深入比较的痕迹。
  • 酷,希望能帮助您查明问题。
【解决方案3】:

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

【讨论】:

    【解决方案4】:

    @Eugene_Dudnyk 答案就在现场,for 或 while 循环内的自动释放池是关键,这是我为 Swift 工作的方法,另外,请使用 AVAssetWriterInputPixelBufferAdaptor 像素缓冲池:

    videoInput.requestMediaDataWhenReady(on: videoInputQueue) { [weak self] in
            while videoInput.isReadyForMoreMediaData {
                autoreleasepool {
                    guard let sample = assetReaderVideoOutput.copyNextSampleBuffer(),
                          let buffer = CMSampleBufferGetImageBuffer(sample) else {
                              print("Error while processing video frames")
                              videoInput.markAsFinished()
                              DispatchQueue.main.async {
                                  videoFinished = true
                                  closeWriter()
                              }
                              return
                          }
    
                    // Process image and render back to buffer (in place operation, where ciProcessedImage is your processed new image)
                    self?.getCIContext().render(ciProcessedImage, to: buffer)
                    let timeStamp = CMSampleBufferGetPresentationTimeStamp(sample)
                    self?.adapter?.append(buffer, withPresentationTime: timeStamp)
                }
            }
        }
    

    我的内存使用量停止上升。

    【讨论】:

      猜你喜欢
      • 2013-05-21
      • 1970-01-01
      • 2011-09-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-01-19
      • 2016-12-12
      相关资源
      最近更新 更多