Occasionally crash in newCGImageFromFramebufferContents -> CFRetain(renderTarget) in GPUImageFramebuffer.m
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:

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?
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!
Please consider my resolution for adding back into the source codes
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);
});
}
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
....
}
did you fix the bug?
It's not working for me. When I call the method of "newCGImageFromFramebufferContents", It carsh every time. Did it work for you?