title: iPhone - Capturing audio from microphone while using AVAssetWriterInputPixelBufferAdaptor to record video
body: <p>I'm trying to make an app that records the contents of a UIImageView along with microphone audio and record them to a video in realtime. (Kinda like the Talking Tom Cat app)</p>
  
  <p>I'm using AVAssetWriterInputPixelBufferAdaptor to record the contents of the UIImageView without trouble, but I have simply no idea how to incorporate audio with this. And it's not from a lack of trying either, I've worked on this specific problem for better than a week combing this website, google, iphone developer forumns, etc. This just isn't my cup of tea.</p>
  
  <p>The closest reference to this is:
  <a href="https://stackoverflow.com/questions/3741323/how-do-i-export-uiimage-array-as-a-movie">How do I export UIImage array as a movie?</a></p>
  
  <p>I can capture the video and audio separately, and then encode them together after the fact (I really have tried hard to solve this problem), but getting this app to record in real time is really ideal.</p>
  
  <p>Here is some framework code that demonstrates the recording I have so far:</p>
  
  <p>Here is the .h File</p>
  
  <pre><code>//
  //  RecordingTestProjectViewController.h
  //  RecordingTestProject
  //
  //  Created by Sean Luck on 7/26/11.
  //  Copyright 2011 __MyCompanyName__. All rights reserved.
  //
  
  #import &lt;UIKit/UIKit.h&gt;
  #import &lt;AVFoundation/AVFoundation.h&gt;
  #import &lt;CoreGraphics/CoreGraphics.h&gt;
  #import &lt;CoreMedia/CoreMedia.h&gt;
  #import &lt;CoreVideo/CoreVideo.h&gt;
  #import &lt;QuartzCore/QuartzCore.h&gt;
  
  @interface RecordingTestProjectViewController : UIViewController 
      {
          UIImageView *testView;
          NSTimer *theTimer;
  
          NSTimer *assetWriterTimer;
          AVMutableComposition *mutableComposition;
          AVAssetWriter *assetWriter;
          AVAssetWriterInput *assetWriterInput;
  
          AVAssetWriterInput *_audioWriterInput;
          AVCaptureDeviceInput *audioInput;
          AVCaptureSession *_capSession;
  
          AVAssetWriterInputPixelBufferAdaptor *assetWriterPixelBufferAdaptor;
          CFAbsoluteTime firstFrameWallClockTime;
          int count;
      }
  
  -(void) writeSample: (NSTimer*) _timer; 
  -(void) startRecording;
  -(void) pauseRecording;
  -(void) stopRecording;
  -(NSString*) pathToDocumentsDirectory; 
  
  
  @end
  </code></pre>
  
  <p>And the .m File</p>
  
  <pre><code>//
  //  RecordingTestProjectViewController.m
  //  RecordingTestProject
  //
  //  Created by Sean Luck on 7/26/11.
  //  Copyright 2011 __MyCompanyName__. All rights reserved.
  //
  
  #import "RecordingTestProjectViewController.h"
  #import &lt;AVFoundation/AVFoundation.h&gt;
  #import &lt;CoreGraphics/CoreGraphics.h&gt;
  #import &lt;CoreMedia/CoreMedia.h&gt;
  #import &lt;CoreVideo/CoreVideo.h&gt;
  #import &lt;QuartzCore/QuartzCore.h&gt;
  
  #define OUTPUT_FILE_NAME @"screen.mov"
  
  #define TIME_SCALE 600
  
  @implementation RecordingTestProjectViewController
  
  - (void)dealloc
      {
          [super dealloc];
      }
  
  - (void)didReceiveMemoryWarning
      {
          // Releases the view if it doesn't have a superview.
          [super didReceiveMemoryWarning];
  
          // Release any cached data, images, etc that aren't in use.
      }
  
  #pragma mark - View lifecycle
  
  
  // Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
  - (void)viewDidLoad
      {
  
          testView = [[UIImageView alloc] initWithImage:nil];
          testView.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height);
          testView.userInteractionEnabled=NO;
          testView.backgroundColor = [UIColor lightGrayColor];
          [self.view addSubview:testView];
          [testView release];
  
  
  
          [super viewDidLoad];
          [self startRecording];
      }
  
  
  
  
  
  
  
  -(void) writeSample: (NSTimer*) _timer 
      {
  
          if ([assetWriterInput isReadyForMoreMediaData]) 
              {
  
  
                  NSLog(@"count=%.i",count);
                  count=count+10;
  
                  UIGraphicsBeginImageContext(testView.frame.size);
                  [testView.image drawInRect:CGRectMake(0, 0, testView.frame.size.width, testView.frame.size.height)];
                  CGContextSetBlendMode(UIGraphicsGetCurrentContext(), kCGBlendModeNormal);
                  CGContextSetRGBStrokeColor(UIGraphicsGetCurrentContext(), 0,1,1,1);
                  CGContextSetLineWidth(UIGraphicsGetCurrentContext(), 5);
                  CGContextBeginPath(UIGraphicsGetCurrentContext());
                  CGContextMoveToPoint(UIGraphicsGetCurrentContext(), 10+count/2, 10+count);
                  CGContextAddLineToPoint(UIGraphicsGetCurrentContext(), 20+count*2, 20+count);
                  CGContextStrokePath(UIGraphicsGetCurrentContext());
                  testView.image = UIGraphicsGetImageFromCurrentImageContext();
                  UIGraphicsEndImageContext();
  
  
                  CVReturn cvErr = kCVReturnSuccess;
                  CGImageRef image = (CGImageRef) [testView.image CGImage];
  
                  // prepare the pixel buffer
                  CVPixelBufferRef pixelBuffer = NULL;
                  CFDataRef imageData= CGDataProviderCopyData(CGImageGetDataProvider(image));
                  cvErr = CVPixelBufferCreateWithBytes(kCFAllocatorDefault,testView.frame.size.width,testView.frame.size.height,kCVPixelFormatType_32BGRA,(void*)CFDataGetBytePtr(imageData),CGImageGetBytesPerRow(image),NULL,NULL,NULL,&amp;pixelBuffer);
  
  
                  // calculate the time
                  CFAbsoluteTime thisFrameWallClockTime = CFAbsoluteTimeGetCurrent();
                  CFTimeInterval elapsedTime = thisFrameWallClockTime - firstFrameWallClockTime;
                  CMTime presentationTime =  CMTimeMake (elapsedTime * TIME_SCALE, TIME_SCALE);
  
                  if (!cvErr) 
                      {
                          // write the sample
                          BOOL appended = [assetWriterPixelBufferAdaptor appendPixelBuffer:pixelBuffer withPresentationTime:presentationTime];
  
                          if (appended) 
                              {
  
                              } 
                          else 
                              {
                                  NSLog (@"failed to append");
                                  [self stopRecording];
                              }
                      }
  
  
                  CVPixelBufferRelease(pixelBuffer);
                  CFRelease(imageData);
  
              }
  
          if (count&gt;1000) 
              {
                  [self stopRecording];
              }
      }
  
  -(void) startRecording 
      {
          // Doesn't record audio at all. Needs to be implemented.
  
          // create the AVAssetWriter
          NSString *moviePath = [[self pathToDocumentsDirectory] stringByAppendingPathComponent:OUTPUT_FILE_NAME];
          if ([[NSFileManager defaultManager] fileExistsAtPath:moviePath]) 
              {
                  [[NSFileManager defaultManager] removeItemAtPath:moviePath error:nil];
              }
  
          NSURL *movieURL = [NSURL fileURLWithPath:moviePath];
          NSError *movieError = nil;
  
          [assetWriter release];
          assetWriter = [[AVAssetWriter alloc] initWithURL:movieURL fileType: AVFileTypeQuickTimeMovie error: &amp;movieError];
          NSDictionary *assetWriterInputSettings = [NSDictionary dictionaryWithObjectsAndKeys: AVVideoCodecH264, AVVideoCodecKey,[NSNumber numberWithInt:testView.frame.size.width], AVVideoWidthKey,[NSNumber numberWithInt:testView.frame.size.height], AVVideoHeightKey,nil];
  
          [assetWriterInput release];
          assetWriterInput =[AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeVideo outputSettings:assetWriterInputSettings];
          assetWriterInput.expectsMediaDataInRealTime = YES;
          [assetWriter addInput:assetWriterInput];
  
  
          [assetWriterPixelBufferAdaptor release];
          assetWriterPixelBufferAdaptor = [[AVAssetWriterInputPixelBufferAdaptor  alloc] initWithAssetWriterInput:assetWriterInput sourcePixelBufferAttributes:nil];
          [assetWriter startWriting];
  
          firstFrameWallClockTime = CFAbsoluteTimeGetCurrent();
          [assetWriter startSessionAtSourceTime: CMTimeMake(0, TIME_SCALE)];
  
          // start writing samples to it
          [assetWriterTimer release];
          assetWriterTimer = [NSTimer scheduledTimerWithTimeInterval:0.2 target:self selector:@selector (writeSample:) userInfo:nil repeats:YES];
  
          [movieURL release];
          [assetWriterInputSettings release];
  
      }
  
  
  -(void) stopRecording 
      {
          if (assetWriterTimer!=nil) 
              {
                  [assetWriterTimer invalidate];
                  assetWriterTimer = nil;
                  [assetWriter finishWriting];
  
  
                  NSString *moviePath = [[self pathToDocumentsDirectory] stringByAppendingPathComponent:OUTPUT_FILE_NAME];
                  UISaveVideoAtPathToSavedPhotosAlbum (moviePath, nil, nil, nil);
  
              }
  
      }
  
  
  
  
  
  -(void) pauseRecording
      {
          // needs to be implemented
      }
  
  
  
  
  -(NSString*) pathToDocumentsDirectory 
      {
          NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
          NSString *documentsDirectory = [paths objectAtIndex:0];
          return documentsDirectory;
  
      }
  
  
  - (void)viewDidUnload
      {
          [super viewDidUnload];
          // Release any retained subviews of the main view.
          // e.g. self.myOutlet = nil;
      }
  
  - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
      {
          // Return YES for supported orientations
          return YES;
      }
  
  @end
  </code></pre>
  
  <p>So that's that. If anyone can modify this code to record microphone audio I'm sure there are many people besides me that would appreciate it. Once the audio is added this would make a straight forward framework to do in app screen cast for developers to demo their apps. </p>
  
  <p>FYI: My code is open source, largely modeled after the VTMScreenRecorderTest project from <a href="http://www.subfurther.com/blog/2011/04/12/voices-that-matter-iphone-spring-2011/" rel="nofollow noreferrer">http://www.subfurther.com/blog/2011/04/12/voices-that-matter-iphone-spring-2011/</a> . I made small-moderate modifications and cleaned up memory leaks.</p>