GLES11Ext.GL_TEXTURE_EXTERNAL_OES的用處是什么?
上一張實現(xiàn)相機預(yù)覽的每一幀的輸出格式是YUV的(YUV)续徽,那么這個擴展紋理的作用就是實現(xiàn)YUV格式到RGB的自動轉(zhuǎn)化饭弓,我們就不需要再為此寫YUV轉(zhuǎn)RGB的代碼了
1. 創(chuàng)建xml和Render
//xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.jdf.camera.ui.CameraV2GLSurfaceView
android:id="@+id/glsurfaceView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
//Activity
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_camera2_oes);
//獲取SurfaceView
glSurfaceView = findViewById(R.id.glsurfaceView);
//封裝相機操作
mCameraLoader = new Camera2OESLoader(this);
//主要是實例化Render诲侮,并綁定
glSurfaceView.init(mCameraLoader, false, Camera2OESActivity.this);
findViewById(R.id.saveImage).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ImageUtils.saveBitmap = true;
}
});
}
//GlSurfaceView
public class CameraV2GLSurfaceView extends GLSurfaceView {
public static final String TAG = "Filter_CameraV2GLSurfaceView";
private JImageRender mCameraV2Renderer;
public void init(Camera2OESLoader camera, boolean isPreviewStarted, Context context) {
setEGLContextClientVersion(2);
mCameraV2Renderer = new JImageRender(new JImageFilter(context));
mCameraV2Renderer.init(this, camera, isPreviewStarted, context);
setRenderer(mCameraV2Renderer);
}
public CameraV2GLSurfaceView(Context context, AttributeSet attributes) {
super(context, attributes);
}
}
2 開啟相機
在Activity的onResume中乾戏,判斷glSurfaceView加載完畢后模暗,獲取相機相機傳感器方向岸晦,并且根據(jù)GlSurfaceView大小獲取合適的預(yù)覽大小
//Activity
@Override
protected void onResume() {
super.onResume();
boolean laidOut = ViewCompat.isLaidOut(glSurfaceView);
boolean b = !glSurfaceView.isLayoutRequested();
JLog.d(TAG, "onResume.....laidOut[%b],[%b]",laidOut,b);
if (laidOut && b) {
mCameraLoader.onResume(glSurfaceView.getWidth(), glSurfaceView.getHeight());
} else {
glSurfaceView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
@Override
public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop,
int oldRight, int oldBottom) {
JLog.d(TAG, "onResume.....onLayoutChange");
glSurfaceView.removeOnLayoutChangeListener(this);
mCameraLoader.onResume(glSurfaceView.getWidth(), glSurfaceView.getHeight());
}
});
}
}
Camera2OESLoader.java
public void onResume(int width, int height) {
JLog.d(TAG, "onResume[%d,%d]...", width, height);
mViewWidth = width;
mViewHeight = height;
setUpCamera();
}
protected void setUpCamera() {
try {
//獲取屏幕方向欧啤,相機傳感器方向,和預(yù)覽大小
setUpCameraOutputs();
if (ActivityCompat.checkSelfPermission(mActivity, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
JLog.e(TAG, "Dont have CAMERA permission");
return;
}
mCameraManager.openCamera(mCameraId, mCameraDeviceCallback, mCameraHandler);
} catch (CameraAccessException e) {
JLog.e(TAG, "Opening camera (ID: " + mCameraId + ") failed.");
e.printStackTrace();
}
}
mCameraHandler是根據(jù)Camera2OESLoader中HandlerThread創(chuàng)建的启上,因為后續(xù)我們需要再Render線程中啟動預(yù)覽邢隧,為了保證相機打開和啟動預(yù)覽在同一個線程,需要指定同一個Handler
相機啟動成功后碧绞,我們?nèi)ender中創(chuàng)建OES紋理id府框,SufaceTexture和啟動預(yù)覽
3 啟動預(yù)覽
- JImageRender.onSurfaceCreated
創(chuàng)建OES 紋理id
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
JLog.i(TAG, "onSurfaceCreated......");
mOESTextureId = Utils.createOESTextureObject();
mFilterEngine.ifNeedInit();
}
- JImageRender.onDrawFrame
然后在onDrawFrame第一次調(diào)用時,完成SurfaceTexture初始化和預(yù)覽啟動
@Override
public void onDrawFrame(GL10 gl) {
Long t1 = System.currentTimeMillis();
if (mSurfaceTexture != null) {
mSurfaceTexture.updateTexImage();
mSurfaceTexture.getTransformMatrix(transformMatrix);
}
if (!bIsPreviewStarted) {
bIsPreviewStarted = initSurfaceTexture();
bIsPreviewStarted = true;
return;
}
glClearColor(1.0f, 0.0f, 0.0f, 0.0f);
mFilterEngine.onDraw(transformMatrix,mOESTextureId,glCubeBuffer,glTextureBuffer);
long t2 = System.currentTimeMillis();
long t = t2 - t1;
Log.i(TAG, "onDrawFrame: time: " + t);
}
- JImageRender. initSurfaceTexture
public boolean initSurfaceTexture() {
mSurfaceTexture = new SurfaceTexture(mOESTextureId);
mCameraLoaer.setPreviewTexture(mSurfaceTexture);
mCameraLoaer.startPreview();
return true;
}
- Camera2OESLoader.startPreview
public void startPreview() {
mSurfaceTexture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());
final Surface surface = new Surface(mSurfaceTexture);
try {
mCameraDevice.createCaptureSession(Arrays.asList( surface), new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(@NonNull CameraCaptureSession session) {
try {
CaptureRequest.Builder builder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
builder.addTarget(surface);
mCaptureRequest = builder.build();
mCameraCaptureSession = session;
mCameraCaptureSession.setRepeatingRequest(mCaptureRequest, null, mCameraHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
@Override
public void onConfigureFailed(@NonNull CameraCaptureSession session) {
}
}, mCameraHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
4 繪制渲染
預(yù)覽啟動后成功后讥邻,會更新SurfaceTexture迫靖,并更新紋理數(shù)據(jù)對應(yīng)的紋理舉證,然后將該SurfaceTexture對應(yīng)的OES紋理ID傳遞到OPENGL進(jìn)行繪制
@Override
public void onDrawFrame(GL10 gl) {
Long t1 = System.currentTimeMillis();
if (mSurfaceTexture != null) {
//更新SurfaceTexture紋理內(nèi)容
mSurfaceTexture.updateTexImage();
mSurfaceTexture.getTransformMatrix(transformMatrix);
}
if (!bIsPreviewStarted) {
bIsPreviewStarted = initSurfaceTexture();
bIsPreviewStarted = true;
return;
}
glClearColor(1.0f, 0.0f, 0.0f, 0.0f);
//使用opengl繪制mOESTextureId對應(yīng)的紋理內(nèi)容
mFilterEngine.onDraw(transformMatrix, mOESTextureId,glCubeBuffer,glTextureBuffer)
}
注意:
updateTexImage()方法只能在包OpenGLES環(huán)境的線程里調(diào)用兴使,即Renderer接口所獨立創(chuàng)建的線程當(dāng)中系宜。一般在onDrawFrame中調(diào)用updateTexImage()將數(shù)據(jù)綁定給OpenGLES對應(yīng)的紋理對象
JimageFilter.onDraw
public void onDraw(float[] transformMatrix, int textureId, FloatBuffer cubeBuffer, FloatBuffer textureBuffer) {
GLES30.glUseProgram(glProgId);
....
if (textureId != OpenGlUtils.NO_TEXTURE) {
...
glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, textureId);
...
glUniformMatrix4fv(uTextureMatrixLocation, 1, false, transformMatrix, 0);
}
...
GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, 0);
}
onDraw過程與上一節(jié)的相機預(yù)覽流程一致,主要差別有兩點
- 綁定和解綁紋理id的類型為GL_TEXTURE_EXTERNAL_OES
- 需要額外將從SurfaceTexture中產(chǎn)生的變換矩陣uTextureMatrixLocation傳遞到著色器中
5 定義OES著色器
因為紋理id使用的是OES格式发魄,著色器中紋理類型也要對應(yīng)修改盹牧,相對于上以上相機預(yù)覽實現(xiàn),作色器主要有兩個變化
頂點著色器中励幼,需要傳遞SurfaceTexture產(chǎn)生的變換矩陣對象uTextureMatrix汰寓,并根據(jù)矩陣值調(diào)整紋理坐標(biāo)位置
attribute vec4 position;
uniform mat4 uTextureMatrix;
attribute vec4 inputTextureCoordinate;
varying vec2 textureCoordinate;
void main()
{
textureCoordinate = (uTextureMatrix * inputTextureCoordinate).xy;
gl_Position = position;
}`
片元著色器中,需要聲明紋理的類型為samplerExternalOES 苹粟,而不是sampler2D; 需要在文件頭聲明使用了samplerExternalOES有滑,否則加載著色器會報錯
#extension GL_OES_EGL_image_external : require
precision mediump float;
uniform samplerExternalOES inputImageTexture;
varying vec2 textureCoordinate;
void main()
{
gl_FragColor = texture2D(inputImageTexture, textureCoordinate);
}
6 顯示效果
7 流程總結(jié)
主要流程為:
定義GlSurfaceView和定義OEG紋理類型的著色器
將GlSurfaceView和Render,設(shè)置渲染模式
在GlSurfaceView加載完畢后,啟動相機
在Render的onSurfaceCreate中嵌削,完成OES紋理id創(chuàng)建和初始化Opengl對象
在相機開啟成功的時候毛好,在onDrawFrame中完成預(yù)覽啟動望艺;之所以在onDrawFrame啟動,是因為其他階段肌访,相機不一定啟動成功
預(yù)覽啟動成功后找默,會在Render的每一幀調(diào)用時,更新SurfaceTexture的內(nèi)容吼驶,并更新對應(yīng)的OES紋理id惩激;將紋理id傳遞到opengl對象中進(jìn)行渲染
渲染成功后,預(yù)覽數(shù)據(jù)會顯示到屏幕上
8 代碼位置
代碼具體實現(xiàn)參考 QCCamera中的JCamera2OESFboActivity