GPUImageOutput

總述

本類主要是針對(duì)輸出而言的,與目標(biāo)、圖像、紋理相關(guān)的處理,簡而言之就是輸出。除此之外,還有線程的管理。

枚舉UIImageOrientation

作者定義了幾種方向的枚舉類型,只看這個(gè)名字,我并不能知道它們到底指的是什么,所以讓我一個(gè)一個(gè)解讀一下。下面都假設(shè)設(shè)備是正向的,就是豎直的,并且home鍵在下面的那種擺放狀態(tài)。
1、UIImageOrientationUp:這個(gè)就是正向。
2、UIImageOrientationDown:正好與正向相反,它是倒立著的。
3、UIImageOrientationLeft:說來也巧,iOS的官方文檔正好也有同名的方向?qū)傩?,這個(gè)方向?qū)嶋H上就是home鍵在右邊,橫向的?;蛘?,是設(shè)備從正向按照逆時(shí)針方向旋轉(zhuǎn)90度。因?yàn)槟鏁r(shí)針的英語是counterclockwise,所以該方向的注釋一般簡寫為90 deg CCW,這就是逆時(shí)針旋轉(zhuǎn)90度的樣子。
4、UIImageOrientationRight:基于UIImageOrientationLeft的講述,該方向就非常好理解了,它就是設(shè)備從正向順時(shí)針旋轉(zhuǎn)90度。就是home在左邊,水平擺放的樣子。
5、UIImageOrientationUpMirrored:它這個(gè)意思是說保持圖像的向上的方向不變,在水平方向上顛倒一下,就是水平鏡像。
6、UIImageOrientationDownMirrored:這個(gè)意思是說圖像首先從正向旋轉(zhuǎn)180度,然后水平方向顛倒一下。
7、UIImageOrientationLeftMirrored:首先把圖像順時(shí)針旋轉(zhuǎn)90度,然后在水平方向上反轉(zhuǎn)一下。
8、UIImageOrientationRightMirrored:首先把圖像逆時(shí)針旋轉(zhuǎn)90度,然后在水平方向上反轉(zhuǎn)一下。
你會(huì)發(fā)現(xiàn)5678都是先旋轉(zhuǎn)一個(gè)角度,然后在水平方向上顛倒一下。

線程和內(nèi)存的環(huán)境控制

