[英]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.