Writing structure to an outputStream in Swift 3 - ios

I'm trying to pass a structure in a stream so that it is then sent over the socket to another device. The code works, but the wrong data is sent. And each time random data is sent - then I'm doing something wrong. Where am I wrong? Here is my code:
public struct PStypes {
var u: UInt32 //< [X_XXXXXX V]
var i: UInt32 //< [X_XXXXXX A]
}
func sendMessage(message: String) {
var task = PStypes(u: 7, i: 9)
var bufferData = NSData(bytes: &task, length: 8)
var data:Data = bufferData as Data
var bufferDataSize = data.count
let bytesWritten = withUnsafePointer(to: &data) {
$0.withMemoryRebound(to: UInt8.self, capacity: bufferDataSize) {
outputStream.write($0, maxLength: bufferDataSize)
}
}
}

The problem is with this code:
let bytesWritten = withUnsafePointer(to: &data) {
$0.withMemoryRebound(to: UInt8.self, capacity: bufferDataSize) {
outputStream.write($0, maxLength: bufferDataSize)
}
}
this ends up giving you a pointer to the Data data structure itself not the data it holds. You can fix this using:
let bytesWritten = data.withUnsafeBytes {
outputStream.write($0, maxLength: 8)
}
this also simplifies the code a little!

var task = PStypes(u: 7, i: 9)
I think you are passing Int value instead of UInt32

Related

Stream microphone audio in Swift 5

I have a working connection between 2 IOS-Devices. Sending a live-stream from one camera to the other device works. But now i want to send mic-audio and this does not work. I get no error, but i just receive "click"-noices.
I also can see, that bytes are transmitted, but i do not know, where the failure is.
Below you find the sending and the receiving functions. I also insert the streaming-function, which works fine for transmitting video.
sender:
func recorder() {
let settings : Dictionary = ["AVSampleRateKey" : 44100.0,
"AVNumberOfChannelsKey" : 1,
"AVFormatIDKey" : 1819304813,
"AVLinearPCMIsNonInterleaved" : 0,
"AVLinearPCMIsBigEndianKey" : 0,
"AVLinearPCMBitDepthKey" : 16,
"AVLinearPCMIsFloatKey" : 0]
audioFormat = AVAudioFormat.init(settings: settings)
audioEngine = AVAudioEngine.init()
audioEngine?.inputNode.installTap(onBus: 0, bufferSize: 4410, format: audioEngine?.inputNode.outputFormat(forBus: 0), block: {buffer, when in
let audioBuffer = buffer.audioBufferList.pointee.mBuffers
let data : Data = Data.init(bytes: audioBuffer.mData!, count: Int(audioBuffer.mDataByteSize))
let arraySize = Int(buffer.frameLength)
let samples = Array(UnsafeBufferPointer(start: buffer.floatChannelData![0], count:arraySize))
self.streamData(data: data, len: 4410)
})
// Start audio engine
self.audioEngine?.prepare()
do {
try self.audioEngine?.start()
}
catch {
NSLog("cannot start audio engine")
}
if(self.audioEngine?.isRunning == true){
NSLog("Audioengine is running")
}
}
sender streamData (working fine for e.g. video)
func streamData(data : Data, len : Int)
{
var baseCaseCondition : Bool = false
var _len : Int = len
var _byteIndex : Int = 0
func recursiveBlock(block: #escaping (()->Void)->Void) -> ()->Void {
return { block(recursiveBlock(block: block)) }
}
let aRecursiveBlock :()->Void = recursiveBlock {recurse in
baseCaseCondition = (data.count > 0 && _byteIndex < data.count) ? true : false
if ((baseCaseCondition)) {
_len = (data.count - _byteIndex) == 0 ? 1 : (data.count - _byteIndex) < len ? (data.count - _byteIndex) : len
NSLog("START | byteIndex: %lu/%lu writing len: %lu", _byteIndex, data.count, _len)
var bytes = [UInt8](repeating:0, count:_len)
data.copyBytes(to: &bytes, from: _byteIndex ..< _byteIndex+_len )
_byteIndex += (self.outputStream?.write(&bytes, maxLength: _len))!
NSLog("END | byteIndex: %lu/%lu wrote len: %lu", _byteIndex, data.count, _len)
recurse()
}
}
if (self.outputStream!.hasSpaceAvailable){
aRecursiveBlock();
}
}
receiver:
func stream(_ aStream: Stream, handle eventCode: Stream.Event) {
...
case Stream.Event.hasBytesAvailable:
var thePCMBuffer : AVAudioPCMBuffer = AVAudioPCMBuffer.init(pcmFormat: (self.audioEngine?.inputNode.outputFormat(forBus: 0))!, frameCapacity: AVAudioFrameCount(mlen))!
thePCMBuffer.frameLength = thePCMBuffer.frameCapacity
let channels = UnsafeBufferPointer(start: thePCMBuffer.floatChannelData, count: Int(thePCMBuffer.format.channelCount))
_ = mdata?.copyBytes(to: UnsafeMutableBufferPointer(start: channels[0], count: Int(thePCMBuffer.frameLength)))
if((self.player?.isPlaying) != nil){
DispatchQueue.global(qos: .background).async {
// Background Thread
DispatchQueue.main.async {
// Run UI Updates
self.player?.scheduleBuffer(thePCMBuffer, completionHandler: {
NSLog("Scheduled buffer")
NSLog("\(self.player!.isPlaying)")
let arraySize = Int(thePCMBuffer.frameLength)
let samples = Array(UnsafeBufferPointer(start: thePCMBuffer.floatChannelData![0], count:arraySize))
for sample in samples{
NSLog("\(sample)")
}
})
}
}
}
}
mdata = Data.init()
mlen = DATA_LENGTH
}
break;
...
}
Found the solution i guess. I tried with one simulator and one real device. Now i read, that there es a problem because of different sample-rates. Running on 2 devices (or 2 simulators) just works fine.

