2012-06-14 14 views
6

Pracuję nad projektem, w którym generuję wideo z UIImage, z kodem, który znalazłem w tym miejscu, i od kilku dni zmagam się, aby go zoptymalizować (dla około 300 obrazów, to wymaga 5 minut na symulatorze i po prostu zawiesza się na urządzeniu z powodu pamięci).Optymalizacja CVPixelBufferRef

zacznę z kodem roboczym mam dzisiaj (pracuję z łuku):

-(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); 

    }]; 
} 

a teraz funkcję, aby uzyskać bufor pikseli, z którą zmagam:

- (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; 
} 

Pierwsza dziwna rzecz: jak widać w tej funkcji, muszę pomnożyć szerokość i wysokość przez 2, w przeciwnym razie wynik wideo jest zawalony i nie mogę zrozumieć, dlaczego (mogę publikować zrzuty ekranu, jeśli to pomaga, piksele wydają się pochodzić z mojego obrazu, ale szerokość nie jest poprawna, a tam jest duży czarny squa ponownie na połowie dolnej części wideo).

Innym problemem jest to, że zajmuje naprawdę dużą ilość pamięci; Myślę, że bufor pikseli nie mógł zostać poprawnie przydzielony, ale nie rozumiem dlaczego.

Wreszcie jest bardzo powolny, ale mam dwa pomysły, aby go poprawić, czego nie używam.

  • Pierwszym jest unikać UIImage stworzyć moje bufory pikseli, ponieważ generują UIImage ja z (uint8_t *) danych. Próbowałem użyć "CVPixelBufferCreateWithBytes", ale to nie zadziałałoby. Oto jak próbowałem go:

    OSType pixFmt = CVPixelBufferGetPixelFormatType(pxbuffer); 
    CVPixelBufferCreateWithBytes(kCFAllocatorDefault, width, height, pixFmt, self.composition.srcImage.resultImageData, width*2, NULL, NULL, (__bridge CFDictionaryRef) attributes, &pxbuffer); 
    

(Argumenty są takie same jak dla powyższych funkcji, moje dane obrazu są kodowane w 16 bity na piksel, a ja nie mogłem znaleźć dobrego argumentu ostype nadać tej funkcji.) Jeśli ktoś wie, jak z niego korzystać (może nie jest to możliwe z danymi 16-bitowymi/pikselowymi?), pomógłby mi uniknąć naprawdę bezużytecznej konwersji.

  • Po drugie, chciałbym uniknąć pliku kCVPixelFormatType_32ARGB dla mojego wideo. Myślę, że byłoby szybciej użyć czegoś z mniejszą liczbą bitów/pikseli, ale gdy go wypróbuję (wypróbowałem wszystkie formaty kCVPixelFormatType_16XXXXX, z kontekstem utworzonym za pomocą 5 bitów/komponentu i kCGImageAlphaNoneSkipFirst), albo zawiesi się, albo wynikowe wideo nie zawiera niczego (z kCVPixelFormatType_16BE555).

wiem zadać wiele tylko w jednym wątku, ale jestem trochę zagubiony w tym kodzie, próbowałem tak wiele kombinacji i żaden z nich pracował ...

+0

To powinno zająć maksymalnie 10 sekund. Brzmi jak wyciek, a także powielanie danych zdjęć. Jak ładujesz swoje obrazy? Być może istnieje bardziej bezpośredni sposób na załadowanie ich do CVPixelBuffer. –

+0

Udało mi się dopiero wczoraj dostać się do czegoś, co nie wycieka! Nie jestem jednak pewien, na czym polegał problem, ponieważ szukałem w tym samym czasie we wszystkich kierunkach ... Użyłem kodu z: http://codethink.no-ip.org/wordpress/archives/673 które bardzo dużo zmodyfikowałem i zakończyłem czymś, co nie wycieka ... Nadal nie jest idealny, ponieważ tworzę obrazy z (int *) danych i konwertuję je do tego później, ale przynajmniej się nie psuje ze względu na problemy z pamięcią ... – Grhyll

+0

czy masz ostateczną wersję tego kod? Mam ten sam problem. Doceniam, czy możesz to udostępnić. Dzięki. – SpaceDog

Odpowiedz

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

Punkty kontra piksele? Ekrany siatkówki o wysokiej rozdzielczości mają dwukrotnie większą liczbę pikseli na punkt.