美文网首页
GPUImageOutput

GPUImageOutput

作者: Stroman | 来源:发表于2018-06-10 15:17 被阅读267次

总述

本类主要是针对输出而言的,与目标、图像、纹理相关的处理,简而言之就是输出。除此之外,还有线程的管理。

枚举UIImageOrientation

作者定义了几种方向的枚举类型,只看这个名字,我并不能知道它们到底指的是什么,所以让我一个一个解读一下。下面都假设设备是正向的,就是竖直的,并且home键在下面的那种摆放状态。
1、UIImageOrientationUp:这个就是正向。
2、UIImageOrientationDown:正好与正向相反,它是倒立着的。
3、UIImageOrientationLeft:说来也巧,iOS的官方文档正好也有同名的方向属性,这个方向实际上就是home键在右边,横向的。或者,是设备从正向按照逆时针方向旋转90度。因为逆时针的英语是counterclockwise,所以该方向的注释一般简写为90 deg CCW,这就是逆时针旋转90度的样子。
4、UIImageOrientationRight:基于UIImageOrientationLeft的讲述,该方向就非常好理解了,它就是设备从正向顺时针旋转90度。就是home在左边,水平摆放的样子。
5、UIImageOrientationUpMirrored:它这个意思是说保持图像的向上的方向不变,在水平方向上颠倒一下,就是水平镜像。
6、UIImageOrientationDownMirrored:这个意思是说图像首先从正向旋转180度,然后水平方向颠倒一下。
7、UIImageOrientationLeftMirrored:首先把图像顺时针旋转90度,然后在水平方向上反转一下。
8、UIImageOrientationRightMirrored:首先把图像逆时针旋转90度,然后在水平方向上反转一下。
你会发现5678都是先旋转一个角度,然后在水平方向上颠倒一下。

线程和内存的环境控制

这个应该是基于过程的代码逻辑,看上去跟C语言似的,有点让人难以理解。
1、dispatch_queue_attr_t GPUImageDefaultQueueAttribute(void):苹果官方文档并没有讲解dispatch_queue_attr_t是个什么样的类型,干什么用的。不过,看上去它应该是描述一个队列应该具备什么样的性质的对象。它代码的实现中判定了一下iOS版本是否是9.0以上的,看来它是要用到iOS9.0以后才有的性质啊。除此之外,还可以看出来,它把队列设定为串行队列,看来是要同步执行任务了。
2、void runOnMainQueueWithoutDeadlocking(void (^block)(void)):从名字可以看出来,这是要在主线程执行任务,并且还要保障它不死锁。这个任务嘛就是block中包含的代码,主线程也很好理解,关键是这个如何做到不死锁呢?很简单,因为主线程是单个线程,既然是单线程那就代表它是同步执行任务的,你只管把任务塞到主线程后面等着它被执行就好了,只要任务和任务之间不互相引用就行。至于说如何做到在主线程执行?很简单,如果当前线程是主线程则直接执行block,否则就把block直接抛到主线程上去。
3、void runSynchronouslyOnVideoProcessingQueue(void (^block)(void)):从这个名字可以知道2个信息,一个是它是同步执行任务的,另一个是它是在一个处理视频的队列上面执行任务的。原来这个视频处理队列就是GPUImageContext中的 contextQueue。思路也是和runOnMainQueueWithoutDeadlocking差不多,就是判断当前队列是不是视频处理队列,如果是就直接执行,否则就抛到这个视频处理队列上面去同步执行。
4、void runAsynchronouslyOnVideoProcessingQueue(void (^block)(void)):基于runSynchronouslyOnVideoProcessingQueue的理解,这个就是异步执行任务的。
5、void runSynchronouslyOnContextQueue(GPUImageContext *context, void (^block)(void)):这个和3是一样的,只不过它这个GPUImageContext的上下文是可选的,而不是单例的那个GPUImageContext的那个上下文了。就是说你可以定制上下文。
6、void runAsynchronouslyOnContextQueue(GPUImageContext *context, void (^block)(void));:这个和4一样,方式和5一样,也是可选GPUImageContext上下文的。
7、void reportAvailableMemoryForGPUImage(NSString *tag):这里面涉及到的内容对我来说还是比较陌生的。task_basic_info结构体在iOS SDK中,iOS有段注释说它不能够在不同大小的任务之间安全地传递,这里面有关线程、内存和任务的数据项。由此可见它能获取到设备的硬件信息,这个属于比较底层的东西了。kern_return_t也是个结构体,task_info它是一个有关任务数据的结构体,task_info也是kern_return_t类型的。mach_task_self()用于获取当前进程,TASK_BASIC_INFO大概是一种默认的信息,(task_info_t)&info很好理解,就是获取已经创建的任务信息的结构体的内存地址,&size就是要开辟size那么大小的内存地址用的。如果获取信息成功了以后,就返回当前内存的占用量,具体用法是task_basic_info中的 resident_size数据项,这个数据项的意思是常住内存的大小,单位是字节。而如果是想获取失败信息,可以可以使用C语言的mach_error_string去处理kern_return_t的变量的地址即可

