Hi, apologies if this is not the correct forum for this type of question - I'm not sure where else active to post.
I have a requirement to save and load a (non-planar) CVPixelBuffer to a file (in raw uncompressed binary format, not as png, jpg, etc), but cannot get CVPixelBufferCreateWithBytes to restore the data correctly.
The code fails with an exception "EXC_BAD_ACCESS (code=2, address=0x16bb0c1ff)" which occurs repeatedly within "libsystem_platform.dylib`_platform_memmove:".
import Foundation
import CoreImage
import UIKit
struct TestPixelBuffer
{
private static func debuginfo( _ heading : String, _ pixelBuffer : CVPixelBuffer )
{
let width = CVPixelBufferGetWidth( pixelBuffer )
, height = CVPixelBufferGetHeight( pixelBuffer )
, pixelbytes = height * width * 4
print( heading )
print( " width = \( width )" )
print( " height = \( height )" )
print( " pixelbytes = \( pixelbytes )" )
print( " dataSize = \( CVPixelBufferGetDataSize( pixelBuffer ) )" )
print( " bytesPerRow = \( CVPixelBufferGetBytesPerRow( pixelBuffer ) )" )
print( " formatType = \( CVPixelBufferGetPixelFormatType( pixelBuffer ) )" )
}
static func save( _ url : URL, _ pixelBuffer : CVPixelBuffer )
{
assert( !CVPixelBufferIsPlanar( pixelBuffer ) )
CVPixelBufferLockBaseAddress( pixelBuffer, CVPixelBufferLockFlags.readOnly )
if let baseAddress = CVPixelBufferGetBaseAddress( pixelBuffer )
{
let pointer = baseAddress.assumingMemoryBound( to: UInt8.self )
, rowbytes = CVPixelBufferGetBytesPerRow( pixelBuffer )
, width = CVPixelBufferGetWidth( pixelBuffer )
, height = CVPixelBufferGetHeight( pixelBuffer )
, pixbytes = rowbytes * height
, data = Data( bytes: pointer, count: pixbytes )
try! data.write( to: url )
Self.debuginfo( "saved", pixelBuffer )
// For testing purposes, load back the same data immediately
let _ = Self.load( url, width, height )
}
}
static func load( _ url : URL, _ width : Int, _ height : Int ) -> CVPixelBuffer?
{
if var data = try? Data( contentsOf: url, options: .uncached )
{
let bytesPerRow = width * 4
var output : CVPixelBuffer?
let result = CVPixelBufferCreateWithBytes(
kCFAllocatorDefault, width, height, kCVPixelFormatType_32BGRA,
&data, bytesPerRow, nil, nil, nil, &output )
guard result == kCVReturnSuccess, let pixelBuffer = output else
{
return nil
}
Self.debuginfo( "loaded", pixelBuffer )
// For testing purposes, check the data loaded correctly by using it
let ciimage = CIImage( cvPixelBuffer : pixelBuffer ) // ok
, uiimage = UIImage( ciImage: ciimage ) // ok
, _ = uiimage.pngData() // EXC_BAD_ACCESS here
return pixelBuffer
}
return nil
}
}
The output of a run shows that the structure is (at least partially correctly) restored from the file data:
saved
width = 1280
height = 720
pixelbytes = 3686400
dataSize = 3686464
bytesPerRow = 5120
formatType = 1111970369
loaded
width = 1280
height = 720
pixelbytes = 3686400
dataSize = 3686400
bytesPerRow = 5120
formatType = 1111970369
I've assumed the additional bytes reported by CVPixelBufferGetDataSize (above: dataSize vs pixelbytes) are not essential to consider since the loaded structure has the correct metrics - ref: avfoundation - Why CVPixelBufferGetDataSize always return 32-byte more data? - Stack Overflow
Any help much appreciated - thanks.