【问题标题】:Processing data with Audio Unit recording callback [iOS][Swift]使用音频单元录制回调处理数据 [iOS][Swift]
【发布时间】:2020-03-22 03:47:35
【问题描述】:

我正在创建一个使用 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
        }
}

【问题讨论】:

    标签: ios swift grand-central-dispatch audiounit opus


    【解决方案1】:

    Apple 建议不要在任何实时音频单元回调中使用信号量或调用 Swift 方法(例如编码器)。只需将数据复制到音频单元回调内的预分配循环缓冲区中即可。时期。在回调之外做所有其他事情。包括信号量和信号。

    因此,您需要创建一个轮询线程。

    在轮询循环、计时器回调或网络就绪回调中执行所有操作。只要 FIFO 中有足够的数据,就可以开始工作。足够频繁地调用(轮询)(足够高的轮询频率或计时器回调率),您不会丢失数据。在轮询循环的每次迭代中处理所有可以处理的数据(可能一次处理多个缓冲区,如果可用)。

    在开始发送之前,您可能需要预先填充循环缓冲区(可能是 640 UDP 帧大小的几倍),以解决网络和计时器抖动问题。

    【讨论】:

    • 感谢您的指导,我终于明白了它是如何工作的。我创建了一个单独的 while 线程,它正在执行所有处理,并且记录回调仅将数据推送到缓冲区。质量有所提高,我只需要使其更加优化。我在正确的轨道上吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-04-23
    • 1970-01-01
    • 2014-12-15
    • 1970-01-01
    • 2012-09-16
    • 2011-04-20
    相关资源
    最近更新 更多