在iOS上使用音频队列播放音频一段时间后声音静音

时间:2015-06-12 02:32:09

标签: ios audioqueue

我正在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);

2 个答案:

答案 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带有空音频数据的伪无限循环更好。苹果对贪婪的应用程序非常惩罚;)

我希望此解决方案会有所帮助。