欢迎关注公众号:sumsmile /专注图像处理的移动开发老兵~~
系列的最后一篇,讲SurfaceTexture的原理,并把前面涉及到的camera预览、OpenGL接入、EGL、帧缓冲、shader串起来,实现一个特效demo。
demo效果
算法参考shadertoy 特效
SurfaceTexture介绍
SurfaceTexture、SurfaceView、GLSurfaceView、TextureView区别
SurfaceTexture相关有三个类,不注意容易混淆。记住一条,SurfaceTexture不能直接显示,可以简单理解成“生产纹理的中间工具”,一般需要配合其他view或者功能模块发挥作用。
注意SurfaceView Texture区别
一个window中所有的view最后会合成一个图像绘制到屏幕上,但SurfaceView除外,SurfaceView有自己的独立窗口,所以SurfaceView性能较好,但是功能上不如TextureView操作方便,不支持旋转,平移等操作。
参考
SurfaceView实现原理分析
developer training
SurfaceFlinger 详解
SurfaceView TextureView对比:
详细区别参阅 SurfaceView、GLSurfaceView、SurfaceTexture、TextureView 区别
SurfaceTexture工作原理
Android开发文档里对SurfaceTexture的工作流有简单的介绍
相机采集的图像经SurfaceTexture处理后,输送到OpenGLES,gl处理完再输送到SurfaceView展示,最后由SurfaceFlinger合成;或经OpenGLES处理后,输送到MediaCodec编码,编码成视频文件。
抽出SurfaceTexture的核心逻辑,如下图。SurfaceTexture中维护了一个队列,存储图像Buffer,从Camera、Decoder等模块输入数据,转换成纹理,输送到OpenGL处理。这是一个典型的生产-消费模式。
注意:在GL环境中处理Texture时,key是“GLES11Ext.GL_TEXTURE_EXTERNAL_OES”,什么是TEXTURE_EXTERNAL_OES呢?
如上图所示,camera等采集的图像数据,不能直接用于opengl,需要以拓展纹理的方式处理,实际经过EglImageKHR转换,这部分工作是EGL做的。我的理解是,图像的生产不在GL线程中,纹理不能共享给OpenGL线程,另外采集的图像数据是YUV格式的,需要转换成普通的RGB。
SurfaceTexture代码分析
代码分析参考谈一谈 Android 上的 SurfeceTexture
来看看SurfaceTexture里的代码,建立直观的认识.
SurfaceTexture构造函数中调用nativeInit初始化
// 构造函数
// singleBufferMode是否是单buffer,默认为false
public SurfaceTexture(int texName, boolean singleBufferMode) {
mCreatorLooper = Looper.myLooper();
mIsSingleBuffered = singleBufferMode;
//native方法nativeInit
nativeInit(false, texName, singleBufferMode, new WeakReference<SurfaceTexture>(this));
}
复制代码
重点在nativeInit方法里,生成生产者和消费者,创建图像缓冲队列。调用updateTexImage,会读取缓冲队列里的数据到TextureId绑定的纹理内存中
// frameworks\base\core\jni\SurfaceTexture.cpp
// texName为应用创建texture名
// weakThiz为SurfaceTexture对象弱引用
static void SurfaceTexture_init(JNIEnv* env, jobject thiz, jboolean isDetached,
jint texName, jboolean singleBufferMode, jobject weakThiz)
{
sp<IGraphicBufferProducer> producer;
sp<IGraphicBufferConsumer> consumer;
// 创建IGraphicBufferProducer及IGraphicBufferConsumer
BufferQueue::createBufferQueue(&producer, &consumer);
if (singleBufferMode) {
consumer->setMaxBufferCount(1);
}
sp<GLConsumer> surfaceTexture;
// isDetached为false
if (isDetached) {
....
} else {
// 将consumer和texName封装为GLConsumer类对象surfaceTexture
surfaceTexture = new GLConsumer(consumer, texName,
GL_TEXTURE_EXTERNAL_OES, true, !singleBufferMode);
}
....
// 收到帧数据是会触发ctx->onFrameAvailable方法
surfaceTexture->setFrameAvailableListener(ctx);
SurfaceTexture_setFrameAvailableListener(env, thiz, ctx);
}
复制代码
原理先讲到这了,下面我们来一段实践,将前面所学的都结合起来,实现一个动效.
实践.基于SurfaceTexture预览+gl特效
完整工程:完整工程 CameraDemo
注意替换fragment_shader_screen.glsl里的逻辑,默认是没有特效的
代码结构:
代码说明:
参考上图,做一些说明辅助理解。
CameraEglSurfaceView
CameraEglSurfaceView继承自GLSurfaceView,作为默认的渲染屏幕。
CameraFboRender
实现Renderer接口,创建surfaceTexture,绑定纹理id cameraRenderTextureId。
onSurfaceListener()回调里,打开相机预览,预览生成的数据绑定到surfaceTexture
onDrawFrame()回调里,更新纹理到cameraRenderTextureId
第一次绘制:调用OpenGL ES的API绘制,将cameraRenderTextureId的纹理绘制到FrameBuffer上,实际上是绘制到FrameBuffer关联的Texture(fboTextureId)。注意,这是一次空绘制,shader里啥也没干,如果你有啥创意,也可以改shader来实现。
相机采集的数据存放到SurfaceTexture的BufferQueue中,调用updateTexImage()更新buffer到textureId。
这个案例中,进行了两次绘制,第一次绘制到FrameBuffer上(离屏渲染),第二次绘制到GLSurfaceView上。这么做是为了展示可以拿到纹理,渲染到不同的缓冲中。
实际上只要你愿意,可以在中间像接火车一样多次渲染到FrameBuffer,每一层可以做特殊的处理,这就是GPUImage最核心的逻辑,将不同的特效滤镜连在一起,像搭积木一样拼凑成任意的酷炫效果,非常好的解耦及复用。当然拆成一个个独立的滤镜也有性能上的缺陷,每多绘制一次,就有一次性能的折损。
CameraRender
第二次绘制,将第一次绘制生成的fboTextureId,作为第二次绘制的输入,绘制到默认的设备上,即CameraEglSurfaceView,可以看到渲染的效果。
添加特效。
shader实现放在工程的raw目录里,可以借用android的API方便的读取。
核心逻辑在片元着色器里,glsl实现参考:
www.shadertoy.com/view/7dXXWs
迁移到自己的工程里,需要做些改造。
注意,shadertoy里的实现是基于OpenGL 3.0实现的,迁移到ES 2.0有些API不一样,主要有:
- fragColor(3.0) –> gl_FragColor(2.0)
- 没有in out关键字(2.0)
- texture(3.0) –> texture2D(2.0)
- …
GLES里用到外部纹理,需要在shader里声明
//申明使用扩展纹理
extension GL_OES_EGL_image_external : require
复制代码
剩下的代码细节,读者朋友们自己分析吧,耐心读完代码,相信你会有不少收获。
欢迎关注公众号:sumsmile /专注图像处理的移动开发老兵~~
参考
[1]
shadertoy 特效: www.shadertoy.com/view/7dXXWs
[2]
SurfaceView实现原理分析: juejin.cn/post/684490…
[3]
developer training: google-developer-training.github.io/android-dev…
[4]
SurfaceFlinger 详解: www.cnblogs.com/blogs-of-lx…
[5]
SurfaceView、GLSurfaceView、SurfaceTexture、TextureView 区别: blog.csdn.net/qq_34712399…
[6]
android SurfaceTexture: source.android.com/devices/gra…
[7]
eglimage in surfaceflinger: waynewolf.github.io/2014/05/17/…
[8]
谈一谈 Android 上的 SurfeceTexture: mp.weixin.qq.com/s/7kwrlxyct…
[9]
完整工程 CameraDemo: github.com/ChinaZeng/O…