這個(gè)應(yīng)該是基于過程的代碼邏輯,看上去跟C語言似的,有點(diǎn)讓人難以理解。
1、dispatch_queue_attr_t GPUImageDefaultQueueAttribute(void):蘋果官方文檔并沒有講解dispatch_queue_attr_t是個(gè)什么樣的類型,干什么用的。不過,看上去它應(yīng)該是描述一個(gè)隊(duì)列應(yīng)該具備什么樣的性質(zhì)的對(duì)象。它代碼的實(shí)現(xiàn)中判定了一下iOS版本是否是9.0以上的,看來它是要用到iOS9.0以后才有的性質(zhì)啊。除此之外,還可以看出來,它把隊(duì)列設(shè)定為串行隊(duì)列,看來是要同步執(zhí)行任務(wù)了。
2、void runOnMainQueueWithoutDeadlocking(void (^block)(void)):從名字可以看出來,這是要在主線程執(zhí)行任務(wù),并且還要保障它不死鎖。這個(gè)任務(wù)嘛就是block中包含的代碼,主線程也很好理解,關(guān)鍵是這個(gè)如何做到不死鎖呢?很簡單,因?yàn)橹骶€程是單個(gè)線程,既然是單線程那就代表它是同步執(zhí)行任務(wù)的,你只管把任務(wù)塞到主線程后面等著它被執(zhí)行就好了,只要任務(wù)和任務(wù)之間不互相引用就行。至于說如何做到在主線程執(zhí)行?很簡單,如果當(dāng)前線程是主線程則直接執(zhí)行block,否則就把block直接拋到主線程上去。
3、void runSynchronouslyOnVideoProcessingQueue(void (^block)(void)):從這個(gè)名字可以知道2個(gè)信息,一個(gè)是它是同步執(zhí)行任務(wù)的,另一個(gè)是它是在一個(gè)處理視頻的隊(duì)列上面執(zhí)行任務(wù)的。原來這個(gè)視頻處理隊(duì)列就是GPUImageContext中的 contextQueue。思路也是和runOnMainQueueWithoutDeadlocking差不多,就是判斷當(dāng)前隊(duì)列是不是視頻處理隊(duì)列,如果是就直接執(zhí)行,否則就拋到這個(gè)視頻處理隊(duì)列上面去同步執(zhí)行。
4、void runAsynchronouslyOnVideoProcessingQueue(void (^block)(void)):基于runSynchronouslyOnVideoProcessingQueue的理解,這個(gè)就是異步執(zhí)行任務(wù)的。
5、void runSynchronouslyOnContextQueue(GPUImageContext *context, void (^block)(void)):這個(gè)和3是一樣的,只不過它這個(gè)GPUImageContext的上下文是可選的,而不是單例的那個(gè)GPUImageContext的那個(gè)上下文了。就是說你可以定制上下文。
6、void runAsynchronouslyOnContextQueue(GPUImageContext *context, void (^block)(void));:這個(gè)和4一樣,方式和5一樣,也是可選GPUImageContext上下文的。
7、void reportAvailableMemoryForGPUImage(NSString *tag):這里面涉及到的內(nèi)容對(duì)我來說還是比較陌生的。task_basic_info結(jié)構(gòu)體在iOS SDK中,iOS有段注釋說它不能夠在不同大小的任務(wù)之間安全地傳遞,這里面有關(guān)線程、內(nèi)存和任務(wù)的數(shù)據(jù)項(xiàng)。由此可見它能獲取到設(shè)備的硬件信息,這個(gè)屬于比較底層的東西了。kern_return_t也是個(gè)結(jié)構(gòu)體,task_info它是一個(gè)有關(guān)任務(wù)數(shù)據(jù)的結(jié)構(gòu)體,task_info也是kern_return_t類型的。mach_task_self()用于獲取當(dāng)前進(jìn)程,TASK_BASIC_INFO大概是一種默認(rèn)的信息,(task_info_t)&info很好理解,就是獲取已經(jīng)創(chuàng)建的任務(wù)信息的結(jié)構(gòu)體的內(nèi)存地址,&size就是要開辟size那么大小的內(nèi)存地址用的。如果獲取信息成功了以后,就返回當(dāng)前內(nèi)存的占用量,具體用法是task_basic_info中的 resident_size數(shù)據(jù)項(xiàng),這個(gè)數(shù)據(jù)項(xiàng)的意思是常住內(nèi)存的大小,單位是字節(jié)。而如果是想獲取失敗信息,可以可以使用C語言的mach_error_string去處理kern_return_t的變量的地址即可

GPUImageOutput內(nèi)部成員都有啥?

