【问题标题】:Experiencing audio dropouts with OS X core audio playback/output使用 OS X 核心音频播放/输出体验音频丢失
【发布时间】:2016-04-14 06:09:18
【问题描述】:

我正在使用为输入和输出配置的简单输出音频单元使用核心音频(OS X、10.11.4 Beta、旧版 mac mini)进行播放(尽管我的所有问题似乎都与输出有关)。这是来自套接字/互联网的流式音频源,馈送到升压无锁队列,然后馈送到输出 AU。我收到的音频丢失似乎是由于核心音频没有间歇性调用 AU 渲染回调的结果。
这是graph。在此部分之前有大约 10 秒的完美音频。

黑色:采样音频,简单正弦波

蓝色:渲染回调 (OutputProc) 的挂钟持续时间,以毫秒为单位,上图指出是 ~120ms

橙色:无锁队列 (playback_buf) 的大小(以 samples/1000 为单位)以很好地适应图形

x 轴:以毫秒为单位的时间

所有内容都记录在 OutputProc 中,因此如果不调用它,则不会记录任何内容,但绘图工具会将这些时间段内的点连接起来。缓冲区中总是有足够的样本。似乎从 ~22475ms 到 ~22780ms,OutputProc 仅在 22640 被调用一次。在该特定实例上它确实有很长的挂钟时间,但似乎是由于抢占。后来在 22800 到 23000 范围内仍然存在丢失,但 OutputProc 的持续时间不会比正常时间长,并且肯定不会超出实时窗口(此处约为 6 毫秒……硬件采样率为 96kHz)。所以,我认为这是其他一些以某种方式先发制人的线程。我希望核心音频线程具有非常高的优先级。我确实有一些并行的 boost asio 套接字输入/输出(例如 boost::asio::io_service io_service),但我希望它总是失去核心音频的优先级。如果您对实际问题有任何指示...总是受欢迎的...但是,如果我能找出在那些感兴趣的时间正在执行哪些线程,我可以取得进展? Xcode 中是否有一些东西可以告诉我调度程序历史记录或线程历史记录,可能是每个 CPU 内核? OutputProc 如果有帮助的话:

OSStatus AudioStream::OutputProc(void *inRefCon,
                                 AudioUnitRenderActionFlags *ioActionFlags,
                                 const AudioTimeStamp *TimeStamp,
                                 UInt32 inBusNumber,
                                 UInt32 inNumberFrames,
                                 AudioBufferList *ioData)
{
    AudioStream *This = (AudioStream *) inRefCon;

    playback_cb_dur_log.StartTime();

    static bool first_call = true;
    if (first_call)
    {
        std::cout << TIME(timer) << " playback starting\n";
        This->playback_state = PLAYBACK_ACTIVE;
        first_call = false;
    }

    int playback_buf_avail = (int) This->playback_buf.read_available();
    playback_buf_size_log.AddPoint(playback_buf_avail/1000.);
    if (playback_buf_avail >= This->playback_buf_thresh)
    {
        std::cout << TIME() << " audio, thresh: " << This->playback_buf_thresh << ", buf_size: " << playback_buf_avail << std::endl;
        // new threshold just one frame of data
        This->playback_buf_thresh = This->frames_total;
        for(int i = 0; i < This->num_channels; i++)
        {
            float *temp = (float *) ioData->mBuffers[i].mData;
            This->playback_buf.pop(temp, inNumberFrames);
            playback_sample_log.AddData(ioData->mBuffers[i].mData, inNumberFrames, This->chan_params.sample_rate);
        }
    }
    else
    {
        std::cout << TIME() << " silence, thresh: " << This->playback_buf_thresh << ", buf_size: " << This->playback_buf.read_available() << std::endl;
        for(int i = 0; i < This->num_channels; i++)
        {
            memset(ioData->mBuffers[i].mData, 0, inNumberFrames * sizeof(Float32));
            playback_sample_log.AddData(ioData->mBuffers[i].mData, inNumberFrames, This->chan_params.sample_rate);
        }
    }


    playback_cb_dur_log.StopAndCaptureTime();

    return noErr;
}

【问题讨论】:

  • 我从来没有遇到过 CoreAudio 渲染回调或对 OutputProc 的间歇性系统调用的挂钟持续时间。回调线程是实时的和基于定时器的。如果生产者线程没有及时提供数据,它不会等待:沉默就是你得到的。我想问题一定出在其他地方。这行是什么:if (playback_buf_avail &gt;= This-&gt;playback_buf_thresh) 以及哪个机制更新了 This 数据结构?

标签: c++ multithreading macos boost core-audio


【解决方案1】:

您的日志记录机制可能会干扰实时线程。任何可以锁定或管理内存(例如字符串创建或标准输出文件 IO)的任何调用都可能导致音频单元回调中的丢失和其他故障。

如果是这种情况,您可以尝试将时间戳填充到无锁循环日志记录 FIFO 中,并在另一个线程中执行任何文件 IO。

【讨论】:

  • 是的,这至少是问题的一部分。我还必须从套接字接收函数中删除 std::cout 语句以及日志和 cout 语句,该函数是生产者添加到回放 buf(这是一个 boost::lockfree::spsc_queue)。谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多