How does OSReadLittleInt16() translate to Swift? - swift

I want to translate my Obj-C code to Swift.
I got these 3 lines in Obj-C:
NSData* data = ...
unsigned char* bytes = (unsigned char*) data.bytes;
int16_t delta = OSReadLittleInt16(opticalEncoderBytes, 0);
The first two lines translate to:
NSData data = ...
let bytes = UnsafePointer<UInt8>(data.bytes)
The third line is not that easy as I don't know:
Does int16_t simply translate to Int16?
OSReadLittleInt16 is not available in Swift. Do I need to import something?
OSReadLittleInt16 is defined in usr/include/libkern/OSByteOrder.h

Use .bigEndian and .littleEndian
let i :Int16 = 1
print("i: \(i)")
let le :Int16 = i.littleEndian
print("le: \(le)")
let be :Int16 = i.bigEndian
print("be: \(be)")
i: 1
le: 1
be: 256
let data: NSData! = "12345678".dataUsingEncoding(NSUTF8StringEncoding)
let bytes = UnsafePointer<UInt16>(data.bytes)
let ui0 = bytes[0]
let ui1 = bytes[1]
print("ui0: \(String(ui0, radix:16))")
print("ui1: \(String(ui1, radix:16))")
let be0 = bytes[0].bigEndian
let be1 = bytes[1].bigEndian
print("be0: \(String(be0, radix:16))")
print("be1: \(String(be1, radix:16))")
let le0 = bytes[0].littleEndian
let le1 = bytes[1].littleEndian
print("le0: \(String(le0, radix:16))")
print("le1: \(String(le1, radix:16))")
ui0: 3231
ui1: 3433
be0: 3132
be1: 3334
le0: 3231
le1: 3433
Note that the default in iOS is little endian.

Here is an alternative approach: OSReadLittleInt16() is a defined
as a macro in <libkern/OSByteOrder.h> as
#define OSReadLittleInt16(base, byteOffset) _OSReadInt16(base, byteOffset)
The macro is not imported into Swift, but the _OSReadInt16()
function is, so you can do
let delta = UInt16(littleEndian: _OSReadInt16(bytes, 0))
A possible advantage is that this works also on odd offsets, even if the architecture allows only aligned memory access.

Related

How to get CVPixelBuffer handle from UnsafeMutablePointer<UInt8> in Swift?

