簡體   English   中英

在 iOS 上使用 Audio Queue 播放音頻一段時間后聲音靜音

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

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM