• Open Menu Close Menu
  • Apple
  • Shopping Bag
  • Apple
  • Mac
  • iPad
  • iPhone
  • Watch
  • TV
  • Music
  • Support
  • Search apple.com
  • Shopping Bag

Lists

Open Menu Close Menu
  • Terms and Conditions
  • Lists hosted on this site
  • Email the Postmaster
  • Tips for posting to public mailing lists
Parsing AAC/m4a With AudioConverterFillComplexBuffer
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Parsing AAC/m4a With AudioConverterFillComplexBuffer


  • Subject: Parsing AAC/m4a With AudioConverterFillComplexBuffer
  • From: "Puhl, Scott" <email@hidden>
  • Date: Mon, 2 Apr 2007 13:43:41 -0500
  • Thread-topic: Parsing AAC/m4a With AudioConverterFillComplexBuffer

Yet Another Parsing Problem: AAC/m4a With AudioConverterFillComplexBuffer

I've been trying for some time to coax my current audio file parsing system, based around AudioConverterFillComplexBuffer(), to handle AAC files - such as an .m4a created by iTunes.  So far, no luck.

I've been over all the example code I can find and the handful of posts to coreaudio-dev concerning this problem.  When others have had problems using AudioConverterFillComplexBuffer () and asked online, it seems the have been directed to look at PlayAudioFileLite and PlayFile projects.  Neither of these help me, as PlayAudioFileLite doesn't parse AAC files, and PlayFile doesn't use AudioConverterFillComplexBuffer(),  but instead Apple's AudioFilePlayerAU - which doesn't give me the control I need.


My problem manifests on the first call to AudioConverterFillComplexBuffer(), which fails with a EXC_BAD_ACCESS after several (13) seemingly successful calls to my callback, ParseAAC_Callback, which handles the parsing of the file packets using AudioFileReadPackets().

Stumped.

I've created a simplified command line project demonstrating what I'm working with, and where it's failing.

Here's the callstack after the EXC_BAD_ACCESS event:

#0	0x93d618ba in BufferedAudioConverter::FillBuffer
#1	0x93d61a2d in AudioConverterChain::RenderOutput
#2	0x93d6189f in BufferedAudioConverter::FillBuffer
#3	0x93d6172c in AudioConverterFillComplexBuffer
#4	0x0000310a in Test_Read_AAC_Audio_File at main.c:183
#5	0x000031cf in main at main.c:41

The example project only has one source file (main.c), which has been interspersed with comments marking the steps I'm taking to try to parse an AAC.  All of the comments are in the Test_Read_AAC_Audio_File() function.

//STEP 01: Open An AAC Audio File
//STEP 02: Get ASBD For The Audio File
//STEP 03: Set ASBD For Output Stream
//STEP 04: Create An AudioBufferList Corresponding To The Output Format
//STEP 05: Create An Audio Converter
//STEP 06: Get Audio File's Magic Cookie info(if exists) And Set Converter's Magic Cookie Property
//STEP 07: Prepare Data For Call To AudioConverterFillComplexBuffer
//STEP 08: Prepare The Callback Data Used In ParseAAC_Callback(...)
//STEP 09: Call AudioConverterFillComplexBuffer - Which Will Repeatedly Call ParseAAC_Callback()
//STEP 10: Cleanup

With this demo, I'm not trying to play the file - just parse it - as I'm trying to keep it as simple as possible.  Much of the example code is normally handled by utility functions, making it a lot neater and compact; but, I wanted to present it with all the tasks unrolled and divided into commented steps so it can be read linearly.

Here's main.c  copied in full:

//main.c

#include <AudioToolbox/AudioToolbox.h>
#include "CAAudioBufferList.h"

const UInt32 kIONumberFrames = 10240;
const UInt32 kNumberChannels = 2;
const UInt32 kBufferSize = kIONumberFrames * sizeof(float);

typedef struct ParseAAC_CallbackData
{
	AudioFileID *fileIDPtr;

	void *sourceBuffer;
	UInt64 packetOffset;
	bool moreData;

	UInt64 totalPacketCount;
	UInt32 maxPacketSize;

	AudioStreamPacketDescription aspd;
	UInt32 bytesCounter;
} ParseAAC_CallbackData;

void Test_Read_AAC_Audio_File( FSRef* AAC_FSRefPtr );

OSStatus ParseAAC_Callback(	AudioConverterRef                           inAudioConverter,
							UInt32                                      *ioNumberDataPackets,
							AudioBufferList                             *ioData,
							AudioStreamPacketDescription				**outDataPacketDescription,
							void*                                       inUserData);

