项目背景
在唱吧 App 视频录制流程中,随着对视频品质的要求不断提升,逐渐暴露出了视频处理能力不足的问题。更大的分辨率、更丰富的滤镜玩儿法都在挑战着硬件性能的极限。一款更高效的视频处理框架无疑会为日后的扩展提供更大的空间。
设计思路
问题分析
在设计新框架前,先要分析现有实现,发现问题和不足,从而在新框架中规避。经过分析,发现了一些现有实现中可供优化的点:
非必要中间结果过多。
可聚合的 GL 操作过多。
与 GPU 传递数据不够灵活。
对流程的控制权不足,需要一些多余操作来保证上下文的正确。
同步串行的流程没有压榨出全部的硬件性能。
流程设计

如流程图所示,KTVVideoProcess 共有 3 个关键节点:
Source - 用于获取原始数据,有文件读取、摄像头采集、播放器输出等方式。
Pipeline - 用于渲染特效,内部由多个滤镜串联组成,将效果叠加至原始数据。
Output - 用于分发渲染结果,可输出至屏幕、写入文件、直播发送等。
一次完整运行过程大致为:
Source 吐出原始 Frame,异步交给 Pipeline。
Pipeiline 将原始 Frame 送至内部的滤镜串处理,当全部滤镜依次作用在 Frame 后得到最终的处理结果,异步交给 Output。
Output 将结果 Frame 按自身的特性进行分发。
制定原则
结合自身的运行方式和之前的问题,为尽可能保证性能,制定了如下原则:
避免无用中间结果 。
增强 Frame 的流通性,避免因流通而引发多余操作。
在不影响结构的前提下,尽可能聚合 GL 操作。
按需转换 glTexture/CVPixelBuffer,避免触发不必要的转换。
严格控制 glFlush/glFinish 之类操作的调用时机,仅在必要时使用。
结果分发采用异步处理,避免阻塞 Pipeline 的渲染。
尝试并行。当 Pipeline 的消费能力弱于 Source 的生产能力且硬件性能仍有压榨空间时,可使用多条 Pipeline 来并行处理(流程图中 Concurrent Pipeline 部分)。
技术实现
通过上文已可以了解到 KTVVideoProcess 的运行过程,下面简单介绍一下用于内部支撑的几个类。
KTVVPFrame
@interface KTVVPFrame : NSObject
@property (nonatomic, assign) CMTime timeStamp;
@property (nonatomic, strong) KTVVPFrameLayout * layout;
@property (nonatomic, assign) GLuint texture;
- (void)uploadIfNeeded:(KTVVPFrameUploader *)uploader;
- (CVPixelBufferRef)corePixelBuffer;
- (void)lock;
- (void)unlock;
@end
KTVVPFrame 是流通于整个流程的视频帧对象,其有 layout 属性来描述图像内容的布局,如旋转、翻转、尺寸等信息,有 texture 属性来获取纹理 ID,有 corePixelBuffer 方法来获取图像数据,有 lock/lock 方法处理引用问题等接口。KTVVPFrame 为抽象类,可根据具体场景选择如下子类:
KTVVPGLTextureFrame
KTVVPGLDrawableFrame
KTVVPCVPixelBufferFrame
KTVVPCMSmapleBufferFrame
KTVVPFramePool
@interfaceKTVVPFramePool:NSObject
-(__kindof KTVVPFrame*)frameWithKey:(NSString*)key factory:(__kindof KTVVPFrame*(^)(void))factory;
@end
KTVVPFramePool 负责维护 KTVVPFrame 实例,管理它们的生命周期和复用情况。通过 Pool 获取到的 Frame 会被 lock。当使用完成调用 Frame 的 unlock 方法时,便会将其归还到 Pool 以便下次使用。
KTVVPFilter
@interfaceKTVVPFilter:NSObject<KTVVPFrameInput>
@property(atomic,weak)id <KTVVPFrameInput>output;
@end
KTVVPFilter 用于为 Input Frame 添加具体的一个特效。将通过 KTVVPFrameInput 接口获取到 Input Frame 进行处理,并向后传递给 self.output。KTVVPFrame 为抽象类,目前提供了一些功能类型的 Basic Filter 和一些效果类型的 Extension Filter:
Basic Filter
KTVVPTransformFilter
KTVVPPassthroughFilter
......
Extension Filter
KTVVPBlackAndWhiteFilter
KTVVPBrightnessFilter
KTVVPExposureFilter
KTVVPRGBFilter
......
KTVVPMessageLoop
@interface KTVVPMessageLoop : NSObject
@property (nonatomic, strong, readonly) NSThread * thread;
@property (nonatomic, strong, readonly) KTVVPObjectQueue * messageQueue;
- (void)putMessage:(KTVVPMessage *)message;
- (void)putMessage:(KTVVPMessage *)message delay:(NSTimeInterval)delay;
- (void)waitUntilFinished;
- (void)waitUntilStoped;
- (void)start;
- (void)stop;
@end
KTVVPMessageLoop 是为避免 OpenGL Context 频繁在不同线程中切换。其内部包含一个独立的子线程,用于保证添加到消息循环中的消息都将在此线程处理。并提供一些接口来处理多线程协作的状态同步。项目中所有需异步处理的任务均交由此类完成,是项目线程模型的根基,被广泛应用在 KTVVPSerialPipeline、KTVVPFrameView、KTVVPFrameWriter 等类。
OpenGL 相关
KTVVPGLImageTexture - 可将 UIImage 转为 glTexture。
KTVVPGLModel - 用于处理顶点、索引数据。
KTVVPGLPlaneModel - 用于二维平面的数据模型,支持旋转、翻转等变换。
KTVVPGLProgram - 快速创建、加载、绑定、链接 Shader。
KTVVPGLStandardProgram - 基于该项目标准 Shader 模板的 Program。
最后
项目开源在 GitHub,欢迎 Issue & PR。地址:https://github.com/ChangbaDevs/KTVVideoProcess
本文以回顾总结为主,并未对细节做过多解释说明。如有疑问建议可通过 GitHub 与我联系。




