项目github地址

前面几篇文章梳理了 android 相机的基本使用流程及相关的 api,完成了预览拍照等功能。

就预览而言,后续想做实时滤镜等功能的话,如果按照之前的方法用 ImageReader 拿到数据之后做处理再显示,一是繁琐,而是效率太低,卡顿严重。所以需要使用 opengles 对相机的预览数据进行渲染,可以很大的提高效率,防止卡顿。

这篇文章先不做滤镜,而是先实现 opengles 渲染相机数据的功能,为后续实时滤镜等功能打基础。

opengles 基本使用

opengles 在 android 平台上的简单使用可以参考之前写的 opengles 相关的文章。

主要流程:

  1. 布局中使用 GLSurfacView 作为预览窗口。
  2. 准备相关的顶点属性数据和着色器文件。
  3. 实现 GLSurfaceView.Render 接口,编写具体的渲染代码。

具体的操作在之前 opengles 相关的文章中都有,完整的代码放在 android-opengl-demo项目

opengles 相机预览

android 相机的预览数据可以输出到 SurfaceTexture 上,所以用 opengles 做相机预览的主要思路是

  1. 在 GLSurfaceView.Render 中创建一个纹理,再使用该纹理创建一个 SurfaceTexture
  2. 使用该 SurfaceTexture 创建一个 Surface 传给相机,相机预览数据就输出到一个纹理上了
  3. 使用 GLSurfaceView.Render 将该纹理渲染到 GLSurfaceView 窗口上
  4. 使用 SurfaceTexture 的 setOnFrameAvailableListener 方法 给 SurfaceTexture 添加一个数据帧数据可用的监听器,在监听器中 调用 GLSurfaceView 的 requestRender 方法渲染该帧数据,这样相机每次输出一帧数据就可以渲染一次,在GLSurfaceView窗口中就可以看到相机的预览数据了

准备着色器

顶点着色器

#version 300 eslayout (location = 0) in vec4 a_Position;
layout (location = 1) in vec2 a_texCoord;out vec2 v_texCoord;void main()
{gl_Position = a_Position;v_texCoord = a_texCoord;
}

片段着色器,需要注意的是,做相机预览的话,纹理的类型需要使用 samplerExternalOES ,而不是之前渲染图片的 sampler2D。

#version 300 es
#extension GL_OES_EGL_image_external_essl3 : require
precision mediump float;in vec2 v_texCoord;
out vec4 outColor;
uniform samplerExternalOES s_texture;void main(){outColor = texture(s_texture, v_texCoord);
}

实现Render

流程和之前文章里基本一样,需要注意的是需要使用纹理创建一个 SurfaceTexture,并提供 SurfaceTexture 实例的获取方法,以便后续相机获取使用。

另外注意渲染的时候先调用 SurfaceTexture 的 updateTexImage 方法,更新纹理。

