Утечка памяти при захвате видеокадра в данные Bitmap - PullRequest
0 голосов
/ 26 мая 2020

Я работаю над проектом, в котором мне нужно захватить кадры из видеопотока камеры и передать их как растровое изображение в библиотеку C ++, интегрированную в проект xCode. Я успешно могу создавать растровые данные из sampleBuffer. Однако эти растровые данные продолжают расти в размере, достигают гигабайт за несколько секунд, и приложение вылетает. Пожалуйста посоветуй.

  1. Код для захвата кадров из предварительного просмотра видео и преобразования в растровое изображение.
func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer:   CMSampleBuffer, from connection: AVCaptureConnection){


        var img:UIImage?

        let bytesPerPixel = 4

        if let cvImageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) {

            let ciimage = CIImage(cvImageBuffer: cvImageBuffer)
            let context = CIContext()

            if let cgImage = context.createCGImage(ciimage, from: ciimage.extent) {

                img = UIImage(cgImage: cgImage)

                let bitmap = ImageHelper.convertUIImage(toBitmapRGBA8: img)

// pass bitmap to C++ library

            }
        }
    }
Использование вспомогательного класса obj- c для получения данных растрового изображения из UIimage (http://paulsolt.com/blog/2010/09/ios-converting-uiimage-to-rgba8-bitmaps-and-back)


@implementation ImageHelper


+ (unsigned char *) convertUIImageToBitmapRGBA8:(UIImage *) image {

    CGImageRef imageRef = image.CGImage;

    // Create a bitmap context to draw the uiimage into
    CGContextRef context = [self newBitmapRGBA8ContextFromImage:imageRef];

    if(!context) {
        return NULL;
    }

    size_t width = CGImageGetWidth(imageRef);
    size_t height = CGImageGetHeight(imageRef);

    CGRect rect = CGRectMake(0, 0, width, height);

    // Draw image into the context to get the raw image data
    CGContextDrawImage(context, rect, imageRef);

    // Get a pointer to the data    
    unsigned char *bitmapData = (unsigned char *)CGBitmapContextGetData(context);

    // Copy the data and release the memory (return memory allocated with new)
    size_t bytesPerRow = CGBitmapContextGetBytesPerRow(context);
    size_t bufferLength = bytesPerRow * height;

    unsigned char *newBitmap = NULL;

    if(bitmapData) {
        newBitmap = (unsigned char *)malloc(sizeof(unsigned char) * bytesPerRow * height);

        if(newBitmap) { // Copy the data
            for(int i = 0; i < bufferLength; ++i) {
                newBitmap[i] = bitmapData[i];
            }
        }

        free(bitmapData);

    } else {
        NSLog(@"Error getting bitmap pixel data\n");
    }

    CGContextRelease(context);

    return newBitmap;   
}

+ (CGContextRef) newBitmapRGBA8ContextFromImage:(CGImageRef) image {
    CGContextRef context = NULL;
    CGColorSpaceRef colorSpace;
    uint32_t *bitmapData;

    size_t bitsPerPixel = 32;
    size_t bitsPerComponent = 8;
    size_t bytesPerPixel = bitsPerPixel / bitsPerComponent;

    size_t width = CGImageGetWidth(image);
    size_t height = CGImageGetHeight(image);

    size_t bytesPerRow = width * bytesPerPixel;
    size_t bufferLength = bytesPerRow * height;

    colorSpace = CGColorSpaceCreateDeviceRGB();

    if(!colorSpace) {
        NSLog(@"Error allocating color space RGB\n");
        return NULL;
    }

    // Allocate memory for image data
    bitmapData = (uint32_t *)malloc(bufferLength);

    if(!bitmapData) {
        NSLog(@"Error allocating memory for bitmap\n");
        CGColorSpaceRelease(colorSpace);
        return NULL;
    }

    //Create bitmap context

    context = CGBitmapContextCreate(bitmapData, 
            width, 
            height, 
            bitsPerComponent, 
            bytesPerRow, 
            colorSpace, 
            kCGImageAlphaPremultipliedLast);    // RGBA
    if(!context) {
        free(bitmapData);
        NSLog(@"Bitmap context not created");
    }

    CGColorSpaceRelease(colorSpace);

    return context; 
}

+ (UIImage *) convertBitmapRGBA8ToUIImage:(unsigned char *) buffer 
        withWidth:(int) width
       withHeight:(int) height {


    size_t bufferLength = width * height * 4;
    CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, buffer, bufferLength, NULL);
    size_t bitsPerComponent = 8;
    size_t bitsPerPixel = 32;
    size_t bytesPerRow = 4 * width;

    CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();
    if(colorSpaceRef == NULL) {
        NSLog(@"Error allocating color space");
        CGDataProviderRelease(provider);
        return nil;
    }

    CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault | kCGImageAlphaPremultipliedLast;
    CGColorRenderingIntent renderingIntent = kCGRenderingIntentDefault;

    CGImageRef iref = CGImageCreate(width, 
                height, 
                bitsPerComponent, 
                bitsPerPixel, 
                bytesPerRow, 
                colorSpaceRef, 
                bitmapInfo, 
                provider,   // data provider
                NULL,       // decode
                YES,            // should interpolate
                renderingIntent);

    uint32_t* pixels = (uint32_t*)malloc(bufferLength);

    if(pixels == NULL) {
        NSLog(@"Error: Memory not allocated for bitmap");
        CGDataProviderRelease(provider);
        CGColorSpaceRelease(colorSpaceRef);
        CGImageRelease(iref);       
        return nil;
    }

    CGContextRef context = CGBitmapContextCreate(pixels, 
                 width, 
                 height, 
                 bitsPerComponent, 
                 bytesPerRow, 
                 colorSpaceRef, 
                 bitmapInfo); 

    if(context == NULL) {
        NSLog(@"Error context not created");
        free(pixels);
    }

    UIImage *image = nil;
    if(context) {

        CGContextDrawImage(context, CGRectMake(0.0f, 0.0f, width, height), iref);

        CGImageRef imageRef = CGBitmapContextCreateImage(context);

        // Support both iPad 3.2 and iPhone 4 Retina displays with the correct scale
        if([UIImage respondsToSelector:@selector(imageWithCGImage:scale:orientation:)]) {
            float scale = [[UIScreen mainScreen] scale];
            image = [UIImage imageWithCGImage:imageRef scale:scale orientation:UIImageOrientationUp];
        } else {
            image = [UIImage imageWithCGImage:imageRef];
        }

        CGImageRelease(imageRef);   
        CGContextRelease(context);  
    }

    CGColorSpaceRelease(colorSpaceRef);
    CGImageRelease(iref);
    CGDataProviderRelease(provider);

    if(pixels) {
        free(pixels);
    }   
    return image;
}

@end

Проблема, с которой я столкнулся выше код с новым захватом каждого кадра, старые растровые данные не кажутся удаленными, что приводит к увеличению размера буфера. Что нужно сделать, чтобы все старые растровые данные были удалены из памяти при захвате новых кадров и преобразовании их в растровое изображение?

Пожалуйста, помогите. Заранее спасибо!

...