4

I'm attempting to convert CVPixelBufferRefs from a video source to CGImageRefs using vImage convert libraries on 10.10. This for the most part works fine. However, each time I initialize a new vImage_Buffer from my CVPixelBufferRef, memory is gobbled up that is never returned.

Here is a simplified version of the conversion, that should ideally use no memory at the end of the day:

CVPixelBufferRef pixelBuffer = ...; // retained CVPixelBufferRef from somewhere else
vImage_Buffer buffer;
vImage_CGImageFormat format = {.bitsPerComponent = 8, .bitsPerPixel = 32, .colorSpace = NULL, .bitmapInfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little, .version = 0, .decode = NULL, .renderingIntent = kCGRenderingIntentAbsoluteColorimetric};

vImage_Error imageError = vImageBuffer_InitWithCVPixelBuffer(&buffer, &format, pixelBuffer, NULL, NULL, kvImagePrintDiagnosticsToConsole);

// Do conversion here

free(buffer.data);

Commenting out the last two lines (the init and free) effectively uses no more memory than what I started with. With the two lines there, however, 6 MB are being consumed each time.

If I only comment out the free, even more memory is being consumed, so the free is definitely doing something, but I can only assume vImageBuffer_InitWithCVPixelBuffer is using more memory than it is supposed to. Has anyone else seen this?

For completion, here is the whole conversion method to go from CVPixelBufferRef to NSImage:

CVPixelBufferRef pixelBuffer = ...; // retained CVPixelBufferRef from somewhere else

NSImage *image = nil;

vImage_Buffer buffer;
vImage_CGImageFormat format = {.bitsPerComponent = 8, .bitsPerPixel = 32, .colorSpace = NULL, .bitmapInfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little, .version = 0, .decode = NULL, .renderingIntent = kCGRenderingIntentAbsoluteColorimetric};

vImage_Error imageError = vImageBuffer_InitWithCVPixelBuffer(&buffer, &format, pixelBuffer, NULL, NULL, kvImagePrintDiagnosticsToConsole);

if (imageError != 0) {
    NSLog(@"vImageBuffer_InitWithCVPixelBuffer Error: %zd", imageError);
} else {
    CGImageRef imageRef = vImageCreateCGImageFromBuffer(&buffer, &format, NULL, NULL, kvImagePrintDiagnosticsToConsole|kvImageHighQualityResampling, &imageError);

    if (!imageRef) {
        NSLog(@"vImageCreateCGImageFromBuffer Error: %zd", imageError);
    } else {
        image = [[NSImage alloc] initWithCGImage:imageRef size:NSMakeSize(CGImageGetWidth(imageRef), CGImageGetHeight(imageRef))];

        CGImageRelease(imageRef);

        NSAssert(image != nil, @"Creating the image failed!");
    }
}

free(buffer.data);
Dimitri Bouniol
  • 735
  • 11
  • 15
  • It seems likely that either there is a leak, or vImageBuffer_InitWithCVPixelBuffer is causing the CVPixelBufferRef to allocate extra internal storage which will go away when the CVPixelBufferRef is destroyed. Depending on your pipeline, it is possible the CVPixelBufferRef has no pixels in it until you ask for them. – Ian Ollmann Mar 28 '15 at 01:00
  • I'm the one making the `CVPixelBuffer`s from memory buffers coming from elsewhere, and I'm quite sure they are being released correctly. Is there anyway to access the extra `vImage` storage that is being tied to the `CVPixelBuffer` to correctly de-allocate it? – Dimitri Bouniol Mar 30 '15 at 18:20
  • It is not clear from the question where the memory is being allocated. You can run your app with environment variable MallocStackLogging=1, and then run leaks from the command line. You should get a backtrace. If there are no leaks, but just memory going up, you might be able to spot it with vmmap . Some Accelerate allocations are tagged as such. There is quite a lot to be said for filing a reproducible bug report with Apple. – Ian Ollmann Mar 31 '15 at 23:28

0 Answers0