首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >OpenGLES通过SurfaceTexture预览摄像头画面

OpenGLES通过SurfaceTexture预览摄像头画面

作者头像
曾大稳
发布于 2018-09-11 02:29:21
发布于 2018-09-11 02:29:21
4.5K00
代码可运行
举报
文章被收录于专栏:曾大稳的博客曾大稳的博客
运行总次数:0
代码可运行

在这篇文章主要用到的知识点有如下,建议先看一下:

OpenGLES绘制图片纹理

OpenGLES顶点缓冲VBO

OpenGLES帧缓冲FBO

有一个渲染流数据的相关的示例,也可以看一下,这样对本篇理解就会很简单 :

Android OpenGLES渲染MediaCodec解码数据

原理

利用OpenGL生成纹理并绑定到SurfaceTexture,然后把camera的预览数据设置显示到SurfaceTexture中,这样就可以在OpenGL中拿到摄像头数据并显示了。

主要步骤

1.OpenGL ES生成纹理 2.OpenGL ES创建SurfaceTexture并绑定 3.OpenGL ES摄像头预览

比如美颜相机那些,处理摄像头数据展示出来,为了提高预览的效率,所以这里使用了VBOFBO,如果不知道这个,请看上面的文章。

FBO所需的glsl: vertex_shader.glsl

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
attribute vec4 av_Position;//顶点位置
attribute vec2 af_Position;//纹理位置
varying vec2 v_texPo;//纹理位置  与fragment_shader交互
uniform mat4 u_Matrix;//矩阵变换

void main() {
    v_texPo = af_Position;
    gl_Position = av_Position * u_Matrix;
}

fragment_shader.glsl

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#extension GL_OES_EGL_image_external : require //申明使用扩展纹理
precision mediump float;//精度 为float
varying vec2 v_texPo;//纹理位置  接收于vertex_shader
uniform samplerExternalOES  sTexture;//加载流数据(摄像头数据)
void main() {
    gl_FragColor=texture2D(sTexture, v_texPo);
}

这里使用FBO里面使用samplerExternalOES是为了加载流数据,摄像头数据属于流数据,所以这里需要用这个。

创建相机预览扩展纹理:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制

/**
    * 创建摄像头预览扩展纹理
    */
   private void createCameraRenderTexture() {
       int[] textureIds = new int[1];
       GLES20.glGenTextures(1, textureIds, 0);
       cameraRenderTextureId = textureIds[0];
       GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, cameraRenderTextureId);
       //环绕(超出纹理坐标范围)  (s==x t==y GL_REPEAT 重复)
       GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_REPEAT);
       GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_REPEAT);
       //过滤(纹理像素映射到坐标点)  (缩小、放大:GL_LINEAR线性)
       GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
       GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);

       surfaceTexture = new SurfaceTexture(cameraRenderTextureId);
       surfaceTexture.setOnFrameAvailableListener(this);

       if (onSurfaceListener != null) {
           //这里相机拿到surfaceTexture绑定
           onSurfaceListener.onSurfaceCreate(surfaceTexture);
       }

       // 解绑扩展纹理
       GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, 0);
   }

camera绑定SurfaceTexture:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
camera.setPreviewTexture(surfaceTexture);

预览画面,先通过fbo处理,然后拿到fbo的纹理id渲染即可:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制

@Override
    public void onDrawFrame() {
        //调用触发onFrameAvailable
        surfaceTexture.updateTexImage();
        //清空颜色
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
        //设置背景颜色
        GLES20.glClearColor(1.0f, 1.0f, 1.0f, 1.0f);

        //使用程序
        GLES20.glUseProgram(program);

        //绑定fbo
        GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, fboId);

        //摄像头预览扩展纹理赋值
        GLES20.glActiveTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES);
        GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, cameraRenderTextureId);
        GLES20.glUniform1i(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, 0);

        //给变换矩阵赋值
        GLES20.glUniformMatrix4fv(uMatrix, 1, false, matrix, 0);

        GLES20.glEnableVertexAttribArray(avPosition);
        GLES20.glEnableVertexAttribArray(afPosition);

        //使用VBO设置纹理和顶点值
        useVboSetVertext();

        //绘制 GLES20.GL_TRIANGLE_STRIP:复用坐标
        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, vertexCount);
        GLES20.glDisableVertexAttribArray(avPosition);
        GLES20.glDisableVertexAttribArray(afPosition);

        GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);

        //渲染显示
        cameraRender.onDraw(fboTextureId);
    }

摄像头方向调整

默认的摄像头预览不同的角度预览出来效果是不同的,我们需要把它给矫正,一般通常是在camera里面设置parms,这里通过OpenGLES自己矫正,通过变换矩阵实现即可:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 //变换矩阵 location
private int uMatrix;

 //变换矩阵
private float[] matrix = new float[16];

public void onSurfaceCreated() {
    //...
uMatrix = GLES20.glGetUniformLocation(program, "u_Matrix");
}
 
 @Override
 public void onDrawFrame() {
 //...
 //给变换矩阵赋值
GLES20.glUniformMatrix4fv(uMatrix, 1, false, matrix, 0); 
 }


