ここで見つけたコードを使用して、UIImage からビデオを生成するプロジェクトに取り組んでおり、最適化するために数日間苦労しています (約 300 の画像の場合、シミュレーターで 5 分ほどかかります。メモリが原因でデバイスでクラッシュするだけです)。
私は今日持っている作業コードから始めます(私は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);
}];
}
そして今、私が苦労しているピクセルバッファを取得する関数:
- (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;
}
最初の奇妙な点: この関数でわかるように、幅と高さを 2 倍にする必要があります。そうしないと、結果のビデオがすべて台無しになり、その理由がわかりません (スクリーンショットが役立つ場合は投稿できます。ピクセルは私の画像から来ているようですが、幅が正しくなく、ビデオの下半分に大きな黒い四角があります)。
もう 1 つの問題は、非常に大量のメモリを必要とすることです。ピクセル バッファの割り当てをうまく解除できなかったと思いますが、その理由はわかりません。
最後に、非常に遅いですが、改善するためのアイデアが 2 つありますが、使用できません。
1 つ目は、(uint8_t *) データを使用して自分で UIImage を生成するため、UIImage を使用してピクセル バッファーを作成しないようにすることです。「CVPixelBufferCreateWithBytes」を使用しようとしましたが、うまくいきません。これが私が試した方法です:
OSType pixFmt = CVPixelBufferGetPixelFormatType(pxbuffer); CVPixelBufferCreateWithBytes(kCFAllocatorDefault, width, height, pixFmt, self.composition.srcImage.resultImageData, width*2, NULL, NULL, (__bridge CFDictionaryRef) attributes, &pxbuffer);
(引数は上記の関数と同じです。私の画像データはピクセルあたり 16 ビットでエンコードされており、関数に与える適切な OSType 引数が見つかりませんでした。) 誰かがそれを使用する方法を知っている場合 (おそらくそれは16ビット/ピクセルデータでは不可能ですか?)、本当に役に立たない変換を避けるのに役立ちます.
- 2 つ目は、ビデオに kCVPixelFormatType_32ARGB を使用したくないということです。ビット/ピクセルが少ないものを使用する方が速いと思いますが、試してみると(5ビット/コンポーネントとkCGImageAlphaNoneSkipFirstで作成されたコンテキストですべてのkCVPixelFormatType_16XXXXXフォーマットを試しました)、クラッシュするか、結果のビデオ何も含まれていません (kCVPixelFormatType_16BE555 を使用)。
たった1回の投稿で多くのことを尋ねていることは知っていますが、このコードではちょっと迷っています。非常に多くの組み合わせを試しましたが、どれもうまくいきませんでした...