#cocoa #macos #qtkit #quartz-composer
#cocoa #macos #qtkit #quartz-composer
Вопрос:
в приложении cocoa, которое я в настоящее время кодирую, я получаю моментальные изображения из средства визуализации Quartz Composer (объекты NSImage), и я хотел бы закодировать их в QTMovie размером 720 * 480, частотой 25 кадров в секунду и кодеком H264, используя метод addImage: . Вот соответствующий фрагмент кода:
qRenderer = [[QCRenderer alloc] initOffScreenWithSize:NSMakeSize(720,480) colorSpace:CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB) composition:[QCComposition compositionWithFile:qcPatchPath]]; // define an "offscreen" Quartz composition renderer with the right image size
imageAttrs = [NSDictionary dictionaryWithObjectsAndKeys: @"avc1", // use the H264 codec
QTAddImageCodecType, nil];
qtMovie = [[QTMovie alloc] initToWritableFile: outputVideoFile error:NULL]; // initialize the output QT movie object
long fps = 25;
frameNum = 0;
NSTimeInterval renderingTime = 0;
NSTimeInterval frameInc = (1./fps);
NSTimeInterval myMovieDuration = 70;
NSImage * myImage;
while (renderingTime <= myMovieDuration){
if(![qRenderer renderAtTime: renderingTime arguments:NULL])
NSLog(@"Rendering failed at time %.3fs", renderingTime);
myImage = [qRenderer snapshotImage];
[qtMovie addImage:myImage forDuration: QTMakeTimeWithTimeInterval(frameInc) withAttributes:imageAttrs];
[myImage release];
frameNum ;
renderingTime = frameNum * frameInc;
}
[qtMovie updateMovieFile];
[qRenderer release];
[qtMovie release];
Оно работает, однако мое приложение не способно выполнять это в режиме реального времени на моем новом MacBook Pro, хотя я знаю, что QuickTime Broadcaster может кодировать изображения в режиме реального времени в H264 с еще более высоким качеством, чем то, которое я использую, на том же компьютере.
Итак, почему? В чем здесь проблема? Это проблема с управлением оборудованием (многоядерная потоковая передача, графический процессор, …) или я что-то упускаю? Позвольте мне предварить, что я новичок (2 недели практики) в мире разработки Apple, как в библиотеках objective-C, cocoa, X-code, Quicktime и Quartz Composer и т.д.
Спасибо за любую помощь
Комментарии:
1. Вы уверены, что хотите 720×480 со скоростью 25 кадров в секунду? Разве это не должно быть 720×480 при 29,97 кадрах в секунду или 720×576 при 25 кадрах в секунду? Я сомневаюсь, что это исправит вашу проблему со скоростью, но это кажется странным форматом.
Ответ №1:
AVFoundation — это более эффективный способ рендеринга анимации QuartzComposer в видеопотоке H.264.
size_t width = 640;
size_t height = 480;
const char *outputFile = "/tmp/Arabesque.mp4";
QCComposition *composition = [QCComposition compositionWithFile:@"/System/Library/Screen Savers/Arabesque.qtz"];
QCRenderer *renderer = [[QCRenderer alloc] initOffScreenWithSize:NSMakeSize(width, height)
colorSpace:CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB) composition:composition];
unlink(outputFile);
AVAssetWriter *videoWriter = [[AVAssetWriter alloc] initWithURL:[NSURL fileURLWithPath:@(outputFile)] fileType:AVFileTypeMPEG4 error:NULL];
NSDictionary *videoSettings = @{ AVVideoCodecKey : AVVideoCodecH264, AVVideoWidthKey : @(width), AVVideoHeightKey : @(height) };
AVAssetWriterInput* writerInput = [[AVAssetWriterInput alloc] initWithMediaType:AVMediaTypeVideo outputSettings:videoSettings];
[videoWriter addInput:writerInput];
[writerInput release];
AVAssetWriterInputPixelBufferAdaptor *pixelBufferAdaptor = [AVAssetWriterInputPixelBufferAdaptor assetWriterInputPixelBufferAdaptorWithAssetWriterInput:writerInput sourcePixelBufferAttributes:NULL];
int framesPerSecond = 30;
int totalDuration = 30;
int totalFrameCount = framesPerSecond * totalDuration;
[videoWriter startWriting];
[videoWriter startSessionAtSourceTime:kCMTimeZero];
__block long frameNumber = 0;
dispatch_queue_t workQueue = dispatch_queue_create("com.example.work-queue", DISPATCH_QUEUE_SERIAL);
NSLog(@"Starting.");
[writerInput requestMediaDataWhenReadyOnQueue:workQueue usingBlock:^{
while ([writerInput isReadyForMoreMediaData]) {
NSTimeInterval frameTime = (float)frameNumber / framesPerSecond;
if (![renderer renderAtTime:frameTime arguments:NULL]) {
NSLog(@"Rendering failed at time %.3fs", frameTime);
break;
}
CVPixelBufferRef frame = (CVPixelBufferRef)[renderer createSnapshotImageOfType:@"CVPixelBuffer"];
[pixelBufferAdaptor appendPixelBuffer:frame withPresentationTime:CMTimeMake(frameNumber, framesPerSecond)];
CFRelease(frame);
frameNumber ;
if (frameNumber >= totalFrameCount) {
[writerInput markAsFinished];
[videoWriter finishWriting];
[videoWriter release];
[renderer release];
NSLog(@"Rendered %ld frames.", frameNumber);
break;
}
}
}];
В моем тестировании это примерно в два раза быстрее, чем ваш опубликованный код, использующий QTKit. Самое большое улучшение, по-видимому, связано с тем, что кодирование H.264 передается графическому процессору, а не выполняется программным обеспечением. При беглом взгляде на профиль кажется, что оставшиеся узкие места — это рендеринг самой композиции и считывание обработанных данных обратно из графического процессора в пиксельный буфер. Очевидно, что сложность вашей композиции окажет некоторое влияние на это.
Возможно, удастся дополнительно оптимизировать это, используя QCRenderer
возможность предоставления снимков в виде CVOpenGLBufferRef
файлов, которые могут сохранять данные кадра на графическом процессоре, а не считывать их обратно для передачи кодировщику. Впрочем, я не стал заходить слишком далеко в этом вопросе.