Использование AVAssetWriter для создания фильма QuickTime из необработанных файлов H.264/AAC

Используя 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 НЕ записывает данные NAL H.264 в 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)...

Приветствуются все идеи, комментарии и предложения.

Спасибо!

7
задан Chuck at Cimarron Systems 09 авг. '12 в 7:44
источник поделиться
1 ответ

Проблема заключается в том, что для CMSampleBufferCreate вы передаете нуль numSampleSizeEntries и NULL sampleSizeArray. Если вы ничего не передадите, тогда ничего не будет написано!

0
ответ дан malhal 12 сент. '12 в 13:40
источник поделиться

Другие вопросы по меткам