Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

matias4000

macrumors newbie
Original poster
Feb 24, 2014
1
0
Hey guys, I hope someone can help me with this. I'm trying to play AAC files using AudioQueue, I tested the code using WAV files, and it works. But whenever I use either a .CAF or .M4A file, and try to play it, this error comes up:

Code:
ERROR: >aq> 1608: failed (-66674); will stop (66150/0 frames)

I've been searching for this error code -66674 on the Apple's Development support documents and it says that I have an error involving a problem with AudioQueuePrime or AudioQueueStart (right now AudioQueuePrime is not on the code, but I've been testing this also). I know that it's possible to use AAC with AudioQueues.

Code:
- (IBAction)play:(id)sender {
    //OSStatus result;
    NSArray *audioTracks = [NSArray arrayWithObjects:
                            @"/Users/mauro_ptt/Documents/XCODE/SimpleAQPlayViewController/Sample01.caf",
                            nil];

    for (id object in audioTracks) {
    // Open the audio file from an existing NSString path
    NSURL *sndFileURL = [NSURL fileURLWithPath:object];

    AudioFileOpenURL((__bridge CFURLRef)sndFileURL, kAudioFileReadPermission, 0, &mAudioFile);

    // get audio format
    UInt32 dataFormatSize = sizeof(mDataFormat);
    AudioFileGetProperty(mAudioFile, kAudioFilePropertyDataFormat, &dataFormatSize, &mDataFormat);

    // create playback queue
    AudioQueueNewOutput(&mDataFormat, AQOutputCallback, (__bridge void *)(self), CFRunLoopGetCurrent(), kCFRunLoopCommonModes, 0, &mQueue);

    // get buffer size, number of packets to read
    UInt32 maxPacketSize;
    UInt32 propertySize = sizeof(maxPacketSize);
    // get the theoretical max packet size without scanning the entire file
    AudioFileGetProperty(mAudioFile, kAudioFilePropertyPacketSizeUpperBound, &propertySize, &maxPacketSize);
    // get sizes for up to 0.5 seconds of audio
    DeriveBufferSize(mDataFormat, maxPacketSize, 0.5, &bufferByteSize, &mNumPacketsToRead);

    // allocate packet descriptions array
    bool isFormatVBR = (mDataFormat.mBytesPerPacket == 0 || mDataFormat.mFramesPerPacket == 0);
    if (isFormatVBR) {
        mPacketsDescs = (AudioStreamPacketDescription*) malloc(mNumPacketsToRead * sizeof(AudioStreamPacketDescription));
    } else {
        mPacketsDescs = NULL;
    }

    // Get magic cookie (COMPRESSED AAC)
    UInt32 cookieSize = sizeof(UInt32);
    OSStatus couldNotGetProperty = AudioFileGetPropertyInfo(mAudioFile, kAudioFilePropertyMagicCookieData, &cookieSize, NULL);
    if ((couldNotGetProperty == noErr) && cookieSize) {
        char *magicCookie = (char *) malloc(cookieSize);
        AudioFileGetProperty(mAudioFile, kAudioFilePropertyMagicCookieData, &cookieSize, magicCookie);
        AudioQueueSetProperty(mQueue, kAudioQueueProperty_MagicCookie, magicCookie, cookieSize);
        free(magicCookie);
    }

    // Allocate and prime audio queue buffers
    mCurrentPacket = 0;
    for (int i=0; i < kNumberBuffers; ++i) {
        AudioQueueAllocateBuffer(mQueue, bufferByteSize, &mBuffers[i]);
        AQOutputCallback((__bridge void *)(self), mQueue, mBuffers[i]);
    }
        mIsRunning = true;
    AudioQueueStart(mQueue, NULL);

    }
}

Thank you
 
AAC works just fine with AQ here. Here's a working player (a direct iOS port of the one in Ch. 5 of teh Core Audio book) I've tested with an AAC file on my 7.0.4 iPhone 5:

Code:
#include <AudioToolbox/AudioToolbox.h>

...

void CalculateBytesForTime (AudioFileID inAudioFile, AudioStreamBasicDescription inDesc, Float64 inSeconds, UInt32 *outBufferSize, UInt32 *outNumPackets);


#define kNumberPlaybackBuffers	3
typedef struct MyPlayer {
	// AudioQueueRef				queue; // the audio queue object
	// AudioStreamBasicDescription dataFormat; // file's data stream description
	AudioFileID					playbackFile; // reference to your output file
	SInt64						packetPosition; // current packet index in output file
	UInt32						numPacketsToRead; // number of packets to read from file
	AudioStreamPacketDescription *packetDescs; // array of packet descriptions for read buffer
	// AudioQueueBufferRef			buffers[kNumberPlaybackBuffers];
	Boolean						isDone; // playback has completed
} MyPlayer;


#pragma mark - utility functions -

// generic error handler - if err is nonzero, prints error message and exits program.
static void CheckError(OSStatus error, const char *operation)
{
	if (error == noErr) return;
	
	char str[20];
	// see if it appears to be a 4-char-code
	*(UInt32 *)(str + 1) = CFSwapInt32HostToBig(error);
	if (isprint(str[1]) && isprint(str[2]) && isprint(str[3]) && isprint(str[4])) {
		str[0] = str[5] = '\'';
		str[6] = '\0';
	} else
		// no, format it as an integer
		sprintf(str, "%d", (int)error);
	
	fprintf(stderr, "Error: %s (%s)\n", operation, str);
	
	exit(1);
}

// we only use time here as a guideline
// we're really trying to get somewhere between 16K and 64K buffers, but not allocate too much if we don't need it
void CalculateBytesForTime (AudioFileID inAudioFile, AudioStreamBasicDescription inDesc, Float64 inSeconds, UInt32 *outBufferSize, UInt32 *outNumPackets)
{
	
	// we need to calculate how many packets we read at a time, and how big a buffer we need.
	// we base this on the size of the packets in the file and an approximate duration for each buffer.
	//
	// first check to see what the max size of a packet is, if it is bigger than our default
	// allocation size, that needs to become larger
	UInt32 maxPacketSize;
	UInt32 propSize = sizeof(maxPacketSize);
	CheckError(AudioFileGetProperty(inAudioFile, kAudioFilePropertyPacketSizeUpperBound,
									&propSize, &maxPacketSize), "couldn't get file's max packet size");
	
	static const int maxBufferSize = 0x10000; // limit size to 64K
	static const int minBufferSize = 0x4000; // limit size to 16K
	
	if (inDesc.mFramesPerPacket) {
		Float64 numPacketsForTime = inDesc.mSampleRate / inDesc.mFramesPerPacket * inSeconds;
		*outBufferSize = numPacketsForTime * maxPacketSize;
	} else {
		// if frames per packet is zero, then the codec has no predictable packet == time
		// so we can't tailor this (we don't know how many Packets represent a time period
		// we'll just return a default buffer size
		*outBufferSize = maxBufferSize > maxPacketSize ? maxBufferSize : maxPacketSize;
	}
	
	// we're going to limit our size to our default
	if (*outBufferSize > maxBufferSize && *outBufferSize > maxPacketSize)
		*outBufferSize = maxBufferSize;
	else {
		// also make sure we're not too small - we don't want to go the disk for too small chunks
		if (*outBufferSize < minBufferSize)
			*outBufferSize = minBufferSize;
	}
	*outNumPackets = *outBufferSize / maxPacketSize;
}


// many encoded formats require a 'magic cookie'. if the file has a cookie we get it
// and configure the queue with it
static void MyCopyEncoderCookieToQueue(AudioFileID theFile, AudioQueueRef queue ) {
	UInt32 propertySize;
	OSStatus result = AudioFileGetPropertyInfo (theFile, kAudioFilePropertyMagicCookieData, &propertySize, NULL);
	if (result == noErr && propertySize > 0)
	{
		Byte* magicCookie = (UInt8*)malloc(sizeof(UInt8) * propertySize);
		CheckError(AudioFileGetProperty (theFile, kAudioFilePropertyMagicCookieData, &propertySize, magicCookie), "get cookie from file failed");
		CheckError(AudioQueueSetProperty(queue, kAudioQueueProperty_MagicCookie, magicCookie, propertySize), "set cookie on queue failed");
		free(magicCookie);
	}
}


#pragma mark - audio queue -

static void MyAQOutputCallback(void *inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inCompleteAQBuffer)
{
	MyPlayer *aqp = (MyPlayer*)inUserData;
	if (aqp->isDone) return;
	
	// read audio data from file into supplied buffer
	UInt32 numBytes;
	UInt32 nPackets = aqp->numPacketsToRead;
	CheckError(AudioFileReadPackets(aqp->playbackFile,
									false,
									&numBytes,
									aqp->packetDescs,
									aqp->packetPosition,
									&nPackets,
									inCompleteAQBuffer->mAudioData),
			   "AudioFileReadPackets failed");
	
	// enqueue buffer into the Audio Queue
	// if nPackets == 0 it means we are EOF (all data has been read from file)
	if (nPackets > 0)
	{
		inCompleteAQBuffer->mAudioDataByteSize = numBytes;
		AudioQueueEnqueueBuffer(inAQ,
								inCompleteAQBuffer,
								(aqp->packetDescs ? nPackets : 0),
								aqp->packetDescs);
		aqp->packetPosition += nPackets;
	}
	else
	{
		CheckError(AudioQueueStop(inAQ, false), "AudioQueueStop failed");
		aqp->isDone = true;
	}
}


- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    // Override point for customization after application launch.
    
    MyPlayer player = {0};
    
    NSURL *guitarLoop   = [[NSBundle mainBundle] URLForResource: @"IMG_2066.MOV.stream1" withExtension: @"aac"];
	CFURLRef myFileURL = (__bridge CFURLRef) guitarLoop;
    
	CheckError(AudioFileOpenURL(myFileURL, kAudioFileReadPermission, 0, &player.playbackFile), "AudioFileOpenURL failed");
	CFRelease(myFileURL);
	
	// get the audio data format from the file
	AudioStreamBasicDescription dataFormat;
	UInt32 propSize = sizeof(dataFormat);
	CheckError(AudioFileGetProperty(player.playbackFile, kAudioFilePropertyDataFormat,
									&propSize, &dataFormat), "couldn't get file's data format");
	
	// create a output (playback) queue
	AudioQueueRef queue;
	CheckError(AudioQueueNewOutput(&dataFormat, // ASBD
								   MyAQOutputCallback, // Callback
								   &player, // user data
								   NULL, // run loop
								   NULL, // run loop mode
								   0, // flags (always 0)
								   &queue), // output: reference to AudioQueue object
			   "AudioQueueNewOutput failed");
	
	
	// adjust buffer size to represent about a half second (0.5) of audio based on this format
 	UInt32 bufferByteSize;
	CalculateBytesForTime(player.playbackFile, dataFormat,  0.5, &bufferByteSize, &player.numPacketsToRead);
	
	// check if we are dealing with a VBR file. ASBDs for VBR files always have
	// mBytesPerPacket and mFramesPerPacket as 0 since they can fluctuate at any time.
	// If we are dealing with a VBR file, we allocate memory to hold the packet descriptions
	bool isFormatVBR = (dataFormat.mBytesPerPacket == 0 || dataFormat.mFramesPerPacket == 0);
	if (isFormatVBR)
		player.packetDescs = (AudioStreamPacketDescription*)malloc(sizeof(AudioStreamPacketDescription) * player.numPacketsToRead);
	else
		player.packetDescs = NULL; // we don't provide packet descriptions for constant bit rate formats (like linear PCM)
	
	// get magic cookie from file and set on queue
	MyCopyEncoderCookieToQueue(player.playbackFile, queue);
	
	// allocate the buffers and prime the queue with some data before starting
	AudioQueueBufferRef	buffers[kNumberPlaybackBuffers];
	player.isDone = false;
	player.packetPosition = 0;
	int i;
	for (i = 0; i < kNumberPlaybackBuffers; ++i)
	{
		CheckError(AudioQueueAllocateBuffer(queue, bufferByteSize, &buffers[i]), "AudioQueueAllocateBuffer failed");
		
		// manually invoke callback to fill buffers with data
		MyAQOutputCallback(&player, queue, buffers[i]);
		
		// EOF (the entire file's contents fit in the buffers)
		if (player.isDone)
			break;
	}
	
	
	//CheckError(AudioQueueAddPropertyListener(aqp.queue, kAudioQueueProperty_IsRunning, MyAQPropertyListenerCallback, &aqp), "AudioQueueAddPropertyListener(kAudioQueueProperty_IsRunning) failed");
	
	// start the queue. this function returns immedatly and begins
	// invoking the callback, as needed, asynchronously.
	CheckError(AudioQueueStart(queue, NULL), "AudioQueueStart failed");
	
	// and wait
	printf("Playing...\n");
	do
	{
		CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.25, false);
	} while (!player.isDone /*|| gIsRunning*/);
	
	// isDone represents the state of the Audio File enqueuing. This does not mean the
	// Audio Queue is actually done playing yet. Since we have 3 half-second buffers in-flight
	// run for continue to run for a short additional time so they can be processed
	CFRunLoopRunInMode(kCFRunLoopDefaultMode, 2, false);
	
	// end playback
	player.isDone = true;
	CheckError(AudioQueueStop(queue, TRUE), "AudioQueueStop failed");
	
