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ł ...
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. –
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
czy masz ostateczną wersję tego kod? Mam ten sam problem. Doceniam, czy możesz to udostępnić. Dzięki. – SpaceDog