RE: Parsing AAC/m4a With AudioConverterFillComplexBuffer
RE: Parsing AAC/m4a With AudioConverterFillComplexBuffer
- Subject: RE: Parsing AAC/m4a With AudioConverterFillComplexBuffer
- From: "Puhl, Scott" <email@hidden>
- Date: Wed, 4 Apr 2007 13:15:04 -0500
- Thread-topic: Parsing AAC/m4a With AudioConverterFillComplexBuffer
Figured it out.
I was mis-using the outPacketDescription argument to AudioConverterFillComplexBuffer. NULL works for my requirements. Otherwise, I needed an array of ioOutputDataPacketSize packet descriptions. I had been passing the address of a single packet description.
The revised call to AudioConverterFillComplexBuffer:
err = AudioConverterFillComplexBuffer( theConverterRef, ParseAAC_Callback, &callbackData, &requestFrames, theABL, NULL/*&outPacketDescription*/);
-----Original Message-----
From: coreaudio-api-bounces+spuhl=email@hidden on behalf of Puhl, Scott
Sent: Mon 4/2/2007 1:43 PM
To: email@hidden
Cc:
Subject: 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
_______________________________________________
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