2012-06-14 9 views
6

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 ...

+0

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. –

+0

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

+0

hai la versione finale di questo codice? Sto avendo lo stesso problema. Apprezzo se puoi condividerlo. Grazie. – SpaceDog

risposta

0

I have to multiply the width and height by 2, otherwise, the result video is all messed up, and I can't understand why

Punti contro pixel? Gli schermi retina ad alta risoluzione hanno il doppio del numero di pixel per punto.