1、GPUImageFramebuffer *outputFramebuffer:看來這是該紋理的數(shù)據(jù)所在,它內(nèi)部有一個(gè)幀緩沖區(qū)。
2、NSMutableArray *targets:它是有目標(biāo)的,而且還是個(gè)復(fù)數(shù),就證明它可以接受不只一個(gè)的目標(biāo)。至于說這個(gè)目標(biāo)是什么東西,應(yīng)該是閑言少敘,下回分解的東西。
3、NSMutableArray *targetTextureIndices:這個(gè)像是個(gè)目標(biāo)紋理的下標(biāo),應(yīng)該是用于檢索目標(biāo)用的。
4、CGSize inputTextureSize:輸入的紋理的大小,這個(gè)暫時(shí)沒啥可說的,意思正如名字所述的那樣子。
5、CGSize cachedMaximumOutputSize:首先它是緩存的,這證明它是有內(nèi)存復(fù)用機(jī)制的,而且還有個(gè)最大的輸出大小的限制。
6、CGSize forcedMaximumSize:強(qiáng)制的,誰強(qiáng)制的,不知道哎。反正它也有個(gè)最大大小的限制。
7、BOOL overrideInputSize:是否重寫輸入的大小,正更證明了輸入的大小是被復(fù)用的。
8、BOOL allTargetsWantMonochromeData:是否所有的目標(biāo)都想要黑白的數(shù)據(jù),那這意味著什么呢?
9、BOOL usingNextFrameForImageCapture:從這個(gè)意思上講,這個(gè)要用下一幀來獲取圖片,但是這又說明了什么呢?
10、BOOL shouldSmoothlyScaleOutput:是否要柔和地縮放輸出,這個(gè)我就不知道,柔和地是指什么?難道是要慢慢地?
11、BOOL shouldIgnoreUpdatesToThisTarget:針對(duì)于某個(gè)目標(biāo)是否應(yīng)該忽略更新,目標(biāo)的更新是指什么呢?
12、GPUImageMovieWriter audioEncodingTarget:從名字看是音頻加密的對(duì)象,GPUImageMovieWriter是個(gè)什么類型?不管怎樣反正是個(gè)自定義的類型。
13、id<GPUImageInput> targetToIgnoreForUpdates:需要被忽略更新的對(duì)象,它遵循了GPUImageInput協(xié)議,有GPUImageInput就應(yīng)該有GPUImageOutput,那么它們到底是干什么的?
14、void(^frameProcessingCompletionBlock)(GPUImageOutput
, CMTime):這是個(gè)回調(diào)用的block屬性,回調(diào)的是一個(gè)GPUImageOutput,和一個(gè)CMTime。CMTime是個(gè)什么類型?根據(jù)蘋果官方文檔的解釋,它是一個(gè)結(jié)構(gòu)體,表征的一個(gè)有理數(shù)形式的時(shí)間。
15、BOOL enabled:怎么?這個(gè)玩意還有個(gè)開關(guān)功能的控制?
16、GPUTextureOptions outputTextureOptions:這個(gè)前面已經(jīng)知道了,就是設(shè)置紋理的包裹類型,并且還是用于輸出的,這又讓我對(duì)什么是輸入和輸出有了疑問。

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

這個(gè)輸入的target遵循了GPUImageInput協(xié)議,而且還有個(gè)下標(biāo)的位置。GPUImageOutput把自身持有的GPUImageFramebuffer,被稱為outputFramebuffer的,插入到target的指定位置上面。那么可以聯(lián)想到target維持了一個(gè)數(shù)組,專門用來放置,用于放置輸出的GPUImageFramebuffer的。

- (GPUImageFramebuffer *)framebufferForOutput

這個(gè)很簡單,其實(shí)就是把GPUImageOutput所持有的outputFramebuffer輸出給調(diào)用方。

- (void)removeOutputFramebuffer

這個(gè)其實(shí)也很簡單,就是GPUImageOutput把所持有的outputFramebuffer釋放掉,再具體點(diǎn)就是置為nil。

- (void)notifyTargetsAboutNewOutputTexture

簡單地說一說這個(gè)方法就是告訴GPUImageOutput把自身持有的outputFramebuffer重新賦值給所有指定位置的target。從這個(gè)方法的實(shí)現(xiàn)中也可以知道GPUImageOutput所持有的targets和targetTextureIndices有什么用。Targets就是GPUImageOutput所持有的目標(biāo),targetTextureIndices是用來存儲(chǔ)target所對(duì)應(yīng)的紋理的標(biāo)號(hào)的。于是可以知道該方法把目標(biāo)放在指定的紋理所在的位置上面。再簡單點(diǎn),就是重置目標(biāo)和紋理之間的對(duì)應(yīng)關(guān)系。

- (NSArray*)targets

用GPUImageOutput所持有的targets重新創(chuàng)建一個(gè)數(shù)組,并把這個(gè)數(shù)組返回。

- (void)addTarget:(id<GPUImageInput>)newTarget