I got a decoded AVFrame whose format shows 160/Videotoolbox_vld. After googled some articles(here) and viewed the FFmpeg source code(here, and here), the CVBuffer handle should be at AVFrame.data[3]. But the CVBuffer I got seems invalid, any CVPixelBufferGetXXX() function returns 0 or nil.
If I used the av_hwframe_transfer_data() like the ffmpeg's example/hw_decode.c did, the sample can be downloaded from HW to SW buffer. Its AVFrame.format will be nv12. After converted via sws_scale to bgra, the sample can be showed on view with correct content.
I think the VideoToolbox decoded frame is OK. The way I convert AVFrame.data[3] to CVBuffer may be wrong. Just learned accessing c pointer in swift but I am not sure how to read a resource handle(CVBuffer) in a pointer correctly.
The following is how I try to extract CVBuffer from AVFrame
var pFrameOpt: UnsafeMutablePointer<AVFrame>? = av_frame_alloc()
avcodec_receive_frame(..., pFrameOpt)
let data3: UnsafeMutablePointer<UInt8>? = pFrameOpt?.pointee.data.3
data3?.withMemoryRebound(to: CVBuffer.self, capacity: 1) { pCvBuf in
let fW = pFrameOpt!.pointee.width // print 3840
let fH = pFrameOpt!.pointee.height // print 2160
let fFmt = pFrameOpt!.pointee.format // print 160
let cvBuf: CVBuffer = pCvBuf.pointee
let a1 = CVPixelBufferGetDataSize(cvBuf) // print 0
let a2 = CVPixelBufferGetPixelFormatType(cvBuf) // print 0
let a3 = CVPixelBufferGetWidth(cvBuf) // print 0
let a4 = CVPixelBufferGetHeight(cvBuf) // print 0
let a5 = CVPixelBufferGetBytesPerRow(cvBuf) // print 0
let a6 = CVPixelBufferGetBytesPerRowOfPlane(cvBuf, 0) // print 0
let a7 = CVPixelBufferGetWidthOfPlane(cvBuf, 0) // print 0
let a8 = CVPixelBufferGetHeightOfPlane(cvBuf, 0) // print 0
let a9 = CVPixelBufferGetPlaneCount(cvBuf) // print 0
let a10 = CVPixelBufferIsPlanar(cvBuf) // print false
let a11 = CVPixelBufferGetIOSurface(cvBuf) // print nil
let a12 = CVPixelBufferGetBaseAddress(cvBuf) // print nil
let a13 = CVPixelBufferGetBaseAddressOfPlane(cvBuf, 0) // print nil
let b1 = CVImageBufferGetCleanRect(cvBuf) // print 0, 0, 0, 0
let b2 = CVImageBufferGetColorSpace(cvBuf) // print nil
let b3 = CVImageBufferGetDisplaySize(cvBuf) // print 0, 0, 0, 0
let b4 = CVImageBufferGetEncodedSize(cvBuf) // print 0, 0, 0, 0
let b5 = CVImageBufferIsFlipped(cvBuf) // print false
// bad exec
var cvTextureOut: CVMetalTexture?
CVMetalTextureCacheCreateTextureFromImage(kCFAllocatorDefault, ..., cvBuf, nil, .bgra8Unorm, 3840, 2160, 0, ...)
}
CVBuffer is not a fixed size, so rebinding the memory won't work in this way. You need to do this:
Unmanaged<CVBuffer>.fromOpaque(data!).takeRetainedValue()
However, the bottom line is FFmpeg's VideoToolbox backend is not creating a CVPixelBuffer with kCVPixelBufferMetalCompatibilityKey set to true. You won't be able to call CVMetalTextureCacheCreateTextureFromImage(...) successfully in any case.
You could consider using a CVPixelBufferPool with appropriate settings (including kCVPixelBufferMetalCompatibilityKey set to true) and then using VTPixelTransferSession to quickly copy FFmpeg's pixel buffer to your own.
It seems like I wrongly cast void* to CVPixelBuffer* instead of casting void* directly to CVPixelBuffer. I cannot find a swift way to do such c style casting from pointer to numeric value. (Using as! CVPixelBuffer causes crash).
So I create a function for void* to CVPixelBufferRef in C code to do such casting job.
// util.h
#include <CoreVideo/CVPixelBuffer.h>
CVPixelBufferRef CastToCVPixelBuffer(void* p);
// util.c
CVPixelBufferRef CastToCVPixelBuffer(void* p)
{
return (CVPixelBufferRef)p;
}
// BridgeHeader.h
#include "util.h"
Then pass the UnsafeMutablePointer<UInt8> in, get CVPixelBuffer handle out.
let pFrameOpt: UnsafeMutablePointer<AVFrame>? = ...
let data3: UnsafeMutablePointer<UInt8>? = pFrameOpt?.pointee.data.3
let cvBuf: CVBuffer = CastToCVPixelBuffer(data3).takeUnretainedValue()
let width = CVPixelBufferGetWidth(cvBuf) // print 3840
let height = CVPixelBufferGetHeight(cvBuf) // print 2160
Try this
let cvBuf: CVBuffer = Array(UnsafeMutableBufferPointer(start: data3, count: 3))
.withUnsafeBufferPointer {
$0.baseAddress!.withMemoryRebound(to: CVBuffer.self, capacity: 1) { $0 }
}.pointee
or maybe even
let cvBuf: CVBuffer = unsafeBitcast(UnsafeMutableBufferPointer(start: data3, count: 3), to: CVBuffer.self)
/**
#function CVPixelBufferGetBaseAddressOfPlane
#abstract Returns the base address of the plane at planeIndex in the PixelBuffer.
#discussion Retrieving the base address for a PixelBuffer requires that the buffer base address be locked
via a successful call to CVPixelBufferLockBaseAddress. On OSX 10.10 and earlier, or iOS 8 and
earlier, calling this function with a non-planar buffer will have undefined behavior.
#param pixelBuffer Target PixelBuffer.
#param planeIndex Identifying the plane.
#result Base address of the plane, or NULL for non-planar CVPixelBufferRefs.
*/
#available(iOS 4.0, *)
public func CVPixelBufferGetBaseAddressOfPlane(_ pixelBuffer: CVPixelBuffer, _ planeIndex: Int) -> UnsafeMutableRawPointer?
maybe you can try use CVPixelBufferLockBaseAddress before use CVPixelBufferGetBaseAddressOfPlane

