2010-04-07 73 views
11

我正在为iPhone编写远程桌面客户端,我试图实现音频重定向。
客户端通过套接字连接连接到服务器,并且服务器一次发送32K大小的PCM数据块。使用音频队列服务通过套接字连接播放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--; 
} 

谢谢!

回答

0

通常,使用圆形音频缓冲时,你应防止缓冲区钻入。如果您缺少必要的数据(由于网络拥塞等原因),请尝试使用静音填充音频数据或暂停音频播放。

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

+3

你不必填充任何沉默。如果在调用回调函数时没有任何数据返回队列,则不要在其中放入任何数据。如果您的队列中的数据用完了,它将自行播放静默,直到您再次排列数据。如果所有缓冲区都不在队列中,则不会再次调用回调,但仍可以将数据从不是回调的另一个函数(例如,一旦数据再次到达)排入队列。这不是海报的问题。他的问题是他在队列中只有一个缓冲区开始。您至少需要2. – Mecki 2010-05-10 22:39:23

+0

谢谢Mecki,我有类似的问题,即使解决方案尚未写入,至少这指向了正确的方向...... – Nick 2010-07-22 18:18:18

1

使用CoreAudio的音频排队服务非常简单,它可以确保您在完成回放后立即重新排队每个缓冲区,这样在播放完成时会触发回调。音频数据从网络接收时应位于单独的循环缓冲区中,以便网络和音频代码不会直接耦合。

为确保您不会丢失音频,请在数据中排队固定数量的缓冲区;这充当了抖动缓冲器。直到所有缓冲区排队后才开始播放。一旦每个缓冲区完成回放,立即用下一个数据包重新排队。如果没有数据可用,只需排队一个无声的缓冲区;因为到达的音频数据包最终会赶上,这只是以减少额外延迟为代价来减少音频丢失。

0

我发现它很愚蠢,我可以发布答案,但如果我没有足够的分数,这里没有评论。我只是想增加下面的答案:

“这可能是因为一旦你的缓冲区链欠载,你需要重新开始播放,但我从来没有真正低于AudioQueue缓冲区,但我记得从Win32编程,这是如果我错了,请随时纠正我。“

我已经在我最近制作的音频播放器中测试了这个场景。我从头开始制作FLAC解码器,并且此时仅支持16位歌曲。如果我偶然发现一首24位的歌曲,我会继续失去与正在播放的歌曲同步 - 它根本无法播放 - 并且可能需要一段时间,比如30秒才能恢复。这使得音频队列变得非常糟糕,当我最终开始再次向音频队列发送缓冲区时,需要30秒的静音 - 赶上下一首歌曲再次播放。

这只是我的观察,我还没有仔细考虑为什么我正在观察这种行为。也许它会丢弃样本以符合样本数量AudioQueue认为它应该在此刻播放 - 它在饥饿期间丢失了?我的音频播放器似乎快速播放歌曲,直到达到想要再次播放的点。

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

相关问题