package com.research.glrecoder; import android.opengl.GLES20; import android.opengl.GLSurfaceView; import android.opengl.Matrix; import android.util.Log; import java.io.File; import java.io.IOException; import javax.microedition.khronos.egl.EGL10; import javax.microedition.khronos.egl.EGL11; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.egl.EGLDisplay; /** * Created by weiersyuan on 2016/11/17. */ public class GLRecoder { private static Object obj = new Object(); private static final int EGL_RECORDABLE_ANDROID = 0x3142; private static float[] mIdentityMatrix; private static final String TAG = "GLRecoder"; private static ScreenRectangle mScreenRectangle; private static int mWidth; private static int mHeight; private static WindowSurface mWindowSurface; private static EGLConfig mEglConfig; private static VideoThread mVideoThread; private static boolean isRecord; private static int mTick; // A simple EGL config chooser for get recordable config. private static GLSurfaceView.EGLConfigChooser mDefaultConfigChooser = new GLSurfaceView.EGLConfigChooser() { @Override public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) { int renderableType = 4; // The actual surface is generally RGBA or RGBX, so situationally omitting alpha // doesn't really help. It can also lead to a huge performance hit on glReadPixels() // when reading into a GL_RGBA buffer. int[] attribList = { EGL11.EGL_RED_SIZE, 8, EGL11.EGL_GREEN_SIZE, 8, EGL11.EGL_BLUE_SIZE, 8, EGL11.EGL_ALPHA_SIZE, 8, //EGL11.EGL_DEPTH_SIZE, 16, //EGL11.EGL_STENCIL_SIZE, 8, EGL11.EGL_RENDERABLE_TYPE, renderableType, EGL_RECORDABLE_ANDROID, 1, // set recordable [@-3] EGL11.EGL_NONE }; EGLConfig[] configs = new EGLConfig[1]; int[] numConfigs = new int[1]; if (!egl.eglChooseConfig(display, attribList, configs, configs.length, numConfigs)) { return null; } return configs[0]; } }; public static GLSurfaceView.EGLConfigChooser getEGLConfigChooser() { return mDefaultConfigChooser; } public static void init(int width, int height, EGLConfig eglConfig) { mWidth = width; mHeight = height; mEglConfig = eglConfig; mIdentityMatrix = new float[16]; Matrix.setIdentityM(mIdentityMatrix, 0); mScreenRectangle = new ScreenRectangle(width, height); } public static void startEncoder (File file) throws IOException { int BIT_RATE = 4000000; mVideoThread = new VideoThread("video thread"); mVideoThread.start(); mVideoThread.init(mWidth, mHeight,BIT_RATE, file); mWindowSurface = new WindowSurface(mEglConfig, mVideoThread.getInputSurface()); isRecord = true; } public static void stopEncoder() { mVideoThread.stopRecording(); mVideoThread = null; mWindowSurface.releaseEncoderSurface(); } public static void beginDraw() { if (mTick % 2 == 0) return; // bind the framebuffer to off-screen GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, mScreenRectangle.getmFrameBufferID()); GLUtil.checkGLError("beginDraw glBindFramebuffer"); if (GLES20.glCheckFramebufferStatus(GLES20.GL_FRAMEBUFFER) != GLES20.GL_FRAMEBUFFER_COMPLETE) { Log.e(TAG, "glCheckFramebufferStatus error"); } } public static void endDraw() { Log.i(TAG, "enter endDraw"); // bind the framebuffer to the screen, also the default display GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0); GLUtil.checkGLError("endDraw glBindFramebuffer"); GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT); // draw to the default surface mScreenRectangle.draw(mIdentityMatrix); // draw to encoder if (isRecord) { // record half if (mTick % 2 == 0) { ++mTick; return; } ++mTick; mVideoThread.frameAvailableSoon(); mWindowSurface.makeEncoderSurfaceCurrent(); GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); mScreenRectangle.draw(mIdentityMatrix); mWindowSurface.swapEncoderSurfaceBuffer(); mWindowSurface.makeWindowSurfaceCurrent(); } } }