public class GLRender implements GLSurfaceView.Renderer{private static final String VERTEX_ATTRIB_POSITION = "a_Position";private static final int VERTEX_ATTRIB_POSITION_SIZE = 3;private static final String VERTEX_ATTRIB_TEXTURE_POSITION = "a_texCoord";private static final int VERTEX_ATTRIB_TEXTURE_POSITION_SIZE = 2;private static final String UNIFORM_TEXTURE = "s_texture";private  float[] vertex ={-1f,1f,0.0f,//左上-1f,-1f,0.0f,//左下1f,-1f,0.0f,//右下1f,1f,0.0f//右上};//纹理坐标,(s,t),t坐标方向和顶点y坐标反着public float[] textureCoord = {0.0f,1.0f,1.0f,1.0f,1.0f,0.0f,0.0f,0.0f};private FloatBuffer vertexBuffer;private FloatBuffer textureCoordBuffer;private int program;//接收相机数据的纹理private int[] textureId = new int[1];//接收相机数据的 SurfaceTexturepublic SurfaceTexture surfaceTexture;public GLRender() {//初始化顶点数据initVertexAttrib();}private void initVertexAttrib() {textureCoordBuffer = GLUtil.getFloatBuffer(textureCoord);vertexBuffer = GLUtil.getFloatBuffer(vertex);}//向外提供 surfaceTexture 实例public SurfaceTexture getSurfaceTexture(){return surfaceTexture;}@Overridepublic void onSurfaceCreated(GL10 gl, EGLConfig config) {//创建纹理对象glGenTextures(textureId.length, textureId, 0);//将纹理对象绑定到srufaceTexturesurfaceTexture = new SurfaceTexture(textureId[0]);//创建并连接程序program = GLUtil.createAndLinkProgram(R.raw.texture_vertex_shader, R.raw.texture_fragtment_shader);//设置清除渲染时的颜色glClearColor(1.0f, 1.0f, 1.0f, 0.0f);}@Overridepublic void onSurfaceChanged(GL10 gl, int width, int height) {glViewport(0,0,width,height);}@Overridepublic void onDrawFrame(GL10 gl) {//srufaceTexture 获取新的纹理数据surfaceTexture.updateTexImage();glClear(GL_COLOR_BUFFER_BIT);glUseProgram(program);int vertexLoc = glGetAttribLocation(program, VERTEX_ATTRIB_POSITION);int textureLoc = glGetAttribLocation(program, VERTEX_ATTRIB_TEXTURE_POSITION);glEnableVertexAttribArray(vertexLoc);glEnableVertexAttribArray(textureLoc);glVertexAttribPointer(vertexLoc,VERTEX_ATTRIB_POSITION_SIZE,GL_FLOAT,false,0,vertexBuffer);glVertexAttribPointer(textureLoc,VERTEX_ATTRIB_TEXTURE_POSITION_SIZE,GL_FLOAT,false,0,textureCoordBuffer);//绑定0号纹理单元纹理glActiveTexture(GL_TEXTURE0);//将纹理放到当前单元的 GL_TEXTURE_BINDING_EXTERNAL_OES 目标对象中glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, textureId[0]);//设置纹理过滤参数glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER,GL_NEAREST);glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_LINEAR);glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE);//将片段着色器的纹理属性值(s_texture)设置为 0 号单元int uTextureLoc = glGetUniformLocation(program, UNIFORM_TEXTURE);glUniform1i(uTextureLoc,0);glDrawArrays(GL_TRIANGLE_FAN,0,vertex.length / 3);glDisableVertexAttribArray(vertexLoc);glDisableVertexAttribArray(textureLoc);}
}

相机使用前面创建的SurfaceTexture实例

通过 SurfaceTexture 实例创建一个 Surface ,传给相机即可。

            //获取Render中的 SurfaceTexture 实例surfaceTexture = glRender.getSurfaceTexture();if (surfaceTexture == null) {return;}surfaceTexture.setDefaultBufferSize(photoSize.getWidth(), photoSize.getHeight());//添加帧可用监听,让GLSurfaceView 进行渲染surfaceTexture.setOnFrameAvailableListener(new SurfaceTexture.OnFrameAvailableListener() {@Overridepublic void onFrameAvailable(final SurfaceTexture surfaceTexture) {glSurfaceView.requestRender();}});//通过 SurfaceTexture 实例创建一个 Surfacesurface = new Surface(surfaceTexture);try {cameraDevice = camera;previewRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);//将 surface 传给相机previewRequestBuilder.addTarget(surface);previewRequest = previewRequestBuilder.build();cameraDevice.createCaptureSession(Arrays.asList(surface), sessionsStateCallback, null);} catch (CameraAccessException e) {e.printStackTrace();Log.d(TAG, "相机访问异常");}

总结

本文梳理了使用 opengles 将相机预览数据渲染到 GLSurfaceView 的基本流程,后续以此为基础,结合 opengles 的离屏渲染机制实现实时滤镜功能。

android-camera2相机开发-7-使用opengles进行相机预览相关推荐

  1. android camera预览帧,android camera2处理每一帧并显示其预览

    我使用 https://github.com/googlesamples/android-Camera2Basic,但尝试修改它,我可以访问每个帧,然后在surfaceview上绘制. 我明白了,我应 ...

  2. Android通过屏幕方向和摄像头方向实现屏幕预览

    Android通过屏幕方向和摄像头方向实现屏幕预览 1. 前言 2. Android 的屏幕方向 2.1 什么是屏幕方向 2.2 为什么要获取或设置屏幕方向 2.3 如何获取与设置屏幕方向 2.3.1 ...

  3. 关于微信小程序开发环境苹果IOS真机预览报SSL协议错误问题解决方案

