GPUImage icon indicating copy to clipboard operation
GPUImage copied to clipboard

Occasionally crash in newCGImageFromFramebufferContents -> CFRetain(renderTarget) in GPUImageFramebuffer.m

Open magicboker opened this issue 9 years ago • 6 comments

I found several crash logs in the latest version

GPUImageFramebuffer.m, line 333: CFRetain(renderTarget);

The renderTarget is NULL or released here!

Code snippet:

UIImage *output;
@autoreleasepool {
	GPUImagePicture *source = [[GPUImagePicture alloc] initWithImage: inputImage];
	GPUImageUnsharpMaskFilter *unsharpFilter = [[GPUImageUnsharpMaskFilter alloc] init];
	GPUImageLuminanceThresholdFilter *thresholdFilter = [[GPUImageLuminanceThresholdFilter alloc] init];
	MyBlendFilter *blendFilter = [[MyBlendFilter alloc] init];
	unsharpFilter.intensity = 5;
	unsharpFilter.blurRadiusInPixels = 2;
	thresholdFilter.threshold = 50.0 / 255.0;
	[source addTarget: thresholdFilter];
	[source addTarget: unsharpFilter];
	[thresholdFilter addTarget: blendFilter];
	[unsharpFilter addTarget: blendFilter];
	[blendFilter useNextFrameForImageCapture];
	[source processImage];
	output = [blendFilter imageFromCurrentFramebufferWithOrientation: UIImageOrientationUp];
}

Call stack: call stack

Did anyone have the same problem with me?

PS: The usage is quite simple and straightforward because it's a single task so there should be no multi-thread concurrent access issue. How could the renderTarget be released before calling the CFRetain?

magicboker avatar Nov 16 '16 15:11 magicboker

I discovered a possible problem:

In GPUImagePicture.m

- (void)processImage;
{
    [self processImageWithCompletionHandler:nil];
}

- (BOOL)processImageWithCompletionHandler:(void (^)(void))completion;
{
    hasProcessedImage = YES;

    //    dispatch_semaphore_wait(imageUpdateSemaphore, DISPATCH_TIME_FOREVER);

    if (dispatch_semaphore_wait(imageUpdateSemaphore, DISPATCH_TIME_NOW) != 0)
    {
        return NO;
    }

// runAsynchronouslyOnVideoProcessingQueue will let the following codes run later
    runAsynchronouslyOnVideoProcessingQueue(^{        
        for (id<GPUImageInput> currentTarget in targets)
        {
            NSInteger indexOfObject = [targets indexOfObject:currentTarget];
            NSInteger textureIndexOfTarget = [[targetTextureIndices objectAtIndex:indexOfObject] integerValue];

            [currentTarget setCurrentlyReceivingMonochromeInput:NO];
            [currentTarget setInputSize:pixelSizeOfImage atIndex:textureIndexOfTarget];
            [currentTarget setInputFramebuffer:outputFramebuffer atIndex:textureIndexOfTarget];
            [currentTarget newFrameReadyAtTime:kCMTimeIndefinite atIndex:textureIndexOfTarget];
        }

        dispatch_semaphore_signal(imageUpdateSemaphore);

        if (completion != nil) {
            completion();
        }
    });

    return YES;
}

[source processImage]; // <--- the async block inside is still running output = [blendFilter imageFromCurrentFramebufferWithOrientation: UIImageOrientationUp]; // this call may run before the above sync block is finished and cause framebuffer dealloc issue

Replacing runAsynchronouslyOnVideoProcessingQueue with runSynchronouslyOnVideoProcessingQueue may resolve the issue!

magicboker avatar Nov 17 '16 15:11 magicboker

Please consider my resolution for adding back into the source codes

magicboker avatar Nov 17 '16 15:11 magicboker

A better solution is to modify processImage only and keep the async call, processImageWithCompletionHandler, unmodified

/* modified to resolve the occasional app crash issue
- (void)processImage;
{
     [self processImageWithCompletionHandler:nil];
}
instead, use the following modified codes */
- (void)processImage {
    hasProcessedImage = YES;

    if (dispatch_semaphore_wait(imageUpdateSemaphore, DISPATCH_TIME_NOW) != 0)
        return;

    runSynchronouslyOnVideoProcessingQueue(^{
        for (id<GPUImageInput> currentTarget in targets)
        {
            NSInteger indexOfObject = [targets indexOfObject:currentTarget];
            NSInteger textureIndexOfTarget = [[targetTextureIndices objectAtIndex:indexOfObject] integerValue];

            [currentTarget setCurrentlyReceivingMonochromeInput:NO];
            [currentTarget setInputSize:pixelSizeOfImage atIndex:textureIndexOfTarget];
            [currentTarget setInputFramebuffer:outputFramebuffer atIndex:textureIndexOfTarget];
            [currentTarget newFrameReadyAtTime:kCMTimeIndefinite atIndex:textureIndexOfTarget];
        }

        dispatch_semaphore_signal(imageUpdateSemaphore);
    });
}

magicboker avatar Nov 18 '16 06:11 magicboker

In GPUImageFrameBuffer.m,

- (void)generateFramebuffer 
{
	.....
	CVReturn err = CVPixelBufferCreate(kCFAllocatorDefault, (int)_size.width, (int)_size.height, kCVPixelFormatType_32BGRA, attrs, &renderTarget); 
	if (err)
	{
	    NSLog(@"FBO size: %f, %f", _size.width, _size.height);
	    NSAssert(NO, @"Error at CVPixelBufferCreate %d", err);
	}
// renderTarget could be NULL when it's failed
// NSAssert could be disabled in the release build so renderTarget is not assigned
// and the codes go through up to CFRetain(renderTarget) in newCGImageFromFramebufferContents
	....
}

magicboker avatar Mar 20 '17 17:03 magicboker

did you fix the bug?

stoneeagle avatar Nov 27 '17 09:11 stoneeagle

It's not working for me. When I call the method of "newCGImageFromFramebufferContents", It carsh every time. Did it work for you?

oushizishu avatar Aug 05 '19 09:08 oushizishu