AudioQueue 吃了我的缓冲区(前 15 毫秒)

Posted

技术标签:

【中文标题】AudioQueue 吃了我的缓冲区(前 15 毫秒)【英文标题】:AudioQueue ate my buffer (first 15 milliseconds of it) 【发布时间】:2010-04-02 03:11:04 【问题描述】:

我正在以编程方式生成音频。我听到缓冲区之间的沉默间隙。当我将手机连接到示波器时,我发现每个缓冲区的前几个样本都丢失了,取而代之的是静默。这种静默的长度从几乎没有变化到长达 20 毫秒。

我的第一个想法是我原来的回调函数花费了太多时间。我用最短的可能替换它——它一遍又一遍地重新排列相同的缓冲区。我观察到同样的行为。

AudioQueueRef aq;
AudioQueueBufferRef aq_buffer;
AudiostreamBasicDescription asbd;

void aq_callback (void *aqData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer) 
    OSStatus s = AudioQueueEnqueueBuffer(aq, aq_buffer, 0, NULL);


void aq_init(void) 
    OSStatus s;

    asbd.mSampleRate = AUDIO_SAMPLES_PER_S;
    asbd.mFormatID = kAudioFormatLinearPCM;
    asbd.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked; 
    asbd.mBytesPerPacket = 1;
    asbd.mFramesPerPacket = 1; 
    asbd.mBytesPerFrame = 1;
    asbd.mChannelsPerFrame = 1;
    asbd.mBitsPerChannel = 8;
    asbd.mReserved = 0;


    int PPM_PACKETS_PER_SECOND = 50;
    // one buffer is as long as one PPM frame
    int BUFFER_SIZE_BYTES = asbd.mSampleRate/PPM_PACKETS_PER_SECOND*asbd.mBytesPerFrame;

    s = AudioQueueNewOutput(&asbd, aq_callback, NULL, CFRunLoopGetCurrent(), kCFRunLoopCommonModes, 0, &aq);
    s = AudioQueueAllocateBuffer(aq, BUFFER_SIZE_BYTES, &aq_buffer);

    // put samples in the buffer
    buffer_data(my_data, aq_buffer);

    s = AudioQueueStart(aq, NULL);
    s = AudioQueueEnqueueBuffer(aq, aq_buffer, 0, NULL);

【问题讨论】:

【参考方案1】:

我不熟悉 iPhone 音频 API,但它似乎类似于其他通常您会排队多个缓冲区的 API,这样当系统完成处理第一个缓冲区时,它可以立即开始处理正在执行第一个缓冲区的完成回调时,下一个缓冲区(因为它已经排队)。

类似:

AudioQueueRef aq;
AudioQueueBufferRef aq_buffer[2];
AudioStreamBasicDescription asbd;

void aq_callback (void *aqData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer) 
    // note that the callback tells us which buffer has been completed, so all
    //  we have to do is queue it back up
    OSStatus s = AudioQueueEnqueueBuffer(aq, inBuffer, 0, NULL);


void aq_init(void) 
    OSStatus s;

    asbd.mSampleRate = AUDIO_SAMPLES_PER_S;
    asbd.mFormatID = kAudioFormatLinearPCM;
    asbd.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked; 
    asbd.mBytesPerPacket = 1;
    asbd.mFramesPerPacket = 1; 
    asbd.mBytesPerFrame = 1;
    asbd.mChannelsPerFrame = 1;
    asbd.mBitsPerChannel = 8;
    asbd.mReserved = 0;


    int PPM_PACKETS_PER_SECOND = 50;
    // one buffer is as long as one PPM frame
    int BUFFER_SIZE_BYTES = asbd.mSampleRate/PPM_PACKETS_PER_SECOND*asbd.mBytesPerFrame;

    s = AudioQueueNewOutput(&asbd, aq_callback, NULL, CFRunLoopGetCurrent(), kCFRunLoopCommonModes, 0, &aq);
    s = AudioQueueAllocateBuffer(aq, BUFFER_SIZE_BYTES, &aq_buffer[0]);
    s = AudioQueueAllocateBuffer(aq, BUFFER_SIZE_BYTES, &aq_buffer[1]);

    // put samples in the buffer - fill both buffers
    buffer_data(my_data, aq_buffer[0]);
    buffer_data(my_data, aq_buffer[1]);

    s = AudioQueueStart(aq, NULL);
    s = AudioQueueEnqueueBuffer(aq, aq_buffer[0], 0, NULL);
    s = AudioQueueEnqueueBuffer(aq, aq_buffer[1], 0, NULL);

【讨论】:

感谢您指出我可以在 init() 中将多个缓冲区排入队列——这个重要的事实让我无法理解。实施此建议以及对回调的更改改善了我的情况,但并没有解决它。我仍然想念一些缓冲区的前几毫秒。现在这种情况仅每 7 或 8 个数据包发生一次,我错过的样本更少,但仍然很明显。 在这个简单的例子中,这似乎并不重要,但以防万一:超过 2 个缓冲区是否有帮助(如果你还没有尝试过)?此外,您确定buffer_data() 正在用整整 1/50 秒的数据包填充缓冲区吗? AUDIO_SAMPLES_PER_S 的值是多少? 感谢您一直以来的支持。如果我使用 3 个缓冲区而不是 2 个缓冲区,我就不会丢失样本。这看起来很神奇……这种魔法让我感到不舒服,因为我无法解释。我想知道是否有某种双缓冲正在进行。我以前尝试过使用缓冲区的大小,使它们长达 1 秒 (PPM_PACKETS_PER_SECOND = 1)。使用 2 个长缓冲区,我仍然会丢失样本。使用 3 个短缓冲区,我可以取回我放入的所有内容。 为了完整起见,您的其他问题的答案:我用定义明确的值填充缓冲区的末端,直到它们的长度(buffer_data 检查AudioQueueBuffer.mAudioDataBytesCapacity)和AUDIO_SAMPLES_PER_S#define' d 到 44100。

以上是关于AudioQueue 吃了我的缓冲区(前 15 毫秒)的主要内容,如果未能解决你的问题,请参考以下文章

AVCaptureSession 和 AudioQueue

AudioQueue 录制音频样本

使用 AudioQueue 和 Monotouch 静态声音录制

简单的 AudioQueue 正弦波——为啥会失真?

iOS AudioQueue播放AAC数据总是延迟2-3秒

AudioQueue 如何从 CallBack 读取缓冲区