前言
GPUImage系列解析已经接近尾声,这次介绍的是:
- 纹理输入输出
GPUImageTextureOutput
和GPUImageTextureOutput
- 二进制数据输入输出
GPUImageRawDataInput
和GPUImageRawDataOutput
- 滤镜通道
GPUImageFilterPipeline
demo用来展示如何使用GPUImageRawDataOutput
。
概念介绍
1、GPUImageTextureOutput
GPUImageTextureOutput类实现GPUImageInput协议,可以接受响应链的图像,并返回对应的OpenGL ES纹理。
- delegate属性:实现了GPUImageTextureOutputDelegate协议的回调对象;
- texture属性:OpenGL ES的纹理,只读;
- enabled属性:是否有效,默认为有效;
- -doneWithTexture方法:结束处理纹理图像,解锁firstInputFramebuffer。
2、GPUImageTextureInput
GPUImageTextureInput类继承GPUImageOutput类,可以作为响应链的起点,把OpenGL ES纹理对应的纹理信息导入响应链处理。
textureSize属性为纹理尺寸;
初始化的时候,分配一个GPUImageFramebuffer,缓存纹理单元的信息;
process的时候直接调用targets对应的就绪方法,因为图像信息就在OpenGL ES控制内存中。
GPUImageTextureOutput 和 GPUImageTextureInput 用于 向OpenGL ES 输入或者输出纹理,把GPUImage的输出作为OpenGL ES的纹理或者把OpenGL ES的输出作为GPUImage的纹理输入。
3、GPUImageRawDataOutput
GPUImageRawDataOutput类实现协议GPUImageInput,可以接受响应链的图像信息,并且以二进制的格式返回数据;
rawBytesForImage属性: 二进制数据的指针;
GPUByteColorVector结构体:RGBA颜色空间结构体,便于读取二进制数据;
supportsFastTextureUpload用的BGRA的颜色格式;
如果需要输出RGBA,则可以对BGRA格式再做一次RGBA->BRGA的颜色转换;
RGBA -> BGRA 的操作如下:
texture2D(inputImageTexture, textureCoordinate).bgra;
lockNextFramebuffer属性:标志是否要读取图像信息如果为YES,会调用CVPixelBufferLockBaseAddress锁住对应的CVPixelBufferRef;
4、GPUImageRawDataInput
GPUImageRawDataInput类继承GPUImageOutput类,可以接受二进制数据,按照特定的颜色格式,把数据转成图像并传入响应链;
GPUImageRawDataInput不会对传入的数据copied或者retained,但你不需要在使用完之后去释放;二进制数据发送到GPU的纹理单元,默认的颜色格式是BGRA和整型数据。
- 上传图片的逻辑:先申请outputframebuffer ,然后绑定纹理,最后用glTexImage2D 上传图像数据到GPU。
- processData方法:处理图片;如果上一次操作还未完成,则直接返回。
5、GPUImageFilterPipeline
GPUImageFilterPipeline类是滤镜通道,把inputs的滤镜组合起来,然后添加output为最后的输出目标。核心代码如下:
- filters为输入的滤镜,output为输出目标;
- 把filters的滤镜按照链表的形式串联起来。
- (void)_refreshFilters {
id prevFilter = self.input;
GPUImageOutput *theFilter = nil;
for (int i = 0; i < [self.filters count]; i++) {
theFilter = [self.filters objectAtIndex:i];
[prevFilter removeAllTargets];
[prevFilter addTarget:theFilter];
prevFilter = theFilter;
}
[prevFilter removeAllTargets];
if (self.output != nil) {
[prevFilter addTarget:self.output];
}
}
demo思路
GPUImage-滤镜视频录制的流程图为左边部分;
demo的流程图为右边部分:
- 1、这次把GPUImageVideoCamera的输出设置为GPUImageRawDataOutput
self.mOutput = [[GPUImageRawDataOutput alloc] initWithImageSize:CGSizeMake(640, 480) resultsInBGRAFormat:YES];
[videoCamera addTarget:self.mOutput];
- 2、输出的二进制数据转换为CVPixelBufferRef
[strongOutput lockFramebufferForReading];
GLubyte *outputBytes = [strongOutput rawBytesForImage];
NSInteger bytesPerRow = [strongOutput bytesPerRowInOutput];
CVPixelBufferRef pixelBuffer = NULL;
CVReturn ret = CVPixelBufferCreateWithBytes(kCFAllocatorDefault, 640, 480, kCVPixelFormatType_32BGRA, outputBytes, bytesPerRow, nil, nil, nil, &pixelBuffer);
- 3、根据buffer创建CGImageRef,再是UIImage
CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB();
CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, strongOutput.rawBytesForImage, bytesPerRow * 480, NULL);
CGImageRef cgImage = CGImageCreate(640, 480, 8, 32, bytesPerRow, rgbColorSpace, kCGImageAlphaPremultipliedFirst|kCGBitmapByteOrder32Little, provider, NULL, true, kCGRenderingIntentDefault);
UIImage *image = [UIImage imageWithCGImage:cgImage];
- 4、最后添加到UIView的UIImageView上。
// 此处必须在主线程操作UI
dispatch_async(dispatch_get_main_queue(), ^{
self.mImageView.image = image;
});
遇到的问题
1、创建像素错误
CVPixelBufferCreateWithBytes
方法返回kCVReturnInvalidArgument
错误。
检查CVPixelBufferCreateWithBytes的width、height、pixelFormatType参数。
2、输出图像方向错误
GPUImageVideoCamera输出图像旋转了90°。
In previous iOS versions, the front-facing camera would always deliver buffers in VCaptureVideoOrientationLandscapeLeft。
添加以下代码:
videoCamera.outputImageOrientation = UIInterfaceOrientationPortrait;
videoCamera.horizontallyMirrorFrontFacingCamera = YES;
3、创建图像失败
通过二进制数据创建NSData,再创建UIImage,发现image为nil。
NSData* data = [[NSData alloc] initWithBytes:strongOutput.rawBytesForImage length:bytesPerRow * 480];
UIImage *image = [[UIImage alloc] initWithData:data];
暂未找到原因,猜测是data参数有问题。
+initWithData:的data参数要求必须是系统支持的image类型。
The data in the data parameter must be formatted to match the file format of one of the system’s supported image types.
4、图像未显示
检查UIImage对象不为nil,屏幕未显示。
GPUImageVideoCamera回调不在主线程,直接赋值image无效。
总结
GPUImageTextureOutput和GPUImageTextureInput用于GPUImage和OpenGL ES之间的协调,GPUImageRawDataOutput和GPUImageRawDataInput用于GPUImage和UIKit之间的协调, GPUImageFilterPipeline用于把多个滤镜简单串联。
代码地址