title: Using AVAssetWriter to create QuickTime movie from raw H.264 / AAC files
body: <p>Using Mac OSX Objective-C, I'm trying to create a command line tool that takes a single H.264 and a single AAC file as inputs - encoded from the same source material - and, using AVAssetWriter, create a single QuickTime-compatible mov (and/or m4v) file for further editing / distribution.</p>
  
  <p>What I've done so far: 
   1) Using AVFoundation Framework components, i.e., AVAssetWriter, AVAssetWriterInput, etc. and Core Media Framework components, i.e., CMSampleBufferCreate, CMBlockBufferReplaceDataBytes, etc. - I've prototyped the CL tool.
   2) I've hooked up the input/output file URLs, created the AVAssetWriter and the AVAssetWriterInput, the CMBlockBuffer, etc.
   3) When I execute my runLoop, AVAssetWriter creates the m4v file but, although it is well-formed, it is only a 136 byte file representing the movie header atoms with no video track data.
   4) I have searched StackOverflow - as well as the Apple forums and the Internet generally -  to find an answer to my specific set of issues.</p>
  
  <p>Using the error checking in my code as well as the Xcode debugger, I find that AVAssetWriter is set up properly - it starts to construct the movie file - but CMBlockBufferReplaceDataBytes DOES NOT write H.264 NAL data into the CMBlockBuffer (as I believe I should do). So what am I missing?</p>
  
  <p>Here is the relevant portion of my runLoop code:</p>
  
  <pre><code>// Create the videoFile.m4v AVAssetWriter.
  AVAssetWriter *videoFileWriter = [[AVAssetWriter alloc] initWithURL:destinationURL fileType:AVFileTypeQuickTimeMovie error:&amp;error];
  NSParameterAssert(videoFileWriter);
  if (error) {
      NSLog(@"AVAssetWriter initWithURL failed with error= %@", [error localizedDescription]);
  }
  
  // Create the video file settings dictionary.
  NSDictionary *videoSettings = [NSDictionary dictionaryWithObjectsAndKeys: AVVideoCodecH264, AVVideoCodecKey, [NSNumber numberWithInt:1280],
                                 AVVideoWidthKey, [NSNumber numberWithInt:720], AVVideoHeightKey, nil];
  
  // Perform video settings check.
  if ([videoFileWriter canApplyOutputSettings:videoSettings forMediaType:AVMediaTypeVideo]) {
      NSLog(@"videoFileWriter can apply videoSettings...");    
  }
  
  // Create the input to the videoFileWriter AVAssetWriter.
  AVAssetWriterInput *videoFileWriterInput = [[AVAssetWriterInput alloc] initWithMediaType:AVMediaTypeVideo outputSettings:videoSettings];
  videoFileWriterInput.expectsMediaDataInRealTime = YES;
  NSParameterAssert(videoFileWriterInput);
  NSParameterAssert([videoFileWriter canAddInput:videoFileWriterInput]);
  
  // Connect the videoFileWriterInput to the videoFileWriter.
  if ([videoFileWriter canAddInput:videoFileWriterInput]) {
     [videoFileWriter addInput:videoFileWriterInput]; 
  }
  
  // Get the contents of videoFile.264 (using current Mac OSX methods).
  NSData *sourceData = [NSData dataWithContentsOfURL:sourceURL];
  const char *videoFileData = [sourceData bytes];
  size_t sourceDataLength = [sourceData length];
  NSLog(@"The value of 'sourceDataLength' is: %ld", sourceDataLength);
  
  // Set up to create the videoSampleBuffer.
  int32_t videoWidth = 1280;
  int32_t videoHeight = 720;
  CMBlockBufferRef videoBlockBuffer = NULL;
  CMFormatDescriptionRef videoFormat = NULL;
  CMSampleBufferRef videoSampleBuffer = NULL;
  CMItemCount numberOfSampleTimeEntries = 1;
  CMItemCount numberOfSamples = 1;
  
  // More set up to create the videoSampleBuffer.
  CMVideoFormatDescriptionCreate(kCFAllocatorDefault, kCMVideoCodecType_H264, videoWidth, videoHeight, NULL, &amp;videoFormat);
  result = CMBlockBufferCreateWithMemoryBlock(kCFAllocatorDefault, NULL, 150000, kCFAllocatorDefault, NULL, 0, 150000, kCMBlockBufferAssureMemoryNowFlag,
                                              &amp;videoBlockBuffer);
  NSLog(@"After 'CMBlockBufferCreateWithMemoryBlock', 'result' is: %d", result);
  
  // The CMBlockBufferReplaceDataBytes method is supposed to write videoFile.264 data bytes into the videoSampleBuffer.
  result = CMBlockBufferReplaceDataBytes(videoFileData, videoBlockBuffer, 0, 150000);
  NSLog(@"After 'CMBlockBufferReplaceDataBytes', 'result' is: %d", result);
  
  CMSampleTimingInfo videoSampleTimingInformation = {CMTimeMake(1, 30)};
  result = CMSampleBufferCreate(kCFAllocatorDefault, videoBlockBuffer, TRUE, NULL, NULL, videoFormat, numberOfSamples, numberOfSampleTimeEntries,
                                &amp;videoSampleTimingInformation, 0, NULL, &amp;videoSampleBuffer);
  NSLog(@"After 'CMSampleBufferCreate', 'result' is: %d", result);
  
  // Set the videoSampleBuffer to ready (is this needed?).
  result = CMSampleBufferMakeDataReady(videoSampleBuffer);
  NSLog(@"After 'CMSampleBufferMakeDataReady', 'result' is: %d", result);
  
  // Start writing...
  if ([videoFileWriter startWriting]) {
      [videoFileWriter startSessionAtSourceTime:kCMTimeZero];
  }
  
  // Start the first while loop (DEBUG)...
  </code></pre>
  
  <p>All ideas, comments, and suggestions are welcome.</p>
  
  <p>Thank you!</p>