Data to Buffers

If you had a file stored as data which is much larger than your buffer and you wanted to iterate over this data in pieces of a buffer size, what is a good way to do this? If you could provide some context, that would be great.
I was thinking,
let bufferSize: Int = 20000
let myData: Data = Data(..)
var buffer: ??? = ???
var theOffset: ??? = ???
func runWhenReady() {
buffer = &myData
let amount = sendingData(&buffer[bufferOffset] maxLength: bufferSize - bufferOffset)
bufferOffset += amount
}
// pseudocode
// from foundation, but changed a bit (taken from Obj-C foundations just for types)
// writes the bytes from the specified buffer to the stream up to len bytes. Returns the number of bytes actually written.
func sendingData(_ buffer: const uint8_t *, maxLength len: NSUInteger) -> Int {
...
}
If you want to iterate you need a loop.
This is an example to slice the data in chunks of bufferSize with stride.
let bufferSize = 20000
var buffer = [UInt8]()
let myData = Data(..)
let dataCount = myData.count
for currentIndex in stride(from: 0, to: dataCount, by: bufferSize) {
let length = min(bufferSize, dataCount - currentIndex) // ensures that the last chunk is the remainder of the data
let endIndex = myData.index(currentIndex, offsetBy: length)
buffer = [UInt8](myData[currentIndex..<endIndex])
// do something with buffer
}
Open the file
let fileUrl: URL = ...
let fileHandle = try! FileHandle(forReadingFrom: fileUrl)
defer {
fileHandle.closeFile()
}
Create buffer:
let bufferSize = 20_000
let buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: bufferSize)
defer {
buffer.deallocate()
}
Read until end of file is reached:
while true {
let bytesRead = read(fileHandle.fileDescriptor, buffer, bufferSize)
if bytesRead < 0 {
// handle error
break
}
if bytesRead == 0 {
// end of file
break
}
// do something with data in buffer
}

What is the best way to write a struct to file?

