簡體   English   中英

使用 Audio Unit 錄音回調處理數據 [iOS][Swift]

[英]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)
        }

我試圖通過使用DispatchGroupsDispatchSourceTimersDispatchSemaphoresDispatchQueues來處理另一個線程內的繁重處理,但我就是無法獲得我需要的結果。 誰能幫忙?

任何人都可以指導我如何使編碼獨立於實時音頻線程,我試圖創建一個輪詢線程,但即使這樣也沒有用。 我需要幫助在具有不同數據大小要求的 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM