android-camera2相机开发-7-使用opengles进行相机预览
项目github地址
前面几篇文章梳理了 android 相机的基本使用流程及相关的 api,完成了预览拍照等功能。
就预览而言,后续想做实时滤镜等功能的话,如果按照之前的方法用 ImageReader 拿到数据之后做处理再显示,一是繁琐,而是效率太低,卡顿严重。所以需要使用 opengles 对相机的预览数据进行渲染,可以很大的提高效率,防止卡顿。
这篇文章先不做滤镜,而是先实现 opengles 渲染相机数据的功能,为后续实时滤镜等功能打基础。
opengles 基本使用
opengles 在 android 平台上的简单使用可以参考之前写的 opengles 相关的文章。
主要流程:
- 布局中使用 GLSurfacView 作为预览窗口。
- 准备相关的顶点属性数据和着色器文件。
- 实现 GLSurfaceView.Render 接口,编写具体的渲染代码。
具体的操作在之前 opengles 相关的文章中都有,完整的代码放在 android-opengl-demo项目
opengles 相机预览
android 相机的预览数据可以输出到 SurfaceTexture 上,所以用 opengles 做相机预览的主要思路是
- 在 GLSurfaceView.Render 中创建一个纹理,再使用该纹理创建一个 SurfaceTexture
- 使用该 SurfaceTexture 创建一个 Surface 传给相机,相机预览数据就输出到一个纹理上了
- 使用 GLSurfaceView.Render 将该纹理渲染到 GLSurfaceView 窗口上
- 使用 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进行相机预览相关推荐
- android camera预览帧,android camera2处理每一帧并显示其预览
我使用 https://github.com/googlesamples/android-Camera2Basic,但尝试修改它,我可以访问每个帧,然后在surfaceview上绘制. 我明白了,我应 ...
- Android通过屏幕方向和摄像头方向实现屏幕预览
Android通过屏幕方向和摄像头方向实现屏幕预览 1. 前言 2. Android 的屏幕方向 2.1 什么是屏幕方向 2.2 为什么要获取或设置屏幕方向 2.3 如何获取与设置屏幕方向 2.3.1 ...
- 关于微信小程序开发环境苹果IOS真机预览报SSL协议错误问题解决方案
关于微信小程序开发环境苹果IOS真机预览报SSL协议错误问题解决方案 参考文章: (1)关于微信小程序开发环境苹果IOS真机预览报SSL协议错误问题解决方案 (2)https://www.cnblog ...
- 在 Apple 芯片设备上用 Android Studio?别忘了使用 Apple 芯片预览版!
感谢大家通过微信等渠道为打造更好的 Android Studio 提供的诸多反馈,在这些反馈中,我们了解到 Android Studio 对于 Apple 芯片的支持一直备受期待.随着 Android ...
- 微信小程序云开发实现上传文件和预览下载文件
微信小程序云开发实现上传文件和预览下载文件 一.前言 目前微信提供了一个接口 wx.chooseMessageFile 它能让用户从聊天记录里面选择一个或者多个文件,然后返回它的一些信息,列入文件的p ...
- android ffmpeg 仿剪映播放器 剪辑视频 预览条 快速精准抽帧
android ffmpeg 仿剪映播放器 剪辑视频 预览条 快速精准抽帧 由于本人想学习音视频相关的东西,所以找了剪映作为借鉴,通过仿照剪映的功能学习音视频相关的东西,所以有了这个项目 暂时这个项目 ...
- taro开发微信小程序-添加开发者预览,上传测试版本(十四)
taro开发微信小程序,上传测试版本,如果需要访问网络需要打开调试模式,如果配置了https协议的服务,提示对应的服务器证书无效,那么必须正确配置ssl证书,可以在阿里云或者腾讯云申请. 添加开发者预 ...
- android拍照保存照片方向,Android:Camera2开发详解(上):实现预览、拍照、保存照片等功能...
android.jpg 前言 在前几篇文章中介绍了如何调用系统相机拍照和使用Camera1的实现自定义相机拍照.人脸检测等功能 文章传送门: 接下来的几篇文章中,我将给大家介绍如何使用Camera2实 ...
- android开发自动拍照,Android:Camera2开发详解(上):实现预览、拍照、保存照片等功能...
android.jpg 前言 在前几篇文章中介绍了如何调用系统相机拍照和使用Camera1的实现自定义相机拍照.人脸检测等功能 文章传送门: 接下来的几篇文章中,我将给大家介绍如何使用Camera2实 ...
最新文章
- 懂语言者得天下:NLP凭什么被称为人工智能的掌上明珠?
- 干掉 RESTful!GraphQL 真香!
- python如何输入多组数据_Python 中如何实现多组的输入输出
- mysql 定期备份策略,MySQL--3--mysqldump备份策略
- 软件测试的学习之路------软件质量
- 如何使用CSS将文本垂直居中?
- 搭载高通骁龙855+UFS 3.0闪存 iQOO Neo 855版正式发布
- Hibernate Session get()vs load()实例差异
- 苹果激活锁功能可被长字符串溢出
- Cadence 17.4 等长布线
- 安全测试简历项目经验怎么写?
- mysql 用户 多次登录失败_限制用户登录失败次数,在连续登陆失败10次后冻结该用户。...
- 360桌面整理计算机图标,电脑桌面脏乱差 360桌面助手一键整理
- html 播放微信amr音频文件,如何在微信中播放amr格式的文件?
- java上课听不懂怎么办_上课听不懂怎么办?我们告诉你解决办法!
- 数字电路基础知识——乘法器的设计(二)( 串行、流水线、有符号数八位乘法器)
- XR行业的“跨维度”战争打响,谁在竞逐第一序列?
- 灰色关联分析法无量纲处理方法
- 大数据——Spark GraphX中算法介绍
- Manifest文件详解