GPUImage源碼解析 -- GPUImageOutput/Input

在上一篇文章中介紹了GPUImage框架中的核心載體GPUImageFrameBuffer,在接下來的文章中,我們將介紹如何使用這個載體實(shí)現(xiàn)渲染和傳遞。本文將重點(diǎn)介紹GPUImage中的一個非常重要的基類GPUImageOutput和一個協(xié)議GPUImageInput。基本上所有重要的GPUImage處理類都是GPUImageOutput的子類,它實(shí)現(xiàn)了一個輸出的基本功能。

同樣的,基本上所有的GPUImage處理類也都遵循GPUImageInput協(xié)議。它定義了一個能夠接收frameBuffer的接收者所必須實(shí)現(xiàn)的基本功能。主要包括:
*接收上一個GPUImageOutput的相關(guān)信息;
*接收并處理上一個GPUImageOutput渲染完成的通知;

GPUImageOutput

GPUImageOuput實(shí)現(xiàn)了一些作為一個Output的基本功能,主要包括:

  • 生成,管理到GPUImageFrameBuffer
  • Target的添加以及管理,用來生成整個FilterChain;
  • 產(chǎn)出一個渲染的結(jié)果;

GPUImageFrameBuffer的管理

每個GPUImageOutput都會包含一個自己的GPUImageFrameBuffer對象;可以通過outputFrameBuffer方法獲得。這個FrameBuffer對象就是當(dāng)前Output渲染的對象。

這個FrameBuffer對象不是一直存在的,而是當(dāng)這個Output需要進(jìn)行渲染的時候,才會從GPUImageFrameBufferCache中取一個。因此,并不是隨時都能夠獲得一個GPUImageFrameBuffer對象的。一般情況下,當(dāng)渲染完畢并且通知了FilterChain中的下一個target之后,就會被remove掉,并且歸還給GPUImageFrameBufferCache以供重用。

與FrameBuffer相關(guān)的方法有:

- (void)setInputFramebufferForTarget:(id<GPUImageInput>)target atIndex:(NSInteger)inputTextureIndex;

這個方法的調(diào)用發(fā)生在當(dāng)前output渲染完畢后,需要通知下一個receiver可以開始渲染的時候,把當(dāng)前Output的FrameBuffer傳遞給下一個Input,讓它可以使用這個FrameBuffer的結(jié)果進(jìn)行渲染。

- (GPUImageFramebuffer *)framebufferForOutput;

這個方法可以獲得當(dāng)前正在渲染的frameBuffer,但是這個方法更大的用處是給子類提供一個覆蓋的接口。子類可以通過覆蓋這個方法,來提供輸出的frameBuffer。因?yàn)橛幸恍┒鄠€pass的濾鏡可能會有多個FrameBuffer。

- (void)removeOutputFramebuffer;

這個方法用來移除當(dāng)前渲染的frameBuffer,同樣,這個方法更大的用處是給子類提供一個移除當(dāng)前FrameBuffer的機(jī)會。

Target的管理

GPUImageOutput既然作為一個輸出,那么自然就應(yīng)該有對應(yīng)的接受者來接受這個FrameBuffer并且使用這個Output處理的結(jié)果進(jìn)行渲染。這些接受者我們都將它稱之為target。每個Target都是一個實(shí)現(xiàn)了GPUImageInput協(xié)議的對象。這些對象可以接收FrameBuffer,處理當(dāng)前Output渲染完畢的通知等等。GPUImageInput會在接下來的解析中詳細(xì)介紹。

與Target管理相關(guān)的方法有:

- (NSArray*)targets;

這個方法可以獲取到當(dāng)前Output所有的target。每個Output都可以添加多個target,當(dāng)這個Output渲染完成之后,每個target都會收到通知。

- (void)addTarget:(id<GPUImageInput>)newTarget;
- (void)addTarget:(id<GPUImageInput>)newTarget atTextureLocation:(NSInteger)textureLocation;

這兩個addTarget方法的作用都是將下一個實(shí)現(xiàn)了GPUImageInput協(xié)議的對象添加到FilterChain當(dāng)中來。在一個GPUImageInput被添加到FilterChain之后,它做的主要事情包括:將當(dāng)前Output的outputFrameBuffer作為input傳遞給這個GPUImageInput對象;設(shè)置這個outputFrameBuffer所在的TextureLocation。TextureLocation的作用是:有些可以同時接受多個Input的對象,它要將不同的frameBuffer的texture綁定到不同的位置上。因?yàn)槊總€GL_TEXTURE同時只能接受一個texture的綁定。

[self setInputFramebufferForTarget:newTarget atIndex:textureLocation];
[targets addObject:newTarget];
[targetTextureIndices addObject:[NSNumber numberWithInteger:textureLocation]];

移除Targets:

- (void)removeTarget:(id<GPUImageInput>)targetToRemove;
- (void)removeAllTargets;

這兩個方法的作用是將某一個或者所有的target都移出FilterChain。當(dāng)一個target被移出FilterChain之后,它將不會再收到任何當(dāng)前Output渲染完成的通知。

