Android Camera开发实践(4) SurfaceTexture与特效

参考
SurfaceView实现原理分析
developer training
SurfaceFlinger 详解

SurfaceView TextureView对比:

SurfaceView VS 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。

关于EglImageKHR,详细参考.

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(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 producer;
sp consumer;
// 创建IGraphicBufferProducer及IGraphicBufferConsumer

BufferQueue::createBufferQueue(&producer, &consumer);

if (singleBufferMode) {
consumer->setMaxBufferCount(1);
}

sp 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

github.com/ChinaZeng/O…

注意替换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方便的读取。
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

img

img

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

《设计思想解读开源框架》

第一章、 热修复设计

  • 第一节、 AOT/JIT & dexopt 与 dex2oat

  • 第二节、 热修复设计之 CLASS_ISPREVERIFIED 问题

  • 第三节、热修复设计之热修复原理

  • 第四节、Tinker 的集成与使用(自动补丁包生成)

    第二章、 插件化框架设计

  • 第一节、 Class 文件与 Dex 文件的结构解读

  • 第二节、 Android 资源加载机制详解

  • 第三节、 四大组件调用原理

  • 第四节、 so 文件加载机制

  • 第五节、 Android 系统服务实现原理

    第三章、 组件化框架设计

  • 第一节、阿里巴巴开源路由框——ARouter 原理分析

  • 第二节、APT 编译时期自动生成代码&动态类加载

  • 第三节、 Java SPI 机制

  • 第四节、 AOP&IOC

  • 第五节、 手写组件化架构

    第四章、图片加载框架

  • 第一节、图片加载框架选型

  • 第二节、Glide 原理分析

  • 第三节、手写图片加载框架实战

    第五章、网络访问框架设计

  • 第一节、网络通信必备基础

  • 第二节、OkHttp 源码解读

  • 第三节、Retrofit 源码解析

    第六章、 RXJava 响应式编程框架设计

  • 第一节、链式调用

  • 第二节、 扩展的观察者模式

  • 第三节、事件变换设计

  • 第四节、Scheduler 线程控制

    第七章、 IOC 架构设计

  • 第一节、 依赖注入与控制反转

  • 第二节、ButterKnife 原理上篇、中篇、下篇

  • 第三节、Dagger 架构设计核心解密

    第八章、 Android 架构组件 Jetpack

  • 第一节、 LiveData 原理

  • 第二节、 Navigation 如何解决 tabLayout 问题

  • 第三节、 ViewModel 如何感知 View 生命周期及内核原理

  • 第四节、 Room 架构方式方法

  • 第五节、 dataBinding 为什么能够支持 MVVM

  • 第六节、 WorkManager 内核揭秘

  • 第七节、 Lifecycles 生命周期


    本文包含不同方向的自学编程路线、面试题集合/面经、及系列技术文章等,资源持续更新中…

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

[外链图片转存中…(img-v4Dl46ec-1713000741035)]
本文包含不同方向的自学编程路线、面试题集合/面经、及系列技术文章等,资源持续更新中…
[外链图片转存中…(img-bpuXHi7Z-1713000741035)]

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

  • 13
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值