一、多GPUImageView方案
用GPUImage进行多路视频的渲染,有一个非常简单的方案:多个GPUImageView方式,每路视频画面单独渲染。
一路视频对应一个滤镜链,拿到视频数据后进行裁剪,直接显示到对应的GPUImageView上;多个GPUImageView组成多路视频画面,通过改变GPUImageView的坐标可以实现画面拼接的效果。
用两个mp4视频作为源数据,用GPUImageMovie作为滤镜链的起始,用GPUImageCropFilter对视频进行裁剪,再经过GPUImageTransformFilter转置,最终显示在GPUImageView上。
这个方案的优缺点也很明显:
优点:实现简单,画面拼接由UIKit层的API实现;
缺点:渲染到屏幕的次数增多,渲染频率远大于屏幕显示帧率;
二、单GPUImageView方案
上面的方案最明显的问题就是渲染到屏幕的次数比屏幕刷新的次数还多,那么自然延伸出来一种方案:只用一个GPUImageView,视频统一渲染到GPUImageView。
GPUImageView显示区域划分成多个区域,每个区域对应一路视频;多路视频画面都采用离屏渲染的方式,绘制到纹理的对应区域中,再由multiTexFilter进行处理;multiTexFilter集合多路视频的渲染,如果没有更新则使用上次的数据,再把纹理传递给GPUImageView,最终GPUImageView渲染到屏幕上;
用两个mp4视频作为源数据,仍然用GPUImageMovie作为滤镜链的起始,再经过GPUImageCropFilter、GPUImageTransformFilter处理,交给LYMultiTextureFilter,最终显示在GPUImageView上。
此方案的核心在于LYMultiTextureFilter类,我们来仔细分析下其构成:
/**
把多个Texture渲染到同一个FrameBuffer
使用时需要先绑定frameBuffer和显示区域rect
在newFrame回调的时候,会根据index把图像绘制在绑定的rect
特别的,会制定一个mainIndex,当此index就绪时调用newFrame通知响应链的下一个
*/
@interface LYMultiTextureFilter : GPUImageFilter
- (instancetype)initWithMaxFilter:(NSInteger)maxFilter;
/**
设置绘制区域rect,并且绑定纹理id
@param rect 绘制区域 origin是起始点,size是矩形大小;(取值范围是0~1,点(0,0)表示左下角,点(1,1)表示右上角, Size(1,1)表示最大区域)
@param filterIndex 纹理id
*/ - (void)setDrawRect:(CGRect)rect atIndex:(NSInteger)filterIndex;
- (void)setMainIndex:(NSInteger)filterIndex;
LYMultiTextureFilter继承自GPUImageFilter,通过-setDrawRect绑定纹理的绘制区域;特别的,可以设置一个mainIndex,当此index就绪时调用newFrame通知滤镜链的下一个目标。
具体实现有几个注意事项:
1、通过顶点指定特别的渲染区域;
2、因为GPUImageFramebuffer默认会被复用,导致无法保存上一次的纹理,这里需要修改逻辑-renderToTextureWithVertices:和-informTargetsAboutNewFrameAtTime,使得LYMultiTextureFilter一直使用同一个GPUImageFramebuffer;
3、dealloc的时候需要释放outputFramebuffer,避免内存泄露;
这个方案的特点:
优点:统一渲染,避免的渲染次数大于屏幕帧率;
缺点:multiTexFilter实现复杂,画面拼接需要用shader来实现;
网友评论