獲取當(dāng)前FrameBuffer的處理結(jié)果

GPUImageOutput提供了一些從當(dāng)前Output獲得處理結(jié)果的方法,讓使用者可以簡單的獲得處理的結(jié)果:

- (CGImageRef)newCGImageFromCurrentlyProcessedOutput;
- (CGImageRef)newCGImageByFilteringCGImage:(CGImageRef)imageToFilter;
- (UIImage *)imageFromCurrentFramebuffer;
- (UIImage *)imageFromCurrentFramebufferWithOrientation:(UIImageOrientation)imageOrientation;
- (UIImage *)imageByFilteringImage:(UIImage *)imageToFilter;
- (CGImageRef)newCGImageByFilteringImage:(UIImage *)imageToFilter;

其中最核心的方法是newCGImageFromCurrentlyProcessedOutput,基本上所有的方法最終都調(diào)用了這個方法。但是GPUImageOutput并沒有為這個方法提供默認(rèn)的實(shí)現(xiàn),而是提供了一個方法定義。具體的實(shí)現(xiàn)在它的兩個重要的子類GPUImageFilterGPUImageFilterGroup中。而實(shí)際上最終調(diào)用的方法都在GPUImageFilter中實(shí)現(xiàn)了,GPUImageFilterGroup的實(shí)現(xiàn)實(shí)際上是調(diào)用了它的terminalFilter中的實(shí)現(xiàn)。

在獲取一個FilterChain中的一個GPUImageOutput的處理結(jié)果時,有一個非常重要的方法需要調(diào)用:

- (void)useNextFrameForImageCapture;

在我們上一篇介紹GPUImageFrameBuffer的文章中,我們說到了所有的FrameBuffer都是有引用計數(shù)的,當(dāng)一個FrameBuffer的引用計數(shù)為零的時候,它會被歸還到FrameBufferCache中。當(dāng)一個GPUImageOutput處于一個FilterChain中的時候,他渲染完成并且通知下一個input,當(dāng)下一個input也渲染完成之后,這個FrameBuffer的引用計數(shù)就會為零,因此也會被釋放掉。這個時候如果調(diào)用newCGImageFromCurrentlyProcessedOutput方法的話,就會多次釋放一個FrameBuffer導(dǎo)致Crash,或者獲取不到這個FrameBuffer。

useNextFrameForImageCapture方法的左右就是:在渲染的時候,再調(diào)用一次GPUImageFrameBufferlock方法,強(qiáng)行將引用計數(shù)+1.這樣這個FrameBuffer就會一直存在于這個Output中,可以用來進(jìn)行渲染結(jié)果的獲取。

GPUImageInput

GPUImageInput協(xié)議是為了保證每一個接收GPUImageOutput輸出的對象都能夠正確地處理對應(yīng)的輸入的。它的功能主要包括:

  • 接收GPUmageOutput的輸出信息;
  • 處理GPUImageOutput渲染完成的通知;

接收GPUImageOutput的輸出信息

根據(jù)之前介紹的GPUImageOutput,它添加的每一個Target都必須實(shí)現(xiàn)了GPUImageInput協(xié)議。因此GPUImageInput協(xié)議保證了所有實(shí)現(xiàn)它的對象都能夠接收output的輸出。主要輸出信息包括:

- (void)setInputFramebuffer:(GPUImageFramebuffer *)newInputFramebuffer atIndex:(NSInteger)textureIndex;
- (NSInteger)nextAvailableTextureIndex;
- (void)setInputSize:(CGSize)newSize atIndex:(NSInteger)textureIndex;
- (void)setInputRotation:(GPUImageRotationMode)newInputRotation atIndex:(NSInteger)textureIndex;

GPUImageInput可以接收的信息包括上一個Output輸出的frameBuffer,frameBuffer的size以及rotation。同樣的這些textureIndex都是為了提供個需要多個input的Filter準(zhǔn)備的。

處理GPUImageOutput渲染完成的通知

GPUImageOutput在渲染完成之后,會通知它所有的targets,因此,GPUImageInput需要實(shí)現(xiàn)下面這個方法來接收這個通知,并且開始自己的渲染:

- (void)newFrameReadyAtTime:(CMTime)frameTime atIndex:(NSInteger)textureIndex;

其中,參數(shù)frameTime主要是給視頻處理的時候使用的,當(dāng)多個輸入都是視頻的時候,可以使用frameTime來確保多個輸入是同步的;
本文主要介紹了GPUImage中最重要的基類和協(xié)議GPUImageOutputGPUImageInput,它們具體的實(shí)現(xiàn)都在GPUImageFilter以及GPUImageFilterGroup中,我們會在接下來的解析中具體看看他們的實(shí)現(xiàn)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,428評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,024評論 3 413
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,285評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,548評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,328評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 54,878評論 1 321
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,971評論 3 439
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,098評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,616評論 1 331
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,554評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,725評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,243評論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 43,971評論 3 345
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,361評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,613評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,339評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,695評論 2 370