cleanup:
	AudioQueueDispose(queue, TRUE);
	AudioFileClose(player.playbackFile);
	
    return YES;
}

And here's the full project: https://dl.dropboxusercontent.com/u/81986513/032014/01 5c support/AACPlayer 2.zip (just make sure you copy an AAC file in the bundle. Here, I've named it "IMG_2066.MOV.stream1")
 
  • Like
Reactions: atpetrick_
AAC works just fine with AQ here. Here's a working player (a direct iOS port of the one in Ch. 5 of teh Core Audio book) I've tested with an AAC file on my 7.0.4 iPhone 5:

Code:
#include <AudioToolbox/AudioToolbox.h>

...

void CalculateBytesForTime (AudioFileID inAudioFile, AudioStreamBasicDescription inDesc, Float64 inSeconds, UInt32 *outBufferSize, UInt32 *outNumPackets);


#define kNumberPlaybackBuffers    3
typedef struct MyPlayer {
    // AudioQueueRef                queue; // the audio queue object
    // AudioStreamBasicDescription dataFormat; // file's data stream description
    AudioFileID                    playbackFile; // reference to your output file
    SInt64                        packetPosition; // current packet index in output file
    UInt32                        numPacketsToRead; // number of packets to read from file
    AudioStreamPacketDescription *packetDescs; // array of packet descriptions for read buffer
    // AudioQueueBufferRef            buffers[kNumberPlaybackBuffers];
    Boolean                        isDone; // playback has completed
} MyPlayer;