/**
 * 初始化矩阵
 */
public void resetMatirx() {
    //初始化
    Matrix.setIdentityM(matrix, 0);
}


/**
 * 旋转
 *
 * @param angle
 * @param x
 * @param y
 * @param z
 */
public void setAngle(float angle, float x, float y, float z) {
    //旋转
    Matrix.rotateM(matrix, 0, angle, x, y, z);
}

在外层调用:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public void previewAngle(Context context) {
        int angle = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getRotation();
        render.resetMatirx();
        switch (angle) {
            case Surface.ROTATION_0:
                if (cameraId == Camera.CameraInfo.CAMERA_FACING_BACK) {
                    render.setAngle(90, 0, 0, 1);
                    render.setAngle(180, 1, 0, 0);
                } else {
                    render.setAngle(90f, 0f, 0f, 1f);
                }

                break;
            case Surface.ROTATION_90:
                if (cameraId == Camera.CameraInfo.CAMERA_FACING_BACK) {
                    render.setAngle(180, 0, 0, 1);
                    render.setAngle(180, 0, 1, 0);
                } else {
                    render.setAngle(90f, 0f, 0f, 1f);
                }
                break;
            case Surface.ROTATION_180:
                if (cameraId == Camera.CameraInfo.CAMERA_FACING_BACK) {
                    render.setAngle(90f, 0.0f, 0f, 1f);
                    render.setAngle(180f, 0.0f, 1f, 0f);
                } else {
                    render.setAngle(-90, 0f, 0f, 1f);
                }
                break;
            case Surface.ROTATION_270:
                if (cameraId == Camera.CameraInfo.CAMERA_FACING_BACK) {
                    render.setAngle(180f, 0.0f, 1f, 0f);
                } else {
                    render.setAngle(0f, 0f, 0f, 1f);
                }
                break;
        }
    }

