Can't play AAC files using AudioQueue

Discussion in 'iOS Programming' started by matias4000, Feb 24, 2014.

  1. matias4000 macrumors newbie

    Joined:
    Feb 24, 2014
    #1
    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
     
  2. Menneisyys2 macrumors 603

    Joined:
    Jun 7, 2011
    #2
    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")
     
  3. atpetrick_, Jul 5, 2018
    Last edited: Jul 5, 2018

    atpetrick_ macrumors newbie

    Joined:
    Jul 5, 2018
    #3

    Thank for your code (y)
     
  4. rascalie, Jul 24, 2018
    Last edited: Jul 26, 2018

    rascalie macrumors newbie

    Joined:
    Jul 19, 2018
    #4
    thanks for your sharing
    btw, I want to share movies in an ISO file with my friend but it cannot be extracted by my home DVD player. I'm passionate about watching movies on my DVD player, after all. He told me to burn ISO to DVD with a proper tool. It did works!
     

Share This Page