Как декодировать сжатые кадры AAC в PCM с помощью AudioConverterFillComplexBuffer iOS

Я хочу реализовать вызовы SIP в своем приложении, и первая проблема, которую мне нужно решить, это преобразование аудио из сжатого формата AAC с заголовком ADTS в линейный PCM.

Мои входные данные представляют собой NSArray кадров ADTS с разным размером кадра. Каждый кадр имеет тип NSMutableData. Каждый кадр имеет одинаковый формат и частоту дискретизации, разница только в размере кадра.

Я попытался реализовать пример кода, предложенный Игорем Ротару для эта проблема, но не могу заставить ее работать.

Теперь мой код выглядит так. Первым делом настраиваю AudioConverter:

- (void)configureAudioConverter {
    AudioStreamBasicDescription inFormat;
    memset(&inFormat, 0, sizeof(inFormat));
    inputFormat.mBitsPerChannel = 0;
    inputFormat.mBytesPerFrame = 0;
    inputFormat.mBytesPerPacket = 0;
    inputFormat.mChannelsPerFrame = 1;
    inputFormat.mFormatFlags = kMPEG4Object_AAC_LC;
    inputFormat.mFormatID = kAudioFormatMPEG4AAC;
    inputFormat.mFramesPerPacket = 1024;
    inputFormat.mReserved = 0;
    inputFormat.mSampleRate = 22050;

    AudioStreamBasicDescription outputFormat;
    memset(&outputFormat, 0, sizeof(outputFormat));
    outputFormat.mSampleRate       = inputFormat.mSampleRate;
    outputFormat.mFormatID         = kAudioFormatLinearPCM;
    outputFormat.mFormatFlags      = kLinearPCMFormatFlagIsSignedInteger;
    outputFormat.mBytesPerPacket   = 2;
    outputFormat.mFramesPerPacket  = 1;
    outputFormat.mBytesPerFrame    = 2;
    outputFormat.mChannelsPerFrame = 1;
    outputFormat.mBitsPerChannel   = 16;
    outputFormat.mReserved         = 0;

    AudioClassDescription *description = [self
                                      getAudioClassDescriptionWithType:kAudioFormatMPEG4AAC
                                      fromManufacturer:kAppleSoftwareAudioCodecManufacturer];

    OSStatus status =  AudioConverterNewSpecific(&inputFormat, &outputFormat, 1, description, &_audioConverter);

    if (status != 0) {
        printf("setup converter error, status: %i\n", (int)status);
    }
}

После этого я написал функцию обратного вызова:

struct MyUserData {
    UInt32 mChannels;
    UInt32 mDataSize;
    const void* mData;
    AudioStreamPacketDescription mPacket;
};

OSStatus inInputDataProc(AudioConverterRef inAudioConverter,
                         UInt32 *ioNumberDataPackets,
                         AudioBufferList *ioData,
                         AudioStreamPacketDescription **outDataPacketDescription,
                         void *inUserData)
{
    struct MyUserData* userData = (struct MyUserData*)(inUserData);

    if (!userData->mDataSize) {
        *ioNumberDataPackets = 0;
        return kNoMoreDataError;
    }

    if (outDataPacketDescription) {
        userData->mPacket.mStartOffset = 0;
        userData->mPacket.mVariableFramesInPacket = 0;
        userData->mPacket.mDataByteSize = userData->mDataSize;
        *outDataPacketDescription = &userData->mPacket;
    }

    ioData->mBuffers[0].mNumberChannels = userData->mChannels;
    ioData->mBuffers[0].mDataByteSize = userData->mDataSize;
    ioData->mBuffers[0].mData = (void *)userData->mData;

    // No more data to provide following this run.
    userData->mDataSize = 0;

    return noErr;
}

А моя функция декодирования кадров выглядит так:

- (void)startDecodingAudio {
    if (!_converterConfigured){
        return;
    }

    while (true){
        if ([self hasFramesToDecode]){
            struct MyUserData userData = {1, (UInt32)_decoderBuffer[_currPosInDecoderBuf].length, _decoderBuffer[_currPosInDecoderBuf].bytes};

            uint8_t *buffer = (uint8_t *)malloc(128 * sizeof(short int));
            AudioBufferList decBuffer;
            decBuffer.mNumberBuffers = 1;
            decBuffer.mBuffers[0].mNumberChannels = 1;
            decBuffer.mBuffers[0].mDataByteSize = 128 * sizeof(short int);
            decBuffer.mBuffers[0].mData = buffer;

            UInt32 numFrames = 128;

            AudioStreamPacketDescription outPacketDescription;
            memset(&outPacketDescription, 0, sizeof(AudioStreamPacketDescription));
            outPacketDescription.mDataByteSize = 128;
            outPacketDescription.mStartOffset = 0;
            outPacketDescription.mVariableFramesInPacket = 0;

            OSStatus status = AudioConverterFillComplexBuffer(_audioConverter,
                                                              inInputDataProc,
                                                              &userData,
                                                              &numFrames,
                                                              &decBuffer,
                                                              &outPacketDescription);

            NSError *error = nil;

            if (status == kNoMoreDataError) {
                NSLog(@"%u bytes decoded", (unsigned int)decBuffer.mBuffers[0].mDataByteSize);
                [_decodedData appendData:[NSData dataWithBytes:decBuffer.mBuffers[0].mData length:decBuffer.mBuffers[0].mDataByteSize]];
                _currPosInDecoderBuf += 1;
            } else {
                error = [NSError errorWithDomain:NSOSStatusErrorDomain code:status userInfo:nil];
            }
        } else {
            break;
        }
    }
}

Каждый раз AudioConverterFillComplexBuffer возвращает статус 1852797029, который, согласно Apple API, kAudioCodecIllegalOperationError. Если кому-то удалось конвертировать в такие форматы, поделитесь примерами или советом.


person avsmirnov567    schedule 22.03.2017    source источник
comment
Вы уже решили свою проблему?   -  person Vladislav Rudskoy    schedule 29.03.2017
comment
@VladislavRudskoy да, смотрите мой ответ ниже.   -  person avsmirnov567    schedule 29.03.2017


Ответы (1)


Наконец, я расшифровал свои байты с помощью библиотеки StreamingKit (оригинальный репозиторий можно найти здесь).

person avsmirnov567    schedule 29.03.2017
comment
Привет, @avsmirnov567, ты пробовал конвертировать файлы AAC? Я получаю фрагменты AAC в массивах байтов из сокета реального времени и не могу понять, будет ли эта библиотека мне полезна. - person Oz Shabat; 17.02.2020
comment
@OzShabat Я написал собственный источник данных для использования этой библиотеки с потоками сокетов. Позвольте мне посмотреть, если у меня все еще есть этот код локально, я обновлю свой ответ выше. Если нет, то, боюсь, я уже забыл это дело. - person avsmirnov567; 18.02.2020
comment
Снова привет, @OzShabat, я нашел мой форк библиотеки на моем github. См. класс STKInputStreamDataSource. Буду очень рад, если поможет. - person avsmirnov567; 18.02.2020
comment
Я работаю над твоей вилкой уже больше дня. Очень умное решение! К сожалению, аудиопакеты, которые я получаю, не настроены для поддержки AAC с рекламой, поэтому я не думаю, что эта библиотека мне поможет. - person Oz Shabat; 18.02.2020