在iOS上使用音频队列播放音频一段时间后,声音静音
我正在iOS上编写一个实时音频播放程序 它从对等方接收音频RTP包,并将其放入音频队列中播放 开始播放时,声音正常。但在1或2分钟后,声音静音,并且AudioQueue API没有报告任何错误。回调函数继续正常调用,没有异常 但它只是沉默了 我的回调函数: 1:循环,直到有足够的数据可以复制到音频队列缓冲区在iOS上使用音频队列播放音频一段时间后,声音静音,ios,audioqueue,Ios,Audioqueue,我正在iOS上编写一个实时音频播放程序 它从对等方接收音频RTP包,并将其放入音频队列中播放 开始播放时,声音正常。但在1或2分钟后,声音静音,并且AudioQueue API没有报告任何错误。回调函数继续正常调用,没有异常 但它只是沉默了 我的回调函数: 1:循环,直到有足够的数据可以复制到音频队列缓冲区 do { read_bytes_enabled = g_audio_playback_buf.GetReadByteLen(); if (read_bytes_enable
do
{
read_bytes_enabled = g_audio_playback_buf.GetReadByteLen();
if (read_bytes_enabled >= kAudioQueueBufferLength)
{
break;
}
usleep(10*1000);
}
while (true);
2:复制到AudioQueue缓冲区,并将其排队。此回调函数保持正常运行,没有错误
//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;
}
...
read_bytes_enabled=g_audio_playback_buf.GetReadByteLen();
if(已启用读取字节mAudioData,0x00,kaudioquebufferlength);
}
其他的
{
inBuffer->mAudioDataByteSize=kAudioQueueBufferLength;
}
...
如果使用AudioQueuePause,可以让AudioQueue等待
在本例中,在Swift 5中,我使用了一个通用队列。当这个队列为空时,正如您所做的那样,我在callback和callaudioqueuepause中用空数据填充缓冲区。需要注意的是,在播放调用AudioQueuePause之前,所有AudioQueueBuffer都会使用AudioQueueBuffer发送到AudioQueueRef
创建userData类以将所需的所有内容发送到回调:
class UserData {
let dataQueue = Queue<Data>()
let semaphore = DispatchSemaphore(value: 1)
}
private var inQueue: AudioQueueRef!
private var userData = UserData()
生成所有缓冲区,不要直接将它们排队:调用回调函数:
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)
}
private let audioQueueOutputCallback:audioQueueOutputCallback={(inUserData、inAQ、inBuffer)在
//从UnsageMutableRawPointer获取数据
让userData:userData=(inUserData!.bindMemory(收件人:userData.self,容量:1).pointee)
让queue=userData.dataQueue
让信号量=userData.semaphore
//将UnsafeMutableRawPointer绑定到数据复制的UnsafeMutablePointer
让audioBuffer=inBuffer.pointee.mAudioData.bindMemory(to:UInt8.self,容量:320)
如果queue.isEmpty{
打印(“队列为空:暂停”)
音频队列暂停(inAQ)
audioBuffer.assign(重复:0,计数:320)
inBuffer.pointee.mAudioDataByteSize=320
}否则{
信号量。等待()
如果let data=queue.dequeue(){
data.copyBytes(to:audioBuffer,count:data.count)
inBuffer.pointee.mAudioDataByteSize=data.count
}否则{
打印(“错误:队列为空”)
信号量
返回
}
信号量
}
audioqueuenbuffer(inAQ、inBuffer、0、nil)
}
在我的例子中,我使用320字节的缓冲区来存储20ms的PCM数据,16位,8kHz,单声道。
这个解决方案更为复杂,但比CPU使用空音频数据的伪无限循环要好。苹果对贪婪的应用程序非常严厉;)
我希望这个解决方案能有所帮助。@davin zhao,我也面临同样的问题,我的有时不工作,声音停止,有两个问题要问你,可能答案也会解决我的问题1)KaudioqueBufferLength的值是多少2)如果可能,你会分享这个代码的示例吗?非常感谢你的帮助
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)
}