[英]Processing data with Audio Unit recording callback [iOS][Swift]
我正在創建一個使用 UDP 發送和接收數據的跨平台 VOIP 應用程序。 我正在使用音頻單元進行實時錄制和播放。 處理原始數據時,通信快速順暢,但當我涉及像OPUS這樣的編解碼器時,正在編碼並從 iPhone 發送到 Android 的數據之間有咔噠聲和爆裂聲。 我一直在努力解決這個問題。
從 Android 到 iPhone 的編碼數據可以完美播放,沒有任何問題。 我在錄制和播放時使用TPCircularBuffer來處理數據。
這是我到目前為止在錄音回調中的內容:
var samplesForEncoder: UInt32 = 640
var targetBuffer = [opus_int16](repeating: 0, count: 1500)
_ = TPCircularBufferProduceBytes(&circularBuffer, mData, inNumberFrames * 2)
self.samplesSinceLastCall += inNumberFrames
encodingQueue.async {
if self.samplesSinceLastCall > self.samplesForEncoder {
let samplesToCopy = min(self.bytesToCopy, Int(self.availableBytes))
self.bufferTailPointer = TPCircularBufferTail(&self.circularBuffer, &self.availableBytes)
memcpy(&self.targetBuffer, self.bufferTailPointer, samplesToCopy)
self.semaphore.signal()
self.semaphore.wait()
self.opusHelper?.encodeStream(of: self.targetBuffer)
self.semaphore.signal()
self.semaphore.wait()
TPCircularBufferConsume(&self.circularBuffer, UInt32(samplesToCopy))
self.samplesSinceLastCall = 0
self.semaphore.signal()
self.semaphore.wait()
}
}
這是編碼函數:
var encodedData = [UInt8](repeating: 0, count: 1500)
self.encodedLength = opus_encode(self.encoder!, samples, OpusSettings.FRAME_SIZE, &self.encodedData, 1500)
let opusSlice = Array(self.encodedData.prefix(Int(self.encodedLength!)))
self.seqNumber += 1
self.protoModel.sequenceNumber = self.seqNumber
self.protoModel.timeStamp = Date().currentTimeInMillis()
self.protoModel.payload = opusSlice.data
do {
_ = try self.udpClient?.send(data: self.protoModel)
} catch {
print(error.localizedDescription)
}
我試圖通過使用DispatchGroups 、 DispatchSourceTimers 、 DispatchSemaphores 、 DispatchQueues來處理另一個線程內的繁重處理,但我就是無法獲得我需要的結果。 誰能幫忙?
任何人都可以指導我如何使編碼獨立於實時音頻線程,我試圖創建一個輪詢線程,但即使這樣也沒有用。 我需要幫助在具有不同數據大小要求的 2 個線程之間傳輸數據。 我從麥克風接收到 341-342 字節,但我需要將 640 字節發送到編碼器,因此我合並了 2 個樣本並重新使用剩下的字節供以后使用。
@hotpaw2 推薦這個https://stackoverflow.com/a/58947295/12020007但我只需要多一點指導。
根據@hotpaw2 的回答更新了代碼:
錄音回調:
_ = TPCircularBufferProduceBytes(&circularBuffer, mData, inNumberFrames * 2)
self.samplesSinceLastCall += inNumberFrames
if !shouldStartSending {
startLooping()
}
更新的投票主題:
func startLooping() {
loopingQueue.async {
repeat {
if self.samplesSinceLastCall > self.samplesForEncoder {
let samplesToCopy = min(self.bytesToCopy, Int(self.availableBytes))
self.bufferTailPointer = TPCircularBufferTail(&self.circularBuffer, &self.availableBytes)
memcpy(&self.targetBuffer, self.bufferTailPointer, samplesToCopy)
self.semaphore.signal()
self.semaphore.wait()
self.opusEncodedStream = self.opusHelper?.encodeStream(of: self.targetBuffer)
self.semaphore.signal()
self.semaphore.wait()
self.send(stream: self.opusEncodedStream!)
self.semaphore.signal()
self.semaphore.wait()
TPCircularBufferConsume(&self.circularBuffer, UInt32(samplesToCopy))
self.samplesSinceLastCall = 0
}
self.shouldStartSending = true
} while true
}
}
Apple 建議不要在任何實時音頻單元回調中使用信號量或調用 Swift 方法(例如編碼器)。 只需將數據復制到音頻單元回調內的預分配循環緩沖區中。 時期。 執行回調之外的所有其他操作。 包括信號量和信號。
因此,您需要創建一個輪詢線程。
在輪詢循環、計時器回調或網絡就緒回調中執行所有操作。 只要 FIFO 中有足夠的數據,就可以開始工作。 足夠頻繁地調用(輪詢)(足夠高的輪詢頻率或計時器回調率),您不會丟失數據。 在輪詢循環的每次迭代中處理您可以處理的所有數據(可能一次處理多個緩沖區,如果可用的話)。
在開始發送之前,您可能需要稍微預填充循環緩沖區(可能是 640 UDP 幀大小的幾倍),以解決網絡和計時器抖動問題。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.