每個(gè)目標(biāo)都具備為新來到的紋理找到合適位置的能力,因?yàn)槊總€(gè)目標(biāo)都具備nextAvailableTextureIndex方法,前提是這要這個(gè)目標(biāo)遵循了GPUImageInput協(xié)議。然后GPUImageOutput會(huì)把這個(gè)目標(biāo)放到指定的紋理的位置上,換句話說就是把目標(biāo)和某個(gè)紋理位置聯(lián)系起來。如果這個(gè)目標(biāo)是要被忽略更新的,那么就把這個(gè)目標(biāo)賦值給GPUImageOutput持有的那個(gè)被忽略更新的目標(biāo)??梢园l(fā)現(xiàn)每個(gè)output對(duì)象都是只持有1個(gè)應(yīng)該被忽略的目標(biāo)。

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

該方法就是把目標(biāo)放在紋理應(yīng)該在的位置上。它首先是檢查一下本身所維持的成員變量targets目標(biāo)數(shù)組中是否已經(jīng)包含了該目標(biāo),然后它把緩存cachedMaximumOutputSize置為0,為什么要把cachedMaximumOutputSize置為0呢?目的何在?先不管。接下來它把這個(gè)新來的target放到了指定的位置上,然后設(shè)置了它的輸入的幀緩沖區(qū)。然后再把它持有起來,其實(shí)就是放到自己的targets數(shù)組中去,與此同時(shí),它把target所在的紋理編號(hào)插入到targetTextureIndices中,想不到紋理的位置也是需要存儲(chǔ)的。allTargetsWantMonochromeData明顯是個(gè)標(biāo)志位,我想這個(gè)標(biāo)志位有利于提升處理的效率,因?yàn)槿筒糠痔幚淼姆绞綐O有可能是不同的。allTargetsWantMonochromeData是什么意思?這個(gè)是純色的意思。為什么要區(qū)分純色和非純色呢?我想大概是因?yàn)樘幚矸绞缴喜煌?,純色?yīng)該處理起來效率更高些。它計(jì)算這個(gè)標(biāo)志位的方式尤其值得一提提,因?yàn)樗怯靡延械臉?biāo)志位去和新來的目標(biāo)的標(biāo)志位進(jìn)行與運(yùn)算,那意思就是說只要有一個(gè)不是純色的,那么整個(gè)純色標(biāo)志位就不能為真,那倒的確是這樣的,因?yàn)槭聦?shí)確實(shí)是那樣的,感覺像是一條魚腥一鍋湯。

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

從名字來看它是要移除目標(biāo),移除誰的目標(biāo)?原來還是移除自身的某個(gè)目標(biāo),這個(gè)答案可以從if(![targets containsObject:targetToRemove]) { return; }中知道,它要移除的是自身持有targets的中的某個(gè)目標(biāo),如果沒有就不用多此一舉了。_targetToIgnoreForUpdates是用于標(biāo)識(shí)需要被忽略更新的目標(biāo),如果此時(shí)這個(gè)目標(biāo)需要被移除,那么_targetToIgnoreForUpdates指針也需要被置空。cachedMaximumOutputSize這個(gè)緩存的大小,到現(xiàn)在我也沒徹底搞清楚,到底為什么準(zhǔn)備的呢?不過在此處它被置為空。同樣的,因?yàn)槟繕?biāo)所對(duì)應(yīng)的紋理是被分別存儲(chǔ)的,所以你還需要專門祛除紋理所在的下標(biāo)值。接下來的過程它是在一個(gè)隊(duì)列里面處理該目標(biāo)的收尾工作的,它先是把該目標(biāo)的輸入大小設(shè)置為0,然后把該目標(biāo)的方向置為NO,接下來從目標(biāo)所對(duì)應(yīng)的紋理的下標(biāo)數(shù)組中把該紋理的下標(biāo)對(duì)象移除,從targets把該目標(biāo)移除,然后終止該輸入的新目標(biāo)的處理過程。這里面有涉及到輸入和輸出的概念了,看來這兩個(gè)概念對(duì)于目標(biāo)來說是個(gè)很重要的概念。

- (void)removeAllTargets

