2011-05-17 17 views
7

en una aplicación de cacao Actualmente estoy codificando, obtengo imágenes instantáneas de un procesador de Quartz Composer (objetos NSImage) y me gustaría codificarlas en un QTMovie en tamaño 720 * 480, 25 fps y códec H264 usando el método addImage: Aquí está la pieza correspondiente de código:¿Por qué mi aplicación de codificación de imágenes basada en QTKit es tan lenta?

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]; 

Funciona, sin embargo, mi solicitud no es capaz de hacer eso en tiempo real en mi nuevo MacBook Pro, aunque sé que QuickTime Broadcaster puede codificar imágenes en tiempo real en H264 con una calidad aún mayor que la que uso, en la misma computadora.

¿Por qué? ¿Cuál es el problema aquí? ¿Es esto un problema de administración de hardware (multi-core threading, GPU, ...) o me falta algo? Déjame aclarar que soy nuevo (2 semanas de práctica) en el mundo de desarrollo de Apple, tanto en las bibliotecas de Objective-C, el cacao, el X-código, Quicktime y compositor de cuarzo, etc.

Gracias por cualquier ayuda

+0

¿Estás seguro de que quieres 720x480 a 25 fps? ¿No debería ser 720x480 a 29.97 fps o 720x576 a 25 fps? Dudo que arregle su problema de velocidad, pero parece un formato extraño. – user1118321

Respuesta

5

AVFoundation es una forma más eficiente de renderizar una animación QuartzComposer a una transmisión de video 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; 
     } 

    } 
}]; 

En mis pruebas, esto es alrededor de dos veces más rápido que su código publicado que utiliza QTKit. La mayor mejora parece provenir de la codificación H.264 que se transfiere a la GPU en lugar de realizarse en el software. A partir de un vistazo rápido a un perfil, parece que los cuellos de botella restantes son la representación de la composición en sí misma y la lectura de los datos renderizados de la GPU en un búfer de píxeles. Obviamente, la complejidad de su composición tendrá un impacto en esto.

Es posible optimizar aún más utilizando la capacidad QCRenderer de proporcionar instantáneas como CVOpenGLBufferRef s, que pueden mantener los datos del marco en la GPU en lugar de leerlos para entregarlos al codificador. Sin embargo, no lo miré demasiado lejos.

Cuestiones relacionadas