0
votes

The code is below:

- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx {
    CGContextRef context = ctx;
    CGContextRetain(context);
    CGContextSetInterpolationQuality(context, kCGInterpolationNone);
    CGContextSetLineCap(context, kCGLineCapButt);
    CGContextSetLineWidth(context, 1);
    CGContextSetRGBStrokeColor(context, 0, 0, 0, 0.8);

    // draw image
    CGContextSaveGState(context);
    CGAffineTransform flipVertical = CGAffineTransformMake(
                                                           1, 0, 0, -1, 0, layer.bounds.size.height
                                                           );
    CGContextConcatCTM(context, flipVertical);

    CGContextDrawImage(context, layer.bounds, imageRef);
    CGContextRestoreGState(context);

    // draw borders
    CGContextAddRect(context, layer.bounds);
    CGContextDrawPath(context, kCGPathStroke);

    CGFloat apertureSize = scaleFactor * (2 * samplingLevel - 1) - 1;
    CGFloat x = self.bounds.size.width / 2 - apertureSize / 2;
    CGFloat y = self.bounds.size.height / 2 - apertureSize / 2;
    CGRect apertureRect = CGRectMake(x, y, apertureSize, apertureSize);

    // draw the aperture
    CGContextSaveGState(context);
    CGContextSetRGBStrokeColor(context, 0, 0, 0, 1);      
    CGContextSetShouldAntialias(context, NO);
    CGContextStrokeRect(context, apertureRect);
    CGContextRestoreGState(context);

    CGContextRelease(context);
}

and it crashes with:

(lldb) thread backtrace
* thread #1: tid = 0x1c03, 0x37537a5c CoreGraphics`argb32_image_mark_rgb32 + 800, stop reason = EXC_BAD_ACCESS (code=1, address=0x7314b0)
frame #0: 0x37537a5c CoreGraphics`argb32_image_mark_rgb32 + 800
frame #1: 0x3750e400 CoreGraphics`argb32_image + 5948
frame #2: 0x313c5894 libRIP.A.dylib`ripl_Mark + 16
frame #3: 0x313ca68a libRIP.A.dylib`ripl_BltImage + 998
frame #4: 0x313c2468 libRIP.A.dylib`ripc_RenderImage + 180
frame #5: 0x313be300 libRIP.A.dylib`ripc_DrawImage + 584
frame #6: 0x3750a928 CoreGraphics`CGContextDelegateDrawImage + 48
frame #7: 0x3750a794 CoreGraphics`CGContextDrawImage + 292
frame #8: 0x00166e34 MyApp`-[MyView drawLayer:inContext:] + 380 at MyView.m:43
frame #9: 0x33f054e4 QuartzCore`-[CALayer drawInContext:] + 116
frame #10: 0x33f04b3e QuartzCore`CABackingStoreUpdate_ + 1782
frame #11: 0x33f04334 QuartzCore`CA::Layer::display_() + 956
frame #12: 0x33f03f60 QuartzCore`CA::Layer::display() + 128

Anyone knows why? Thanks! I guess it is caused by either CGImage or CGContext, but both of them are Non-null when debugging. The image I used to update the view is generated from camera capturing.

3
What was the nature of the crash? EXC_BAD_ACCESS? If so, did it show what address it was trying to access? The most likely culprit would be that imageRef is NULL. - Ken Thomases
The imageRef did have an value, so I'm wondering if this image is valid? But I have no idea of how to check a CGImageRef. - wtl
Well, it could be a garbage pointer value. If it points to a real, extent CGImageRef, it should be valid unless you have smashed the heap. Do a Build and Analyze and resolve any issues it identifies. Beyond that, you'll probably need to show where imageRef is declared and all of the code which touches that variable. (Oh, and I see my first comment was dumb. The crash details were there but scrolled off to the right where I didn't see them.) - Ken Thomases
Thanks @Ken, you are right, the problem lies in imageRef. I've found out the solution and written it down. - wtl

3 Answers

4
votes

I used sample codes provided by apple (AV Foundation Programming Guide) to create imageRef.

UIImage *imageFromSampleBuffer(CMSampleBufferRef sampleBuffer) {

    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.
    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.
    static CGColorSpaceRef colorSpace = NULL;
    if (colorSpace == NULL) {
        colorSpace = CGColorSpaceCreateDeviceRGB();
            if (colorSpace == NULL) {
            // Handle the error appropriately.
            return nil;
        }
    }

    // Get the base address of the pixel buffer.
    void *baseAddress = CVPixelBufferGetBaseAddress(imageBuffer);
    // Get the data size for contiguous planes of the pixel buffer.
    size_t bufferSize = CVPixelBufferGetDataSize(imageBuffer);

    // Create a Quartz direct-access data provider that uses data we supply.
    CGDataProviderRef dataProvider =
        CGDataProviderCreateWithData(NULL, baseAddress, bufferSize, NULL);
    // Create a bitmap image from data supplied by the data provider.
    CGImageRef cgImage =
        CGImageCreate(width, height, 8, 32, bytesPerRow,
                        colorSpace, kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Little,
                        dataProvider, NULL, true, kCGRenderingIntentDefault);
    CGDataProviderRelease(dataProvider);

    // Create and return an image object to represent the Quartz image.
    UIImage *image = [UIImage imageWithCGImage:cgImage];
    CGImageRelease(cgImage);

    CVPixelBufferUnlockBaseAddress(imageBuffer, 0);

    return image;
}

Note this line:

CGDataProviderRef dataProvider =
        CGDataProviderCreateWithData(NULL, baseAddress, bufferSize, NULL);

CMSampleBuffer is used directly to create a CGImageRef, so CGImageRef will become invalid once the buffer is released.

Use copy of the buffer data will resolve this problem:

NSData *data = [NSData dataWithBytes:baseAddress length:bufferSize];
CGDataProviderRef provider = CGDataProviderCreateWithCFData((__bridge CFDataRef)data);
3
votes

I encountered the same issue, but the problem was related to ARC and releasing the CGImageRef properly. Make sure the following is called:

CGImageRelease(imageRef);
0
votes

change

CGDataProviderRef dataProvider =
    CGDataProviderCreateWithData(NULL, baseAddress, bufferSize, NULL);

to

NSData *data = [NSData dataWithBytes:baseAddress length:bufferSize];
CGDataProviderRef provider = CGDataProviderCreateWithCFData((__bridge CFDataRef)data);

really helps.