[英]UIImage built from CMSampleBuffer not retained and causing EXC_BAD_ACCESS
我正在從CMSampleBuffer構建UIImage。 從主線程,我調用一個函數來訪問CMSampleBuffer中的像素數據,並將YCbCr平面轉換為ABGR位圖,並將其包裝在UIImage中。 我從主線程調用該函數:
let priority = DISPATCH_QUEUE_PRIORITY_DEFAULT
dispatch_async(dispatch_get_global_queue(priority, 0), {() -> Void in
let image = self.imageFromSampleBuffer(frame)
dispatch_async(dispatch_get_main_queue(), {() -> Void in
self.testView.image = image
self.testView.hidden = false
})
})
正如我希望的那樣,這將保持UI和主線程的響應能力。 處理緩沖區的函數是:
func imageFromSampleBuffer(sampleBuffer: CMSampleBuffer) -> UIImage {
let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer)!
CVPixelBufferLockBaseAddress(pixelBuffer, 0)
let lumaBaseAddress = CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 0)
let chromaBaseAddress = CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 1)
let width = CVPixelBufferGetWidth(pixelBuffer)
let height = CVPixelBufferGetHeight(pixelBuffer)
let lumaBytesPerRow = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 0)
let chromaBytesPerRow = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 1)
let lumaBuffer = UnsafeMutablePointer<UInt8>(lumaBaseAddress)
let chromaBuffer = UnsafeMutablePointer<UInt8>(chromaBaseAddress)
var rgbaImage = [UInt8](count: 4*width*height, repeatedValue: 0)
for var x = 0; x < width; x++ {
for var y = 0; y < height; y++ {
let lumaIndex = x+y*lumaBytesPerRow
let chromaIndex = (y/2)*chromaBytesPerRow+(x/2)*2
let yp = lumaBuffer[lumaIndex]
let cb = chromaBuffer[chromaIndex]
let cr = chromaBuffer[chromaIndex+1]
let ri = Double(yp) + 1.402 * (Double(cr) - 128)
let gi = Double(yp) - 0.34414 * (Double(cb) - 128) - 0.71414 * (Double(cr) - 128)
let bi = Double(yp) + 1.772 * (Double(cb) - 128)
let r = UInt8(min(max(ri,0), 255))
let g = UInt8(min(max(gi,0), 255))
let b = UInt8(min(max(bi,0), 255))
rgbaImage[(x + y * width) * 4] = b
rgbaImage[(x + y * width) * 4 + 1] = g
rgbaImage[(x + y * width) * 4 + 2] = r
rgbaImage[(x + y * width) * 4 + 3] = 255
}
}
let colorSpace = CGColorSpaceCreateDeviceRGB()
let dataProvider: CGDataProviderRef = CGDataProviderCreateWithData(nil, rgbaImage, 4 * width * height, nil)!
let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.NoneSkipFirst.rawValue | CGBitmapInfo.ByteOrder32Little.rawValue)
let cgImage = CGImageCreate(width, height, 8, 32, width * 4, colorSpace!, bitmapInfo, dataProvider, nil, true, CGColorRenderingIntent.RenderingIntentDefault)!
let image = UIImage(CGImage: cgImage)
CVPixelBufferUnlockBaseAddress(pixelBuffer,0)
return image
}
如果在函數返回之前放置一個斷點,則可以使用“快速查看”並查看圖像(這正是我所期望的)。 但是,一旦函數返回,我將無法在其他任何地方使用image
並且“快速查找”總是會失敗。 如果我嘗試將UIImageView設置為返回的圖像,則UI中的任何內容都不會更改:
testView.image = image \\The UIImageView does not update.
如果我嘗試以任何其他方式訪問圖像(例如,嘗試將其保存到Parse),則代碼將因EXC_BAD_ACCESS而崩潰。 同樣,如果我將圖像保存到上述函數中的Parse中,它會按預期顯示在后端數據庫中。
我還嘗試了直接通過調用處理函數而不調度到全局隊列和主隊列的方法。 結果總是一樣的。
我相信這是因為未保留圖像。 我嘗試在類和文件級別上定義image和CGImage上下文,但是都沒有改變結果。 我以為這將保留一個參考,但顯然沒有。 我對Swift足夠陌生,以至於我顯然不了解ARC在這種情況下是如何工作的。
我還認為,在使用Quick Look進行調試的過程中,有幾次我第一次單擊Quick Look時“不可用” ...但是等待幾秒鍾並再次單擊會出現圖像。 是否有可能需要更長的時間才能提供數據? 也許GPU-> CPU? 如果是這樣,如何檢查/延遲以避免崩潰?
如何維護參考? 有沒有更好的方法來處理從CMSampleBuffer創建的圖像?
問題在於創建CGImage的方式。 使用dataProvider
和CGImageCreate
是特定問題:
let dataProvider = CGDataProviderCreateWithData(nil, rgbaImage, 4 * width * height, nil)!
let cgImage = CGImageCreate(width, height, 8, 32, width * 4, colorSpace!, bitmapInfo, dataProvider, nil, true, CGColorRenderingIntent.RenderingIntentDefault)!
使用CGBitmapContextGetData
和CGBitmapContextCreateImage
有效解決方案如下:
func imageFromSampleBuffer(sampleBuffer: CMSampleBuffer) -> UIImage? {
let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer)!
CVPixelBufferLockBaseAddress(pixelBuffer, 0)
let lumaBaseAddress = CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 0)
let chromaBaseAddress = CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 1)
let width = CVPixelBufferGetWidth(pixelBuffer)
let height = CVPixelBufferGetHeight(pixelBuffer)
let lumaBytesPerRow = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 0)
let chromaBytesPerRow = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 1)
let lumaBuffer = UnsafeMutablePointer<UInt8>(lumaBaseAddress)
let chromaBuffer = UnsafeMutablePointer<UInt8>(chromaBaseAddress)
let contextBytesPerRow = Int(width) * 4
let contextByteCount = contextBytesPerRow * Int(height)
let colorSpace = CGColorSpaceCreateDeviceRGB()
let bitmapData = malloc(contextByteCount)
let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.PremultipliedFirst.rawValue | CGBitmapInfo.ByteOrder32Little.rawValue)
let context = CGBitmapContextCreate(bitmapData, width, height, 8, contextBytesPerRow, colorSpace, bitmapInfo.rawValue)
let data = CGBitmapContextGetData(context)
let rgbaImage = UnsafeMutablePointer<UInt8>(data)
for var x = 0; x < width; x++ {
for var y = 0; y < height; y++ {
let lumaIndex = x+y*lumaBytesPerRow
let chromaIndex = (y/2)*chromaBytesPerRow+(x/2)*2
let yp = lumaBuffer[lumaIndex]
let cb = chromaBuffer[chromaIndex]
let cr = chromaBuffer[chromaIndex+1]
let ri = Double(yp) + 1.402 * (Double(cr) - 128)
let gi = Double(yp) - 0.34414 * (Double(cb) - 128) - 0.71414 * (Double(cr) - 128)
let bi = Double(yp) + 1.772 * (Double(cb) - 128)
let r = UInt8(min(max(ri,0), 255))
let g = UInt8(min(max(gi,0), 255))
let b = UInt8(min(max(bi,0), 255))
rgbaImage[(x + y * width) * 4] = b
rgbaImage[(x + y * width) * 4 + 1] = g
rgbaImage[(x + y * width) * 4 + 2] = r
rgbaImage[(x + y * width) * 4 + 3] = 255
}
}
let quartzImage = CGBitmapContextCreateImage(context)
CVPixelBufferUnlockBaseAddress(pixelBuffer,0)
let image = UIImage(CGImage: quartzImage!, scale: CGFloat(1.0), orientation: UIImageOrientation.Right)
return (image)
// frontCameraImageOrientation = UIImageOrientation.LeftMirrored
// backCameraImageOrientation = UIImageOrientation.Right
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.