12

我正在为 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

4 回答 4

1

使用 CoreAudio 的音频排队服务大大简化了,确保您总是在播放完成后立即重新排队每个缓冲区,方法是在播放完成时触发的回调中这样做。当从网络接收到音频数据时,它应该位于一个单独的循环缓冲区中,这样网络和音频代码就不会直接耦合。

为确保您不会丢失音频,请将固定数量的缓冲区与数据排队;这充当抖动缓冲器。在所有缓冲区都排队之前不要开始播放。一旦每个缓冲区完成播放,立即将其与下一个数据包重新排队。如果没有数据可用,只需排队一个静音缓冲区;由于到达的音频数据包最终会赶上,这只会以额外的延迟为代价减少丢失的音频。

于 2010-10-06T16:43:48.097 回答
0

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

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

于 2010-04-26T13:46:33.167 回答
0

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

于 2010-04-12T00:10:42.530 回答
0

如果我没有足够的分数,我发现我可以在这里发布答案但不能发表评论是愚蠢的。我只是想添加到以下答案:

“可能是一旦您的缓冲区链欠载,您就需要重新开始播放。我从未真正运行过 AudioQueue 缓冲区,但我记得在 Win32 编程中就是这种情况,所以如果我是,请随时纠正我错误的。”

我实际上已经在我最近制作的音频播放器中测试了这个场景。我从头开始制作了一个 FLAC 解码器,目前它只支持 16 位歌曲。如果我偶然发现一首 24 位的歌曲,我会一直与我正在播放的歌曲失去同步——它根本不会播放——这可能需要任何时间,比如 30 秒来恢复。这使音频队列非常饥饿,当我终于开始再次将缓冲区发送到音频队列时,需要 30 秒的静音赶上下一首歌曲才能再次播放。

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

编辑:只要您为每个回调发布一个新缓冲区,您就永远不需要重新开始播放或任何事情。在我的播放器中,如果在“回调”下一个缓冲区时我没有完成对缓冲区的处理,则该缓冲区的线程将被阻塞,直到第一个缓冲区完成填充。这是通过 NSLock 完成的。这是当我的播放器不理解 24 位 FLAC 时我失去同步时 AudioQueues 严重饥饿的主要原因。当 AudioQueue 为您提供更多要填充的缓冲区时,NSLock 还可以防止任何竞争条件。我使用 3 个低延迟的缓冲区。太低的延迟会导致完全静音,因此您需要为您的系统找到一个“合适的尺寸”。

于 2014-06-20T02:55:36.620 回答