我正在iOS上编写实时音频播放程序。
它从对等端接收音频RTP包,并将其放入音频队列中播放。
开始播放时,声音正常。但是在1或2分钟后,声音静音,并且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);
答案 0 :(得分:1)
问题已经解决。
关键是,你不能让音频队列缓冲区等待,你必须继续喂它,否则它可能会被静音。如果您没有足够的数据,请填写空白数据。
所以应该更改以下代码:
/tmp/scratch$ . PythonPath.sh
/tmp/scratch$ echo $PYTHONPATH
/tmp/scratch/../../../module1:/tmp/scratch/../../../module2:/tmp/scratch/../../../module3:/tmp/scratch/../../../module4
改为:
do
{
read_bytes_enabled = g_audio_playback_buf.GetReadByteLen();
if (read_bytes_enabled >= kAudioQueueBufferLength)
{
break;
}
usleep(10*1000);
}
while (true);
答案 1 :(得分:0)
如果使用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字节缓冲区来存储20ms的PCM数据16位,8kHz,单声道。 此解决方案更复杂,但比CPU带有空音频数据的伪无限循环更好。苹果对贪婪的应用程序非常惩罚;)
我希望此解决方案会有所帮助。