how safe is UnsafeMutableRawPointer( variable )

If i want to obtain the unsafemutablerawpointer of a variable.
Yet without creating copies or buffer, what is the best/efficient way to do so?
The below example works!
var number:UInt = 5
let numberPointer = UnsafeMutableRawPointer(&number)
var pointer:UnsafeMutablePointer<UInt8> = numberPointer.bindMemory(to: UInt8.self, capacity: size)
pointer[0] = 88
print(numberPointer) // 88
Yet from apple's docs:
docs
It is not safe as in Apple's doc or as in my comment.
If you want to do it in some safe way, you may need to write something like this:
var number: UInt = 5
let size = MemoryLayout.size(ofValue: number)
withUnsafeMutableBytes(of: &number) {numberUmbp in
let numberPointer = numberUmbp.baseAddress!
let pointer: UnsafeMutablePointer<UInt8> = numberPointer.bindMemory(to: UInt8.self, capacity: size)
pointer[0] = 88
} //`pointer` (or `numberPointer`) is guaranteed to be valid only inside this closure
print(number) // 88
Of course, the pointer pointer is only valid inside the closure.
If you want to extract some more stable and permanent address, you cannot avoid creating copies or buffer, in the current specification of Swift.

Convert UnsafeMutablePointer<Int16> to UInt8

I am trying to convert this Int16 mutable pointer to UInt8 to be written on a OutputStream. I tried to use the function .withMemoryRebound but I don't know how to do it correctly. I would like to do it using this function, I tried once but no success. I am able to get something working with the code below, but I don't think it is correct.
unwrappedOutputStream.open()
let buffer: UnsafeMutablePointer<Int16> = avAudioPCMBuffer.int16ChannelData![0]
let size = MemoryLayout<UInt8>.size
let bound: UnsafeMutablePointer<UInt16> = UnsafeMutablePointer.allocate(capacity: 1)
bound.pointee = UInt16(bitPattern: buffer.pointee)
let bytePointer: UnsafeMutablePointer<UInt8> = UnsafeMutablePointer.allocate(capacity: 1)
bytePointer.pointee = UInt8(bound.pointee >> 0x8)
unwrappedOutputStream.write(bytePointer, maxLength: size)
bytePointer.pointee = UInt8(bound.pointee & 0xff)
unwrappedOutputStream.write(bytePointer, maxLength: size)
bound.deallocate(capacity: 1)
bytePointer.deallocate(capacity: 1)
unwrappedOutputStream.close()
I am currently using Swift 4, is there anything I can do?
Thank you and I appreciate your patience.
Casting an Unsafe(Mutable)Pointer<Int16> to UnsafePointer<Int8>
would simply be:
let buffer: UnsafeMutablePointer<Int16> = ...
let count: Int = ... // # of Int16 values
let result = buffer.withMemoryRebound(to: UInt8.self, capacity: 2 * count) {
outputStream.write($0, maxLength: 2 * count)
}

Find enum label in LLDB