    关于微信小程序开发环境苹果IOS真机预览报SSL协议错误问题解决方案 参考文章: (1)关于微信小程序开发环境苹果IOS真机预览报SSL协议错误问题解决方案 (2)https://www.cnblog ...

  4. 在 Apple 芯片设备上用 Android Studio?别忘了使用 Apple 芯片预览版!

    感谢大家通过微信等渠道为打造更好的 Android Studio 提供的诸多反馈,在这些反馈中,我们了解到 Android Studio 对于 Apple 芯片的支持一直备受期待.随着 Android ...

  5. 微信小程序云开发实现上传文件和预览下载文件

    微信小程序云开发实现上传文件和预览下载文件 一.前言 目前微信提供了一个接口 wx.chooseMessageFile 它能让用户从聊天记录里面选择一个或者多个文件,然后返回它的一些信息,列入文件的p ...

  6. android ffmpeg 仿剪映播放器 剪辑视频 预览条 快速精准抽帧

    android ffmpeg 仿剪映播放器 剪辑视频 预览条 快速精准抽帧 由于本人想学习音视频相关的东西,所以找了剪映作为借鉴,通过仿照剪映的功能学习音视频相关的东西,所以有了这个项目 暂时这个项目 ...

  7. taro开发微信小程序-添加开发者预览,上传测试版本(十四)

    taro开发微信小程序,上传测试版本,如果需要访问网络需要打开调试模式,如果配置了https协议的服务,提示对应的服务器证书无效,那么必须正确配置ssl证书,可以在阿里云或者腾讯云申请. 添加开发者预 ...

  8. android拍照保存照片方向,Android:Camera2开发详解(上):实现预览、拍照、保存照片等功能...

    android.jpg 前言 在前几篇文章中介绍了如何调用系统相机拍照和使用Camera1的实现自定义相机拍照.人脸检测等功能 文章传送门: 接下来的几篇文章中,我将给大家介绍如何使用Camera2实 ...

  9. android开发自动拍照,Android:Camera2开发详解(上):实现预览、拍照、保存照片等功能...

    android.jpg 前言 在前几篇文章中介绍了如何调用系统相机拍照和使用Camera1的实现自定义相机拍照.人脸检测等功能 文章传送门: 接下来的几篇文章中,我将给大家介绍如何使用Camera2实 ...

最新文章

  1. 懂语言者得天下:NLP凭什么被称为人工智能的掌上明珠?
  2. 干掉 RESTful!GraphQL 真香!
  3. python如何输入多组数据_Python 中如何实现多组的输入输出
  4. mysql 定期备份策略,MySQL--3--mysqldump备份策略
  5. 软件测试的学习之路------软件质量
  6. 如何使用CSS将文本垂直居中?
  7. 搭载高通骁龙855+UFS 3.0闪存 iQOO Neo 855版正式发布
  8. Hibernate Session get()vs load()实例差异
  9. 苹果激活锁功能可被长字符串溢出
  10. Cadence 17.4 等长布线
  11. 安全测试简历项目经验怎么写?
  12. mysql 用户 多次登录失败_限制用户登录失败次数,在连续登陆失败10次后冻结该用户。...
  13. 360桌面整理计算机图标,电脑桌面脏乱差 360桌面助手一键整理
  14. html 播放微信amr音频文件,如何在微信中播放amr格式的文件?
  15. java上课听不懂怎么办_上课听不懂怎么办?我们告诉你解决办法!
  16. 数字电路基础知识——乘法器的设计(二)( 串行、流水线、有符号数八位乘法器)
  17. XR行业的“跨维度”战争打响,谁在竞逐第一序列?
  18. 灰色关联分析法无量纲处理方法
  19. 大数据——Spark GraphX中算法介绍
  20. Manifest文件详解

热门文章

  1. 可穿戴设备未来市场巨大
  2. 【数学建模|Python】规划问题之线性规划
  3. cpp map 获取所有 key_久一英语,6-8月雅思口语题库-Part 1-Map
  4. 中本聪更新个人状态;中国区块链技术应用反诈骗中心正式成立
  5. UE5——材质学习笔记(3):水着色器
  6. DWR(1):DWR服务端推送技术介绍
  7. 以数助实攻坚产业协作 蚂蚁集团数字科技业务亮相云栖大会
  8. RSA密码原理详解及算法实现(六步即可掌握)
  9. 调用百度大脑智能里的文字识别实现图片识别
  10. 容器技术的未来——京东云技术专访