AVAudioRecorder record AAC/m4a

13,453

Solution 1

The iOS SDK is apparently stupid. Setting the format ID key to kAudioFormatMPEG4AAC is not enough for it to actually encode the file; in QuickTime it will play but in its properties you'll just see AAC and no further details. Changing the output file extension to m4a should help do the trick and make it apply the right encoding.

Solution 2

For hardware-accelerated encoders, only one session can be active at any given time. This means that, for AAC, it will work in the simulator (where a software encoder is used) but might fail on the device (where a hardware encoder is used) unless your app activates the AVAudioSession.

I also was attempting to use AVAudioRecorder to record audio in an AAC encoded format preferably in a .m4a file. I had a beast of a time doing this. I was quickly able to get the code to record to AAC inside of a core audio file (.caf), but I couldn't get AVAudioRecorder to properly format a .m4a. I was just about to rewrite the recording code in my application using the lower level Audio Units API but instead I put that on the back burner and added in the code to handle the shared audio session. Once that was set up, I went back and tried to save as an .m4a and it just worked.

Here is some example code:

NSArray *dirPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docsDir = [dirPaths objectAtIndex:0];
NSURL *tmpFileUrl = [NSURL fileURLWithPath:[docsDir stringByAppendingPathComponent:@"tmp.m4a"]];
NSDictionary *recordSettings = [NSDictionary dictionaryWithObjectsAndKeys:
                        [NSNumber numberWithInt: kAudioFormatMPEG4AAC], AVFormatIDKey,
                        [NSNumber numberWithFloat:16000.0], AVSampleRateKey,
                        [NSNumber numberWithInt: 1], AVNumberOfChannelsKey,
                        nil];
NSError *error = nil;
AVAudioRecorder *recorder = [[AVAudioRecorder alloc] initWithURL:tmpFileUrl settings:recordSettings error:&error];
[recorder prepareToRecord];

AVAudioSession *session = [AVAudioSession sharedInstance];
[session setCategory:AVAudioSessionCategoryRecord error:nil];
[session setActive:YES error:nil];

[recorder record];

Then to end the recording I used:

[recorder stop];
AVAudioSession *session = [AVAudioSession sharedInstance];
int flags = AVAudioSessionSetActiveFlags_NotifyOthersOnDeactivation;
[session setActive:NO withFlags:flags error:nil];

Then the file at 'tmpFileUrl' can be used as you please.

Solution 3

The hints about AVAudioSession are very helpful!

Since setActive:withFlags:error: has become deprecated in iOS 6.0 the code for stopping audio session should be adjusted to:

AVAudioSession *session = [AVAudioSession sharedInstance];
int flags = AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation;
[session setActive:NO withOptions:flags error:nil];
Share:
13,453
Chris Kooken
Author by

Chris Kooken

Currently the CTO for DataFinch Technologies

Updated on June 14, 2022

Comments

  • Chris Kooken
    Chris Kooken almost 2 years

    I am trying to record audio on the Device using AVAudioRecorder. That file is transmitted to a web server and I want to play that resulting file in a web browser..

    I have tried various combinations of settings on the device...nothing seems to encode the file into the correct AAC format.

    QuickTime says that it doesn't know how to play this file.

    Sample code

        private void InitializeRecordingSession() {
             string fileName = string.Format("{0}.m4a", Guid.NewGuid());
             string tmpdir = Environment.GetFolderPath(Environment.SpecialFolder.Personal) + "/audio";
              if (!Directory.Exists(tmpdir))
                   Directory.CreateDirectory(tmpdir);
              audioFilePath = Path.Combine(tmpdir, fileName);
    
              Console.WriteLine("Audio File Path: " + audioFilePath);
    
              var url = NSUrl.FromFilename(audioFilePath);
              var settings = new AVAudioRecorderSettings() {
                   AudioFormat = AudioFormatType.MPEG4AAC,
                   SampleRate = 44100,
                   NumberChannels = 1,
                   AudioQuality = AVAudioQuality.High,    
              };
    
              recorder = AVAudioRecorder.ToUrl(url, settings, out error);
    
              //Set Recorder to Prepare To Record
              recorder.PrepareToRecord();  
         }
    

    Edit-By putting this code in before the line

    recorder = AVAudioRecorder.ToUrl(url, settings, out error);
    

    Everything worked.

    NSError error;    
    AVAudioSession audioSession = AVAudioSession.SharedInstance();
    audioSession.SetCategory(AVAudioSession.CategoryRecord, out error);
    audioSession.SetActive(true, out error);