I'm writing an audio converter using AudioToolbox on macOS and need a method to cross-reference what these integer enums in C are. My code:
let url = URL(fileURLWithPath: "/path/to/file.wav") as CFURL
var audioFile: ExtAudioFileRef? = nil
ExtAudioFileOpenURL(url, &audioFile)
var format = AudioStreamBasicDescription()
var propertySize = UInt32(MemoryLayout.stride(ofValue: format))
ExtAudioFileGetProperty(audioFile!, kExtAudioFileProperty_FileDataFormat, &propertySize, &format)
print(format.mFormatID) // prints 1819304813
I went through the header file and print out the keys one by one:
(lldb) p kAudioFormatLinearPCM
(AudioFormatID) $R2 = 1819304813
So I know it's Linear PCM. Is there a faster way to do it?

Pointers, Pointer Arithmetic, and Raw Data in Swift

My application uses a somewhat complex inmutable data structure that is encoded in a binary file. I need to have access to it at the byte level, avoiding any copying. Normally, I would use C or C++ pointer arithmetic and typecasts, to access and interpret the raw byte values. I would like to do the same with Swift.
I have found that the following works:
class RawData {
var data: NSData!
init(rawData: NSData) {
data = rawData
}
func read<T>(byteLocation: Int) -> T {
let bytes = data.subdataWithRange(NSMakeRange(byteLocation, sizeof(T))).bytes
return UnsafePointer<T>(bytes).memory
}
func example_ReadAnIntAtByteLocation5() -> Int {
return read(5) as Int
}
}
However, I am not sure how efficient it is. Do data.subdataWithRange and NSMakeRange allocate objects every time I call them, or are they just syntactic sugar for dealing with pointers?
Is there a better way to do this in Swift?
EDIT:
I have created a small Objective-C class that just encapsulates a function to offset a pointer by a given number of bytes:
#implementation RawDataOffsetPointer
inline void* offsetPointer(void* ptr, int bytes){
return (char*)ptr + bytes;
}
#end
If I include this class in the bridging header, then I can change my read method to
func read<T>(byteLocation: Int) -> T {
let ptr = offsetPointer(data.bytes, CInt(byteLocation))
return UnsafePointer<T>(ptr).memory
}
which will not copy data from my buffer, or allocate other objects.
However, it would still be nice to do some pointer arithmetic from Swift, if it were possible.
If you just want to do it directly, UnsafePointer<T> can be manipulated arithmetically:
let oldPointer = UnsafePointer<()>
let newPointer = oldPointer + 10
You can also cast a pointer like so (UnsafePointer<()> is equivalent to void *)
let castPointer = UnsafePointer<MyStruct>(oldPointer)
I would recommend looking into NSInputStream, which allows you to read NSData as a series of bytes (UInt8 in Swift).
Here is a little sample I put together in the playground:
func generateRandomData(count:Int) -> NSData
{
var array = Array<UInt8>(count: count, repeatedValue: 0)
arc4random_buf(&array, UInt(count))
return NSData(bytes: array, length: count)
}
let randomData = generateRandomData(256 * 1024)
let stream = NSInputStream(data: randomData)
stream.open() // IMPORTANT
var readBuffer = Array<UInt8>(count: 16 * 1024, repeatedValue: 0)
var totalBytesRead = 0
while (totalBytesRead < randomData.length)
{
let numberOfBytesRead = stream.read(&readBuffer, maxLength: readBuffer.count)
// Do something with the data
totalBytesRead += numberOfBytesRead
}
You can create an extension to read primitive types like so:
extension NSInputStream
{
func readInt32() -> Int
{
var readBuffer = Array<UInt8>(count:sizeof(Int32), repeatedValue: 0)
var numberOfBytesRead = self.read(&readBuffer, maxLength: readBuffer.count)
return Int(readBuffer[0]) << 24 |
Int(readBuffer[1]) << 16 |
Int(readBuffer[2]) << 8 |
Int(readBuffer[3])
}
}
I would recommend the simple way to use UnsafeArray.
let data = NSData(contentsOfFile: filename)
let ptr = UnsafePointer<UInt8>(data.bytes)
let bytes = UnsafeBufferPointer<UInt8>(start:ptr, count:data.length)