【问题标题】:iOS memory building up while creating UIImage from CMSampleBufferRef从 CMSampleBufferRef 创建 UIImage 时 iOS 内存增加
【发布时间】:2015-01-29 15:22:35
【问题描述】:

我正在从CMSampleBufferRef 的创建UIImage 对象。我在一个单独的队列(在后台)中执行此操作,因此我将处理包含在@autorealease 池中。问题是内存正在积累而没有任何泄漏通知。下面是我正在使用的方法:

- (UIImage *) imageFromSampleBuffer:(CMSampleBufferRef) sampleBuffer
{
    @autoreleasepool {
        // Get a CMSampleBuffer's Core Video image buffer for the media data
        CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
        // Lock the base address of the pixel buffer
        CVPixelBufferLockBaseAddress(imageBuffer, 0);

        // Get the number of bytes per row for the pixel buffer
        void *baseAddress = CVPixelBufferGetBaseAddress(imageBuffer);

       // Get the number of bytes per row for the pixel buffer
       size_t bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer);
       // Get the pixel buffer width and height
       size_t width = CVPixelBufferGetWidth(imageBuffer);
       size_t height = CVPixelBufferGetHeight(imageBuffer);

       // Create a device-dependent RGB color space
       CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();

       // Create a bitmap graphics context with the sample buffer data
       CGContextRef context = CGBitmapContextCreate(baseAddress, width, height, 8,
                                                 bytesPerRow, colorSpace, kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst);
      // Create a Quartz image from the pixel data in the bitmap graphics context
       CGImageRef quartzImage = CGBitmapContextCreateImage(context);
       // Unlock the pixel buffer
       CVPixelBufferUnlockBaseAddress(imageBuffer,0);

       // Free up the context and color space
       CGContextRelease(context);
       CGColorSpaceRelease(colorSpace);

       // Create an image object from the Quartz image
       UIImage *image = [[UIImage imageWithCGImage:quartzImage] retain];

       // Release the Quartz image
       CGImageRelease(quartzImage);

       return (image);
   }
}

这就是我使用它的方式:

- (void)captureOutput:(AVCaptureOutput *)captureOutput
didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
       fromConnection:(AVCaptureConnection *)connection {

    CFRetain(sampleBuffer);
    dispatch_async(movieWritingQueue, ^{
    @autoreleasepool {

        if (self.returnCapturedImages && captureOutput != audioOutput) {

            UIImage *capturedImage = [self imageFromSampleBuffer: sampleBuffer];

            dispatch_async(callbackQueue, ^{

                @autoreleasepool {

                    if (self.delegate && [self.delegate respondsToSelector: @selector(recorderCapturedImage:)]) {
                        [self.delegate recorderCapturedImage: capturedImage];
                    }

                    [capturedImage release];
                }
            });
        }
        CFRelease(sampleBuffer);
    }
});

【问题讨论】:

  • 你为什么要转换成uiimage?如果要将图像写入 avassetwriter,则只需要使用示例缓冲区和 cvpixelbufferref 对象,因为它们比转换为 uiimage 快一个数量级。

标签: ios memory-leaks uiimage cmsamplebufferref


【解决方案1】:

我找到了一个临时解决方案。我正在做相同的操作,但在主队列上。这一点都不优雅也不高效,但至少内存不会再增加了。

我想知道这是否是 iOS 错误...?

更新: 这就是我在主线程上处理 CMSampleBuffers 的方式:

[[NSOperationQueue mainQueue] addOperationWithBlock:^ {

    CGImageRef cgImage = [self cgImageFromSampleBuffer:sampleBuffer];
    UIImage *capturedImage =     [UIImage imageWithCGImage: cgImage ];

    //do something with the image - I suggest in a background thread
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
       // do something with the image
    });

    CGImageRelease( cgImage );
    CFRelease(sampleBuffer);
}];

- (CGImageRef) cgImageFromSampleBuffer:(CMSampleBufferRef) sampleBuffer
{
    CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
    CVPixelBufferLockBaseAddress(imageBuffer,0);        // Lock the image buffer

    uint8_t *baseAddress = (uint8_t *)CVPixelBufferGetBaseAddressOfPlane(imageBuffer, 0);   // Get information of the image
    size_t bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer);
    size_t width = CVPixelBufferGetWidth(imageBuffer);
    size_t height = CVPixelBufferGetHeight(imageBuffer);
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();

    CGContextRef newContext = CGBitmapContextCreate(baseAddress, width, height, 8, bytesPerRow, colorSpace, kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst);
    CGImageRef newImage = CGBitmapContextCreateImage(newContext);
    CGContextRelease(newContext);

    CGColorSpaceRelease(colorSpace);
    CVPixelBufferUnlockBaseAddress(imageBuffer,0);
    /* CVBufferRelease(imageBuffer); */  // do not call this!

    return newImage;
}

【讨论】:

  • 你好,米海在主队列中怎么做?谢谢!
  • 哦,如果您对多张图像快速连续调用 didOutputSampleBuffer 方法并在后台执行 imageFromSampleBuffer 方法,那么您的操作可能会重叠。在这种情况下,内存会增加,这不是 iOS 错误。
  • 我明白了。对此有什么解决方案?
  • 所以您可能已经遍历了一组图像(尽管您没有将其包含在更新的代码中,但我猜是这种情况),将它们添加到 NSOperationQueue,然后执行一次进行一项或多项操作。问题是因为您在后台线程上执行didOutputSampleBuffer:cgImageFromSampleBuffer:didOutputSampleBuffer: 完成之前返回,并且您实际上一次执行的不止是几次didOutputSampleBuffer: 迭代,所以执行didOutputSampleBuffer:在前台实际上可以消除这个问题。
【解决方案2】:

其实我前几天也遇到过类似的问题……

您已经发布了您的CMSampleBufferRef,但也尝试发布您的CVPixelBufferRef,例如:

- (UIImage *) imageFromSampleBuffer:(CMSampleBufferRef) sampleBuffer
{
    @autoreleasepool {

       // ...

       // Free up the context and color space
       CGContextRelease(context);
       CGColorSpaceRelease(colorSpace);

       // Create an image object from the Quartz image
       UIImage *image = [[UIImage imageWithCGImage:quartzImage] retain];

       // Release the Quartz image
       CGImageRelease(quartzImage);

       CVPixelBufferRelease(imageBuffer); <-- release your pixel buffer

       return (image);
   }
}

【讨论】:

  • 感谢您的建议。我已经尝试过了,但它在CFRelease(sampleBuffer) 线上崩溃了。使用CMSampleBufferGetImageBuffer 获取像素缓冲区不会保留数据。这是 Apple 文档:“调用者不拥有返回的 dataBuffer,如果调用者需要维护对它的引用,则必须明确保留它。”
  • @MihaiGhete CMSampleBufferGetDataBuffer 文档中的那一行与 CVPixelBufferRef 被保留完全无关。 (请注意,我的答案是指您在 imageFromSampleBuffer 中创建的 pixel 数据缓冲区,而不是 sample 缓冲区。)在该方法期间释放像素缓冲区不应该有任何对CFRelease(sampleBuffer) 的影响,因为您从imageFromSampleBuffer: 返回图像。
猜你喜欢
  • 2011-03-19
  • 2018-08-13
  • 1970-01-01
  • 1970-01-01
  • 2013-01-01
  • 2013-05-04
  • 2013-01-15
  • 2012-03-10
相关资源
最近更新 更多