GPUImageOutput内部成员都有啥?

1、GPUImageFramebuffer *outputFramebuffer:看来这是该纹理的数据所在,它内部有一个帧缓冲区。
2、NSMutableArray *targets:它是有目标的,而且还是个复数,就证明它可以接受不只一个的目标。至于说这个目标是什么东西,应该是闲言少叙,下回分解的东西。
3、NSMutableArray *targetTextureIndices:这个像是个目标纹理的下标,应该是用于检索目标用的。
4、CGSize inputTextureSize:输入的纹理的大小,这个暂时没啥可说的,意思正如名字所述的那样子。
5、CGSize cachedMaximumOutputSize:首先它是缓存的,这证明它是有内存复用机制的,而且还有个最大的输出大小的限制。
6、CGSize forcedMaximumSize:强制的,谁强制的,不知道哎。反正它也有个最大大小的限制。
7、BOOL overrideInputSize:是否重写输入的大小,正更证明了输入的大小是被复用的。
8、BOOL allTargetsWantMonochromeData:是否所有的目标都想要黑白的数据,那这意味着什么呢?
9、BOOL usingNextFrameForImageCapture:从这个意思上讲,这个要用下一帧来获取图片,但是这又说明了什么呢?
10、BOOL shouldSmoothlyScaleOutput:是否要柔和地缩放输出,这个我就不知道,柔和地是指什么?难道是要慢慢地?
11、BOOL shouldIgnoreUpdatesToThisTarget:针对于某个目标是否应该忽略更新,目标的更新是指什么呢?
12、GPUImageMovieWriter audioEncodingTarget:从名字看是音频加密的对象,GPUImageMovieWriter是个什么类型?不管怎样反正是个自定义的类型。
13、id<GPUImageInput> targetToIgnoreForUpdates:需要被忽略更新的对象,它遵循了GPUImageInput协议,有GPUImageInput就应该有GPUImageOutput,那么它们到底是干什么的?
14、void(^frameProcessingCompletionBlock)(GPUImageOutput
, CMTime):这是个回调用的block属性,回调的是一个GPUImageOutput,和一个CMTime。CMTime是个什么类型?根据苹果官方文档的解释,它是一个结构体,表征的一个有理数形式的时间。
15、BOOL enabled:怎么?这个玩意还有个开关功能的控制?
16、GPUTextureOptions outputTextureOptions:这个前面已经知道了,就是设置纹理的包裹类型,并且还是用于输出的,这又让我对什么是输入和输出有了疑问。

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

这个输入的target遵循了GPUImageInput协议,而且还有个下标的位置。GPUImageOutput把自身持有的GPUImageFramebuffer,被称为outputFramebuffer的,插入到target的指定位置上面。那么可以联想到target维持了一个数组,专门用来放置,用于放置输出的GPUImageFramebuffer的。

- (GPUImageFramebuffer *)framebufferForOutput

这个很简单,其实就是把GPUImageOutput所持有的outputFramebuffer输出给调用方。

- (void)removeOutputFramebuffer

这个其实也很简单,就是GPUImageOutput把所持有的outputFramebuffer释放掉,再具体点就是置为nil。

- (void)notifyTargetsAboutNewOutputTexture

简单地说一说这个方法就是告诉GPUImageOutput把自身持有的outputFramebuffer重新赋值给所有指定位置的target。从这个方法的实现中也可以知道GPUImageOutput所持有的targets和targetTextureIndices有什么用。Targets就是GPUImageOutput所持有的目标,targetTextureIndices是用来存储target所对应的纹理的标号的。于是可以知道该方法把目标放在指定的纹理所在的位置上面。再简单点,就是重置目标和纹理之间的对应关系。

- (NSArray*)targets

用GPUImageOutput所持有的targets重新创建一个数组,并把这个数组返回。

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

每个目标都具备为新来到的纹理找到合适位置的能力,因为每个目标都具备nextAvailableTextureIndex方法,前提是这要这个目标遵循了GPUImageInput协议。然后GPUImageOutput会把这个目标放到指定的纹理的位置上,换句话说就是把目标和某个纹理位置联系起来。如果这个目标是要被忽略更新的,那么就把这个目标赋值给GPUImageOutput持有的那个被忽略更新的目标。可以发现每个output对象都是只持有1个应该被忽略的目标。

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

该方法就是把目标放在纹理应该在的位置上。它首先是检查一下本身所维持的成员变量targets目标数组中是否已经包含了该目标,然后它把缓存cachedMaximumOutputSize置为0,为什么要把cachedMaximumOutputSize置为0呢?目的何在?先不管。接下来它把这个新来的target放到了指定的位置上,然后设置了它的输入的帧缓冲区。然后再把它持有起来,其实就是放到自己的targets数组中去,与此同时,它把target所在的纹理编号插入到targetTextureIndices中,想不到纹理的位置也是需要存储的。allTargetsWantMonochromeData明显是个标志位,我想这个标志位有利于提升处理的效率,因为全和部分处理的方式极有可能是不同的。allTargetsWantMonochromeData是什么意思?这个是纯色的意思。为什么要区分纯色和非纯色呢?我想大概是因为处理方式上不同吧,纯色应该处理起来效率更高些。它计算这个标志位的方式尤其值得一提提,因为它是用已有的标志位去和新来的目标的标志位进行与运算,那意思就是说只要有一个不是纯色的,那么整个纯色标志位就不能为真,那倒的确是这样的,因为事实确实是那样的,感觉像是一条鱼腥一锅汤。

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

