实现效果
一、GLSurfaceView
众所周知,GLSurfaceView
提供了一个EGL
环境,只需实现其中的Renderer
,即可使用OpenGLES API
进行绘制操作
Renderer
有三个方法,需要我们实现
-
onSurfaceCreated(GL10 gl, EGLConfig config)
此处进行一些初始化工作,比如加载着色器、加载纹理、初始化顶点和纹理坐标等
-
onSurfaceChanged(GL10 gl, int width, int height)
此处进行窗口大小的设置
-
onDrawFrame(GL10 gl)
此处进行一些渲染工作,比如绑定纹理、传入顶点和纹理坐标、矩阵等
这里只做个简单的介绍,笔者是默认您会OpenGLES
基本渲染步骤,如果还不太了解,相关的介绍网络上有很多
二、EGLTextureView
既然有了GLSurfaceView,那么为什么还要自定义一个EGLTextureView呢?
因为需要EGLTextureView
里面使用的是TextureView
实现,可以满足Android View
的一些特性,而GLSurfaceView
是SurfaceView
实现,对View
的一些特性是不支持的
如何自定义EGLTextureView?
- 新建类
EGLTextureView
- 复制
GLSurfaceView
的所有内容到EGLTextureView
- 修改继承的类的
TextureView
,并实现TextureView.SurfaceTextureListener
接口 - 删除所有报错的地方
- 修改
EGLHelper
里createSurface
里创建EGLSurface
的地方
如果觉得麻烦,可以参考笔者定义的EGLTextureView
到此,我们将环境搭建好了,那么接下来Renderer
就是关键,如果你看了笔者定义的EGLTextureView
,你会发现Renderer
有些不一样,这个是因为我觉得既然都自定义EGLTextureView
了,那么Renderer
自然弄简单一点,也好理解一点
public interface Renderer {
void onCreate();
void onChange(int width, int height);
void onDraw();
}
三、BaseRender
注意到,EGLTextureView
里面,setRenderer
方法只能调用一次,因为在调用setRenderer
的时候,如果发现GLThread
已经存在了,则直接抛出异常
private void checkRenderThreadState() {
if (mGLThread != null) {
throw new IllegalStateException(
"setRenderer has already been called for this instance.");
}
}
也就是说,设置了Renderer
后,就不能更换了,那么如果有需求:本来是渲染一个灰度滤镜的,当想变换其他滤镜的时候,又不能重新设置Renderer
,此时应该如何
其实可以这样,Renderer
可以只做一个调度者,Renderer
里面可以写很多的滤镜,也可以变换滤镜,那么就可以满足我们的需求
那么接下来就先来定义一个基础滤镜
首先第一步,创建一个IRender
public interface IRender {
/** * 创建 */
void onCreate();
/** * 设置尺寸 */
void onChange(int width, int height);
/** * 绘制 */
void onDraw(int textureId);
/** * 释放资源 */
void onRelease();
}
和Renderer
有些许不同,修改和增加了一些方法
新建BaseRender,实现IRender接口
public class BaseRender implements IRender {
/** * Context */
private Context context;
/** * 渲染数据 */
private BaseRenderBean renderBean;
/** * 顶点坐标 */
private FloatBuffer vertexBuffer;
/** * 纹理坐标 */
private FloatBuffer coordinateBuffer;
/** * 顶点坐标维数(即x, y, z) */
private int vertexSize = 2;
/** * 纹理坐标维数(即x, y, z) */
private int coordinateSize = 2;
/** * 顶点坐标步长(即维数 * 字节数) */
private int vertexStride = vertexSize * 4;
/** * 纹理坐标步长(即维数 * 字节数) */
private int coordinateStride = coordinateSize * 4;
/** * 顶点个数 */
private int vertexCount = 4;
/** * 纹理点个数 */
private int coordinateCount = 4;
/** * vertex shader */
private int vertexShader;
/** * frag shader */
private int fragShader;
/** * program */
private int program;
/** * 纹理 id */
private int textureId;
/** * fbo纹理id */
private int fboTextureId;
/** * fbo id */
private int fboId;
/** * vbo id */
private int vboId;
/** * 顶点着色器代码路径 */
private String vertexFilename;
/** * 片元着色器代码路径 */
private String fragFilename;
/** * 尺寸 */
private int width;
private int height;
/** * 是否绑定Fbo */
private boolean isBindFbo = false;
/** * 着色器顶点坐标位置 */
private int aPosLocation;
/** * 着色器纹理坐标位置 */
private int aCoordinateLocation;
/** * 着色器纹理位置 */
private int uSamplerLocation;
/** * 是否执行了onCreate */
private boolean isCreate = false;
/** * 是否执行了onChange */
private boolean isChange = false;
public BaseRender(Context context) {
this(context, "render/base/base/vertex.frag", "render/base/base/frag.frag");
}
public BaseRender(Context context, String vertexFilename, String fragFilename) {
this.context = context;
this.vertexFilename = vertexFilename;
this.fragFilename = fragFilename;
}
@Override
public void onCreate() {
if (isCreate) {
return;
}
onCreatePre();
onClearColor();
onInitBlend();
onInitVertexBuffer();
onInitCoordinateBuffer();
onInitVbo();
onInitProgram();
onCreateAfter();
isCreate = true;
}
@Override
public void onChange(int width, int height) {
if (isChange) {
return;
}
onChangePre();
setWidth(width);
setHeight(height);
onViewport();
onInitFbo();
onChangeAfter();
isChange = true;
}
@Override
public void onDraw(int textureId) {
if (!onReadyToDraw()) {
return;
}
onDrawPre();
onClear();
onUseProgram();
onInitLocation();
onBindFbo();
onBindVbo();
onActiveTexture(textureId);
onEnableVertexAttributeArray();
onSetVertexData();
onSetCoordinateData();
onSetOtherData();
onDraw();
onDisableVertexAttributeArray();
onUnBind();
onDrawAfter();
}
@Override
public void onRelease() {
onDeleteProgram(program);
onDeleteShader(vertexShader);
onDeleteShader(fragShader);
onDeleteTexture(textureId);
onDeleteTexture(fboTextureId);
onDeleteFbo(fboId);
onDeleteVbo(vboId);
}
/** * 创建之前 */
public void onCreatePre() {
}
/** * 设置背景颜色 */
public void onClearColor() {
GLES20.glClearColor(0, 0, 0, 1);
}
/** * 是否启用混色 */
public boolean onEnableBlend() {
return false;
}
/** * 初始化混色 */
private void onInitBlend() {
if (!onEnableBlend()) {
return;
}
GLES20.glEnable(GLES20.GL_BLEND);
GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA);
}
/** * 初始化顶点坐标 */
public void onInitVertexBuffer() {
vertexBuffer = OpenGLESUtils.getSquareVertexBuffer();
}
/** * 初始化纹理坐标 */
public void onInitCoordinateBuffer() {
if (isBindFbo) {
coordinateBuffer = OpenGLESUtils.getSquareCoordinateReverseBuffer();
} else {
coordinateBuffer = OpenGLESUtils.getSquareCoordinateBuffer();
}
}
/** * 初始化Vbo */
public void onInitVbo() {
vboId = OpenGLESUtils.getVbo(vertexBuffer, coordinateBuffer);
}
/** * 初始化Program */
public void onInitProgram() {
String vertexShaderCode = OpenGLESUtils.getShaderCode(context, vertexFilename);
String fragShaderCode = OpenGLESUtils.getShaderCode(context, fragFilename);
vertexShader = OpenGLESUtils.loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode);
fragShader = OpenGLESUtils.loadShader(GLES20.GL_FRAGMENT_SHADER, fragShaderCode);
program = OpenGLESUtils.linkProgram(vertexShader, fragShader);
}
/** * 创建之后 */
public void onCreateAfter() {
}
/** * 设置尺寸之前 */
public void onChangePre() {
}
/** * 设置窗口尺寸 */
public void onViewport() {
GLES20.glViewport(0, 0, width, height);
}
/** * 初始化Fbo */
public void onInitFbo() {
if (!isBindFbo) {
return;
}
int[] fboData = OpenGLESUtils.getFbo(width, height);
fboId = fboData[0];
fboTextureId = fboData[1];
}
/** * 设置尺寸之后 */
public void onChangeAfter() {
}
/** * 绘制之前的准备 */
public boolean onReadyToDraw() {
return true;
}
/** * 绘制之前 */
public void onDrawPre() {
}
/** * 清屏 */
public void onClear() {
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
}
/** * 使用Program */
public void onUseProgram() {
GLES20.glUseProgram(program);
}
/** * 初始化着色器各个位置 */
public void onInitLocation() {
aPosLocation = GLES20.glGetAttribLocation(program, "aPos");
aCoordinateLocation = GLES20.glGetAttribLocation(program, "aCoordinate");
uSamplerLocation = GLES20.glGetUniformLocation(program, "uSampler");
}
/** * 绑定Fbo */
public void onBindFbo() {
if (!isBindFbo) {
return;
}
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, fboId);
GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0,
GLES20.GL_TEXTURE_2D, fboTextureId, 0);
GLES20.glViewport(0, 0, width, height);
}
/** * 绑定Vbo */
public void onBindVbo() {
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vboId);
}
/** * 激活并绑定纹理 */
public void onActiveTexture(int textureId) {
this.textureId = textureId;
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId);
GLES20.glUniform1i(uSamplerLocation, 0);
}
/** * 启用顶点坐标 */
public void onEnableVertexAttributeArray() {
GLES20.glEnableVertexAttribArray(aPosLocation);
GLES20.glEnableVertexAttribArray(aCoordinateLocation);
}
/** * 设置顶点坐标 */
public void onSetVertexData() {
GLES20.glVertexAttribPointer(aPosLocation, vertexSize, GLES20.GL_FLOAT, false, vertexStride, 0);
}
/** * 设置纹理坐标 */
public void onSetCoordinateData() {
GLES20.glVertexAttribPointer(aCoordinateLocation, coordinateSize, GLES20.GL_FLOAT, false, coordinateStride, vertexBuffer.limit() * 4);
}
/** * 设置其他数据 */
public void onSetOtherData() {
}
/** * 绘制 */
public void onDraw() {
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, vertexCount);
}
/** * 禁用顶点坐标 */
public void onDisableVertexAttributeArray() {
GLES20.glDisableVertexAttribArray(aPosLocation);
GLES20.glDisableVertexAttribArray(aCoordinateLocation);
}
/** * 解除绑定 */
public void onUnBind() {
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
}
/** * 绘制之后 */
public void onDrawAfter() {
}
/** * 删除Program */
public void onDeleteProgram(int program) {
GLES20.glDeleteProgram(program);
}
/** * 删除Shader */
public void onDeleteShader(int shader) {
GLES20.glDeleteShader(shader);
}
/** * 删除纹理 */
public void onDeleteTexture(int textureId) {
GLES20.glDeleteTextures(1, new int[]{textureId}, 0);
}
/** * 删除Fbo */
public void onDeleteFbo(int fboId) {
GLES20.glDeleteFramebuffers(1, new int[]{fboId}, 0);
}
/** * 删除Vbo */
public void onDeleteVbo(int vboId) {
GLES20.glDeleteBuffers(1, new int[]{vboId}, 0);
}
public Context getContext() {
return context;
}
public void setContext(Context context) {
this.context = context;
}
public FloatBuffer getVertexBuffer() {
return vertexBuffer;
}
public void setVertexBuffer(FloatBuffer vertexBuffer) {
this.vertexBuffer = vertexBuffer;
}
public FloatBuffer getCoordinateBuffer() {
return coordinateBuffer;
}
public void setCoordinateBuffer(FloatBuffer coordinateBuffer) {
this.coordinateBuffer = coordinateBuffer;
}
public int getVertexSize() {
return vertexSize;
}
public void setVertexSize(int vertexSize) {
this.vertexSize = vertexSize;
}
public int getCoordinateSize() {
return coordinateSize;
}
public void setCoordinateSize(int coordinateSize) {
this.coordinateSize = coordinateSize;
}
public int getVertexStride() {
return vertexStride;
}
public void setVertexStride(int vertexStride) {
this.vertexStride = vertexStride;
}
public int getCoordinateStride() {
return coordinateStride;
}
public void setCoordinateStride(int coordinateStride) {
this.coordinateStride = coordinateStride;
}
public int getVertexCount() {
return vertexCount;
}
public void setVertexCount(int vertexCount) {
this.vertexCount = vertexCount;
}
public int getCoordinateCount() {
return coordinateCount;
}
public void setCoordinateCount(int coordinateCount) {
this.coordinateCount = coordinateCount;
}
public int getProgram() {
return program;
}
public void setProgram(int program) {
this.program = program;
}
public int getFboTextureId() {
return fboTextureId;
}
public void setFboTextureId(int fboTextureId) {
this.fboTextureId = fboTextureId;
}
public int getFboId() {
return fboId;
}
public void setFboId(int fboId) {
this.fboId = fboId;
}
public int getVboId() {
return vboId;
}
public void setVboId(int vboId) {
this.vboId = vboId;
}
public String getVertexFilename() {
return vertexFilename;
}
public void setVertexFilename(String vertexFilename) {
this.vertexFilename = vertexFilename;
}
public String getFragFilename() {
return fragFilename;
}
public void setFragFilename(String fragFilename) {
this.fragFilename = fragFilename;
}
public int getWidth() {
return width;
}
public void setWidth(int width) {
this.width = width;
}
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
public boolean isBindFbo() {
return isBindFbo;
}
public void setBindFbo(boolean bindFbo) {
isBindFbo = bindFbo;
}
public int getPosLocation() {
return aPosLocation;
}
public void setPosLocation(int aPosLocation) {
this.aPosLocation = aPosLocation;
}
public int getCoordinateLocation() {
return aCoordinateLocation;
}
public void setCoordinateLocation(int aCoordinateLocation) {
this.aCoordinateLocation = aCoordinateLocation;
}
public int getSamplerLocation() {
return uSamplerLocation;
}
public void setSamplerLocation(int uSamplerLocation) {
this.uSamplerLocation = uSamplerLocation;
}
public boolean isCreate() {
return isCreate;
}
public void setCreate(boolean create) {
isCreate = create;
}
public boolean isChange() {
return isChange;
}
public void setChange(boolean change) {
isChange = change;
}
public BaseRenderBean getRenderBean() {
return renderBean;
}
public void setRenderBean(BaseRenderBean renderBean) {
this.renderBean = renderBean;
}
public void updateRenderBean(BaseRenderBean renderBean) {
setRenderBean(renderBean);
}
}
代码有点长,但是里面尽可能地考虑到了渲染和扩展的需求
顶点着色器 vertex.frag
attribute vec4 aPos;
attribute vec2 aCoordinate;
varying vec2 vCoordinate;
void main(){
vCoordinate = aCoordinate;
gl_Position = aPos;
}
片元着色器 frag.frag
precision mediump float;
uniform sampler2D uSampler;
varying vec2 vCoordinate;
void main(){
gl_FragColor = texture2D(uSampler, vCoordinate);
}
注意到,里面有用到一个工具类OpenGLESUtils和实体类BaseRenderBean具体就不贴出来了,可以到Github
上查看
四、BaseOesRender
注意到,BaseRender
里面绑定的纹理是2D纹理
,而如果想实现相机预览,则需要使用Oes纹理
,所以需要创建一个BaseOesRender
为相机做渲染
package com.yk.media.opengles.render.base;
import android.content.Context;
import android.graphics.SurfaceTexture;
import android.opengl.GLES11Ext;
import android.opengl.GLES20;
import com.yk.media.utils.OpenGLESUtils;
public class BaseOesRender extends BaseRender {
/** * oes纹理id */
private int oesTextureId;
/** * 顶点变换矩阵位置 */
private int uMatrixLocation;
/** * 纹理变换矩阵位置 */
private int uOesMatrixLocation;
/** * oes尺寸 */
private int oesW = -1;
private int oesH = -1;
/** * 顶点变换矩阵 */
private float[] mMVPMatrix = new float[16];
/** * 纹理变换矩阵 */
private float[] mOesMatrix = {
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1
};
/** * 是否准备好绘制 */
private boolean isReadyToDraw = false;
/** * SurfaceTexture */
private SurfaceTexture surfaceTexture;
/** * SurfaceTexture回调 */
private OnSurfaceTextureListener onSurfaceTextureListener;
public BaseOesRender(Context context) {
super(context, "render/base/oes/vertex.frag", "render/base/oes/frag.frag");
setBindFbo(true);
oesTextureId = OpenGLESUtils.getOesTexture();
}
@Override
public void onInitCoordinateBuffer() {
setCoordinateBuffer(OpenGLESUtils.getSquareCoordinateBuffer());
}
@Override
public boolean onReadyToDraw() {
if (!isReadyToDraw) {
if (onSurfaceTextureListener != null) {
if (surfaceTexture != null) {
surfaceTexture.release();
surfaceTexture = null;
}
surfaceTexture = new SurfaceTexture(oesTextureId);
onSurfaceTextureListener.onSurfaceTexture(surfaceTexture);
isReadyToDraw = true;
} else if (surfaceTexture != null) {
surfaceTexture.attachToGLContext(oesTextureId);
isReadyToDraw = true;
} else {
return false;
}
}
return oesW != -1 && oesH != -1;
}
@Override
public void onDrawPre() {
super.onDrawPre();
mMVPMatrix = OpenGLESUtils.getMatrix(getWidth(), getHeight(), oesW, oesH);
surfaceTexture.updateTexImage();
float[] oesMatrix = new float[16];
surfaceTexture.getTransformMatrix(oesMatrix);
if (!OpenGLESUtils.isIdentityM(oesMatrix)) {
mOesMatrix = oesMatrix;
}
}
@Override
public void onInitLocation() {
super.onInitLocation();
uMatrixLocation = GLES20.glGetUniformLocation(getProgram(), "uMatrix");
uOesMatrixLocation = GLES20.glGetUniformLocation(getProgram(), "uOesMatrix");
}
@Override
public void onActiveTexture(int textureId) {
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, textureId);
GLES20.glUniform1i(getSamplerLocation(), 0);
}
@Override
public void onSetOtherData() {
super.onSetOtherData();
GLES20.glUniformMatrix4fv(uMatrixLocation, 1, false, mMVPMatrix, 0);
GLES20.glUniformMatrix4fv(uOesMatrixLocation, 1, false, mOesMatrix, 0);
}
@Override
public void onRelease() {
super.onRelease();
onDeleteTexture(oesTextureId);
}
/** * 绘制 */
public void onDrawSelf() {
super.onDraw(oesTextureId);
}
/** * 设置oes尺寸 */
public void setOesSize(int width, int height) {
oesW = width;
oesH = height;
}
/** * 设置SurfaceTexture */
public void setSurfaceTexture(SurfaceTexture surfaceTexture) {
this.surfaceTexture = surfaceTexture;
isReadyToDraw = false;
}
/** * 设置SurfaceTexture回调 */
public void setOnSurfaceTextureListener(OnSurfaceTextureListener onSurfaceTextureListener) {
this.onSurfaceTextureListener = onSurfaceTextureListener;
isReadyToDraw = false;
}
}
顶点着色器 vertex.frag
attribute vec4 aPos;
attribute vec4 aCoordinate;
uniform mat4 uMatrix;
uniform mat4 uOesMatrix;
varying vec2 vCoordinate;
void main(){
vCoordinate = (uOesMatrix * aCoordinate).xy;
gl_Position = uMatrix * aPos;
}
片元着色器 frag.frag
#extension GL_OES_EGL_image_external:require
precision mediump float;
uniform samplerExternalOES uSampler;
varying vec2 vCoordinate;
void main(){
gl_FragColor = texture2D(uSampler, vCoordinate);
}
BaseOesRender
是继承BaseRender
的
加入了一些变量
-
oesW
和oesH
用于计算矩阵
-
mMVPMatrix
通过
width、height、oesW、oesH
计算的矩阵 -
mOesMatrix
通过
SurfaceTexture
获取的矩阵 -
isReadyToDraw
是否准备好渲染
重写了一些方法
-
onInitCoordinateBuffer
BaseOesRender
需要绑定FBO
,所有纹理坐标需要设置好,不然会出现倒立 -
onReadyToDraw
此处做一些
SurfaceTexture
绑定纹理和回调 -
onDrawPre
此处做矩阵相关的计算
-
onInitLocation
获取矩阵的Location
-
onActiveTexture
前面有说到,预览相机使用的是
SurfaceTexture
,所有需要修改绑定为Oes纹理
-
onSetOtherData
此处传入矩阵的数据
-
onRelease
此处做一些释放资源的工作
OnSurfaceTextureListener
public interface OnSurfaceTextureListener {
/** * SurfaceTexture回调 */
void onSurfaceTexture(SurfaceTexture surfaceTexture);
}
onDrawSelf
public void onDrawSelf() {
super.onDraw(oesTextureId);
}
因为oesTextureId
是内部创建的,故渲染的话,直接调用super.onDraw(oesTextureId)
,方便外部调用
五、OesRender
前面有说过,EGLTextureView
的setRenderer
只能调用一次,故才会创建BaseRender
,现在来创建Renderer
调度者
public class OesRender implements Renderer {
private Context context;
/** * 输入(FBO保存数据) */
private BaseOesRender inputRender;
/** * 输出(屏幕显示) */
private BaseRender outputRender;
private int width;
private int height;
public OesRender(Context context) {
this.context = context;
inputRender = new BaseOesRender(context);
outputRender = new BaseRender(context);
}
@Override
public void onCreate() {
inputRender.onCreate();
outputRender.onCreate();
}
@Override
public void onChange(int width, int height) {
this.width = width;
this.height = height;
inputRender.onChange(width, height);
outputRender.onChange(width, height);
}
@Override
public void onDraw() {
inputRender.onDrawSelf();
outputRender.onDraw(inputRender.getFboTextureId());
}
public void setOesSize(int width, int height) {
inputRender.setOesSize(width, height);
}
public void setOnSurfaceTextureListener(OnSurfaceTextureListener onSurfaceTextureListener) {
inputRender.setOnSurfaceTextureListener(onSurfaceTextureListener);
}
public void setSurfaceTexture(SurfaceTexture surfaceTexture) {
inputRender.setSurfaceTexture(surfaceTexture);
}
public int getFboTextureId() {
return inputRender.getFboTextureId();
}
}
注意到,OesRender
里面,有两个Render
-
BaseOesRender
渲染
Oes纹理
,并绑定了Fbo
-
BaseRender
做为输出显示
六、CameraView
完成了上面的工作,接下来就可以进入正题,如何在Android
中,使用OpenGLES + Camera1
实现相机预览?
既然我们已经完成了“容器”和Renderer
的创建,那么下面的工作就比较轻松
还记得上一章的Android Camera1相机预览,不记得的同学可以点击链接进入查看,因为接下来需要用到上一章介绍的CameraManager
public class CameraView extends EGLTextureView implements OnCameraListener {
private final CameraManager cameraManager = new CameraManager();
private Activity activity;
public OesRender render;
public CameraView(Context context) {
this(context, null);
}
public CameraView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
private void init(Context context) {
setEGLContextClientVersion(2);
render = new OesRender(context);
setRenderer(render);
setRenderMode(EGLTextureView.RENDERMODE_WHEN_DIRTY);
activity = (Activity) context;
cameraManager.addOnCameraListener(this);
}
public void openCamera() {
render.setOnSurfaceTextureListener(new OnSurfaceTextureListener() {
@Override
public void onSurfaceTexture(SurfaceTexture surfaceTexture) {
surfaceTexture.setOnFrameAvailableListener(new SurfaceTexture.OnFrameAvailableListener() {
@Override
public void onFrameAvailable(SurfaceTexture surfaceTexture) {
requestRender();
}
});
cameraManager.openCamera(activity, surfaceTexture);
}
});
requestRender();
}
public void closeCamera() {
cameraManager.closeCamera();
}
public void switchCamera() {
cameraManager.switchCamera();
openCamera();
}
public void switchCamera(int facing) {
cameraManager.switchCamera(facing);
openCamera();
}
public void addOnCameraListener(OnCameraListener onCameraListener) {
cameraManager.addOnCameraListener(onCameraListener);
}
@Override
public void onCameraOpened(Size cameraSize, int facing) {
render.setOesSize(cameraSize.getHeight(), cameraSize.getWidth());
requestRender();
}
@Override
public void onCameraClosed() {
}
@Override
public void onCameraError(Exception e) {
}
public CameraManager getCameraManager() {
return cameraManager;
}
public int getFboTextureId() {
return render.getFboTextureId();
}
}
新建CameraView
,继承EGLTextureView
初始化
private void init(Context context) {
setEGLContextClientVersion(2);
render = new OesRender(context,process);
setRenderer(render);
setRenderMode(EGLTextureView.RENDERMODE_WHEN_DIRTY);
activity = (Activity) context;
cameraManager.addOnCameraListener(this);
}
此处做了一些初始化工作
-
setEGLContextClientVersion
此处传入的是
2
,表示使用的是OpenGLES 2.0
-
setRenderer
此处将上面创建的
OesRender
传入 -
setRenderMode
此处传入
EGLTextureView.RENDERMODE_WHEN_DIRTY
,表示只有在调用requestRender()
方法时才会渲染对应的,还有一个是
EGLTextureView.RENDERMODE_CONTINUOUSLY
,表示会不停的渲染 -
addOnCameraListener
设置相机的回调
打开相机
public void openCamera() {
render.setOnSurfaceTextureListener(new OnSurfaceTextureListener() {
@Override
public void onSurfaceTexture(SurfaceTexture surfaceTexture) {
surfaceTexture.setOnFrameAvailableListener(new SurfaceTexture.OnFrameAvailableListener() {
@Override
public void onFrameAvailable(SurfaceTexture surfaceTexture) {
requestRender();
}
});
cameraManager.openCamera(activity, surfaceTexture);
}
});
requestRender();
}
首先,设置SurfaceTexture
的回调监听,并调用requestRender()
方法进行渲染请求
在拿到SurfaceTexture
后,需要对SurfaceTexture
进行设置setOnFrameAvailableListener
监听,表示当SurfaceTexture
每帧数据到来的时候,都会调用requestRender()
请求渲染
接下来,就是使用CameraManager
的openCamera
方法,传入activity
和surfaceTexture
进行打开摄像头的操作
关闭相机
public void closeCamera() {
cameraManager.closeCamera();
}
关闭相机比较简单,直接调用CameraManager的closeCamera()
方法即可
切换相机
public void switchCamera() {
cameraManager.switchCamera();
openCamera();
}
切换相机和上一章,先调用cameraManager
的switchCamera()
方法切换facing
,再调用openCamera()
即可
Github
以上相关代码都可以在下面找到
今天的文章Android OpenGLES + Camera1 相机预览分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/17903.html