一、前言
(一)GPUImage的結(jié)構(gòu)明肮,濾鏡鏈的實(shí)現(xiàn)原理
從上一節(jié)的濾鏡鏈的原理中菱农,我們知道了Source總是濾鏡鏈的源頭,必須繼承GPUImageOutput
柿估,通過傳遞outputFramebuffer
給target
循未,實(shí)現(xiàn)濾鏡鏈
二、GPUImageOutput關(guān)鍵屬性和方法
1秫舌、outputFramebuffer
GPUImageOutput
中的outputFramebuffer
的類型是GPUImageFramebuffer
的妖,作用是管理GPUImageOutput(包括Source和Filter)產(chǎn)生的Texture
2、targets
targets是一個保存實(shí)現(xiàn)了GPUImageInput
的數(shù)組足陨,作用是
- 負(fù)責(zé)管理GPUImageOutput的GPUImageInput集合
- 通過addTarget和removeTarget管理GPUImageInput集合
3嫂粟、setInputFramebufferForTarget
GPUImageOutput
生成的outputFrameBuffer
設(shè)置給GPUImageInput target
,這樣就實(shí)現(xiàn)了GPUImageInput
是在GPUImageOutput
處理后的texture
上做處理墨缘,這是濾鏡鏈的關(guān)鍵
三星虹、GPUImageOutput的子類
3.1、GPUImageRawDataInput
從二進(jìn)制的圖片數(shù)據(jù)中镊讼,生成outputFramebuffer
宽涌,初始化時要指定數(shù)據(jù)的格式GPUPixelFormat
,其核心方法是- (void)uploadBytes:(GLubyte *)bytesToUpload
蝶棋,通過glTexImage2D
加載紋理數(shù)據(jù)
- (void)uploadBytes:(GLubyte *)bytesToUpload;
{
[GPUImageContext useImageProcessingContext];
// TODO: This probably isn't right, and will need to be corrected
outputFramebuffer = [[GPUImageContext sharedFramebufferCache] fetchFramebufferForSize:uploadedImageSize textureOptions:self.outputTextureOptions onlyTexture:YES];
glBindTexture(GL_TEXTURE_2D, [outputFramebuffer texture]);
glTexImage2D(GL_TEXTURE_2D, 0, _pixelFormat, (int)uploadedImageSize.width, (int)uploadedImageSize.height, 0, (GLint)_pixelFormat, (GLenum)_pixelType, bytesToUpload);
}
3.2护糖、GPUImageUIElement
- 通過UIView或CALayer初始化,通過layer的
renderInContext
顯示內(nèi)容嚼松,核心方法是- (void)updateWithTimestamp:(CMTime)frameTime
- 將顯示的內(nèi)容繪制到CoreGraphics上下文嫡良,獲取圖像數(shù)據(jù)后通過
glTexImage2D
加載紋理數(shù)據(jù) - 適合在視頻上添加文字水印的功能
- (void)updateWithTimestamp:(CMTime)frameTime;
{
[GPUImageContext useImageProcessingContext];
CGSize layerPixelSize = [self layerSizeInPixels];
GLubyte *imageData = (GLubyte *) calloc(1, (int)layerPixelSize.width * (int)layerPixelSize.height * 4);
CGColorSpaceRef genericRGBColorspace = CGColorSpaceCreateDeviceRGB();
CGContextRef imageContext = CGBitmapContextCreate(imageData, (int)layerPixelSize.width, (int)layerPixelSize.height, 8, (int)layerPixelSize.width * 4, genericRGBColorspace, kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst);
// CGContextRotateCTM(imageContext, M_PI_2);
CGContextTranslateCTM(imageContext, 0.0f, layerPixelSize.height);
CGContextScaleCTM(imageContext, layer.contentsScale, -layer.contentsScale);
// CGContextSetBlendMode(imageContext, kCGBlendModeCopy); // From Technical Q&A QA1708: http://developer.apple.com/library/ios/#qa/qa1708/_index.html
[layer renderInContext:imageContext];
CGContextRelease(imageContext);
CGColorSpaceRelease(genericRGBColorspace);
// TODO: This may not work
outputFramebuffer = [[GPUImageContext sharedFramebufferCache] fetchFramebufferForSize:layerPixelSize textureOptions:self.outputTextureOptions onlyTexture:YES];
glBindTexture(GL_TEXTURE_2D, [outputFramebuffer texture]);
// no need to use self.outputTextureOptions here, we always need these texture options
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (int)layerPixelSize.width, (int)layerPixelSize.height, 0, GL_BGRA, GL_UNSIGNED_BYTE, imageData);
...
3.3、GPUImageVideoCamera
- 通過
AVCaptureVideoDataOutput
獲取持續(xù)的視頻數(shù)據(jù)輸出献酗,在代理方法captureOutput:didOutputSampleBuffer:fromConnection:
可以拿到CMSamleBufferRef
寝受,然后判斷采集的圖像的編碼格式是否為YUV
,再進(jìn)行調(diào)用不同的方法罕偎,加載紋理數(shù)據(jù)很澄,核心方法是- (void)processVideoSampleBuffer:(CMSampleBufferRef)sampleBuffer;
若是YUV
,需要通過調(diào)用CVOpenGLESTextureCacheCreateTextureFromImage
分別產(chǎn)生Y
和UV
紋理。這里紋理的產(chǎn)生大概是這樣的一個過程CMSampleBufferRef
->CVImageBufferRef
->CVOpenGLESTextureRef
->Texture
// processVideoSampleBuffer方法中的這個判斷就是對YUV編碼的圖像處理甩苛,產(chǎn)生紋理的具體代碼
if ([GPUImageContext supportsFastTextureUpload] && captureAsYUV)
{
// 這里產(chǎn)生YUV紋理
}
否則還是調(diào)用glTexImage2D
蹂楣,加載紋理
- 一般用它的子類GPUImageStillCamera,既可以拍照讯蒲,也可以錄制視頻
3.4痊土、GPUImageMovie
- 通過本地的視頻來初始化,產(chǎn)生紋理的核心方法
- (void)startProcessing
- 通過Asset加載墨林,調(diào)用
processAsset
方法赁酝,依賴AVAssetReaderOutput
的copyNextSampleBuffer
方法,獲取CMSampleBufferRef
旭等,拿到了SampleBuffer后酌呆,就跟上面GPUImageVideoCamera
對SampleBuffer的處理方法一樣 - 通過NSURL加載,生成
AVURLAsset
搔耕,然后走processAsset
的流程 - 通過AVPlayerItem加載隙袁,調(diào)用
processPlayerItem
方法,通過AVPlayerItemVideoOutput
逐幀讀取弃榨,通過AVPlayerItemVideoOutput
的copyPixelBufferForItemTime
獲取CVPixelBufferRef
菩收,然后繼續(xù)走YUV判斷的那套流程產(chǎn)生紋理數(shù)據(jù)
- 通過Asset加載墨林,調(diào)用
GPUImageMovie配合AVFoundation,可以做一個視頻編輯的功能惭墓,GPUImageMovie提供實(shí)時預(yù)覽和加濾鏡的功能坛梁,AVFoundation負(fù)責(zé)視頻的編輯,這里我做了一個Demo腊凶,下載地址https://github.com/maple1994/MPVideoEditDemo
3.5划咐、GPUImageTextureInput
通過已經(jīng)存在的紋理初始化
3.6、GPUImagePicture
通過加載圖片的信息钧萍,生成紋理信息褐缠,實(shí)現(xiàn)的核心是圖片->CGImageRef->紋理
四、總結(jié)
GPUImageOutput的作用风瘦,總的一句話队魏,就是產(chǎn)生紋理數(shù)據(jù),將紋理綁定到GPUImageFrameBuffer
万搔。常用生成紋理的途徑可以總結(jié)為以下三種
CMSampleBuffer -> CVImageBuffer/CVPixelBuffer -> CVOpenGLESTextureCacheCreateTextureFromImage生成紋理(YUV)
UIView -> Layer(生成上下文繪制) -> glTextImage2D
UIImage -> CGImageRef -> glTextImage2D