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 вместо этого.

Другие вопросы по тегам