int main (int argc, const char * argv[])
{
	FSRef fileRef;
    char *fileName ="/Users/me/Music/magicAAC.m4a";  //give full path of any properly formatted audio file

    if(argc == 2)
        fileName = (char *)argv[1];

	 FSPathMakeRef ((const UInt8 *)fileName, &fileRef, 0); //Obtain filesystem reference to the file

	 Test_Read_AAC_Audio_File( &fileRef );

	 return( 0 );
}

void Test_Read_AAC_Audio_File( FSRef* AAC_FSRefPtr )
{
	OSStatus err;
	UInt32 size = 0;

//STEP 01: Open An AAC Audio File

	AudioFileID theAudioFileID;

	err = AudioFileOpen (	AAC_FSRefPtr,
							fsRdPerm,
							0,
							&theAudioFileID );


//STEP 02: Get ASBD For The Audio File

	AudioStreamBasicDescription fileASBD;
	size = sizeof(AudioStreamBasicDescription);
	memset( &fileASBD, 0, size );
	err = AudioFileGetProperty( theAudioFileID, kAudioFilePropertyDataFormat, &size, &fileASBD);


//STEP 03: Set ASBD For Output Stream

	AudioStreamBasicDescription outputASBD;

	outputASBD.mSampleRate = 44100;
	outputASBD.mFormatID = 'lpcm';
	outputASBD.mFormatFlags = kAudioFormatFlagIsFloat | kAudioFormatFlagIsPacked | kAudioFormatFlagIsNonInterleaved;
	outputASBD.mBytesPerPacket = 4;
	outputASBD.mFramesPerPacket = 1;
	outputASBD.mBytesPerFrame = 4;
	outputASBD.mChannelsPerFrame = 2;
	outputASBD.mBitsPerChannel = 32;
	outputASBD.mReserved = 0;


//STEP 04: Create An AudioBufferList Corresponding To The Output Format

	AudioBufferList		*theABL = CAAudioBufferList::Create(kNumberChannels);

	if( theABL )
	{
		//  initialize each AudioBuffer in theABL
		for(UInt32 i = 0; i < kNumberChannels; ++i)
		{
			theABL->mBuffers[i].mNumberChannels = 1;

			theABL->mBuffers[i].mDataByteSize = kBufferSize;

			theABL->mBuffers[i].mData = malloc( kBufferSize );
		}
	}


//STEP 05: Create An Audio Converter

	AudioConverterRef theConverterRef;
	err = AudioConverterNew( &fileASBD, &outputASBD, &theConverterRef );


//STEP 06: Get Audio File's Magic Cookie info(if exists) And Set Converter's Magic Cookie Property

	UInt32    magicCookieSize = 0;

	err = AudioFileGetPropertyInfo(		theAudioFileID,
										kAudioFilePropertyMagicCookieData,
										&magicCookieSize,
										NULL);

	if (err == noErr)
	{
		void *magicCookie = calloc (1, magicCookieSize);
		if (magicCookie)
		{
			err = AudioFileGetProperty(	theAudioFileID,
										kAudioFilePropertyMagicCookieData,
										&magicCookieSize,
										magicCookie);

			// Give the AudioConverter the magic cookie decompression params if any exist
			if (err == noErr)
			{
				err = AudioConverterSetProperty(	theConverterRef,
													kAudioConverterDecompressionMagicCookie,
													magicCookieSize,
													magicCookie);
			}

			if (magicCookie)
				free(magicCookie);
		}
	}
	else //OK if audio doesn't need magic cookie data
	{
		err = noErr;     //reset error status
	}


//STEP 07: Prepare Data For Call To AudioConverterFillComplexBuffer
	UInt32 requestFrames = kIONumberFrames;
	AudioStreamPacketDescription outPacketDescription;

	size = sizeof(outPacketDescription);
	memset( &outPacketDescription, 0, size );

//STEP 08: Prepare The Callback Data Used In ParseAAC_Callback(...)

	ParseAAC_CallbackData callbackData;

	callbackData.fileIDPtr = &theAudioFileID;
	callbackData.sourceBuffer = NULL;
	callbackData.packetOffset = 0;
	callbackData.moreData = true;
	callbackData.bytesCounter = 0;

	size = sizeof(callbackData.totalPacketCount);
	callbackData.totalPacketCount = 0;
	err = AudioFileGetProperty( theAudioFileID, kAudioFilePropertyAudioDataPacketCount, &size, &(callbackData.totalPacketCount));

	size = sizeof(callbackData.maxPacketSize);
	callbackData.maxPacketSize = 0;
	err = AudioFileGetProperty( theAudioFileID, kAudioFilePropertyMaximumPacketSize, &size, &(callbackData.maxPacketSize));

	size = sizeof( AudioStreamPacketDescription );
	memset( &(callbackData.aspd), 0, size );


//STEP 09: Call AudioConverterFillComplexBuffer - Which Will Repeatedly Call ParseAAC_Callback()

	if( theABL && theConverterRef )
	{
		err = noErr;

		while( err == noErr &&  requestFrames == kIONumberFrames )
		{
			err = AudioConverterFillComplexBuffer( theConverterRef, ParseAAC_Callback, &callbackData, &requestFrames, theABL, &outPacketDescription);
		}
	}

//STEP 10: Cleanup

	for(UInt32 i = 0; i < kNumberChannels; ++i)
	{
		free( theABL->mBuffers[i].mData );
	}
	CAAudioBufferList::Destroy(theABL);

	if (callbackData.sourceBuffer != NULL)
	{
		free(callbackData.sourceBuffer);
	}
}

