【问题标题】:Apple CoreAudio - Why does AUGraphStop() take 25ms to complete?Apple CoreAudio - 为什么 AUGraphStop() 需要 25 毫秒才能完成?
【发布时间】:2011-03-24 19:32:03
【问题描述】:

我们的音频应用正在使用 AUGraph,其中包含一个混音器单元、一个转换器单元和一个输出单元。这是一款实时应用,因此性能是一个高度关注的问题。

一个问题已被标记,其中 AUGraphStop() 在主线程上需要 25 毫秒才能完成,而我们的分析器显示它在这段时间内处于休眠状态。谁能解释为什么会这样?是等待下一个零交叉点,还是等待再渲染一个缓冲区?

我尝试了几种解决方法,包括在尝试停止之前发送一个无声渲染帧(并设置 kAudioUnitRenderAction_OutputIsSilence 标志),并在 kAudioUnitRenderAction_PostRender 通知回调中调用 AUGraphStop() (这有混合的结果,在阅读之后似乎不是推荐的方法)。

我尝试降低每片的帧数,但似乎没有什么不同。我还尝试删除除输出节点之外的所有音频单元,试图将问题缩小到特定单元,但 AUGraphStop() 的成本仍为 25 毫秒。

这是我们初始化图表的方式:

//Configure converter/mixer/output unit descriptors
AudioComponentDescription OutputUnitDesc = { kAudioUnitType_Output ,kAudioUnitSubType_DefaultOutput, kAudioUnitManufacturer_Apple };
AudioComponentDescription MixerUnitDesc = { kAudioUnitType_Mixer, kAudioUnitSubType_MatrixMixer, kAudioUnitManufacturer_Apple };
AudioComponentDescription ConverterUnitDesc = { kAudioUnitType_FormatConverter, kAudioUnitSubType_AUConverter, kAudioUnitManufacturer_Apple };

//Create graph
CA_ERR(NewAUGraph(&mAudioGraph));

//Add converter/mixer/output nodes
CA_ERR(AUGraphAddNode(mAudioGraph, &OutputUnitDesc, &mOutputNode));
CA_ERR(AUGraphAddNode(mAudioGraph, &MixerUnitDesc, &mMixerNode));
CA_ERR(AUGraphAddNode(mAudioGraph, &ConverterUnitDesc, &mConverterNode));

//Connect nodes
CA_ERR(AUGraphConnectNodeInput(mAudioGraph, mConverterNode, 0, mMixerNode, 0));
CA_ERR(AUGraphConnectNodeInput(mAudioGraph, mMixerNode, 0, mOutputNode, 0));

//Open the graph (instantiates the units)
CA_ERR(AUGraphOpen(mAudioGraph));

//Get the created units
CA_ERR(AUGraphNodeInfo(mAudioGraph, mOutputNode, NULL, &mOutputUnit));
CA_ERR(AUGraphNodeInfo(mAudioGraph, mMixerNode, NULL, &mMixerUnit));
CA_ERR(AUGraphNodeInfo(mAudioGraph, mConverterNode, NULL, &mConverterUnit));

//Setup stream format description
mStreamDesc.mFormatID = kAudioFormatLinearPCM;
mStreamDesc.mFormatFlags = kLinearPCMFormatFlagIsPacked | kAudioFormatFlagIsSignedInteger;
mStreamDesc.mChannelsPerFrame = Header->nChannels;
mStreamDesc.mSampleRate = (Float64)Header->nSamplesPerSec;
mStreamDesc.mBitsPerChannel = Header->wBitsPerSample;
mStreamDesc.mBytesPerFrame = (Header->wBitsPerSample >> 3) * Header->nChannels;
mStreamDesc.mFramesPerPacket = 1;
mStreamDesc.mBytesPerPacket = mStreamDesc.mBytesPerFrame * mStreamDesc.mFramesPerPacket;
mStreamDesc.mReserved = 0;

//Set data endianness according to file type - TODO: Get endianness from header
AudioSystem::FileType FileType = mSample->GetFiletype();

if(FileType == AudioSystem::WAV)
        mStreamDesc.mFormatFlags |= kAudioFormatFlagsNativeEndian;
else if(FileType == AudioSystem::OGG)
        mStreamDesc.mFormatFlags |= kLinearPCMFormatFlagIsBigEndian;

//Configure number of input/output busses for mixer unit
int NumChannelsIn = Header->nChannels;
int NumChannelsOut = (int)AudioSystem::GetOutputChannelConfig();
CA_ERR(AudioUnitSetProperty(mMixerUnit, kAudioUnitProperty_BusCount, kAudioUnitScope_Input, 0, &NumChannelsIn, sizeof(u32)));
CA_ERR(AudioUnitSetProperty(mMixerUnit, kAudioUnitProperty_BusCount, kAudioUnitScope_Output, 0, &NumChannelsOut, sizeof(u32)));

