[英]The sound muted after playing audio with Audio Queue on iOS for a while
我正在编写 iOS 上的实时音频播放程序。
接收对端的音频RTP包,放入音频队列播放。
开始播放时,声音正常。 但是过了一两分钟,声音就静音了,AudioQueue API也没有报错。 回调函数继续正常调用,没有异常。
但它只是静音。
我的回调函数:
1:循环直到有足够的数据可以复制到音频队列缓冲区
do
{
read_bytes_enabled = g_audio_playback_buf.GetReadByteLen();
if (read_bytes_enabled >= kAudioQueueBufferLength)
{
break;
}
usleep(10*1000);
}
while (true);
2:复制到AudioQueue Buffer,入队。 这个回调函数一直正常运行,没有错误。
//copy to audio queue buffer
read_bytes = kAudioQueueBufferLength;
g_audio_playback_buf.Read((unsigned char *)inBuffer->mAudioData, read_bytes);
WriteLog(LOG_PHONE_DEBUG, "AudioQueueBuffer(Play): copy [%d] bytes to AudioQueue buffer! Total len = %d", read_bytes, read_bytes_enabled);
inBuffer->mAudioDataByteSize = read_bytes;
UInt32 nPackets = read_bytes / g_audio_periodsize; // mono
inBuffer->mPacketDescriptionCount = nPackets;
// re-enqueue this buffer
AudioQueueEnqueueBuffer(inAQ, inBuffer, 0, NULL);
这个问题已经解决。
关键是,你不能让音频队列缓冲区等待,你必须不断地喂它,否则它可能会被静音。 如果您没有足够的数据,请用空白数据填充。
因此应更改以下代码:
do
{
read_bytes_enabled = g_audio_playback_buf.GetReadByteLen();
if (read_bytes_enabled >= kAudioQueueBufferLength)
{
break;
}
usleep(10*1000);
}
while (true);
改成这样:
read_bytes_enabled = g_audio_playback_buf.GetReadByteLen();
if (read_bytes_enabled < kAudioQueueBufferLength)
{
memset(inBuffer->mAudioData, 0x00, kAudioQueueBufferLength);
}
else
{
inBuffer->mAudioDataByteSize = kAudioQueueBufferLength;
}
...
如果您使用 AudioQueuePause,您可以让 AudioQueue 等待。
在这个例子中,在 Swift 5 中,我使用了一个通用队列。 当这个队列为空时,就像你所做的那样,我在回调中用空数据填充我的缓冲区并调用 AudioQueuePause。 需要注意的是,在播放 AudioQueuePause 之前,所有 AudioQueueBuffer 都会通过 AudioQueueEnqueueBuffer 发送到 AudioQueueRef。
创建一个 userData 类以将您需要的所有内容发送到您的回调:
class UserData {
let dataQueue = Queue<Data>()
let semaphore = DispatchSemaphore(value: 1)
}
private var inQueue: AudioQueueRef!
private var userData = UserData()
在创建 AudioQueue 并启动它时提供此类的实例:
AudioQueueNewOutput(&inFormat, audioQueueOutputCallback, &userData, nil, nil, 0, &inQueue)
AudioQueueStart(inQueue, nil)
生成所有缓冲区,不要直接将它们排入队列:调用您的回调函数:
for _ in 0...2 {
var bufferRef: AudioQueueBufferRef!
AudioQueueAllocateBuffer(inQueue, 320, &bufferRef)
audioQueueOutputCallback(&userData, inQueue, bufferRef)
}
当您收到音频数据时,您可以调用一个方法来将您的数据排入队列并等待回调函数获取它:
func audioReceived(_ audio: Data) {
let dataQueue = userData.dataQueue
let semaphore = userData.semaphore
semaphore.wait()
dataQueue.enqueue(audio)
semaphore.signal()
// Start AudioQueue every time, if it's already started this call do nothing
AudioQueueStart(inQueue, nil)
}
最后,您可以实现这样的回调函数:
private let audioQueueOutputCallback: AudioQueueOutputCallback = { (inUserData, inAQ, inBuffer) in
// Get data from UnsageMutableRawPointer
let userData: UserData = (inUserData!.bindMemory(to: UserData.self, capacity: 1).pointee)
let queue = userData.dataQueue
let semaphore = userData.semaphore
// bind UnsafeMutableRawPointer to UnsafeMutablePointer<UInt8> for data copy
let audioBuffer = inBuffer.pointee.mAudioData.bindMemory(to: UInt8.self, capacity: 320)
if queue.isEmpty {
print("Queue is empty: pause")
AudioQueuePause(inAQ)
audioBuffer.assign(repeating: 0, count: 320)
inBuffer.pointee.mAudioDataByteSize = 320
} else {
semaphore.wait()
if let data = queue.dequeue() {
data.copyBytes(to: audioBuffer, count: data.count)
inBuffer.pointee.mAudioDataByteSize = data.count
} else {
print("Error: queue is empty")
semaphore.signal()
return
}
semaphore.signal()
}
AudioQueueEnqueueBuffer(inAQ, inBuffer, 0, nil)
}
就我而言,我使用 320 字节缓冲区来处理 20 毫秒的 16 位、8kHz、单声道 PCM 数据。 此解决方案更复杂,但比 CPU 的空音频数据的伪无限循环更好。 苹果对贪婪的应用程序非常严厉;)
我希望这个解决方案会有所帮助。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.