The goal is to turn an array of retina images into a single video.
The two functions below are responsible for writing an UIImage into a AVAssetWriterInputPixelBufferAdaptor. They work, and the code produces a seemingly valid video out of the images.
However, Xcode complains with this error:
CGContextScaleCTM: invalid context 0x0. This is a serious error. This application, or a library it uses, is using an invalid context and is thereby contributing to an overall degradation of system stability and reliability. This notice is a courtesy: please fix this problem. It will become a fatal error in an upcoming update.
Presumably, it's complaining about the line CGContextScaleCTM(UIGraphicsGetCurrentContext(), scale, scale)
in fillPixelBufferFromImage
. Removing CGContextScaleCTM(UIGraphicsGetCurrentContext(), scale, scale)
causes problems, however. The retina images wind up rendering at half the size (e.g., 320x568 instead of 640x1136) inside the video.
1) Is this an error to worry about? Other SO posts suggest not.
2) Even if benign, how can you make this error go away? Or what's the right way to render retina images into a AVAssetWriterInputPixelBufferAdaptor?
func appendPixelBufferForImageAtURL(image: UIImage, pixelBufferAdaptor: AVAssetWriterInputPixelBufferAdaptor, presentationTime: CMTime) -> Bool {
var appendSucceeded = false
autoreleasepool {
if let pixelBufferPool = pixelBufferAdaptor.pixelBufferPool {
let pixelBufferPointer = UnsafeMutablePointer<CVPixelBuffer?>.alloc(1)
let status: CVReturn = CVPixelBufferPoolCreatePixelBuffer(
kCFAllocatorDefault,
pixelBufferPool,
pixelBufferPointer
)
if let pixelBuffer = pixelBufferPointer.memory where status == 0 {
fillPixelBufferFromImage(image, pixelBuffer: pixelBuffer)
appendSucceeded = pixelBufferAdaptor.appendPixelBuffer(pixelBuffer, withPresentationTime: presentationTime)
pixelBufferPointer.destroy()
} else {
NSLog("Error: Failed to allocate pixel buffer from pool")
}
pixelBufferPointer.dealloc(1)
}
}
return appendSucceeded
}
func fillPixelBufferFromImage(image: UIImage, pixelBuffer: CVPixelBufferRef) {
/// Lock base address
CVPixelBufferLockBaseAddress(pixelBuffer, 0)
// Set properties for context
let pixelData = CVPixelBufferGetBaseAddress(pixelBuffer)
let rgbColorSpace = CGColorSpaceCreateDeviceRGB()
let scale = image.scale
let scaledWidth = image.size.width * scale
let scaledHeight = image.size.height * scale
// Create CGBitmapContext
let context = CGBitmapContextCreate(
pixelData,
Int(scaledWidth),
Int(scaledHeight),
8,
CVPixelBufferGetBytesPerRow(pixelBuffer),
rgbColorSpace,
CGImageAlphaInfo.PremultipliedFirst.rawValue
)
// Set scale for context
CGContextScaleCTM(UIGraphicsGetCurrentContext(), scale, scale)
// Draw image into context
CGContextDrawImage(context, CGRectMake(0, 0, scaledWidth, scaledHeight), image.CGImage)
/// Unlock base address
CVPixelBufferUnlockBaseAddress(pixelBuffer, 0)
}
Yes, this is an error you should worry about. The error message saying "This is a serious error." and "please fix this problem" is not a joke.
This is incorrect:
CGContextScaleCTM(UIGraphicsGetCurrentContext(), scale, scale)
because none of these are the case:
-drawRect:
methodUIGraphicsBeginImageContext
to set up a contextUIGraphicsPushContext
Therefore, as far as UIKit knows, there is no "current" context, so UIGraphicsGetCurrentContext()
returns nil.
You created the bitmap context yourself, so you should apply the scale to that context:
CGContextScaleCTM(context, scale, scale)