AVFoundation 是蘋果提供的用于處理基于時間的媒體數(shù)據(jù)的一個框架掖棉。我們想要實現(xiàn)一個相機仍翰,需要從手機攝像頭采集數(shù)據(jù)冯凹,離不開這個框架的支持吉嚣。GPUImage 對 AVFoundation 做了一些封裝梢薪,使我們的采集工作變得十分簡單。
另外尝哆,GPUImage 的核心魅力還在于秉撇,它封裝了一個鏈路結(jié)構(gòu)的圖像數(shù)據(jù)處理流程,簡稱濾鏡鏈秋泄。濾鏡鏈的結(jié)構(gòu)使得多層濾鏡的疊加功能變得很容易實現(xiàn)琐馆。
濾鏡鏈簡介
在 GPUImage 中,對圖像數(shù)據(jù)的處理都是通過建立濾鏡鏈來實現(xiàn)的印衔。
這里就涉及到了一個類 GPUImageOutput 和一個協(xié)議 GPUImageInput 啡捶。對于繼承了 GPUImageOutput 的類,可以理解為具備輸出圖像數(shù)據(jù)的能力奸焙;對于實現(xiàn)了 GPUImageInput 協(xié)議的類,可以理解為具備接收圖像數(shù)據(jù)輸入的能力。
顧名思義与帆,濾鏡鏈作為一個鏈路了赌,具有起點和終點。根據(jù)前面的描述玄糟,濾鏡鏈的起點應(yīng)該只繼承了 GPUImageOutput 類勿她,濾鏡鏈的終點應(yīng)該只實現(xiàn)了 GPUImageInput 協(xié)議,而對于中間的結(jié)點應(yīng)該同時繼承了 GPUImageOutput 類并實現(xiàn)了 GPUImageInput 協(xié)議阵翎,這樣才具備承上啟下的作用逢并。
一、濾鏡鏈起點
在 GPUImage 中郭卫,只繼承了 GPUImageOutput砍聊,而沒有實現(xiàn) GPUImageInput 協(xié)議的類有六個,也就是說有六種類型的輸入源:
1贰军、GPUImagePicture
GPUImagePicture 通過圖片來初始化玻蝌,本質(zhì)上是先將圖片轉(zhuǎn)化為 CGImageRef,然后將 CGImageRef 轉(zhuǎn)化為紋理词疼。
2俯树、GPUImageRawDataInput
GPUImageRawDataInput 通過二進制數(shù)據(jù)初始化蕾哟,然后將二進制數(shù)據(jù)轉(zhuǎn)化為紋理综液,在初始化的時候需要指明數(shù)據(jù)的格式(GPUPixelFormat)。
3巴元、GPUImageTextureInput
GPUImageTextureInput 通過已經(jīng)存在的紋理來初始化舵盈。既然紋理已經(jīng)存在米辐,在初始化的時候就不會重新去生成,只是將紋理的索引保存下來书释。
4翘贮、GPUImageUIElement
GPUImageUIElement 可以通過 UIView 或者 CALayer 來初始化,最后都是調(diào)用 CALayer 的 renderInContext: 方法爆惧,將當(dāng)前顯示的內(nèi)容繪制到 CoreGraphics 的上下文中狸页,從而獲取圖像數(shù)據(jù)。然后將數(shù)據(jù)轉(zhuǎn)化為紋理扯再。簡單來說就是截屏芍耘,截取當(dāng)前控件的內(nèi)容。
這個類可以用來實現(xiàn)在視頻上添加文字水印的功能熄阻。因為在 OpenGL 中不能直接進行文本的繪制斋竞,所以如果我們想把一個 UILabel 的內(nèi)容添加到濾鏡鏈里面去,使用 GPUImageUIElement 來實現(xiàn)是很合適的秃殉。
5坝初、GPUImageMovie
GPUImageMovie 通過本地的視頻來初始化浸剩。首先通過 AVAssetReader 來逐幀讀取視頻,然后將幀數(shù)據(jù)轉(zhuǎn)化為紋理鳄袍,具體的流程大概是:AVAssetReaderOutput -> CMSampleBufferRef -> CVImageBufferRef -> CVOpenGLESTextureRef -> Texture 绢要。
6、GPUImageVideoCamera
GPUImageVideoCamera 通過相機參數(shù)來初始化拗小,通過屏幕比例和相機位置(前后置)來初始化相機重罪。這里主要使用 AVCaptureVideoDataOutput 來獲取持續(xù)的視頻流數(shù)據(jù)輸出,在代理方法 captureOutput:didOutputSampleBuffer:fromConnection: 中可以拿到 CMSampleBufferRef 哀九,將其轉(zhuǎn)化為紋理的過程與 GPUImageMovie 類似剿配。
然而,我們在項目中使用的是它的子類 GPUImageStillCamera阅束。 GPUImageStillCamera 在原來的基礎(chǔ)上多了一個 AVCaptureStillImageOutput呼胚,它是我們實現(xiàn)拍照功能的關(guān)鍵,在 captureStillImageAsynchronouslyFromConnection:completionHandler: 方法的回調(diào)中围俘,同樣能拿到我們熟悉 CMSampleBufferRef砸讳。
簡單來說,GPUImageVideoCamera 只能錄制視頻界牡,GPUImageStillCamera 還可以拍照簿寂,因此我們使用 GPUImageStillCamera 。
二宿亡、濾鏡
濾鏡鏈的關(guān)鍵角色是 GPUImageFilter常遂,它同時繼承了 GPUImageOutput 類并實現(xiàn)了 GPUImageInput 協(xié)議。GPUImageFilter 實現(xiàn)承上啟下功能的基礎(chǔ)是「渲染到紋理」挽荠,這個操作我們在 《使用 iOS OpenGL ES 實現(xiàn)長腿功能》 一文中已經(jīng)介紹過了克胳,簡單來說就是將結(jié)果渲染到紋理而不是屏幕上。
這樣圈匆,每一個濾鏡都能把輸出的紋理作為下一個濾鏡的輸入漠另,實現(xiàn)多層濾鏡效果的疊加。
三跃赚、濾鏡鏈終點
在 GPUImage 中笆搓,實現(xiàn)了 GPUImageInput 協(xié)議,而沒有繼承 GPUImageOutput 的類有四個:
1纬傲、GPUImageMovieWriter
GPUImageMovieWriter 封裝了 AVAssetWriter满败,可以逐幀從幀緩存的渲染結(jié)果中讀取數(shù)據(jù),最后通過 AVAssetWriter 將視頻文件保存到指定的路徑叹括。
2算墨、GPUImageRawDataOutput
GPUImageRawDataOutput 通過 rawBytesForImage 屬性,可以獲取到當(dāng)前輸入紋理的二進制數(shù)據(jù)汁雷。
假設(shè)我們的濾鏡鏈在輸入源和終點之間净嘀,連接了三個濾鏡报咳,而我們需要拿到第二個濾鏡渲染后的數(shù)據(jù),用來做人臉識別面粮。那我們可以在第二個濾鏡后面再添加一個 GPUImageRawDataOutput 作為輸出少孝,則可以拿到對應(yīng)的二進制數(shù)據(jù)继低,且不會影響原來的渲染流程熬苍。
3、GPUImageTextureOutput
這個類的實現(xiàn)十分簡單袁翁,提供協(xié)議方法 newFrameReadyFromTextureOutput:柴底,在每一幀渲染結(jié)束后,將自身返回粱胜,通過 texture 屬性就可以拿到輸入紋理的索引柄驻。
4、GPUImageView
GPUImageView 繼承自 UIView焙压,通過輸入的紋理鸿脓,執(zhí)行一遍渲染流程。這次的渲染目標(biāo)不是新的紋理涯曲,而是自身的 layer 野哭。
這個類是我們實現(xiàn)相機功能的重要組成部分,我們所有的濾鏡效果幻件,都要依靠它來呈現(xiàn)拨黔。