在https://blog.csdn.net/llxyy299/article/details/82659683这篇文章中记录了自己创建EGL环境,自己实现一个GLSurfaceView,但是还没有记录怎么用,这篇文章来记录怎么使用自定义的GLSurfaceView。
1、首先创建一个CameraHelper类,里面封装了安卓相机的一些api:
-
package com.leilu.mycamera.camera;
-
-
import android.graphics.SurfaceTexture;
-
import android.hardware.Camera;
-
import android.util.Log;
-
-
import java.io.IOException;
-
import java.util.List;
-
-
/**
-
* Created by ll on 2018/9/13.
-
*/
-
-
public
class CameraHelper {
-
private Camera mCamera;
-
private
int mCameraId =
0;
-
private
int mDesireWidth, mDesireHeight;
-
private
boolean mIsInit;
-
private SurfaceTexture mSurfaceTexture;
-
private
boolean mIsPortrait =
true;
-
-
public CameraHelper() {
-
-
}
-
-
public boolean init(int cameraId, int desireWidth, int desireHeight, SurfaceTexture surfaceTexture) {
-
if (mCamera ==
null) {
-
checkCameraID(cameraId);
-
mCameraId = cameraId;
-
mDesireWidth = desireWidth;
-
mDesireHeight = desireHeight;
-
mSurfaceTexture = surfaceTexture;
-
-
mCamera = Camera.open(mCameraId);
-
Camera.Parameters parameters = mCamera.getParameters();
-
Camera.Size picSize = getFitSize(desireWidth, desireHeight, parameters.getSupportedPictureSizes());
-
if (picSize !=
null) {
-
parameters.setPictureSize(picSize.width, picSize.height);
-
}
-
Log.i(
"==",
"picSize:" + picSize.width +
" " + picSize.height);
-
Camera.Size preSize = getFitSize(desireWidth, desireHeight, parameters.getSupportedPreviewSizes());
-
if (preSize !=
null) {
-
parameters.setPreviewSize(preSize.width, preSize.height);
-
}
-
Log.i(
"==",
"preSize:" + preSize.width +
" " + preSize.height);
-
mCamera.setParameters(parameters);
-
try {
-
mCamera.setPreviewTexture(surfaceTexture);
-
}
catch (IOException e) {
-
e.printStackTrace();
-
return
false;
-
}
-
}
-
mIsInit =
true;
-
return
true;
-
}
-
-
public boolean startPreview() {
-
if (!mIsInit || mCamera ==
null) {
-
return
false;
-
}
-
mCamera.startPreview();
-
return
true;
-
}
-
-
public boolean stopPreview() {
-
if (!mIsInit || mCamera ==
null) {
-
return
false;
-
}
-
mCamera.stopPreview();
-
return
true;
-
}
-
-
public boolean switchCamera(int cameraId) {
-
release();
-
if (!init(cameraId, mDesireWidth, mDesireHeight, mSurfaceTexture)) {
-
return
false;
-
}
-
startPreview();
-
return
true;
-
}
-
-
private Camera.
Size getFitSize(int desireWidth, int desireHeight,
-
List<Camera.Size> preSizeList) {
-
int reqTmpWidth;
-
int reqTmpHeight;
-
// 当屏幕为垂直的时候需要把宽高值进行调换,保证宽大于高
-
if (mIsPortrait) {
-
reqTmpWidth = desireHeight;
-
reqTmpHeight = desireWidth;
-
}
else {
-
reqTmpWidth = desireWidth;
-
reqTmpHeight = desireHeight;
-
}
-
//先查找preview中是否存在与控件相同宽高的尺寸
-
for (Camera.Size size : preSizeList) {
-
if ((size.width == reqTmpWidth) && (size.height == reqTmpHeight)) {
-
return size;
-
}
-
}
-
-
// 得到与传入的宽高比最接近的size
-
float reqRatio = ((
float) reqTmpWidth) / reqTmpHeight;
-
float curRatio, deltaRatio;
-
float deltaRatioMin = Float.MAX_VALUE;
-
Camera.Size retSize =
null;
-
for (Camera.Size size : preSizeList) {
-
curRatio = ((
float) size.width) / size.height;
-
deltaRatio = Math.abs(reqRatio - curRatio);
-
if (deltaRatio < deltaRatioMin) {
-
deltaRatioMin = deltaRatio;
-
retSize = size;
-
}
-
}
-
-
return retSize;
-
}
-
-
private void checkCameraID(int cameraId) {
-
int count = Camera.getNumberOfCameras();
-
if (cameraId <
0 || cameraId >= count) {
-
throw
new IllegalArgumentException(
"Nonsupport cameraID !");
-
}
-
}
-
-
-
public void release() {
-
stopPreview();
-
if (mCamera !=
null) {
-
mCamera.release();
-
mCamera =
null;
-
}
-
mIsInit =
false;
-
}
-
}
2、 创建一个CameraRender类来实现Opengl ES的渲染代码:
-
package com.leilu.mycamera.camera;
-
-
import android.content.Context;
-
import android.graphics.SurfaceTexture;
-
import android.opengl.GLES11Ext;
-
import android.opengl.GLES20;
-
import android.opengl.Matrix;
-
-
import com.leilu.floatwindow.R;
-
import com.leilu.floatwindow.ShaderUtil;
-
-
import java.nio.ByteBuffer;
-
import java.nio.ByteOrder;
-
import java.nio.FloatBuffer;
-
-
-
/**
-
* Created by ll on 2018/9/13.
-
*/
-
-
public
class CameraRender {
-
-
private
static
final
float[] VERTEX_POSITION = {
-
-
1, -
1,
-
1, -
1,
-
-
1,
1,
-
1,
1
-
};
-
private
static
final
float[] FRAGMENT_POSITION = {
-
0,
1,
-
1,
1,
-
0,
0,
-
1,
0
-
};
-
-
-
private SurfaceTexture mSurfaceTexture;
-
-
private
int mProgram;
-
private
int mExtTextureHandle;
-
private
int mTexturePositionHandle;
-
private
int mVertexPositionHandle;
-
private
int mProjectionMatrixHandle;
-
-
private
int[] mVertexBufferIds;
-
private
int[] mFragmentBufferIds;
-
private
int[] mExtTextureIds;
-
private
float[] mProjectionMatrix =
new
float[
16];
-
-
private Context mContext;
-
-
public CameraRender(Context context) {
-
mContext = context.getApplicationContext();
-
Matrix.setIdentityM(mProjectionMatrix,
0);
-
}
-
-
public void init(SurfaceTexture.OnFrameAvailableListener onFrameAvailable) {
-
if (mSurfaceTexture ==
null) {
-
mProgram = ShaderUtil.createProgram(ShaderUtil.getRawResource(mContext, R.raw.camera_vertex_shader),
-
ShaderUtil.getRawResource(mContext, R.raw.camera_fragment_shader));
-
if (mProgram == -
1) {
-
return;
-
}
-
GLES20.glUseProgram(mProgram);
-
// 获取纹理句柄
-
mExtTextureHandle = GLES20.glGetAttribLocation(mProgram,
"sTexture");
-
mTexturePositionHandle = GLES20.glGetAttribLocation(mProgram,
"texturePosition");
-
mVertexPositionHandle = GLES20.glGetAttribLocation(mProgram,
"vertexPosition");
-
mProjectionMatrixHandle = GLES20.glGetUniformLocation(mProgram,
"projectionMatrix");
-
-
// 创建vbo
-
mVertexBufferIds = createVBO(VERTEX_POSITION);
-
mFragmentBufferIds = createVBO(FRAGMENT_POSITION);
-
-
// 创建纹理
-
mExtTextureIds = createTexture();
-
mSurfaceTexture =
new SurfaceTexture(mExtTextureIds[
0]);
-
mSurfaceTexture.setOnFrameAvailableListener(onFrameAvailable);
-
}
-
}
-
-
-
private
int[] createVBO(
float[] datas) {
-
FloatBuffer floatBuffer = ByteBuffer.allocateDirect(datas.length *
4).order(ByteOrder.nativeOrder()).asFloatBuffer();
-
floatBuffer.put(datas);
-
floatBuffer.position(
0);
-
-
int[] vbo =
new
int[
1];
-
GLES20.glGenBuffers(vbo.length, vbo,
0);
-
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vbo[
0]);
-
GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, datas.length *
4,
null, GLES20.GL_STATIC_DRAW);
-
GLES20.glBufferSubData(GLES20.GL_ARRAY_BUFFER,
0, datas.length *
4, floatBuffer);
-
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER,
0);
-
-
floatBuffer.clear();
-
return vbo;
-
}
-
-
private
int[] createTexture() {
-
int[] textures =
new
int[
1];
-
GLES20.glGenTextures(textures.length, textures,
0);
-
GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, textures[
0]);
-
GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
-
GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
-
GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
-
GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
-
GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
0);
-
return textures;
-
}
-
-
public void setViewPort(int width, int height) {
-
GLES20.glViewport(
0,
0, width, height);
-
}
-
-
public void setProjectionMatrix(boolean isBack) {
-
Matrix.setIdentityM(mProjectionMatrix,
0);
-
if (isBack) {
-
Matrix.setRotateM(mProjectionMatrix,
0,
270,
0,
0,
1);
-
}
else {
-
Matrix.setRotateM(mProjectionMatrix,
0,
90,
0,
0,
1);
-
}
-
}
-
-
public void draw() {
-
if (mProgram == -
1) {
-
return;
-
}
-
-
mSurfaceTexture.updateTexImage();
-
-
// 矩阵
-
GLES20.glUniformMatrix4fv(mProjectionMatrixHandle,
1,
false, mProjectionMatrix,
0);
-
-
// 绑定顶点坐标
-
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mVertexBufferIds[
0]);
-
GLES20.glEnableVertexAttribArray(mVertexPositionHandle);
-
GLES20.glVertexAttribPointer(mVertexPositionHandle,
2, GLES20.GL_FLOAT,
false,
0,
0);
-
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER,
0);
-
-
// 绑定片元坐标
-
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mFragmentBufferIds[
0]);
-
GLES20.glEnableVertexAttribArray(mTexturePositionHandle);
-
GLES20.glVertexAttribPointer(mTexturePositionHandle,
2, GLES20.GL_FLOAT,
false,
0,
0);
-
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER,
0);
-
-
// 绑定纹理
-
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
-
GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, mExtTextureIds[
0]);
-
GLES20.glUniform1f(mExtTextureHandle,
0);
-
-
// 绘制
-
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP,
0,
4);
-
-
GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
0);
-
GLES20.glDisableVertexAttribArray(mVertexPositionHandle);
-
GLES20.glDisableVertexAttribArray(mTexturePositionHandle);
-
}
-
-
public SurfaceTexture getSurfaceTexture() {
-
return mSurfaceTexture;
-
}
-
-
public void release() {
-
if (mExtTextureIds !=
null && mExtTextureIds[
0] !=
0) {
-
GLES20.glDeleteTextures(
1, mExtTextureIds,
0);
-
}
-
if (mFragmentBufferIds !=
null && mFragmentBufferIds[
0] !=
0) {
-
GLES20.glDeleteBuffers(
1, mFragmentBufferIds,
0);
-
}
-
if (mVertexBufferIds !=
null && mVertexBufferIds[
0] !=
0) {
-
GLES20.glDeleteBuffers(
1, mVertexBufferIds,
0);
-
}
-
GLES20.glDeleteProgram(mProgram);
-
}
-
-
}
3、创建一个CameraView类,该类直接继承了自定义的GLSurfaceView:
-
package com.leilu.mycamera.camera;
-
-
import android.content.Context;
-
import android.graphics.SurfaceTexture;
-
import android.util.AttributeSet;
-
-
import com.leilu.mycamera.EGLThread;
-
import com.leilu.mycamera.MyGLSurfaceView;
-
-
import
static android.opengl.GLES10.glClear;
-
import
static android.opengl.GLES20.GL_COLOR_BUFFER_BIT;
-
import
static android.opengl.GLES20.glClearColor;
-
import
static com.leilu.mycamera.EGLThread.RENDERMODE_WHEN_DIRTY;
-
-
/**
-
* Created by ll on 2018/9/13.
-
*/
-
-
public
class CameraView extends MyGLSurfaceView implements EGLThread.OnEGLThreadListener, SurfaceTexture.OnFrameAvailableListener {
-
-
private
static
final
int FRONT_CAMERA_ID =
1;
-
private
static
final
int BACK_CAMERA_ID =
0;
-
-
private CameraHelper mCameraHelper;
-
private CameraRender mCameraRender;
-
private
int mCameraId = BACK_CAMERA_ID;
-
-
public CameraView(Context context) {
-
this(context,
null);
-
}
-
-
public CameraView(Context context, AttributeSet attrs) {
-
this(context, attrs,
0);
-
}
-
-
public CameraView(Context context, AttributeSet attrs, int defStyleAttr) {
-
super(context, attrs, defStyleAttr);
-
setRenderMode(RENDERMODE_WHEN_DIRTY);
-
setRenderListener(
this);
-
mCameraHelper =
new CameraHelper();
-
mCameraRender =
new CameraRender(context);
-
}
-
-
@Override
-
public void onCreate() {
-
mCameraRender.init(
this);
-
}
-
-
@Override
-
public void onFrameAvailable(SurfaceTexture surfaceTexture) {
-
requestRender();
-
}
-
-
@Override
-
public void onSurfaceChanged(Object surface, int width, int height) {
-
mCameraRender.setViewPort(width, height);
-
SurfaceTexture surfaceTexture = mCameraRender.getSurfaceTexture();
-
if (surfaceTexture !=
null) {
-
mCameraHelper.init(mCameraId, width, height, surfaceTexture);
-
open();
-
}
-
}
-
-
@Override
-
public void onDrawFrame() {
-
glClear(GL_COLOR_BUFFER_BIT);
-
glClearColor(
0,
0,
0,
1);
-
mCameraRender.draw();
-
}
-
-
public void open() {
-
mCameraHelper.startPreview();
-
mCameraRender.setProjectionMatrix(mCameraId ==
0);
-
}
-
-
public void close() {
-
mCameraHelper.stopPreview();
-
}
-
-
@Override
-
protected void onDetachedFromWindow() {
-
mCameraHelper.release();
-
mCameraHelper =
null;
-
mCameraRender.release();
-
mCameraRender =
null;
-
super.onDetachedFromWindow();
-
}
-
-
public void swithCamera() {
-
if (mCameraId == BACK_CAMERA_ID) {
-
mCameraId = FRONT_CAMERA_ID;
-
}
else {
-
mCameraId = BACK_CAMERA_ID;
-
}
-
if (mCameraHelper.switchCamera(mCameraId)) {
-
mCameraRender.setProjectionMatrix(mCameraId ==
0);
-
}
-
}
-
}
上面三个类创建完成以后使用就非常简单了,直接在布局里面嵌入CameraView这个控件即可:
-
<?xml version=
"1.0" encoding=
"utf-8"?>
-
<LinearLayout xmlns:android=
"http://schemas.android.com/apk/res/android"
-
android:layout_width=
"match_parent"
-
android:layout_height=
"match_parent"
-
android:orientation=
"vertical">
-
-
<com.leilu.mycamera.camera.CameraView
-
android:id=
"@+id/surface_view"
-
android:layout_width=
"match_parent"
-
android:layout_height=
"0dp"
-
android:layout_weight=
"1" />
-
-
<Button
-
android:layout_width=
"match_parent"
-
android:layout_height=
"wrap_content"
-
android:onClick=
"open"
-
android:text=
"打开" />
-
-
<Button
-
android:layout_width=
"match_parent"
-
android:layout_height=
"wrap_content"
-
android:onClick=
"switchCamera"
-
android:text=
"切换" />
-
-
-
<Button
-
android:layout_width=
"match_parent"
-
android:layout_height=
"wrap_content"
-
android:onClick=
"close"
-
android:text=
"关闭" />
-
-
</LinearLayout>
-
package com.leilu.mycamera;
-
-
import android.app.Activity;
-
import android.os.Bundle;
-
import android.support.annotation.Nullable;
-
import android.view.View;
-
-
import com.leilu.floatwindow.R;
-
import com.leilu.mycamera.camera.CameraView;
-
-
-
/**
-
* Created by ll on 2018/9/13.
-
*/
-
-
public
class CameraActivity extends Activity {
-
-
private CameraView mCameraView;
-
-
@Override
-
protected void onCreate(@Nullable Bundle savedInstanceState) {
-
super.onCreate(savedInstanceState);
-
setContentView(R.layout.activity_camera);
-
mCameraView = findViewById(R.id.surface_view);
-
}
-
-
public void open(View view) {
-
mCameraView.open();
-
}
-
-
public void close(View view) {
-
mCameraView.close();
-
}
-
-
public void switchCamera(View view) {
-
mCameraView.swithCamera();
-
}
-
}
此时就可以预览摄像头了。
预留的问题:
1、切换摄像头的时候画面会旋转一下,这个问题下次解决
2、在创建自定义GLSurfaceView的时候说到可以实现纹理共享,在这里还没有用到,下篇文章中就使用共享纹理