Using Audio Converter Services for AAC to Linear PCM

edited February 2014

I'm receiving audio over a stream in AAC format. I have a class that adheres to AEAudioPlayable and need to provide output in linear PCM (as far as I know that's the only option). Audio passes through an Audio File Stream and I receive nice bytes/packets/data of AAC.

So: I'm attempting to use AudioConverter and having a really hard time getting it to work.

I convert each time packets are provided from the AudioFileStream and stack up an AudioBufferList with the conversion output. Here's the problem, though: I don't receive nearly as much data out of the converter as I put in, and playback is just a quick burst of audio. Is there something I'm missing?

void inPropertiesProc(void *inClientData,
                      AudioFileStreamID inAudioFileStream,
                      AudioFileStreamPropertyID inPropertyID,
                      UInt32 *ioFlags)
{
    if(inPropertyID == kAudioFileStreamProperty_ReadyToProducePackets) {
        AudioStreamPlayer *streamPlayer = (__bridge AudioStreamPlayer*)inClientData;
        AudioStreamBasicDescription propertyData;
        unsigned int propertyDataSize = sizeof(propertyData);
        
        OSStatus err = AudioFileStreamGetProperty(streamPlayer->audioFileStream, kAudioFileStreamProperty_DataFormat, &propertyDataSize, &propertyData);
        if(err == noErr) {
            streamPlayer->_inputDescription = propertyData;
            AudioConverterNew(&propertyData, &streamPlayer->_outputDescription, &streamPlayer->audioConverter);
        }
    }
}
  
void inPacketsProc(void *inClientData,
                   UInt32 inNumberBytes,
                   UInt32 inNumberPackets,
                   const void *inInputData,
                   AudioStreamPacketDescription *inPacketDescriptions)
{
    AudioStreamPlayer *streamPlayer = (__bridge AudioStreamPlayer*)inClientData;

    audioConverterInputData inInputUserData;
    inInputUserData.totalBytes = inNumberBytes;
    inInputUserData.currentByte = 0;
    inInputUserData.data = (void*)inInputData;
    inInputUserData.packetDescription = inPacketDescriptions;
    inInputUserData.numberOfPackets = inNumberPackets;
    
    UInt32 ioOutputDataPacketSize = inNumberPackets;
    AudioBufferList *outOutputData = AEAllocateAndInitAudioBufferList(streamPlayer->linearPCMDescription, inNumberPackets);

    OSStatus err = AudioConverterFillComplexBuffer(&streamPlayer->audioConverter, inputDataProc, &inInputUserData, &ioOutputDataPacketSize, outOutputData, NULL);
    if(err) {
        NSLog(@Audio convert error: %d, (int)err);
    }

    // ...fill audio buffer list
}

OSStatus inputDataProc(AudioConverterRef inAudioConverter,
                       UInt32 *ioNumberDataPackets,
                       AudioBufferList *ioData,
                       AudioStreamPacketDescription **outDataPacketDescription,
                       void *inUserData)
{
    audioConverterInputData *inputData = inUserData;
    UInt32 bytesLeft = inputData->totalBytes - inputData->currentByte;
    
    if (bytesLeft == 0) {
        *ioNumberDataPackets = 0;
        return noErr;
    } else {
        ioData->mBuffers[0].mData = inputData->data;
        ioData->mBuffers[0].mDataByteSize = inputData->totalBytes;
        *outDataPacketDescription = inputData->packetDescription;
        *ioNumberDataPackets = inputData->numberOfPackets;
        inputData->currentByte = inputData->totalBytes;
    }
}

Comments

  • edited December 2014

    Hey @nickf,

    I'm seeing the same behavior when decoding an mp3 file. I am playing back a playlist of 10 tracks or so and sometimes (about every other time) AudioConverterFillComplexBuffer starts spitting out 0 packets processed after processing the short burst of audio you mentioned but does not produce error. This always occurs at the beginning of the same track ( 5th track in playlist ) but sometimes is able to keep decoding without this error. Any attempts to reallocate the audio converter and reinitialize the decoding context simply result in playing through the short burst of audio again and back to spitting out 0 packets processed.

    Were you able to shed any more light on the situation?

    I'm going to flag @Michael on this to ask him if he's seen this behavior with AudioConverterFillComplexBuffer before.

    Thanks,

    OTC

  • Hey @nickf ,

    Did you ever find a solution to this problem ?

  • edited January 5

    Hey @nickf ,
    As per apple documentation:

    https://developer.apple.com/library/content/qa/qa1317/_index.html#//apple_ref/doc/uid/DTS10002349

    When you set *ioNumberDataPackets = 0; and return noErr, the AudioConverter is put in a "Finished" state, and thus requiring an AudioConverterReset(); call.

    To signal the converter that the data isn't available but keep converter alive for future use, just return any non-zero value when bytesLeft == 0;

    Also make sure you reset the input buffer, best to do that in the beginning on every call.

    OSStatus inputDataProc(AudioConverterRef inAudioConverter,
                           UInt32 *ioNumberDataPackets,
                           AudioBufferList *ioData,
                           AudioStreamPacketDescription **outDataPacketDescription,
                           void *inUserData)
    {
        audioConverterInputData *inputData = inUserData;
        UInt32 bytesLeft = inputData->totalBytes - inputData->currentByte;
    
        ioData->mBuffers[0].mData = NULL;
        ioData->mBuffers[0].mDataByteSize = 0;
    
        if (bytesLeft == 0) {
            *ioNumberDataPackets = 0;
            return 1;
        } else {
            ioData->mBuffers[0].mData = inputData->data;
            ioData->mBuffers[0].mDataByteSize = inputData->totalBytes;
            *outDataPacketDescription = inputData->packetDescription;
            *ioNumberDataPackets = inputData->numberOfPackets;
            inputData->currentByte = inputData->totalBytes;
            return noErr;
        }
    }
    

    @OTCintelligence @nchery

Sign In or Register to comment.