使用 OS X 核心音频 playback/output 时遇到音频丢失问题
Experiencing audio dropouts with OS X core audio playback/output
我正在使用为输入和输出配置的简单输出音频单元(尽管我所有的问题似乎出在输出上)。这是一个来自 socket/internet 的流式音频源,它被送入一个 boost 无锁队列,然后被送入输出 AU。我收到音频丢失,这似乎是 AU 渲染回调未被核心音频间歇性调用的结果。
这里有一个graph。在这一部分之前有大约 10 秒的完美音频。
黑色:音频样本,简单的正弦波
蓝色:渲染回调 (OutputProc) 的挂钟持续时间(以毫秒为单位),上表之外的点是 ~120 毫秒
橙色:samples/1000 中无锁队列 (playback_buf) 的大小,以使其很好地适应图表
x 轴:以毫秒为单位的时间
所有内容都记录在 OutputProc 中,因此如果未调用它,则不会记录任何内容,但绘图工具会连接这些时期的点。缓冲区中总是有足够的样本。似乎从 ~22475ms 到 ~22780ms,OutputProc 只在 22640 被调用一次。它在那个特定实例上确实有很长的挂钟时间,但似乎是由于抢占。后来在 22800 到 23000 范围内仍然存在丢失,但 OutputProc 不会比正常情况持续更长的时间,并且肯定不会超过实时 window(此处为 ~6ms...HW 采样率为 96kHz)。所以,我认为这是其他一些以某种方式抢先的线程。不过,我希望核心音频线程具有非常高的优先级。我确实有一些 boost asio 套接字 input/output 并行进行(例如 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;
}
您的日志记录机制可能会干扰实时线程。任何可以锁定或管理内存(例如字符串创建或标准输出文件 IO)的任何调用都可能导致音频单元回调中的丢失和其他故障。
如果是这种情况,您可以尝试将时间戳填充到无锁循环日志记录 FIFO 中,并在另一个线程中执行任何文件 IO。
我正在使用为输入和输出配置的简单输出音频单元(尽管我所有的问题似乎出在输出上)。这是一个来自 socket/internet 的流式音频源,它被送入一个 boost 无锁队列,然后被送入输出 AU。我收到音频丢失,这似乎是 AU 渲染回调未被核心音频间歇性调用的结果。
这里有一个graph。在这一部分之前有大约 10 秒的完美音频。
黑色:音频样本,简单的正弦波
蓝色:渲染回调 (OutputProc) 的挂钟持续时间(以毫秒为单位),上表之外的点是 ~120 毫秒
橙色:samples/1000 中无锁队列 (playback_buf) 的大小,以使其很好地适应图表
x 轴:以毫秒为单位的时间
所有内容都记录在 OutputProc 中,因此如果未调用它,则不会记录任何内容,但绘图工具会连接这些时期的点。缓冲区中总是有足够的样本。似乎从 ~22475ms 到 ~22780ms,OutputProc 只在 22640 被调用一次。它在那个特定实例上确实有很长的挂钟时间,但似乎是由于抢占。后来在 22800 到 23000 范围内仍然存在丢失,但 OutputProc 不会比正常情况持续更长的时间,并且肯定不会超过实时 window(此处为 ~6ms...HW 采样率为 96kHz)。所以,我认为这是其他一些以某种方式抢先的线程。不过,我希望核心音频线程具有非常高的优先级。我确实有一些 boost asio 套接字 input/output 并行进行(例如 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;
}
您的日志记录机制可能会干扰实时线程。任何可以锁定或管理内存(例如字符串创建或标准输出文件 IO)的任何调用都可能导致音频单元回调中的丢失和其他故障。
如果是这种情况,您可以尝试将时间戳填充到无锁循环日志记录 FIFO 中,并在另一个线程中执行任何文件 IO。