I'm trying to use NSInputStream for receiving data using TCP socket connection. On the server side I send data size before sending of the data itself. on the iOS client side I need to extract first 4 bytes from the NSInputStream, because I need to check if size of data has received completely, but I have a problem with it:
...
case NSStreamEvent.HasBytesAvailable:
if ( aStream == inputstream){
while (inputstream.hasBytesAvailable){
var readBufferRef = UnsafeMutablePointer<UnsafeMutablePointer<UInt8>>()
var readBufferLengthRef = 0
let readBufferIsAvailable = inputstream.getBuffer(readBufferRef, length: &readBufferLengthRef)
...
}
}
break
After receiving of data readBufferLengthRef always equals to 0.
How it can be?
And how can I get size of the NSInputStream buffer?
UPD:
Code:
case NSStreamEvent.HasBytesAvailable:
NSLog("HasBytesAvaible")
var buffer = [UInt8](count: 1024, repeatedValue: 0)
if ( aStream == inputstream){
while (inputstream.hasBytesAvailable){
var readBufferRef: UnsafeMutablePointer<UInt8> = nil
var readBufferLengthRef = 0
let readBufferIsAvailable = inputstream.getBuffer(&readBufferRef, length: &readBufferLengthRef)
//debugger: readBufferLengthRef = (int)0
}
}
break
In your code, readBufferRef is defined as a "pointer to a pointer"
but never allocated, and therefore it is the NULL pointer.
What you should do is to pass the address of an
UnsafeMutablePointer<UInt8> as an inout argument to the function
(assuming Swift 2):
var readBufferRef: UnsafeMutablePointer<UInt8> = nil
var readBufferLengthRef = 0
let readBufferIsAvailable = inputStream.getBuffer(&readBufferRef, length: &readBufferLengthRef)
On return, readBufferRef is set to the read buffer of the stream (valid until the next read operation), and readBufferLengthRef contains
the number of available bytes.
Related
I've gone over the code for this decoder for elementary h.264 bitstreams a hundred times, tweaking things along the way, with no luck. When I send the output CMSampleBuffers to an AVSampleBufferDisplayLayer, they don't appear, presumably because there's something wrong with how I'm decoding them.
I get no error messages anywhere; the AVSampleBufferDisplayLayer has no error and "status" is "1" (aka .rendering), CMSampleBufferIsValid() returns "true" on the outputted CMSampleBuffers, and I encounter no errors in my decoder either.
I'm stumped and my hope is that a more experienced developer can catch something that I'm missing.
I input raw bytes here (typealias FrameData = [UInt8])
func interpretRawFrameData(_ frameData: inout FrameData) -> CMSampleBuffer? {
let size = UInt32(frameData.count)
var naluType = frameData[4] & 0x1F
var frame: CMSampleBuffer?
// The start indices for nested packets. Default to 0.
var ppsStartIndex = 0
var frameStartIndex = 0
switch naluType {
// SPS
case 7:
print("===== NALU type SPS")
for i in 4..<40 {
if frameData[i] == 0 && frameData[i+1] == 0 && frameData[i+2] == 0 && frameData[i+3] == 1 {
ppsStartIndex = i
spsSize = i - 4 // Does not include the size of the header
sps = Array(frameData[4..<i])
// Set naluType to the nested PPS packet's NALU type
naluType = frameData[i + 4] & 0x1F
break
}
}
// If nested frame was found, fallthrough
if ppsStartIndex != 0 { fallthrough }
// PPS
case 8:
print("===== NALU type PPS")
for i in ppsStartIndex+4..<ppsStartIndex+34 {
if frameData[i] == 0 && frameData[i+1] == 0 && frameData[i+2] == 0 && frameData[i+3] == 1 {
frameStartIndex = i
ppsSize = i - spsSize - 8 // Does not include the size of the header. Subtract 8 to account for both the SPS and PPS headers
pps = Array(frameData[ppsStartIndex+4..<i])
// Set naluType to the nested packet's NALU type
naluType = frameData[i+4] & 0x1F
break
}
}
// If nested frame was found, fallthrough
if frameStartIndex != 0 { fallthrough }
// IDR frame
case 5:
print("===== NALU type IDR frame");
// Replace start code with size
let adjustedSize = size - UInt32(ppsSize) - UInt32(spsSize) - 8
var blockSize = CFSwapInt32HostToBig(adjustedSize)
memcpy(&frameData[frameStartIndex], &blockSize, 4)
if createFormatDescription() {
frame = decodeFrameData(Array(frameData[frameStartIndex...]))
}
// B/P frame
default:
print("===== NALU type B/P frame");
// Replace start code with size
var blockSize = CFSwapInt32HostToBig(size)
memcpy(&frameData[frameStartIndex], &blockSize, 4)
frame = decodeFrameData(Array(frameData[frameStartIndex...]))
break;
}
return frame != nil ? frame : nil
}
And this is where the actual decoding happens:
private func decodeFrameData(_ frameData: FrameData) -> CMSampleBuffer? {
let bufferPointer = UnsafeMutablePointer<UInt8>(mutating: frameData)
let size = frameData.count
var blockBuffer: CMBlockBuffer?
var status = CMBlockBufferCreateWithMemoryBlock(kCFAllocatorDefault,
bufferPointer,
size,
kCFAllocatorNull,
nil, 0, frameData.count,
0, &blockBuffer)
if status != kCMBlockBufferNoErr { return nil }
var sampleBuffer: CMSampleBuffer?
let sampleSizeArray = [size]
status = CMSampleBufferCreateReady(kCFAllocatorDefault,
blockBuffer,
formatDesc,
1, 0, &sampleTimingInfo,
1, sampleSizeArray,
&sampleBuffer)
if let buffer = sampleBuffer, status == kCMBlockBufferNoErr {
let attachments: CFArray? = CMSampleBufferGetSampleAttachmentsArray(buffer, true)
if let attachmentArray = attachments {
let dic = unsafeBitCast(CFArrayGetValueAtIndex(attachmentArray, 0), to: CFMutableDictionary.self)
let key = Unmanaged.passUnretained(kCMSampleAttachmentKey_DisplayImmediately).toOpaque()
let val = Unmanaged.passUnretained(kCFBooleanTrue).toOpaque()
CFDictionarySetValue(dic,
key,
val)
}
print("===== Successfully created sample buffer")
return buffer
}
return nil
}
Other things to note:
The formatDescription contains the correct information (mediaType = "vide", mediaSubType = "avc1", dimensions = "640x480")
The bitstream I'm decoding always groups the SPS, PPS, and IDR frames together and sends them as one big packet every 20 or so frames. Every other time, an individual B/P frame is sent.
Thanks!
That code was pretty ugly so I decided to touch it up a little bit. Turned out that did the trick. Something must have been wrong in there.
Anyways, here's a working version. It sends the decoded & decompressed frame to its delegate. Ideally, whoever calls interpretRawFrameData would be returned a displayable frame, and I'll work on that, but in the meantime this works too.
https://github.com/philipshen/H264Decoder
whole code is like this:
var inputStream :NSInputStream?
var outputStream:NSOutputStream?
NSStream.getStreamsToHostWithName(ip, port: port, inputStream: &inputStream, outputStream: &outputStream)
let reader = inputStream
let writer = outputStream
writer?.open()
reader?.open()
var message : UInt8 = 0
while reader!.read(&message, maxLength: 1)>0
{
let wa = NSString(bytes: &message, length: 1, encoding: CFStringConvertEncodingToNSStringEncoding(CFStringEncoding(CFStringEncodings.GB_18030_2000.rawValue))) as! String
}
when the message i receive is a chinese character, the last line throws this:
fatal error:unexpected found nil while unwrapping an optional value, at the mean time, the value of message is 196
does anybody know how to solve this problem?
According to this page, if the byte you read is in the range 0x81-0xfe, the character is encoded using two or four bytes, so trying to decode just the first byte will fail. The NSString constructor will return nil, and attempting to unwrap it (with as! String) will throw that error.
You need to check what byte you read, and read another byte if necessary based on the first byte. Then you need to check the second byte, and possible read two more bytes. Finally, you need to pass all of the one, two, or four bytes to the NSString constructor in a single buffer.
Try to change message type for inputData like this for example:
let queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
dispatch_async(queue) {
let bufferSize = 1024
var message = Array<UInt8>(count:bufferSize, repeatedValue: 0)
while true {
let bytesRead = self.inputStream!.read(&message, maxLength: bufferSize)
let responseString = NSString(bytes: message, length: message.count, encoding: NSUTF8StringEncoding) as! String
// Do somthing with response
...
}
}
I receive binary information via stream in Swift. Lets say the information is a picture. I now want to save the picture. How is this possible?
I tried following:
let bufferSize = 154000
var buffer = [UInt8](count: bufferSize, repeatedValue: 0)
var bytesRead = inputStream?.read(&buffer, maxLength: bufferSize)
if bytesRead > 0 {
var bytesWrittenSoFar = 0
do {
var diffbytes = bytesRead! - bytesWrittenSoFar
fileStream?.open()
fileStream?.write(UnsafePointer(&buffer[bytesWrittenSoFar]), maxLength: diffbytes)
} while (bytesWrittenSoFar != bytesRead);
But when I try to write (fileStream?.write...) I get following error: "Could not find an overload for 'init' that accepts the supplied arguments
Thank you for your answer in advance!
The problem is with the initialization of UnsafePointer. In this case, you don't need it at all, you can just pass &buffer[bytesWrittenSoFar], as that is an acceptable value to pass to a function that needs an unsafePointer, per the discussion in the apple book "Using Swift with Cocoa and Objective-C".
I'm using GCDAsyncSocket. Write an application, which is used to transfer files between two devices, have a few problems when receiving the file:
When I want to send a size of about 60 KB text file to another receiving devices(iOS devices or iOS Simulator), need to add some packet information before data, like this:
2 bytes header version,
1 byte identify data or command,
4 bytes data size,
25 bytes the reserved space,
After 32 bytes is file data, text files or image files or other files.
So, before I call the socket.writeData, I write a NSMutableData,and send to receiver device.
var sendData: NSMutableData = NSMutableData()
var fileData: NSData = NSData(contentsOfFile: "file path", options: NSDataReadingOptions.DataReadingMapped, error: &error)
// TCP_HEADER_VERSION is Int16, value is 1
var headerVersion = TCP_HEADER_VERSION.bigEndian
var unPackVersionData: NSData = NSData(bytes: &headerVersion, length: 2)
sendData.appendData(unPackVersionData)
// dataType is Int, value is 0
var dataTypeTemp = dataType
sendData.appendData(NSData(bytes: &dataTypeTemp, length: 1))
// dataSize is Int64, value is fileData.size
var dataSizeTemp = dataSize.bigEndian
var packSizeData: NSData = NSData(bytes: &dataSizeTemp, length: 4)
sendData.appendData(packSizeData)
sendData.appendData(NSData(bytes: [0] as Array<Int>, length: 25))
sendData.appendData(fileData)
// send data
socket?.writeData(data, withTimeout: -1, tag: 0)
socket?.readDataWithTimeout(-1, tag: 0)
Every thing is ok at send device, the problem at receiver device
Received four packets. Every packet size is different, but I just send once at sender device, and socket header is chaotic.
// receiver device
func socket(sock: GCDAsyncSocket!, didReadData data: NSData!, withTag tag: Int) {
sock.readDataWithTimeout(-1, tag: 0)
println(data)
// PACKET_HEADER_LENGTH is 32
var packetHeader: NSData = data.subdataWithRange(NSMakeRange(0, PACKET_HEADER_LENGTH))
var packetContent: NSData = data.subdataWithRange(NSMakeRange(PACKET_HEADER_LENGTH, data.length - PACKET_HEADER_LENGTH))
var headerVersion: Int16 = 0
headerPacket.getBytes(&headerVersion, range: NSMakeRange(0, 2))
// headerVersion is wrong, not 1
headerVersion = headerVersion.bigEndian
// packetType is wrong, not 0
var packetType: Int = 0
headerPacket.getBytes(&packetType, range: NSMakeRange(2, 1))
var packetSize: Int64 = 0
headerPacket.getBytes(&packetSize, range: NSMakeRange(3, 4))
packetSize = packetSize.bigEndian
}
And I found some discussion about read data:
Having trouble programming streams
In end of the data add [GCDAsyncSocket CRLFData], at didReadData before use data add readDataToData:[GCDAsyncSocket CRLFData], but also not solve the problem.
Anyone can help me? Thanks!
Well.. I know how to solve this problem.
When I send packet to receiver, is like this:
64kb ... send
64kb ... send
64kb ... send
// ... send other datas to finish
At receiver device:
64kb ... waiting other datas
64kb ... waiting other datas
64kb ... OK call GCDAsyncSocket.didReadData delegate
// clear buffer
64kb ... waiting other datas
64kb ... OK call GCDAsyncSocket.didReadData delegate
So, When receive datas, need save a buffer file ,when send finish, handle buffer file to real file.
If anyone know how to set GCDAsyncSocket buffer size, please tell me.
Hope I can help others guys, thanks.
I'm trying to build an asynchronous file download in Swift based on the Erica Sadun's method. But I need it to handle bigger files, so I found this answer about using a NSOutputStream instead of NSData, makes sense.
However, I can't get it to work. I get this error when I try adding the NSData bytes (in my NSURLConnection didReceiveData function) to the NSOutputStream write function: '()' is not identical to 'UInt8' on this row: bytesWritten = self.downloadStream.write(data.bytes, maxLength: bytesLeftToWrite).
data.bytes is of the type ConstUnsafePointer<()> and the .write() function expects the type to be ConstUnsafePointer<UInt8>, so in that sense, the error make perfect sense. But since I'm new to iOS and of course Swift programming, I can't get my head around how to fix this.
So, how do I convert the data.bytes: ConstUnsafePointer<()> to ConstUnsafePointer<UInt8> alt. make this work some other way?
My didReceiveData function:
func connection(connection: NSURLConnection!, didReceiveData data: NSData!) {
var bytesLeftToWrite: NSInteger = data.length
var bytesWritten: NSInteger = 0
while bytesLeftToWrite > 0 {
bytesWritten = self.downloadStream.write(data.bytes, maxLength: bytesLeftToWrite)
if bytesWritten == -1 {
break
}
bytesLeftToWrite -= bytesWritten
let responseExpectedlenght: NSNumber = NSNumber(longLong: self.downloadResponse!.expectedContentLength)
let dataLength: NSNumber = NSNumber(long: data.length)
self.downloadProgressPercentage = ((dataLength / responseExpectedlenght) * 100)
println("Downloaded: \(self.downloadProgressPercentage)%")
}
}
You can cast the pointer with UnsafePointer():
bytesWritten = self.downloadStream.write(UnsafePointer(data.bytes), maxLength: bytesLeftToWrite)
There is also a problem in your write loop, because you always write the
initial bytes of the data object to the output stream.
It should probably look similar to this (untested):
var bytes = UnsafePointer<UInt8>(data.bytes)
var bytesLeftToWrite: NSInteger = data.length
while bytesLeftToWrite > 0 {
let bytesWritten = self.downloadStream.write(bytes, maxLength: bytesLeftToWrite)
if bytesWritten == -1 {
break // Some error occurred ...
}
bytesLeftToWrite -= bytesWritten
bytes += bytesWritten // advance pointer
// ...
}
I'd would suggest availing yourself of enumerateByteRangesUsingBlock, because NSData no longer guarantees that the underlying data will be held in a single contiguous memory block. For example, according to the documentation for didReceiveData of the NSURLSessionDataDelegate protocol:
Because the NSData object is often pieced together from a number of different data objects, whenever possible, use NSData’s enumerateByteRangesUsingBlock: method to iterate through the data rather than using the bytes method (which flattens the NSData object into a single memory block).
Thus, for example, you could do an extension of NSOutputStream that writes the contents of a NSData:
extension NSOutputStream {
/// Write contents of NSData to `NSOutputStream`
///
/// - parameter data: The `NSData` being written to the stream.
///
/// - returns: The number of bytes written. In case of error, returns -1.
func writeData(data: NSData) -> Int {
var totalBytesWritten = 0
data.enumerateByteRangesUsingBlock() {
buffer, range, stop in
var bytes = UnsafePointer<UInt8>(buffer)
var bytesWritten = 0
var bytesLeftToWrite = range.length
while bytesLeftToWrite > 0 {
bytesWritten = self.write(bytes, maxLength: bytesLeftToWrite)
if bytesWritten < 0 {
stop.initialize(true)
totalBytesWritten = -1
return
}
bytes += bytesWritten
bytesLeftToWrite -= bytesWritten
totalBytesWritten += bytesWritten
}
}
return totalBytesWritten
}
}
Note, the technique of stopping the enumeration in case of error, namely stop.initialize(true), requires Xcode 6 beta 4 or later. Earlier versions of Xcode (and associated compiler) used a more awkward construction for updating the boolean reference to stop the enumeration.