使用音频队列服务通过套接字连接播放PCM数据

时间:2010-04-07 20:58:03

标签: iphone audioqueueservices

我正在为iPhone编写远程桌面客户端,我正在尝试实现音频重定向 客户端通过套接字连接连接到服务器,服务器一次发送32K块PCM数据。

我正在尝试使用AQS来播放数据并且它播放前两秒(1个缓冲区值)。但是,由于下一个数据块尚未进入套接字,因此下一个AudioQueueBuffer为空。当数据进入时,我用数据填充下一个可用缓冲区并将其与AudioQueueEnqueueBuffer一起排队。但是,它永远不会播放这些缓冲区。

如果队列中没有缓冲区,队列是否会停止播放,即使您以后添加了缓冲区?

以下是代码的相关部分:

void
wave_out_write(STREAM s, uint16 tick, uint8 index)
{

    if(items_in_queue == NUM_BUFFERS){
        return;
    }
    if(!playState.busy){
        OSStatus status;
        status = AudioQueueNewOutput(&playState.dataFormat, AudioOutputCallback, &playState, CFRunLoopGetCurrent(), NULL, 0, &playState.queue);

        if(status == 0){
            for(int i=0; i<NUM_BUFFERS; i++){
                AudioQueueAllocateBuffer(playState.queue, 40000, &playState.buffers[i]);

            }
            AudioQueueAddPropertyListener(playState.queue, kAudioQueueProperty_IsRunning, MyAudioQueuePropertyListenerProc, &playState);

            status = AudioQueueStart(playState.queue, NULL);
            if(status ==0){
                playState.busy = True;
            }
            else{
                return;
            }
        }
        else{
            return;
        }
    }
    playState.buffers[queue_hi]->mAudioDataByteSize = s->size;

    memcpy(playState.buffers[queue_hi]->mAudioData, s->data, s->size);

    AudioQueueEnqueueBuffer(playState.queue, playState.buffers[queue_hi], 0, 0);
    queue_hi++;
    queue_hi = queue_hi % NUM_BUFFERS;
    items_in_queue++;
}


void AudioOutputCallback(void* inUserData, AudioQueueRef outAQ, AudioQueueBufferRef outBuffer)
{
    PlayState *playState = (PlayState *)inUserData;
    items_in_queue--;
}

谢谢!

4 个答案:

答案 0 :(得分:1)

使用CoreAudio的音频排队服务可以通过确保在完成播放后立即对每个缓冲区重新排队来大大简化,方法是在播放完成后触发的回调中执行此操作。当从网络接收音频数据时,音频数据应位于单独的循环缓冲区中,以便网络和音频代码不会直接耦合。

为确保不丢弃音频,请使用数据排列固定数量的缓冲区;这充当抖动缓冲区。在所有缓冲区排队之前不要开始播放。每个缓冲区完成播放后,立即使用下一个数据包重新排队。如果没有可用数据,只需排队沉默缓冲区;由于到达的音频数据包最终会赶上,这只会减少音频丢失,但会造成额外的延迟。

答案 1 :(得分:0)

您可能会发现此问题的答案很有用:AudioQueue ate my buffer (first 15 milliseconds of it)

答案 2 :(得分:0)

通常,使用循环音频缓冲区时,应防止缓冲区欠载。如果您缺少必要的数据(例如由于网络拥塞),请尝试用静音填充音频数据或暂停音频播放。

可能一旦你的缓冲链不足,你需要重新开始播放。我从来没有真正欠载过AudioQueue缓冲区,但我记得Win32编程中就是这种情况,所以如果我错了,请随时纠正我。

答案 3 :(得分:0)

如果我没有足够的积分,我发现我可以发布答案而不是评论,我觉得很愚蠢。我只想补充以下答案:

&#34;可能一旦你的缓冲链不足,你需要重新开始播放。我从来没有真正欠载过AudioQueue缓冲区,但我记得在Win32编程中就是这种情况,所以如果我错了,请随时纠正我。&#34;

我实际上已经在我最近制作的音频播放器中测试了这种情况。我从零开始制作FLAC解码器,目前仅支持16位歌曲。如果我偶然发现一首24位的歌曲,我会一直与我正在播放的歌曲失去同步 - 它根本不会播放 - 这可能需要花费任何时间,例如30秒来做一个例子,恢复。这使得音频队列非常糟糕,当我最终再次开始向音频队列发送缓冲区时,需要30秒的静音才能进入下一首歌曲以便再次播放。

这只是我的观察,我还没有考虑过为什么我会观察这种行为。也许它会丢弃样本以匹配样本数量AudioQueue认为它应该正在播放 - 它在饥饿期间丢失了吗?我的音频播放器似乎快速播放这首歌,直到达到它想再次播放的程度。

编辑:只要你为每个回调发布一个新的缓冲区,你就永远不需要重启游戏或任何东西。在我的播放器中,如果我没有在下一个缓冲区被回调时处理缓冲区,则该缓冲区的线程将被阻塞,直到第一个缓冲区完成填充。这是通过NSLock完成的。当我的播放器还没有理解24位FLAC时,当我失去同步时,这就是AudioQueues严重缺乏的主要原因。当AudioQueue为您提供更多缓冲区时,NSLock还可以防止任何竞争条件。我使用3个低延迟的缓冲区。太低的延迟使得完全沉默,所以你需要找到一个好的尺寸&#39;为您的系统。