Sto lavorando a un progetto in cui generi un video da UIImage, con il codice che ho trovato da queste parti, e sto lottando da alcuni giorni per ottimizzarlo (per circa 300 immagini, ci vuole come 5 minuti sul simulatore, e si blocca semplicemente sul dispositivo a causa della memoria).Ottimizzazione di CVPixelBufferRef
Inizierò con il codice di lavoro che ho oggi (io lavoro con l'arco):
-(void) writeImageAsMovie:(NSArray *)array toPath:(NSString*)path size:(CGSize)size duration:(int)duration
{
NSError *error = nil;
AVAssetWriter *videoWriter = [[AVAssetWriter alloc] initWithURL:[NSURL fileURLWithPath:path] fileType:AVFileTypeQuickTimeMovie error:&error];
NSParameterAssert(videoWriter);
NSDictionary *videoSettings = [NSDictionary dictionaryWithObjectsAndKeys:
AVVideoCodecH264, AVVideoCodecKey,
[NSNumber numberWithInt:size.width], AVVideoWidthKey,
[NSNumber numberWithInt:size.height], AVVideoHeightKey,
nil];
AVAssetWriterInput* writerInput = [AVAssetWriterInput
assetWriterInputWithMediaType:AVMediaTypeVideo
outputSettings:videoSettings];
AVAssetWriterInputPixelBufferAdaptor *adaptor = [AVAssetWriterInputPixelBufferAdaptor assetWriterInputPixelBufferAdaptorWithAssetWriterInput:writerInput sourcePixelBufferAttributes:nil];
NSParameterAssert(writerInput);
NSParameterAssert([videoWriter canAddInput:writerInput]);
[videoWriter addInput:writerInput];
//Start a session:
[videoWriter startWriting];
[videoWriter startSessionAtSourceTime:kCMTimeZero];
CVPixelBufferRef buffer = NULL;
buffer = [self newPixelBufferFromCGImage:[[self.frames objectAtIndex:0] CGImage]];
CVPixelBufferPoolCreatePixelBuffer (NULL, adaptor.pixelBufferPool, &buffer);
[adaptor appendPixelBuffer:buffer withPresentationTime:kCMTimeZero];
dispatch_queue_t mediaInputQueue = dispatch_queue_create("mediaInputQueue", NULL);
int frameNumber = [self.frames count];
[writerInput requestMediaDataWhenReadyOnQueue:mediaInputQueue usingBlock:^{
NSLog(@"Entering block with frames: %i", [self.frames count]);
if(!self.frames || [self.frames count] == 0)
{
return;
}
int i = 1;
while (1)
{
if (i == frameNumber)
{
break;
}
if ([writerInput isReadyForMoreMediaData])
{
freeMemory();
NSLog(@"inside for loop %d (%i)",i, [self.frames count]);
UIImage *image = [self.frames objectAtIndex:i];
CGImageRef imageRef = [image CGImage];
CVPixelBufferRef sampleBuffer = [self newPixelBufferFromCGImage:imageRef];
CMTime frameTime = CMTimeMake(1, TIME_STEP);
CMTime lastTime=CMTimeMake(i, TIME_STEP);
CMTime presentTime=CMTimeAdd(lastTime, frameTime);
if (sampleBuffer)
{
[adaptor appendPixelBuffer:sampleBuffer withPresentationTime:presentTime];
i++;
CVPixelBufferRelease(sampleBuffer);
}
else
{
break;
}
}
}
[writerInput markAsFinished];
[videoWriter finishWriting];
self.frames = nil;
CVPixelBufferPoolRelease(adaptor.pixelBufferPool);
}];
}
E ora la funzione per ottenere il buffer di pixel, con la quale sto lottando:
- (CVPixelBufferRef) newPixelBufferFromCGImage: (CGImageRef) image
{
CVPixelBufferRef pxbuffer = NULL;
int width = CGImageGetWidth(image)*2;
int height = CGImageGetHeight(image)*2;
NSMutableDictionary *attributes = [NSMutableDictionary dictionaryWithObjectsAndKeys:[NSNumber kCVPixelFormatType_32ARGB], kCVPixelBufferPixelFormatTypeKey, [NSNumber numberWithInt:width], kCVPixelBufferWidthKey, [NSNumber numberWithInt:height], kCVPixelBufferHeightKey, nil];
CVPixelBufferPoolRef pixelBufferPool;
CVReturn theError = CVPixelBufferPoolCreate(kCFAllocatorDefault, NULL, (__bridge CFDictionaryRef) attributes, &pixelBufferPool);
NSParameterAssert(theError == kCVReturnSuccess);
CVReturn status = CVPixelBufferPoolCreatePixelBuffer(NULL, pixelBufferPool, &pxbuffer);
NSParameterAssert(status == kCVReturnSuccess && pxbuffer != NULL);
CVPixelBufferLockBaseAddress(pxbuffer, 0);
void *pxdata = CVPixelBufferGetBaseAddress(pxbuffer);
NSParameterAssert(pxdata != NULL);
CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef context = CGBitmapContextCreate(pxdata, width,
height, 8, width*4, rgbColorSpace,
kCGImageAlphaNoneSkipFirst);
NSParameterAssert(context);
CGContextDrawImage(context, CGRectMake(0, 0, width,
height), image);
CGColorSpaceRelease(rgbColorSpace);
CGContextRelease(context);
CVPixelBufferUnlockBaseAddress(pxbuffer, 0);
return pxbuffer;
}
Prima cosa strana: come puoi vedere in questa funzione, devo moltiplicare la larghezza e l'altezza per 2, altrimenti il video del risultato è tutto incasinato, e non riesco a capire perché (posso postare screenshot se aiuta, i pixel sembrano venire dalla mia immagine, ma la larghezza non è corretta, e c'è un grande squa nero ri nella metà inferiore del video).
Un altro problema è che richiede molta memoria; Penso che il buffer dei pixel non potrebbe essere ben distolto, ma non vedo perché.
Infine, è molto lento, ma ho due idee per migliorarlo, che non riesco a utilizzare.
Il primo è quello di evitare l'uso UIImage per creare i miei buffer di pixel, da quando ho generare l'UIImage me stesso con (*) uint8_t dati. Ho provato a utilizzare "CVPixelBufferCreateWithBytes", ma non funzionava. Ecco come ho provato:
OSType pixFmt = CVPixelBufferGetPixelFormatType(pxbuffer); CVPixelBufferCreateWithBytes(kCFAllocatorDefault, width, height, pixFmt, self.composition.srcImage.resultImageData, width*2, NULL, NULL, (__bridge CFDictionaryRef) attributes, &pxbuffer);
(Gli argomenti sono gli stessi che per le funzioni di cui sopra, i miei dati immagine sono codificati in 16 bit per pixel, e non sono riuscito a trovare un buon argomento OSType per dare alla funzione.) Se qualcuno sa come usarlo (forse non è possibile con dati a 16 bit/pixel?), mi aiuterebbe a evitare una conversione davvero inutile.
- La seconda cosa è che vorrei evitare kCVPixelFormatType_32ARGB per il mio video. Immagino che sarebbe più veloce usare qualcosa con meno bit/pixel, ma quando lo provo (ho provato tutti i formati kCVPixelFormatType_16XXXXX, con un contesto creato con 5 bit/componente e kCGImageAlphaNoneSkipFirst), o si blocca, sia il video risultante non contiene nulla (con kCVPixelFormatType_16BE555).
so che chiedo molto in un solo post, ma sto tipo di Lost in questo codice, ho provato tante combinazioni e nessuno di loro ha lavorato ...
Questo dovrebbe richiedere al massimo 10 secondi. Sembra che tu stia perdendo e anche duplicando i dati delle tue immagini. Come stai caricando le tue immagini? Forse c'è un modo più diretto per caricarli in un CVPixelBuffer. –
Sono riuscito proprio ieri ad arrivare a qualcosa che non perde! Non sono sicuro di quale sia il problema, dal momento che ho cercato in tutte le direzioni contemporaneamente ... Ho usato il codice da qui: http://codethink.no-ip.org/wordpress/archives/673 che ho modificato molto e terminato con qualcosa che non perde ... Non è ancora perfetto, perché creo le immagini da (int *) dati e li converto nuovamente in quello successivo, ma almeno non si blocca più a causa di problemi di memoria ... – Grhyll
hai la versione finale di questo codice? Sto avendo lo stesso problema. Apprezzo se puoi condividerlo. Grazie. – SpaceDog