iOS - AudioUnitRender вернул ошибку -10876 на устройстве, но в симуляторе работает нормально
Я столкнулся с проблемой, из-за которой я не смог перехватить входной сигнал с микрофона на устройстве (iPhone4). Тем не менее, код отлично работает в симуляторе. Код был первоначально принят из класса Apple MixerHostAudio из примера кода MixerHost. он отлично работает как на устройстве, так и в симуляторе, прежде чем я начал добавлять код для ввода микрофонного ввода. Интересно, кто-нибудь может мне помочь. Заранее спасибо!
Вот моя функция inputRenderCallback, которая подает сигнал на вход микшера:
static OSStatus inputRenderCallback (
void *inRefCon,
AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList *ioData) {
recorderStructPtr recorderStructPointer = (recorderStructPtr) inRefCon;
// ....
AudioUnitRenderActionFlags renderActionFlags;
err = AudioUnitRender(recorderStructPointer->iOUnit,
&renderActionFlags,
inTimeStamp,
1, // bus number for input
inNumberFrames,
recorderStructPointer->fInputAudioBuffer
);
// error returned is -10876
// ....
}
Вот мой связанный код инициализации: теперь я сохраняю только 1 вход в микшере, поэтому микшер кажется избыточным, но работает нормально перед добавлением кода захвата ввода.
// Convenience function to allocate our audio buffers
- (AudioBufferList *) allocateAudioBufferListByNumChannels:(UInt32)numChannels withSize:(UInt32)size {
AudioBufferList* list;
UInt32 i;
list = (AudioBufferList*)calloc(1, sizeof(AudioBufferList) + numChannels * sizeof(AudioBuffer));
if(list == NULL)
return nil;
list->mNumberBuffers = numChannels;
for(i = 0; i < numChannels; ++i) {
list->mBuffers[i].mNumberChannels = 1;
list->mBuffers[i].mDataByteSize = size;
list->mBuffers[i].mData = malloc(size);
if(list->mBuffers[i].mData == NULL) {
[self destroyAudioBufferList:list];
return nil;
}
}
return list;
}
// initialize audio buffer list for input capture
recorderStructInstance.fInputAudioBuffer = [self allocateAudioBufferListByNumChannels:1 withSize:4096];
// I/O unit description
AudioComponentDescription iOUnitDescription;
iOUnitDescription.componentType = kAudioUnitType_Output;
iOUnitDescription.componentSubType = kAudioUnitSubType_RemoteIO;
iOUnitDescription.componentManufacturer = kAudioUnitManufacturer_Apple;
iOUnitDescription.componentFlags = 0;
iOUnitDescription.componentFlagsMask = 0;
// Multichannel mixer unit description
AudioComponentDescription MixerUnitDescription;
MixerUnitDescription.componentType = kAudioUnitType_Mixer;
MixerUnitDescription.componentSubType = kAudioUnitSubType_MultiChannelMixer;
MixerUnitDescription.componentManufacturer = kAudioUnitManufacturer_Apple;
MixerUnitDescription.componentFlags = 0;
MixerUnitDescription.componentFlagsMask = 0;
AUNode iONode; // node for I/O unit
AUNode mixerNode; // node for Multichannel Mixer unit
// Add the nodes to the audio processing graph
result = AUGraphAddNode (
processingGraph,
&iOUnitDescription,
&iONode);
result = AUGraphAddNode (
processingGraph,
&MixerUnitDescription,
&mixerNode
);
result = AUGraphOpen (processingGraph);
// fetch mixer AudioUnit instance
result = AUGraphNodeInfo (
processingGraph,
mixerNode,
NULL,
&mixerUnit
);
// fetch RemoteIO AudioUnit instance
result = AUGraphNodeInfo (
processingGraph,
iONode,
NULL,
&(recorderStructInstance.iOUnit)
);
// enable input of RemoteIO unit
UInt32 enableInput = 1;
AudioUnitElement inputBus = 1;
result = AudioUnitSetProperty(recorderStructInstance.iOUnit,
kAudioOutputUnitProperty_EnableIO,
kAudioUnitScope_Input,
inputBus,
&enableInput,
sizeof(enableInput)
);
// setup mixer inputs
UInt32 busCount = 1;
result = AudioUnitSetProperty (
mixerUnit,
kAudioUnitProperty_ElementCount,
kAudioUnitScope_Input,
0,
&busCount,
sizeof (busCount)
);
UInt32 maximumFramesPerSlice = 4096;
result = AudioUnitSetProperty (
mixerUnit,
kAudioUnitProperty_MaximumFramesPerSlice,
kAudioUnitScope_Global,
0,
&maximumFramesPerSlice,
sizeof (maximumFramesPerSlice)
);
for (UInt16 busNumber = 0; busNumber < busCount; ++busNumber) {
// set up input callback
AURenderCallbackStruct inputCallbackStruct;
inputCallbackStruct.inputProc = &inputRenderCallback;
inputCallbackStruct.inputProcRefCon = &recorderStructInstance;
result = AUGraphSetNodeInputCallback (
processingGraph,
mixerNode,
busNumber,
&inputCallbackStruct
);
// set up stream format
AudioStreamBasicDescription mixerBusStreamFormat;
size_t bytesPerSample = sizeof (AudioUnitSampleType);
mixerBusStreamFormat.mFormatID = kAudioFormatLinearPCM;
mixerBusStreamFormat.mFormatFlags = kAudioFormatFlagsAudioUnitCanonical;
mixerBusStreamFormat.mBytesPerPacket = bytesPerSample;
mixerBusStreamFormat.mFramesPerPacket = 1;
mixerBusStreamFormat.mBytesPerFrame = bytesPerSample;
mixerBusStreamFormat.mChannelsPerFrame = 2;
mixerBusStreamFormat.mBitsPerChannel = 8 * bytesPerSample;
mixerBusStreamFormat.mSampleRate = graphSampleRate;
result = AudioUnitSetProperty (
mixerUnit,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Input,
busNumber,
&mixerBusStreamFormat,
sizeof (mixerBusStreamFormat)
);
}
// set sample rate of mixer output
result = AudioUnitSetProperty (
mixerUnit,
kAudioUnitProperty_SampleRate,
kAudioUnitScope_Output,
0,
&graphSampleRate,
sizeof (graphSampleRate)
);
// connect mixer output to RemoteIO
result = AUGraphConnectNodeInput (
processingGraph,
mixerNode, // source node
0, // source node output bus number
iONode, // destination node
0 // desintation node input bus number
);
// initialize AudioGraph
result = AUGraphInitialize (processingGraph);
// start AudioGraph
result = AUGraphStart (processingGraph);
// enable mixer input
result = AudioUnitSetParameter (
mixerUnit,
kMultiChannelMixerParam_Enable,
kAudioUnitScope_Input,
0, // bus number
1, // on
0
);
3 ответа
Я решил проблему самостоятельно. Это связано с ошибкой в моем коде, вызывающей ошибку 10876 в AudioUnitRender().
Я устанавливаю категорию моей AudioSession как AVAudioSessionCategoryPlayback вместо AVAudioSessionCategoryPlayAndRecord. Когда я установил категорию в AVAudioSessionCategoryPlayAndRecord, я наконец смог успешно захватить ввод с микрофона, вызвав A *udioUnitRender ()* на устройстве.
использование AVAudioSessionCategoryPlayback не приводит к какой-либо ошибке при вызове AudioUnitRender() для захвата ввода с микрофона и хорошо работает в симуляторе. Я думаю, что это должно быть проблемой для симулятора iOS (хотя и не критично).
Во-первых, следует отметить, что код ошибки -10876 соответствует обозначению kAudioUnitErr_NoConnection
, Обычно их можно найти, прибегая к поиску кода ошибки вместе с термином CoreAudio. Это должен быть намек на то, что вы просите систему выполнить рендеринг в AudioUnit, который неправильно подключен.
В обратном вызове рендеринга вы используете void*
пользовательские данные в recorderStructPtr
, Я собираюсь предположить, что когда вы отлаживали этот код, это приведение вернуло ненулевую структуру, в которой есть фактический адрес вашего аудиоустройства. Тем не менее, вы должны сделать это с AudioBufferList
который передается в ваш обратный вызов рендеринга (т.е. inputRenderCallback
функция). Он содержит список образцов из системы, которые вам нужно обработать.
Я также видел, как эта проблема возникает, когда значения в свойстве формата потока модуля ввода-вывода несовместимы. Убедитесь, что ваш AudioStreamBasicDescription
Биты на канал, каналы на кадр, байты на кадр, кадры на пакет и байты на пакет имеют смысл.
В частности, я получил ошибку NoConnection, когда я изменил формат потока со стерео на моно, изменив каналы на кадр, но забыл изменить байты на кадр и байты на пакет, чтобы соответствовать тому факту, что в монофрейме вдвое меньше данных как стерео кадр.
Если вы инициализируете AudioUnit
и не устанавливайте его kAudioUnitProperty_SetRenderCallback
свойство, вы получите эту ошибку, если позвоните AudioUnitRender
в теме.
Вызов AudioUnitProcess
вместо этого.