使用AVAssetWriter从原始H.264/AAC文件创建QuickTime影片

Chu*_*ems 7 aac h.264 avassetwriter

使用Mac OSX Objective-C,我正在尝试创建一个命令行工具,它将单个H.264和单个AAC文件作为输入 - 使用相同的源材料编码 - 并使用AVAssetWriter创建单个QuickTime兼容mov(和/或m4v)文件,用于进一步编辑/分发.

到目前为止我做了什么:1)使用AVFoundation Framework组件,即AVAssetWriter,AVAssetWriterInput等,以及Core Media Framework组件,即CMSampleBufferCreate,CMBlockBufferReplaceDataBytes等. - 我已经为CL工具做了原型.2)我已经连接了输入/输出文件URL,创建了AVAssetWriter和AVAssetWriterInput,CMBlockBuffer等.3)当我执行我的runLoop时,AVAssetWriter创建了m4v文件但是,虽然它是格式良好的,但它是只有136字节的文件表示没有视频轨道数据的电影标题原子.4)我已经搜索了StackOverflow - 以及Apple论坛和一般的互联网 - 来找到我特定问题的答案.

使用我的代码中的错误检查以及Xcode调试器,我发现AVAssetWriter设置正确 - 它开始构建电影文件 - 但是CMBlockBufferReplaceDataBytes不会将H.264 NAL数据写入CMBlockBuffer(因为我相信我应该做).那我错过了什么?

这是我的runLoop代码的相关部分:

// Create the videoFile.m4v AVAssetWriter.
AVAssetWriter *videoFileWriter = [[AVAssetWriter alloc] initWithURL:destinationURL fileType:AVFileTypeQuickTimeMovie error:&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, &videoFormat);
result = CMBlockBufferCreateWithMemoryBlock(kCFAllocatorDefault, NULL, 150000, kCFAllocatorDefault, NULL, 0, 150000, kCMBlockBufferAssureMemoryNowFlag,
                                            &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,
                              &videoSampleTimingInformation, 0, NULL, &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)...
Run Code Online (Sandbox Code Playgroud)

欢迎提出所有想法,意见和建议.

谢谢!

mal*_*hal 0

问题是对于 CMSampleBufferCreate,您传入零 numSampleSizeEntries 和 NULL SampleSizeArray。如果您不传递任何内容,则不会写入任何内容!

  • Malcolm:很明显,我对如何创建 CMSampleBuffer 然后用 H.264 NAL(例如 SPS + PPS + IDR NAL)“填充”它以及生成的 NAL 大小(以字节为单位)感到困惑。如果在 CMSampleBufferCreate 中,我设置 numSampleSizeEntries = 1,然后传递一个指向 sampleSizeArray 的指针,那么我可以简单地将 NAL 字节写入我的 videoBlockBuffer (CMBlockBufferRef dataBuffer) 并更新调用 CMSampleBufferMakeDataReady(videoSampleBuffer) 的sampleSizeArray 元素值吗?您能提供一段代码片段来说明这是如何完成的吗? (2认同)