具体代码下载地址: https://github.com/ChinaZeng/OpenGLESCameraDemo

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2018-08-21,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Android OpenGLES渲染MediaCodec解码数据
1、OpenGL生成纹理 2、纹理绑定到SurfaceTexture上 3、用SurfaceTexture做参数创建Surface 4、MediaCodec解码的视频就往Surface发送,就显示出画面了 Shader编写 vertex_shader.glsl attribute vec4 av_Position; attribute vec2 af_Position; varying vec2 v_texPosition; void main() { v_texPosition = af
曾大稳
2018/09/11
2.8K0
OpenGL ES + MediaPlayer 渲染播放视频+滤镜效果
之前曾经写过用SurfaceView,TextureView+MediaPlayer 播放视频,和 ffmpeg avi 解码后SurfaceView播放视频 ,今天再给大家来一篇 OpenGL ES+MediaPlayer 来播放视频。
字节流动
2020/11/30
2.3K0
OpenGL ES + MediaPlayer 渲染播放视频+滤镜效果
[ - OpenGLES3.0 - ] 第四集 视频接入OpenGLES3.0实现特效
@张风捷特烈 2020.12.08 未允禁转 我的公众号:编程之王 联系我--邮箱:1981462002@qq.com -- 微信: ~ END ~
张风捷特烈
2020/12/09
1.8K0
[ - OpenGLES3.0 - ]  第四集  视频接入OpenGLES3.0实现特效
【Android 音视频开发打怪升级:OpenGL渲染视频画面篇】五、OpenGL FBO数据缓冲区
上一篇文章,讲解了如何使用EGL,并且提到EGL可以建立一个离屏渲染的缓冲区,这种离屏渲染的方式通常用于模拟整个渲染窗口,比如可以用于FFmpeg软编码,将显示在虚拟窗口中的画面编码成H264。
开发的猫
2020/04/02
3K0
【Android 音视频开发打怪升级:OpenGL渲染视频画面篇】五、OpenGL FBO数据缓冲区
「Android音视频编码那点破事」第三章,使用OpenGL为Camera添加各种滤镜
  在第二章中,我们通过一个Camera SurfaceTexture纹理,把摄像头数据绘制到这个纹理上,同时TextureView的SurfaceTexture纹理通过id与第一个纹理关联起来,从而把摄像头画面直接绘制到屏幕上。   对OpenGL有一定了解的人可能会知道,要使用OpenGL渲染各种好看的特效,FBO必不可少。通过FBO,我们可以先把摄像头数据绘制到Camera SurfaceTexture纹理上,然后把这个纹理数据再绘制到一个离屏的FBO,我们可以在这个FBO上做各种特效处理,处理完之后再把离屏FBO中的数据绘制到TextureView的SurfaceTexture纹理,也就是手机屏幕上。
阿利民
2022/05/16
7670
「Android音视频编码那点破事」第三章,使用OpenGL为Camera添加各种滤镜
OpenGLES正交投影
上面我们得到的( ?)是不在归一化坐标范围内的,为了能使OpenGL正确的渲染,我们就需要把(?)以及其他边统一转换到归一化坐标内,这个操作就是正交投影
曾大稳
2018/09/11
9110
OpenGLES正交投影
【Android 音视频开发打怪升级:OpenGL渲染视频画面篇】二、使用OpenGL渲染视频画面
在第一篇文章【音视频基础知识】文章中,就介绍过,视频其实就是一张张图片组成的,在上文【初步了解OpenGL ES】中,介绍了如何通过OpenGL渲染一张图片,可以猜想到,视频的渲染和图片的渲染应该是差不多的。话不多说,马上就来看看。
开发的猫
2020/04/02
2.5K0
【Android 音视频开发打怪升级:OpenGL渲染视频画面篇】二、使用OpenGL渲染视频画面
OpenGL ES for Android 相机预览
Android上打开摄像头需要camera权限,在Android 6.0及以上的版本需要动态申请权限,在`AndroidManifest.xml`中添加camera权限:
老孟Flutter
2020/09/11
1.1K0
OpenGL ES 与 GLSurfaceView 渲染视频帧
大家好,本文是 iOS/Android 音视频专题 的第六篇,该专题中 AVPlayer 项目代码将在 Github 进行托管,你可在微信公众号(GeekDev)后台回复 资料 获取项目地址。
100001509164
2022/01/20
2.7K0
【Android 音视频开发打怪升级:OpenGL渲染视频画面篇】三、OpenGL渲染多视频,实现画中画
距离上次更新已经有两个星期,由于这段时间事情比较多,还请各位关注本系列文章的小伙伴见谅,一有时间我会加紧码字,感谢大家的关注和督促。
开发的猫
2020/04/02
2.7K0
【Android 音视频开发打怪升级:OpenGL渲染视频画面篇】三、OpenGL渲染多视频,实现画中画
[-综合篇-] 相机、OpenGL、视频、Flutter和SurfaceView
认识一个类,相当于结交一位朋友;看一篇源码,相当于一次顶级的会话; 读一个框架,相当于见证一段思想;做一个程序,相当于创造一个生命; 一次Git提交,相当于记录一次成长;生活也许并非那么美好,但一切可以这么崇高。----张风捷特烈
张风捷特烈
2020/04/30
3.3K0
[-综合篇-] 相机、OpenGL、视频、Flutter和SurfaceView
「Android音视频编码那点破事」第二章,使用TextureView渲染Camera画面
  上一章我们讲到了使用SurfaceTexture作为Camera数据的缓冲区,这仅仅是把帧数据缓冲到了纹理上,并没有把它绘制出来,所以这一章我们来实现这个功能。
阿利民
2022/05/16
1.4K0
「Android音视频编码那点破事」第二章,使用TextureView渲染Camera画面
「Android音视频编码那点破事」第一章,使用SurfaceTexture作为Camera输出
  在Android系统中,使用GPU对摄像头画面进行高效可控的渲染,几乎是必须的。说到GPU就不得不提OpenGL,一组GPU暴露给应用层使用的接口。
阿利民
2022/05/16
1.4K0
「Android音视频编码那点破事」第一章,使用SurfaceTexture作为Camera输出
Android OpenGL ES(五)-结合相机
上文中我们已经实现了在纹理上添加滤镜的效果。这编文章就是将OpenGl和相机结合到一起。
deep_sadness
2018/10/10
2.2K0
Android OpenGL ES(五)-结合相机
如何使用TextureView+OpenGL绘制相机预览
使用Camera2 API实现相机预览样板代码太多了,偷一波懒,CV大法发动。。。
雪月清
2020/06/23
3.1K0
RenderDemo(2):用 OpenGL 渲染视频丨音视频工程示例
渲染是音视频技术栈相关的一个非常重要的方向,视频图像在设备上的展示、各种流行的视频特效都离不开渲染技术的支持。
关键帧
2023/02/14
1.5K0
RenderDemo(2):用 OpenGL 渲染视频丨音视频工程示例
Android OpenGLES 绘制图片纹理
把shader代码写入raw里面 vertex_shader.glsl attribute vec4 av_Position;//顶点位置 attribute vec2 af_Position;//纹理位置 varying vec2 v_texPo;//纹理位置 与fragment_shader交互 void main() { v_texPo = af_Position; gl_Position = av_Position; } fragment_shader.glsl precisio
曾大稳
2018/09/11
1.1K0
Android OpenGLES 绘制图片纹理
NDK OpenGL ES 3.0 开发(十六):相机预览
相机开发是 OpenGL ES 开发的重要应用,利用 OpenGL 可以很方便地实现相机美颜、滤镜、塑型以及一些动态特效,其性能显著优于对应功能的 CPU 实现。
字节流动
2020/06/02
2.9K0
OpenGLES帧缓冲FBO
FBO Frame Buffer object 为什么要用FBO 我们需要对纹理进行多次渲染采样时,而这些渲染采样是不需要展示给用户看的,所以我们就可以用一个单独的缓冲对象(离屏渲染)来存储我们的这
曾大稳
2018/09/11
2K0
OpenGLES帧缓冲FBO
OpenGL ES 纹理过滤模式-glTexParameteri
Android中GLES20.glTexParameteri函数表示对纹理的设置,函数结构如下:
老孟Flutter
2020/09/11
1.6K0
推荐阅读
相关推荐
Android OpenGLES渲染MediaCodec解码数据
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档