#pragma mark - utility functions -

// generic error handler - if err is nonzero, prints error message and exits program.
static void CheckError(OSStatus error, const char *operation)
{
    if (error == noErr) return;
  
    char str[20];
    // see if it appears to be a 4-char-code
    *(UInt32 *)(str + 1) = CFSwapInt32HostToBig(error);
    if (isprint(str[1]) && isprint(str[2]) && isprint(str[3]) && isprint(str[4])) {
        str[0] = str[5] = '\'';
        str[6] = '\0';
    } else
        // no, format it as an integer
        sprintf(str, "%d", (int)error);
  
    fprintf(stderr, "Error: %s (%s)\n", operation, str);
  
    exit(1);
}

// we only use time here as a guideline
// we're really trying to get somewhere between 16K and 64K buffers, but not allocate too much if we don't need it
void CalculateBytesForTime (AudioFileID inAudioFile, AudioStreamBasicDescription inDesc, Float64 inSeconds, UInt32 *outBufferSize, UInt32 *outNumPackets)
{
  
    // we need to calculate how many packets we read at a time, and how big a buffer we need.
    // we base this on the size of the packets in the file and an approximate duration for each buffer.
    //
    // first check to see what the max size of a packet is, if it is bigger than our default
    // allocation size, that needs to become larger
    UInt32 maxPacketSize;
    UInt32 propSize = sizeof(maxPacketSize);
    CheckError(AudioFileGetProperty(inAudioFile, kAudioFilePropertyPacketSizeUpperBound,
                                    &propSize, &maxPacketSize), "couldn't get file's max packet size");
  
    static const int maxBufferSize = 0x10000; // limit size to 64K
    static const int minBufferSize = 0x4000; // limit size to 16K
  
    if (inDesc.mFramesPerPacket) {
        Float64 numPacketsForTime = inDesc.mSampleRate / inDesc.mFramesPerPacket * inSeconds;
        *outBufferSize = numPacketsForTime * maxPacketSize;
    } else {
        // if frames per packet is zero, then the codec has no predictable packet == time
        // so we can't tailor this (we don't know how many Packets represent a time period
        // we'll just return a default buffer size
        *outBufferSize = maxBufferSize > maxPacketSize ? maxBufferSize : maxPacketSize;
    }
  
    // we're going to limit our size to our default
    if (*outBufferSize > maxBufferSize && *outBufferSize > maxPacketSize)
        *outBufferSize = maxBufferSize;
    else {
        // also make sure we're not too small - we don't want to go the disk for too small chunks
        if (*outBufferSize < minBufferSize)
            *outBufferSize = minBufferSize;
    }
    *outNumPackets = *outBufferSize / maxPacketSize;
}


