CIContext drawImage 速度很慢
背景:在做视频/图片编辑软件的时候, 自己写一个视频图片的播放器,
播放流程:
视频->CVPixelBufferRef->CIImage->CIFilter处理字幕, 滤镜, 转场->新的CIImage->通过CIContext将CIImage显示到GLKView上
图片->UIImage->CGImageRef->CIImage->CIFilter处理字幕, 滤镜, 转场->新的CIImage->通过CIContext将CIImage显示到GLKView上
导出流程:
视频->CVPixelBufferRef->CIImage->CIFilter处理字幕, 滤镜, 转场->新的CIImage->通过CIContext将CIImage转换成CVPixelBuffer->写入文件
图片->UIImage->CGImageRef->CIImage->CIFilter处理字幕, 滤镜, 转场->新的CIImage->通过CIContext将CIImage转换成CVPixelBuffer->写入文件
然后发现, 视频在播放的时候很顺畅, 但是图片却会卡顿, 很慢, 经排查后发现跟CIImage创建时候的来源有关
CIImage
数据来源CGImageRef, 用CIContext绘制的时候比较慢
_CIImage = [CIImage imageWithCGImage:_image.CGImage];
调试的时候 po _CIImage 如下:
<CIImage: 0x14c9e0bd0 extent [0 0 1280 960]>
affine [1 0 0 -1 0 960] extent=[0 0 1280 960] opaque
colormatch "sRGB IEC61966-2.1"-to-workingspace extent=[0 0 1280 960] opaque
CGImageRef 0x147fd89c0 RGBX8 1280x960 alpha_one extent=[0 0 1280 960] opaque
数据来源CVPixelBufferRef, 用CIContext绘制的时候比较快
_CIImage = [CIImage imageWithCVPixelBuffer:pixelBuffer];
调试时候 po _CIImage 如下:
<CIImage: 0x1291dd5a0 extent [0 0 1920 1080]>
colorkernel filterKernel(image,scaling=[1 1080]) extent=[0 0 1920 1080]
affine [2.85714 0 0 2.85714 0 0] extent=[0 0 1920 1080] opaque
affine [1 0 0 -1 0 378] extent=[0 0 672 378] opaque
colormatch "Composite NTSC"-to-workingspace extent=[0 0 672 378] opaque
IOSurface 0x1258002a8 YCC420v 709 alpha_one extent=[0 0 672 378] opaque
接下来这段纯属个人推断, 没有验证过
=======
CIImage内部的实现应该有分CPU和GPU, 如果同样是CPU到CPU,
UIImage->CGImageRef->CIImage经过处理后->CIImage->UIImage, 假设处理也是在CPU上的话, 这样的处理应该是比较快的,因为没有数据搬移.
或者GPU到GPU的处理,
CVPixelBufferRef->CIImage->经过CIFilter处理->CIImage->通过CIContext的drawImage显示或者renderImage:toCVPixelBuffer生成新的CVPixelBufferRef导出
这样速度应该会很快, 因为不需要数据搬移, 数据的处理也都在GPU层面上
但是如果过程是这样的UIImage->CIImage->经过CIFilter处理->CIImage->CIContext显示在GPU上, 这样的速度就会变慢.
因为经历了CPU->GPU的数据搬移
=======
解决思路:
既然是因为CIImage创建的时候如果从UIImage过来就会慢, 从CVPixelBufferRef来的就不会慢, 是不是可以对图片的处理的时候, 创建CIImage可以类似从CVPixelBufferRef中来
简单的说, 就是可不可以用GPU的方式来创建图片
解决办法:
将UIImage转换成CVPixelBufferRef, 然后CIImage在从CVPixelBufferRef中创建
- (CVPixelBufferRef) pixelBufferFromCGImage: (CGImageRef) image {
NSDictionary *options = @{
(NSString*)kCVPixelBufferCGImageCompatibilityKey : @YES,
(NSString*)kCVPixelBufferCGBitmapContextCompatibilityKey : @YES,
};
CVPixelBufferRef pxbuffer = NULL;
CVReturn status = CVPixelBufferCreate(kCFAllocatorDefault, CGImageGetWidth(image),
CGImageGetHeight(image), kCVPixelFormatType_32ARGB, (__bridge CFDictionaryRef) options,
&pxbuffer);
if (status!=kCVReturnSuccess) {
NSLog(@"Operation failed");
}
NSParameterAssert(status == kCVReturnSuccess && pxbuffer != NULL);
CVPixelBufferLockBaseAddress(pxbuffer, 0);
void *pxdata = CVPixelBufferGetBaseAddress(pxbuffer);
CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef context = CGBitmapContextCreate(pxdata, CGImageGetWidth(image),
CGImageGetHeight(image), 8, 4*CGImageGetWidth(image), rgbColorSpace,
kCGImageAlphaNoneSkipFirst);
NSParameterAssert(context);
// CGContextConcatCTM(context, CGAffineTransformMakeRotation(0));
// CGAffineTransform flipVertical = CGAffineTransformMake( 1, 0, 0, -1, 0, CGImageGetHeight(image) );
// CGContextConcatCTM(context, flipVertical);
// CGAffineTransform flipHorizontal = CGAffineTransformMake( -1.0, 0.0, 0.0, 1.0, CGImageGetWidth(image), 0.0 );
// CGContextConcatCTM(context, flipHorizontal);
CGContextDrawImage(context, CGRectMake(0, 0, CGImageGetWidth(image),
CGImageGetHeight(image)), image);
CGColorSpaceRelease(rgbColorSpace);
CGContextRelease(context);
CVPixelBufferUnlockBaseAddress(pxbuffer, 0);
return pxbuffer;
}
然后
CVPixelBufferRef pixelBuffer = [self pixelBufferFromCGImage:_image.CGImage];
_CIImage = [CIImage imageWithCVPixelBuffer:pixelBuffer];
CVPixelBufferRelease(pixelBuffer);