I have this two structs:
struct pcap_hdr_s {
UInt32 magic_number;
UInt16 version_major;
UInt16 version_minor;
int32_t thiszone;
UInt32 sigfigs;
UInt32 snaplen;
UInt32 network;
};
//packet header
struct pcaprec_hdr_s {
UInt32 ts_sec;
UInt32 ts_usec;
UInt32 incl_len;
UInt32 orig_len;
};
which are initialised as follows(for example):
let pcapHeader : pcap_hdr_s = pcap_hdr_s(magic_number: 0xa1b2c3d4,
version_major: 2,
version_minor: 4,
thiszone: 0,
sigfigs: 0,
snaplen:
pcap_record_size,
network: LINKTYPE_ETHERNET)
let pcapRecHeader : pcaprec_hdr_s = pcaprec_hdr_s(ts_sec: UInt32(ts.tv_sec),
ts_usec: UInt32(ts.tv_nsec),
incl_len: plen,
orig_len: length)
I tried to create Data/NSData objects of the structs like this:
//write pcap header
let pcapHeaderData : NSData = NSData(bytes: pcapHeader, length: sizeofValue(pcapHeader))
//write pcaprec header
let pcapRecHeaderData : NSData = NSData(bytes: pcapRecHeader, length: sizeofValue(pcapRecHeader))
but I always get this error for each line:
"Connot convert value if type 'pcap_hdr_s' to expected arguemnt type 'UsafeRawPointer?'"
I had a look at the documentation of UnsafeRawPointers in Swift, but I don't get it enough as for now, to create the NSData object from the structs.
Am I on the right way or is there a better one to accomplish my intend?
If this Data initialisation would work, my next steps would be
Append pcapRecHeaderData to pcapHeaderData
write pcapHeaderData atomically to file/url with the provided function of Data/NSData
EDIT:
//packet ethernet header
struct ethernet_hdr_s {
let dhost : [UInt8]
let shost : [UInt8]
let type : UInt16
};
let src_mac : [UInt8] = [0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB]
let dest_mac : [UInt8] = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55]
let ethernetHeader : ethernet_hdr_s = ethernet_hdr_s(dhost: dest_mac, shost: src_mac, type: 0x0800)
EDIT 2:
let payloadSize = packet.payload.count
let plen = (payloadSize < Int(pcap_record_size) ? payloadSize : Int(pcap_record_size));
bytesWritten = withUnsafePointer(to: &(packet.payload)) {
$0.withMemoryRebound(to: UInt8.self, capacity: Int(plen)) {
ostream.write($0, maxLength: Int(plen))
}
}
if bytesWritten != (Int(plen)) {
// Could not write all bytes, report error ...
NSLog("error in Writting packet payload, not all Bytes written: bytesWritten: %d|plen: %d", bytesWritten, Int(plen))
}
You can write arbitrary data to an InputStream without creating a
(NS)Data object first. The "challenge" is how to convert the pointer to
the struct to an UInt8 pointer as expected by the write method:
let ostream = OutputStream(url: url, append: false)! // Add error checking here!
ostream.open()
var pcapHeader = pcap_hdr_s(...)
let headerSize = MemoryLayout.size(ofValue: pcapHeader)
let bytesWritten = withUnsafePointer(to: &pcapHeader) {
$0.withMemoryRebound(to: UInt8.self, capacity: headerSize) {
ostream.write($0, maxLength: headerSize)
}
}
if bytesWritten != headerSize {
// Could not write all bytes, report error ...
}
In the same way you can read data from in InputStream:
let istream = InputStream(url: url)! // Add error checking here!
istream.open()
let bytesRead = withUnsafeMutablePointer(to: &pcapHeader) {
$0.withMemoryRebound(to: UInt8.self, capacity: headerSize) {
istream.read($0, maxLength: headerSize)
}
}
if bytesRead != headerSize {
// Could not read all bytes, report error ...
}
If the file was possibly created on a different platform with a
different byte order then you can check the "magic" and swap bytes
if necessary (as described on https://wiki.wireshark.org/Development/LibpcapFileFormat):
switch pcapHeader.magic_number {
case 0xa1b2c3d4:
break // Already in host byte order
case 0xd4c3b2a1:
pcapHeader.version_major = pcapHeader.version_major.byteSwapped
pcapHeader.version_minor = pcapHeader.version_minor.byteSwapped
// ...
default:
// Unknown magic, report error ...
}
To simplify the task of writing and reading structs one can define
custom extension methods, e.g.
extension OutputStream {
enum ValueWriteError: Error {
case incompleteWrite
case unknownError
}
func write<T>(value: T) throws {
var value = value
let size = MemoryLayout.size(ofValue: value)
let bytesWritten = withUnsafePointer(to: &value) {
$0.withMemoryRebound(to: UInt8.self, capacity: size) {
write($0, maxLength: size)
}
}
if bytesWritten == -1 {
throw streamError ?? ValueWriteError.unknownError
} else if bytesWritten != size {
throw ValueWriteError.incompleteWrite
}
}
}
extension InputStream {
enum ValueReadError: Error {
case incompleteRead
case unknownError
}
func read<T>(value: inout T) throws {
let size = MemoryLayout.size(ofValue: value)
let bytesRead = withUnsafeMutablePointer(to: &value) {
$0.withMemoryRebound(to: UInt8.self, capacity: size) {
read($0, maxLength: size)
}
}
if bytesRead == -1 {
throw streamError ?? ValueReadError.unknownError
} else if bytesRead != size {
throw ValueReadError.incompleteRead
}
}
}
Now you can write and read simply with
try ostream.write(value: pcapHeader)
try istream.read(value: &pcapHeader)
Of course this works only with "self-contained" structs like your
pcap_hdr_s and pcaprec_hdr_s.
You can convert pcap_hdr_s to Data and vice versa in Swift 3 with
pcap_hdr_s -> Data
var pcapHeader : pcap_hdr_s = pcap_hdr_s(magic_number ...
let data = withUnsafePointer(to: &pcapHeader) {
Data(bytes: UnsafePointer($0), count: MemoryLayout.size(ofValue: pcapHeader))
}
Data -> pcap_hdr_s
let header: pcap_hdr_s = data.withUnsafeBytes { $0.pointee }
Reference: round trip Swift number types to/from Data

NSMutableData's bytes to UnsafeMutableRawPointer not updating the value of mutableData

I am having a struct object. And a method, whose input is payload. Now I am creating a mutableData named packet, and it's mutable bytes are referring to ICMPHeader struct.
struct ICMPHeader {
var type:UInt8
var code:UInt8
var checksum:UInt16
var identifier:UInt16
var sequenceNumber:UInt16
};
func createPacket(payload:NSData) -> NSData(){
var packet:NSMutableData?
var icmpPtr:ICMPHeader = ICMPHeader(type: 0, code: 0, checksum: 0, identifier: 0, sequenceNumber: 0)
packet = NSMutableData(length: Int(MemoryLayout<ICMPHeader>.size + payload.length))
if packet != nil {
icmpPtr = packet!.mutableBytes.assumingMemoryBound(to: ICMPHeader.self).pointee
icmpPtr.type = type
icmpPtr.code = 0
icmpPtr.checksum = 0
icmpPtr.identifier = CFSwapInt16BigToHost(identifier)
icmpPtr.sequenceNumber = CFSwapInt16HostToBig(identifier)
memcpy(&icmpPtr + 1, payload.bytes, payload.length)
if (requiresChecksum) {
icmpPtr.checksum = in_cksum(packet!.bytes, bufferLen: packet!.length);
}
}
return packet
}
Mutable bytes are successfully getting binded to struct, and values are also getting updated in struct ICMPHeader.
The issue is changing the values in struct is not changing the value of mutable data packet.
And if, I am trying to recreate the packet after creating struct, then it is crashing.
package = NSMutableData(bytes: unsafeBitCast(icmpPtr, to: UnsafeMutableRawPointer.self), length: Int(MemoryLayout<ICMPHeader>.size + payload.length))
I have found the answer in XCode 8.1 Swift release notes.
After making the changes in struct object icmpPtr I have changed it back to buffer pointer.
var byteBuffer = [UInt8]()
withUnsafeBytes(of: &icmpPtr) {
(bytes: UnsafeRawBufferPointer) in byteBuffer += bytes
}
package.replaceBytes(in: NSMakeRange(0, byteBuffer.count), withBytes: byteBuffer)
So instead of creating the new Data object, I have replaced the bytes and it worked like a charm.
As per documentation:
XCode 8.1 Release Notes:
https://developer.apple.com/library/content/releasenotes/DeveloperTools/RN-Xcode/Introduction.html
Swift Section:
A new withUnsafeBytes(of:) function exposes a value's in-memory
representation as an UnsafeRawBufferPointer. This example copies a
heterogenous struct into a homogeneous array of bytes:
struct Header {
var x: Int
var y: Float
}
var header = Header(x: 0, y: 0.0)
var byteBuffer = [UInt8]()
withUnsafeBytes(of: &header) {
(bytes: UnsafeRawBufferPointer) in byteBuffer += bytes
}
A new Array.withUnsafeBytes method exposes an array's underlying
buffer as an UnsafeRawBufferPointer. This example copies an array of
integers into an array of bytes:
let intArray = [1, 2, 3]
var byteBuffer = [UInt8]()
intArray.withUnsafeBytes {
(bytes: UnsafeRawBufferPointer) in byteBuffer += bytes
}
So the final implementation is
struct ICMPHeader {
var type:UInt8
var code:UInt8
var checksum:UInt16
var identifier:UInt16
var sequenceNumber:UInt16
};
func createPacket(payload:NSData) -> NSData(){
var packet:NSMutableData?
var icmpPtr:ICMPHeader = ICMPHeader(type: 0, code: 0, checksum: 0, identifier: 0, sequenceNumber: 0)
packet = NSMutableData(length: Int(MemoryLayout<ICMPHeader>.size + payload.length))
if packet != nil {
icmpPtr = packet!.mutableBytes.assumingMemoryBound(to: ICMPHeader.self).pointee
icmpPtr.type = type
icmpPtr.code = 0
icmpPtr.checksum = 0
icmpPtr.identifier = CFSwapInt16BigToHost(identifier)
icmpPtr.sequenceNumber = CFSwapInt16HostToBig(identifier)
memcpy(&icmpPtr + 1, payload.bytes, payload.length)
if (requiresChecksum) {
icmpPtr.checksum = in_cksum(packet!.bytes, bufferLen: packet!.length);
}
var byteBuffer = [UInt8]()
withUnsafeBytes(of: &icmpPtr) {
(bytes: UnsafeRawBufferPointer) in byteBuffer += bytes
}
packet.replaceBytes(in: NSMakeRange(0, byteBuffer.count), withBytes: byteBuffer)
}
return packet
}

How do I send data from a GKPlayer to another in a match?

I would like to send NSData from a GKPlayer to another in a match. Therefore, the method sendDataToAllPlayers(_:withDataMode:error:) would be the most ideal.
func sendDataToAllPlayers(data: Int,
withDataMode Reliable: GKMatchSendDataMode,
error: NSErrorPointer) -> Bool {
return true
}
Though, how can I use the method above to send an Int variable?
var score:Int = 0
extension Int {
var data: NSData {
var source = self
return NSData(bytes: &source, length: sizeof(Int))
}
}
extension Double {
var data: NSData {
var source = self
return NSData(bytes: &source, length: sizeof(Double))
}
}
extension NSData {
var integerValue:Int {
var result = 0
getBytes(&result, length: sizeof(Int))
return result
}
var doubleValue:Double {
var result:Double = 0
getBytes(&result, length: sizeof(Double))
return result
}
}
let myIntegerData = 123.data // _NSInlineData
let backToInt = myIntegerData.integerValue // 123
let myDoubleData = 123.456789.data // _NSInlineData
let backToDouble = myDoubleData.doubleValue // 123.456789
This version for encapsulating an Int (or other type) into or extracting it fromNSData should work even between devices with different byte orders:
// at sender before sending data
let data = NSMutableData()
let archiver = NSKeyedArchiver(forWritingWithMutableData: data)
archiver.encodeInteger(score, forKey: "score")
archiver.finishEncoding()
// at receiver after receiving data
let unarchiver = NSKeyedUnarchiver(forReadingWithData: data)
let score = unarchiver.decodeIntegerForKey("score")
unarchiver.finishDecoding()

Resources