// many encoded formats require a 'magic cookie'. if the file has a cookie we get it
// and configure the queue with it
static void MyCopyEncoderCookieToQueue(AudioFileID theFile, AudioQueueRef queue ) {
    UInt32 propertySize;
    OSStatus result = AudioFileGetPropertyInfo (theFile, kAudioFilePropertyMagicCookieData, &propertySize, NULL);
    if (result == noErr && propertySize > 0)
    {
        Byte* magicCookie = (UInt8*)malloc(sizeof(UInt8) * propertySize);
        CheckError(AudioFileGetProperty (theFile, kAudioFilePropertyMagicCookieData, &propertySize, magicCookie), "get cookie from file failed");
        CheckError(AudioQueueSetProperty(queue, kAudioQueueProperty_MagicCookie, magicCookie, propertySize), "set cookie on queue failed");
        free(magicCookie);
    }
}


#pragma mark - audio queue -

static void MyAQOutputCallback(void *inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inCompleteAQBuffer)
{
    MyPlayer *aqp = (MyPlayer*)inUserData;
    if (aqp->isDone) return;
  
    // read audio data from file into supplied buffer
    UInt32 numBytes;
    UInt32 nPackets = aqp->numPacketsToRead;
    CheckError(AudioFileReadPackets(aqp->playbackFile,
                                    false,
                                    &numBytes,
                                    aqp->packetDescs,
                                    aqp->packetPosition,
                                    &nPackets,
                                    inCompleteAQBuffer->mAudioData),
               "AudioFileReadPackets failed");
  
    // enqueue buffer into the Audio Queue
    // if nPackets == 0 it means we are EOF (all data has been read from file)
    if (nPackets > 0)
    {
        inCompleteAQBuffer->mAudioDataByteSize = numBytes;
        AudioQueueEnqueueBuffer(inAQ,
                                inCompleteAQBuffer,
                                (aqp->packetDescs ? nPackets : 0),
                                aqp->packetDescs);
        aqp->packetPosition += nPackets;
    }
    else
    {
        CheckError(AudioQueueStop(inAQ, false), "AudioQueueStop failed");
        aqp->isDone = true;
    }
}


- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    // Override point for customization after application launch.
  
    MyPlayer player = {0};
  
    NSURL *guitarLoop   = [[NSBundle mainBundle] URLForResource: @"IMG_2066.MOV.stream1" withExtension: @"aac"];
    CFURLRef myFileURL = (__bridge CFURLRef) guitarLoop;
  
    CheckError(AudioFileOpenURL(myFileURL, kAudioFileReadPermission, 0, &player.playbackFile), "AudioFileOpenURL failed");
    CFRelease(myFileURL);
  
    // get the audio data format from the file
    AudioStreamBasicDescription dataFormat;
    UInt32 propSize = sizeof(dataFormat);
    CheckError(AudioFileGetProperty(player.playbackFile, kAudioFilePropertyDataFormat,
                                    &propSize, &dataFormat), "couldn't get file's data format");
  
    // create a output (playback) queue
    AudioQueueRef queue;
    CheckError(AudioQueueNewOutput(&dataFormat, // ASBD
                                   MyAQOutputCallback, // Callback
                                   &player, // user data
                                   NULL, // run loop
                                   NULL, // run loop mode
                                   0, // flags (always 0)
                                   &queue), // output: reference to AudioQueue object
               "AudioQueueNewOutput failed");
  
  
    // adjust buffer size to represent about a half second (0.5) of audio based on this format
     UInt32 bufferByteSize;
    CalculateBytesForTime(player.playbackFile, dataFormat,  0.5, &bufferByteSize, &player.numPacketsToRead);
  
    // check if we are dealing with a VBR file. ASBDs for VBR files always have
    // mBytesPerPacket and mFramesPerPacket as 0 since they can fluctuate at any time.
    // If we are dealing with a VBR file, we allocate memory to hold the packet descriptions
    bool isFormatVBR = (dataFormat.mBytesPerPacket == 0 || dataFormat.mFramesPerPacket == 0);
    if (isFormatVBR)
        player.packetDescs = (AudioStreamPacketDescription*)malloc(sizeof(AudioStreamPacketDescription) * player.numPacketsToRead);
    else
        player.packetDescs = NULL; // we don't provide packet descriptions for constant bit rate formats (like linear PCM)
  
    // get magic cookie from file and set on queue
    MyCopyEncoderCookieToQueue(player.playbackFile, queue);
  
    // allocate the buffers and prime the queue with some data before starting
    AudioQueueBufferRef    buffers[kNumberPlaybackBuffers];
    player.isDone = false;
    player.packetPosition = 0;
    int i;
    for (i = 0; i < kNumberPlaybackBuffers; ++i)
    {
        CheckError(AudioQueueAllocateBuffer(queue, bufferByteSize, &buffers[i]), "AudioQueueAllocateBuffer failed");
      
        // manually invoke callback to fill buffers with data
        MyAQOutputCallback(&player, queue, buffers[i]);
      
        // EOF (the entire file's contents fit in the buffers)
        if (player.isDone)
            break;
    }
  
  
    //CheckError(AudioQueueAddPropertyListener(aqp.queue, kAudioQueueProperty_IsRunning, MyAQPropertyListenerCallback, &aqp), "AudioQueueAddPropertyListener(kAudioQueueProperty_IsRunning) failed");
  
    // start the queue. this function returns immedatly and begins
    // invoking the callback, as needed, asynchronously.
    CheckError(AudioQueueStart(queue, NULL), "AudioQueueStart failed");
  
    // and wait
    printf("Playing...\n");
    do
    {
        CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.25, false);
    } while (!player.isDone /*|| gIsRunning*/);
  
    // isDone represents the state of the Audio File enqueuing. This does not mean the
    // Audio Queue is actually done playing yet. Since we have 3 half-second buffers in-flight
    // run for continue to run for a short additional time so they can be processed
    CFRunLoopRunInMode(kCFRunLoopDefaultMode, 2, false);
  
    // end playback
    player.isDone = true;
    CheckError(AudioQueueStop(queue, TRUE), "AudioQueueStop failed");
  
cleanup:
    AudioQueueDispose(queue, TRUE);
    AudioFileClose(player.playbackFile);
  
    return YES;
}

And here's the full project: https://dl.dropboxusercontent.com/u/81986513/032014/01 5c support/AACPlayer 2.zip (just make sure you copy an AAC file in the bundle. Here, I've named it "IMG_2066.MOV.stream1")


Thank for your code (y)
 
Last edited:
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.