【问题标题】:Why might my AudioQueueOutputCallback not be called?为什么我的 AudioQueueOutputCallback 可能不会被调用?
【发布时间】:2011-11-26 09:38:45
【问题描述】:

我正在使用Audio Queue Services API 通过 iPhone 上的 TCP 套接字连接播放从服务器流式传输的音频。我可以播放从套接字连接填充的缓冲区,但我似乎无法让我的 AudioQueue 调用我的 AudioQueueOutputCallback 函数,而且我没有想法。

高级设计

  1. 数据从套接字连接传递给播放器,并写入 立即进入内存中的循环缓冲区。
  2. 随着 AudioQueueBuffers 变得可用,数据从循环缓冲区复制到 可用的 AudioQueueBuffer,立即重新排队。 (或者,如果我的回调发生了)

会发生什么

缓冲区全部填满并成功入队,我清楚地听到了音频流。为了测试,我使用了大量的缓冲区(15 个)并且所有缓冲区都无缝播放,但 AudioQueueOutputCallback 从未被调用,因此我从不重新排队任何缓冲区,尽管一切似乎都运行良好。如果我不等待我的回调,假设它永远不会被调用,而是根据写入的数据驱动缓冲区的排队,我可以无限期地播放音频流,重用和重新排队缓冲区,就好像它们一样已通过回调明确返回给我。正是这样一个事实:我可以完美地播放流,同时根据需要重用缓冲区,这让我最困惑。为什么没有调用回调?

可能相关的代码

流的格式是 16 位线性 PCM,8 kHz,单声道:

_streamDescription.mSampleRate = 8000.0f;
_streamDescription.mFormatID = kAudioFormatLinearPCM;
_streamDescription.mBytesPerPacket = 2;
_streamDescription.mFramesPerPacket = 1;
_streamDescription.mBytesPerFrame = sizeof(AudioSampleType);
_streamDescription.mChannelsPerFrame = 1;
_streamDescription.mBitsPerChannel = 8 * sizeof(AudioSampleType)
_streamDescription.mReserved = 0;
_streamDescription.mFormatFlags = (kLinearPCMFormatFlagIsBigEndian | 
                                   kLinearPCMFormatFlagIsPacked);

我的回调原型和实现如下。没有什么花哨的,而且和我目前看到的每个例子都差不多:

// Prototype, declared above the class's @implementation
void AQBufferCallback(void* inUserData, AudioQueueRef inAudioQueue, AudioQueueBufferRef inAudioQueueBuffer);

// Definition at the bottom of the file.
void AQBufferCallback(void* inUserData, AudioQueueRef inAudioQueue, AudioQueueBufferRef inAudioQueueBuffer) {

    printf("callback\n");
    [(MyAudioPlayer *)inUserData audioQueue:inAudioQueue didAquireBufferForReuse:inAudioQueueBuffer];
}

我这样创建 AudioQueue:

OSStatus status = 0;
status = AudioQueueNewOutput(&_streamDescription, 
                             AQBufferCallback, // <-- Doesn't work...
                             self, 
                             CFRunLoopGetCurrent(),
                             kCFRunLoopCommonModes, 
                             0,
                             &_audioQueue);
if (status) {

    // This is not called...
    NSLog(@"Error creating new audio output queue: %@", [MyAudioPlayer stringForOSStatus:status]);
    return;
}

我像这样将缓冲区排入队列。至此,就知道本地缓冲区包含了正确的复制数据量:

memcpy(aqBuffer->mAudioData, localBuffer, kAQBufferSize);
aqBuffer->mAudioDataByteSize = kAQBufferSize;

OSStatus status = AudioQueueEnqueueBuffer(_audioQueue, aqBuffer, 0, NULL);
if (status) {
    // This is also not called.
    NSLog(@"Error enqueueing buffer %@", [MyAudioPlayer stringForOSStatus:status]);
}

请救救我。

【问题讨论】:

  • 你打电话给AudioQueueStart吗?只使用了低级 CoreAudio API,你必须调用 AudioDeviceStartAudioDeviceStop
  • 如果没有任何效果,也许 OpenAL 更容易上手
  • @Mattias:是的,我调用 AudioQueueStart()。这就是我能够听到音频播放的方式。我从来没有看过 OpenAL,而且我还不能放弃 98% 的工作解决方案。几天后我们会看到我的感受......
  • 我在 .h 文件中声明了回调,我不知道这是否重要...
  • @frowing:我怀疑函数原型是否会对行为产生任何影响。我试过了,为了彻底。没有区别。

标签: iphone ios core-audio audioqueueservices


【解决方案1】:

这是在主线程还是后台线程上执行的?如果CFRunLoopGetCurrent() 返回一个可能消失的线程的运行循环(线程池等)或者是一个不关心kCFRunLoopCommonModes 的运行循环,则可能不好。

尝试将CFRunLoopGetCurrent() 更改为CFRunLoopGetMain() 或确保AudioQueueNewOutput()CFRunLoopGetCurrent() 在主线程或您可以控制并具有正确运行循环的线程上执行。

【讨论】:

  • 正如承诺的那样:绿色支票。非常感谢。
【解决方案2】:

尝试将self 更改为(void*)self。像这样:

status = AudioQueueNewOutput(&_streamDescription, AQBufferCallback, (void*)self, CFRunLoopGetCurrent(), kCFRunLoopCommonModes, 0, &_audioQueue);

【讨论】:

  • 没有投掷自我的骰子。我可以将 inUserData 参数设置为任何值,结果都是一样的:播放良好,没有回调。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-07-22
  • 2018-11-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多