繁体   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