回答晚了,但如果您仍然卡在播放 TCP 字节,请尝试按照我的回答,将您的 tcp 音频字节放入循环缓冲区并通过 AudioUnit 播放。
下面的代码从 TCP 接收字节并将它们放入 TPCircularBuffer
func tcpReceive() {
receivingQueue.async {
repeat {
do {
let datagram = try self.tcpClient?.receive()
var byteData = datagram?["data"] as? Data
let dataLength = datagram?["length"] as? Int
let _ = TPCircularBufferProduceBytes(&self.circularBuffer, byteData.bytes, UInt32(decodedLength * 2))
} catch {
fatalError(error.localizedDescription)
}
} while true
}
}
创建音频单元...
var desc = AudioComponentDescription(
componentType: OSType(kAudioUnitType_Output),
componentSubType: OSType(kAudioUnitSubType_VoiceProcessingIO),
componentManufacturer: OSType(kAudioUnitManufacturer_Apple),
componentFlags: 0,
componentFlagsMask: 0
)
let inputComponent = AudioComponentFindNext(nil, &desc)
status = AudioComponentInstanceNew(inputComponent!, &audioUnit)
if status != noErr {
print("Audio component instance new error \(status!)")
}
// Enable IO for playback
status = AudioUnitSetProperty(
audioUnit!,
kAudioOutputUnitProperty_EnableIO,
kAudioUnitScope_Output,
kOutputBus,
&flag,
SizeOf32(flag)
)
if status != noErr {
print("Enable IO for playback error \(status!)")
}
//Use your own format, I have sample rate of 16000 and pcm 16 Bit
var ioFormat = CAStreamBasicDescription(
sampleRate: 16000.0,
numChannels: 1,
pcmf: .int16,
isInterleaved: false
)
//This is playbackCallback
var playbackCallback = AURenderCallbackStruct(
inputProc: AudioController_PlaybackCallback, //This is a delegate where audioUnit puts the bytes
inputProcRefCon: UnsafeMutableRawPointer(Unmanaged.passUnretained(self).toOpaque())
)
status = AudioUnitSetProperty(
audioUnit!,
AudioUnitPropertyID(kAudioUnitProperty_SetRenderCallback),
AudioUnitScope(kAudioUnitScope_Input),
kOutputBus,
&playbackCallback,
MemoryLayout<AURenderCallbackStruct>.size.ui
)
if status != noErr {
print("Failed to set recording render callback \(status!)")
}
//Init Audio Unit
status = AudioUnitInitialize(audioUnit!)
if status != noErr {
print("Failed to initialize audio unit \(status!)")
}
//Start AudioUnit
status = AudioOutputUnitStart(audioUnit!)
if status != noErr {
print("Failed to initialize output unit \(status!)")
}
这是我的播放回调函数,我从循环缓冲区播放音频
func performPlayback(
_ ioActionFlags: UnsafeMutablePointer<AudioUnitRenderActionFlags>,
inTimeStamp: UnsafePointer<AudioTimeStamp>,
inBufNumber: UInt32,
inNumberFrames: UInt32,
ioData: UnsafeMutablePointer<AudioBufferList>
) -> OSStatus {
let buffer = ioData[0].mBuffers
let bytesToCopy = ioData[0].mBuffers.mDataByteSize
var bufferTail: UnsafeMutableRawPointer?
var availableBytes: UInt32 = 0
bufferTail = TPCircularBufferTail(&self.circularBuffer, &availableBytes)
let bytesToWrite = min(bytesToCopy, availableBytes)
var bufferList = AudioBufferList(
mNumberBuffers: 1,
mBuffers: ioData[0].mBuffers)
var monoSamples = [Int16]()
let ptr = bufferList.mBuffers.mData?.assumingMemoryBound(to: Int16.self)
monoSamples.append(contentsOf: UnsafeBufferPointer(start: ptr, count: Int(inNumberFrames)))
print(monoSamples)
memcpy(buffer.mData, bufferTail, Int(bytesToWrite))
TPCircularBufferConsume(&self.circularBuffer, bytesToWrite)
return noErr
}
对于 TPCircularBuffer,我使用了这个 pod
'TPCircularBuffer', '~> 1.6'