从名字来看它是要移除目标,移除谁的目标?原来还是移除自身的某个目标,这个答案可以从if(![targets containsObject:targetToRemove]) { return; }中知道,它要移除的是自身持有targets的中的某个目标,如果没有就不用多此一举了。_targetToIgnoreForUpdates是用于标识需要被忽略更新的目标,如果此时这个目标需要被移除,那么_targetToIgnoreForUpdates指针也需要被置空。cachedMaximumOutputSize这个缓存的大小,到现在我也没彻底搞清楚,到底为什么准备的呢?不过在此处它被置为空。同样的,因为目标所对应的纹理是被分别存储的,所以你还需要专门祛除纹理所在的下标值。接下来的过程它是在一个队列里面处理该目标的收尾工作的,它先是把该目标的输入大小设置为0,然后把该目标的方向置为NO,接下来从目标所对应的纹理的下标数组中把该纹理的下标对象移除,从targets把该目标移除,然后终止该输入的新目标的处理过程。这里面有涉及到输入和输出的概念了,看来这两个概念对于目标来说是个很重要的概念。

- (void)removeAllTargets

一看名字就知道这个比较暴力,移除所有的目标。整个实现几乎都是在队列中同步执行的,实现的过程首先是把cachedMaximumOutputSize置为0,这个东西已经出现过很多次了,看样子是很重要的。接下来毫无疑问了,就是遍历自身持有的targets,然后所做的事情基本都是- (void)removeTarget:(id<GPUImageInput>)targetToRemove所做的事情,接下来就是清空targets和targetTextureIndices,最后把整体的纯色标志位allTargetsWantMonochromeData置为YES。

管理输出纹理

这里面涉及到了2个方法,都是没有实现的。没有实现意味着这两个方法都是要被继承的,完全是交给子类去实现的。- (void)forceProcessingAtSize:(CGSize)frameSize和- (void)forceProcessingAtSizeRespectingAspectRatio:(CGSize)frameSize,那这两个方法的意思是什么呢?它是要设定根据输入的帧的大小来设定处理的方式,后者大体上也是这样的,只不过它是等比例的。那这给子类留下了充分的空间。

静态图像的处理

这里用still一词来代指静态图像。- (void)useNextFrameForImageCapture好像是说本次并不能用来生成图像,下一帧却可以,本实现是空的,这说明它是专门供子类去实现的。- (CGImageRef)newCGImageFromCurrentlyProcessedOutput是要从本次处理的输出中生成一个CGImage,默认的实现仍然是个nil,只不过因为该方法是有返回值的,于是只能返回nil。
-(CGImageRef)newCGImageByFilteringCGImage:(CGImageRef)imageToFilter:这个意思是说把自身当成了一个滤镜,然后对输入的imageToFilter经过滤镜的处理得到了一个新的CGImageRef。CGImageRef是个CGImage的结构体,是个系统的类型。- (BOOL)providesMonochromeOutput:这应该是个getter,用来指示是否输出纯色的图像。

基于平台的输出方法

-(UIImage *)imageFromCurrentFramebuffer该方法用于从当前的帧缓冲区中输出UIImage,并会根据当前设备的朝向来设定图像的方向,由此可知UIImage其实是带有方向信息的。- (UIImage *)imageFromCurrentFramebufferWithOrientation:(UIImageOrientation)imageOrientation会根据输入的方向,来从帧缓冲区中输出UIImage,它是借助CGImageRef来完成的,再根据图像的方向生成UIImage的,这个借助CGImageRef生成UIImage的方式值得学习的。
-(UIImage *)imageByFilteringImage:(UIImage *)imageToFilter:这是把imageToFilter经过自身的滤镜处理,然后得到UIImage,它也是通过CGImageRef来生成的。
-(CGImageRef)newCGImageByFilteringImage:(UIImage *)imageToFilter:把输入的图像经过自身的处理得到CGImageRef。具体的实现都是通过自身的内部方法实现的。

一些必要的存取器

-(void)setAudioEncodingTarget:(GPUImageMovieWriter *)newValue:它是成员变量audioEncodingTarget的setter,除了正常的逻辑之外,它还会把audioEncodingTarget的hasAudioTrack重置为YES。
-(void)setOutputTextureOptions:(GPUTextureOptions)outputTextureOptions:除了设置成员变量之外,它还设置了纹理的包裹方式。

上一篇:GPUImageFramebufferCache

相关文章

网友评论

      本文标题:GPUImageOutput

      本文链接:https://www.haomeiwen.com/subject/pfnfeftx.html