OSStatus ParseAAC_Callback(	AudioConverterRef                           inAudioConverter,
							UInt32                                      *ioNumberDataPackets,
							AudioBufferList                             *ioData,
							AudioStreamPacketDescription				**outDataPacketDescription,
							void*                                       inUserData)
{
	OSStatus err = noErr;

    UInt32 bytesReturned = 0;

    // initialize in case of failure
    ioData->mBuffers[0].mData = NULL;
    ioData->mBuffers[0].mDataByteSize = 0;

	if( !inUserData )
		return( -1 );

	ParseAAC_CallbackData* callbackData = (ParseAAC_CallbackData*)inUserData;

    // if there are not enough packets to satisfy request, then read what's left
    if( callbackData->packetOffset + *ioNumberDataPackets > callbackData->totalPacketCount )
		*ioNumberDataPackets = callbackData->totalPacketCount - callbackData->packetOffset;


    // do nothing if there are no packets available
    if (*ioNumberDataPackets > 0)
    {
        if (callbackData->sourceBuffer != NULL)
		{
            free(callbackData->sourceBuffer);
            callbackData->sourceBuffer = NULL;
        }

        callbackData->sourceBuffer = (void *) calloc( 1, *ioNumberDataPackets * callbackData->maxPacketSize );

        //read the amount of data needed(ioNumberDataPackets) from AudioFile
        err = AudioFileReadPackets (*(callbackData->fileIDPtr), true, &bytesReturned, &(callbackData->aspd), callbackData->packetOffset, ioNumberDataPackets, callbackData->sourceBuffer);

		callbackData->bytesCounter += bytesReturned;

		if( outDataPacketDescription )
		{
			*outDataPacketDescription = &(callbackData->aspd);
		}

        if(err)
		{
			callbackData->moreData = false;   //end of data reached;
        }

        callbackData->packetOffset += *ioNumberDataPackets;	// keep track of where we want to read from next time

        ioData->mBuffers[0].mData = callbackData->sourceBuffer;		// tell the Audio Converter where it's source data is
        ioData->mBuffers[0].mDataByteSize = bytesReturned;		// tell the Audio Converter how much source data there is
    }
    else
    {
        // there aren't any more packets to read at this time
        ioData->mBuffers[0].mData = NULL;
        ioData->mBuffers[0].mDataByteSize = 0;
        callbackData->moreData = false;
    }

    // it's not an error if we just read the remainder of the file
    if (err == eofErr && *ioNumberDataPackets)
        err = noErr;

    return err;
}


Many thanks to anyone who can offer me new insights in this matter.

Scott
 _______________________________________________
Do not post admin requests to the list. They will be ignored.
Coreaudio-api mailing list      (email@hidden)
Help/Unsubscribe/Update your Subscription:

This email sent to email@hidden

  • Follow-Ups:
    • RE: Parsing AAC/m4a With AudioConverterFillComplexBuffer
      • From: "Puhl, Scott" <email@hidden>
  • Prev by Date: Re: Where to get started
  • Next by Date: AudioTrack to movie.......
  • Previous by thread: Re: Where to get started
  • Next by thread: RE: Parsing AAC/m4a With AudioConverterFillComplexBuffer
  • Index(es):
    • Date
    • Thread