//Set render callback
AURenderCallbackStruct callback = { AudioRenderCallback, this };
CA_ERR(AUGraphSetNodeInputCallback(mAudioGraph, mConverterNode, 0, &callback));

//Set stream format to something native to CoreAudio
AudioStreamBasicDescription OutputDesc = {0};
UInt32 Size = sizeof(AudioStreamBasicDescription);
CA_ERR(AudioUnitGetProperty(mMixerUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &OutputDesc, &Size));

//Set num output channels
OutputDesc.mChannelsPerFrame = (int)AudioSystem::GetOutputChannelConfig();

//Set stream format
CA_ERR(AudioUnitSetProperty(mConverterUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &mStreamDesc, sizeof(AudioStreamBasicDescription)));
CA_ERR(AudioUnitSetProperty(mConverterUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &OutputDesc, sizeof(AudioStreamBasicDescription)));
CA_ERR(AudioUnitSetProperty(mMixerUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &OutputDesc, sizeof(AudioStreamBasicDescription)));
CA_ERR(AudioUnitSetProperty(mMixerUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &OutputDesc, sizeof(AudioStreamBasicDescription)));
CA_ERR(AudioUnitSetProperty(mOutputUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &OutputDesc, sizeof(AudioStreamBasicDescription)));

//Initialise graph
CA_ERR(AUGraphInitialize(mAudioGraph));

//Set notification callback
CA_ERR(AUGraphAddRenderNotify(mAudioGraph, AudioNotifyCallback, this));

//Set global mixer volume
CA_ERR(AudioUnitSetParameter(mMixerUnit, kMatrixMixerParam_Volume, kAudioUnitScope_Global, 0xFFFFFFFF, 1.0, 0));

//Set input channel volumes
for(int i = 0; i < Header->nChannels; i++)
{
        CA_ERR(AudioUnitSetParameter(mMixerUnit, kMatrixMixerParam_Volume, kAudioUnitScope_Input, i, 1.0, 0));
}

//Set output channel volumes
for(int i = 0; i < (int)AudioSystem::GetOutputChannelConfig(); i++)
{
        CA_ERR(AudioUnitSetParameter(mMixerUnit, kMatrixMixerParam_Volume, kAudioUnitScope_Output, i, 1.0, 0));
}

图形是使用 AUGraphStart() 和 AUGraphStop() 来启动和停止的,没什么特别的。这是使用 Shark Profiler 捕获的调用堆栈:

| | | | | | | | | + 1.3%, AudioUnitGraph::Stop(), AudioToolbox
| | | | | | | | | | + 1.3%, AudioOutputUnitStop, AudioUnit
| | | | | | | | | | | + 1.3%, CallComponentDispatch, CarbonCore
| | | | | | | | | | | | + 1.3%, DefaultOutputAUEntry, CoreAudio
| | | | | | | | | | | | | + 1.3%, AUHALEntry, CoreAudio
| | | | | | | | | | | | | | + 1.3%, usleep$UNIX2003, libSystem.B.dylib
| | | | | | | | | | | | | | | + 1.3%, nanosleep$UNIX2003, libSystem.B.dylib
| | | | | | | | | | | | | | | |   1.3%, __semwait_signal, libSystem.B.dylib

它把整个 AUGraphStop() 都花在睡觉了!

有什么线索吗?

【问题讨论】:

    标签: iphone macos core-audio


    【解决方案1】:

    如果您已经达到每片的最小可配置帧数,那么 AUHAL 中的这 25 毫秒很可能是硬件限制的表现(访问和/或(重新)配置某些音频 DAC 控制需要多长时间寄存器或状态,和/或某些音频放大器控制状态)被锁定此硬件访问的 iOS 音频驱动程序线程中的固定延迟处理不当。但这只是一个疯狂的猜测。

    对于硬实时应用,我可能会考虑将 AUGraphStop()(以及 AUGraphStart())移出主 UI 线程,并为 AU 工作处理您自己的线程信号量。

    【讨论】:

    • 我担心线程解决方案可能是这里的答案!似乎只是为了停止图表而过于夸张,但我一定会试一试。
    • 哦,这是在 OS X 而不是 iPhone 上,如果这对任何人都有任何影响的话
    猜你喜欢
    • 2016-12-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-02-24
    • 1970-01-01
    相关资源
    最近更新 更多