一看名字就知道這個(gè)比較暴力,移除所有的目標(biāo)。整個(gè)實(shí)現(xiàn)幾乎都是在隊(duì)列中同步執(zhí)行的,實(shí)現(xiàn)的過程首先是把cachedMaximumOutputSize置為0,這個(gè)東西已經(jīng)出現(xiàn)過很多次了,看樣子是很重要的。接下來毫無疑問了,就是遍歷自身持有的targets,然后所做的事情基本都是- (void)removeTarget:(id<GPUImageInput>)targetToRemove所做的事情,接下來就是清空targets和targetTextureIndices,最后把整體的純色標(biāo)志位allTargetsWantMonochromeData置為YES。

管理輸出紋理

這里面涉及到了2個(gè)方法,都是沒有實(shí)現(xiàn)的。沒有實(shí)現(xiàn)意味著這兩個(gè)方法都是要被繼承的,完全是交給子類去實(shí)現(xiàn)的。- (void)forceProcessingAtSize:(CGSize)frameSize和- (void)forceProcessingAtSizeRespectingAspectRatio:(CGSize)frameSize,那這兩個(gè)方法的意思是什么呢?它是要設(shè)定根據(jù)輸入的幀的大小來設(shè)定處理的方式,后者大體上也是這樣的,只不過它是等比例的。那這給子類留下了充分的空間。

靜態(tài)圖像的處理

這里用still一詞來代指靜態(tài)圖像。- (void)useNextFrameForImageCapture好像是說本次并不能用來生成圖像,下一幀卻可以,本實(shí)現(xiàn)是空的,這說明它是專門供子類去實(shí)現(xiàn)的。- (CGImageRef)newCGImageFromCurrentlyProcessedOutput是要從本次處理的輸出中生成一個(gè)CGImage,默認(rèn)的實(shí)現(xiàn)仍然是個(gè)nil,只不過因?yàn)樵摲椒ㄊ怯蟹祷刂档?,于是只能返回nil。
-(CGImageRef)newCGImageByFilteringCGImage:(CGImageRef)imageToFilter:這個(gè)意思是說把自身當(dāng)成了一個(gè)濾鏡,然后對(duì)輸入的imageToFilter經(jīng)過濾鏡的處理得到了一個(gè)新的CGImageRef。CGImageRef是個(gè)CGImage的結(jié)構(gòu)體,是個(gè)系統(tǒng)的類型。- (BOOL)providesMonochromeOutput:這應(yīng)該是個(gè)getter,用來指示是否輸出純色的圖像。

基于平臺(tái)的輸出方法

-(UIImage *)imageFromCurrentFramebuffer該方法用于從當(dāng)前的幀緩沖區(qū)中輸出UIImage,并會(huì)根據(jù)當(dāng)前設(shè)備的朝向來設(shè)定圖像的方向,由此可知UIImage其實(shí)是帶有方向信息的。- (UIImage *)imageFromCurrentFramebufferWithOrientation:(UIImageOrientation)imageOrientation會(huì)根據(jù)輸入的方向,來從幀緩沖區(qū)中輸出UIImage,它是借助CGImageRef來完成的,再根據(jù)圖像的方向生成UIImage的,這個(gè)借助CGImageRef生成UIImage的方式值得學(xué)習(xí)的。
-(UIImage *)imageByFilteringImage:(UIImage *)imageToFilter:這是把imageToFilter經(jīng)過自身的濾鏡處理,然后得到UIImage,它也是通過CGImageRef來生成的。
-(CGImageRef)newCGImageByFilteringImage:(UIImage *)imageToFilter:把輸入的圖像經(jīng)過自身的處理得到CGImageRef。具體的實(shí)現(xiàn)都是通過自身的內(nèi)部方法實(shí)現(xiàn)的。

一些必要的存取器

-(void)setAudioEncodingTarget:(GPUImageMovieWriter *)newValue:它是成員變量audioEncodingTarget的setter,除了正常的邏輯之外,它還會(huì)把a(bǔ)udioEncodingTarget的hasAudioTrack重置為YES。
-(void)setOutputTextureOptions:(GPUTextureOptions)outputTextureOptions:除了設(shè)置成員變量之外,它還設(shè)置了紋理的包裹方式。

上一篇:GPUImageFramebufferCache

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容