Tôi đang làm việc trên một dự án mà tôi tạo video từ UIImage, với mã tôi tìm thấy ở đây và tôi đang gặp khó khăn trong một số ngày để tối ưu hóa nó (cho khoảng 300 hình ảnh, 5 phút trên trình mô phỏng và chỉ đơn giản là treo trên thiết bị vì bộ nhớ).Tối ưu hóa CVPixelBufferRef
Tôi sẽ bắt đầu với mã làm việc tôi có ngày hôm nay (tôi làm việc với arc):
-(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);
}];
}
Và bây giờ là chức năng để có được bộ đệm pixel, mà tôi đang gặp khó khăn:
- (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;
}
Điều kỳ lạ đầu tiên: như bạn có thể thấy trong hàm này, tôi phải nhân chiều rộng và chiều cao cho 2, nếu không, video kết quả sẽ bị rối loạn và tôi không thể hiểu tại sao (tôi có thể đăng ảnh chụp màn hình nếu nó giúp, các pixel dường như đến từ hình ảnh của tôi, nhưng chiều rộng là không chính xác, và có một squa đen lớn ở nửa dưới của video).
Một vấn đề khác là phải mất một lượng bộ nhớ thực sự lớn; Tôi nghĩ rằng bộ đệm pixel có thể không được giải quyết tốt, nhưng tôi không hiểu tại sao.
Cuối cùng, nó rất chậm, nhưng tôi có hai ý tưởng để cải thiện nó, mà tôi không sử dụng.
Đầu tiên là để tránh sử dụng UIImage để tạo bộ đệm điểm ảnh của tôi, kể từ khi tôi tạo ra UIImage bản thân mình với (uint8_t *) dữ liệu. Tôi đã cố gắng sử dụng 'CVPixelBufferCreateWithBytes', nhưng nó sẽ không hoạt động. Đây là cách tôi đã thử nó:
OSType pixFmt = CVPixelBufferGetPixelFormatType(pxbuffer); CVPixelBufferCreateWithBytes(kCFAllocatorDefault, width, height, pixFmt, self.composition.srcImage.resultImageData, width*2, NULL, NULL, (__bridge CFDictionaryRef) attributes, &pxbuffer);
(Những lập luận đều giống nhau như đối với các chức năng nêu trên; dữ liệu hình ảnh của tôi được mã hóa trong 16 bit cho mỗi pixel, và tôi không thể tìm thấy một cuộc tranh luận OSType tốt để cung cấp cho chức năng.) Nếu ai đó biết cách sử dụng nó (có thể không thể với dữ liệu 16 bit/pixel?), nó sẽ giúp tôi tránh được việc chuyển đổi thực sự vô dụng.
- Điều thứ hai là tôi muốn tránh kCVPixelFormatType_32ARGB cho video của mình. Tôi đoán sẽ nhanh hơn khi sử dụng thứ gì đó với ít bit/pixel hơn, nhưng khi tôi thử nó (tôi đã thử tất cả các định dạng kCVPixelFormatType_16XXXXX, với ngữ cảnh được tạo với 5 bit/thành phần và kCGImageAlphaNoneSkipFirst), hoặc nó bị treo, hoặc là video kết quả không chứa gì (với kCVPixelFormatType_16BE555).
Tôi biết tôi hỏi rất nhiều chỉ trong một bài, nhưng tôi loại mất trong mã này, tôi đã cố gắng rất nhiều kết hợp và không ai trong số họ làm việc ...
Điều đó sẽ mất tối đa 10 giây. Có vẻ như bạn đang bị rò rỉ và cũng sao chép dữ liệu hình ảnh của bạn. Làm thế nào bạn tải hình ảnh của bạn? Có thể có một cách trực tiếp hơn để tải chúng vào một CVPixelBuffer. –
Tôi quản lý chỉ ngày hôm qua để có được một cái gì đó mà không bị rò rỉ! Tôi không thực sự chắc chắn về những gì đã được vấn đề tuy nhiên, kể từ khi tôi đã được tìm kiếm trong tất cả các hướng cùng một lúc ... Tôi đã sử dụng mã từ đây: http://codethink.no-ip.org/wordpress/archives/673 mà tôi đã sửa đổi rất nhiều và kết thúc bằng thứ gì đó không bị rò rỉ ... Nó vẫn không hoàn hảo, bởi vì tôi tạo ra hình ảnh từ (int *) dữ liệu và chuyển đổi chúng lại sau đó, nhưng ít nhất nó không sụp đổ nữa vì vấn đề bộ nhớ ... – Grhyll
bạn có phiên bản cuối cùng này không mã? Tôi đang gặp vấn đề tương tự. Tôi đánh giá cao nếu bạn có thể chia sẻ nó. Cảm ơn. – SpaceDog