GLSurfaceView继承自SurfaceView,在类里面维护着一个GLThread的子线程,用于保持GL的上下文GLConext一致性的操作。
承接上篇博客:OpenGL Texture C++ 预览Camera视频-CSDN博客
代码地址:GitHub - wangyongyao1989/WyFFmpeg: 音视频相关基础实现
效果显示:
自定义GLSurfaceView
一、GLSurfaceView源码分析:
首先从setRenderer()方法着手分析,在这个方法中创建了EGLConfigChooser、EGLContextFactory、EGLWindowSurfaceFactory、GLThread。其中的EGLConfigChooser、EGLContextFactory、EGLWindowSurfaceFactory三个类在google开源项目grafika中EglCore.java/EglSurfaceBase.java/WindowSurface.java做了类似封装。
java
public void setRenderer(Renderer renderer) {
checkRenderThreadState();
if (mEGLConfigChooser == null) {
mEGLConfigChooser = new SimpleEGLConfigChooser(true);
}
if (mEGLContextFactory == null) {
mEGLContextFactory = new DefaultContextFactory();
}
if (mEGLWindowSurfaceFactory == null) {
mEGLWindowSurfaceFactory = new DefaultWindowSurfaceFactory();
}
mRenderer = renderer;
mGLThread = new GLThread(mThisWeakRef);
mGLThread.start();
}
1、EGLConfigChooser:
选择一个具有指定r、g、b、a尺寸以及至少指定深度和模具尺寸的配置。
java
/**
* Choose a configuration with exactly the specified r,g,b,a sizes,
* and at least the specified depth and stencil sizes.
*/
private class ComponentSizeChooser extends BaseConfigChooser {
public ComponentSizeChooser(int redSize, int greenSize, int blueSize,
int alphaSize, int depthSize, int stencilSize) {
super(new int[] {
EGL10.EGL_RED_SIZE, redSize,
EGL10.EGL_GREEN_SIZE, greenSize,
EGL10.EGL_BLUE_SIZE, blueSize,
EGL10.EGL_ALPHA_SIZE, alphaSize,
EGL10.EGL_DEPTH_SIZE, depthSize,
EGL10.EGL_STENCIL_SIZE, stencilSize,
EGL10.EGL_NONE});
mValue = new int[1];
mRedSize = redSize;
mGreenSize = greenSize;
mBlueSize = blueSize;
mAlphaSize = alphaSize;
mDepthSize = depthSize;
mStencilSize = stencilSize;
}
@Override
public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display,
EGLConfig[] configs) {
for (EGLConfig config : configs) {
int d = findConfigAttrib(egl, display, config,
EGL10.EGL_DEPTH_SIZE, 0);
int s = findConfigAttrib(egl, display, config,
EGL10.EGL_STENCIL_SIZE, 0);
if ((d >= mDepthSize) && (s >= mStencilSize)) {
int r = findConfigAttrib(egl, display, config,
EGL10.EGL_RED_SIZE, 0);
int g = findConfigAttrib(egl, display, config,
EGL10.EGL_GREEN_SIZE, 0);
int b = findConfigAttrib(egl, display, config,
EGL10.EGL_BLUE_SIZE, 0);
int a = findConfigAttrib(egl, display, config,
EGL10.EGL_ALPHA_SIZE, 0);
if ((r == mRedSize) && (g == mGreenSize)
&& (b == mBlueSize) && (a == mAlphaSize)) {
return config;
}
}
}
return null;
}
private int findConfigAttrib(EGL10 egl, EGLDisplay display,
EGLConfig config, int attribute, int defaultValue) {
if (egl.eglGetConfigAttrib(display, config, attribute, mValue)) {
return mValue[0];
}
return defaultValue;
}
private int[] mValue;
// Subclasses can adjust these values:
protected int mRedSize;
protected int mGreenSize;
protected int mBlueSize;
protected int mAlphaSize;
protected int mDepthSize;
protected int mStencilSize;
}
2、EGLContextFactory:
创建GLConext的环境,这里的GLConext就是OpenGL运行所需的上下文。
java
private class DefaultContextFactory implements EGLContextFactory {
private int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
public EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig config) {
int[] attrib_list = {EGL_CONTEXT_CLIENT_VERSION, mEGLContextClientVersion,
EGL10.EGL_NONE };
return egl.eglCreateContext(display, config, EGL10.EGL_NO_CONTEXT,
mEGLContextClientVersion != 0 ? attrib_list : null);
}
public void destroyContext(EGL10 egl, EGLDisplay display,
EGLContext context) {
if (!egl.eglDestroyContext(display, context)) {
Log.e("DefaultContextFactory", "display:" + display + " context: " + context);
if (LOG_THREADS) {
Log.i("DefaultContextFactory", "tid=" + Thread.currentThread().getId());
}
EglHelper.throwEglException("eglDestroyContex", egl.eglGetError());
}
}
}
3、EGLWindowSurfaceFactory:
在surface中EGLDisplay与显示NativeWindow创建相关联的联系。
java
private static class DefaultWindowSurfaceFactory implements EGLWindowSurfaceFactory {
public EGLSurface createWindowSurface(EGL10 egl, EGLDisplay display,
EGLConfig config, Object nativeWindow) {
EGLSurface result = null;
try {
result = egl.eglCreateWindowSurface(display, config, nativeWindow, null);
} catch (IllegalArgumentException e) {
// This exception indicates that the surface flinger surface
// is not valid. This can happen if the surface flinger surface has
// been torn down, but the application has not yet been
// notified via SurfaceHolder.Callback.surfaceDestroyed.
// In theory the application should be notified first,
// but in practice sometimes it is not. See b/4588890
Log.e(TAG, "eglCreateWindowSurface", e);
}
return result;
}
public void destroySurface(EGL10 egl, EGLDisplay display,
EGLSurface surface) {
egl.eglDestroySurface(display, surface);
}
}
4、GLThread:
GLThread其核心就是维护一个子线程,在子线程中运行着与OpenGL相关的操作,其中GLConext一定要在这个线程中保持唯一。在线程采用弱应用的方式来维护着GLSurfaceView的持有。
java
static class GLThread extends Thread {
GLThread(WeakReference<GLSurfaceView> glSurfaceViewWeakRef) {
super();
mWidth = 0;
mHeight = 0;
mRequestRender = true;
mRenderMode = RENDERMODE_CONTINUOUSLY;
mWantRenderNotification = false;
mGLSurfaceViewWeakRef = glSurfaceViewWeakRef;
}
@Override
public void run() {
setName("GLThread " + getId());
if (LOG_THREADS) {
Log.i("GLThread", "starting tid=" + getId());
}
try {
guardedRun();
} catch (InterruptedException e) {
// fall thru and exit normally
} finally {
sGLThreadManager.threadExiting(this);
}
}
//省略代码。。。。
}
- EglHelper:获取EGLConetxt、EglDisplay、EGLWindowSurfaceFactory的实例,并对三者进行创建------销毁------交换等操作,具体方法如createSurfae()/createGL()/destroySurface()。
java
private static class EglHelper {
public EglHelper(WeakReference<GLSurfaceView> glSurfaceViewWeakRef) {
mGLSurfaceViewWeakRef = glSurfaceViewWeakRef;
}
public void start() {
if (LOG_EGL) {
Log.w("EglHelper", "start() tid=" + Thread.currentThread().getId());
}
mEgl = (EGL10) EGLContext.getEGL();
mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
if (mEglDisplay == EGL10.EGL_NO_DISPLAY) {
throw new RuntimeException("eglGetDisplay failed");
}
int[] version = new int[2];
if(!mEgl.eglInitialize(mEglDisplay, version)) {
throw new RuntimeException("eglInitialize failed");
}
GLSurfaceView view = mGLSurfaceViewWeakRef.get();
if (view == null) {
mEglConfig = null;
mEglContext = null;
} else {
mEglConfig = view.mEGLConfigChooser.chooseConfig(mEgl, mEglDisplay);
mEglContext = view.mEGLContextFactory.createContext(mEgl, mEglDisplay, mEglConfig);
}
if (mEglContext == null || mEglContext == EGL10.EGL_NO_CONTEXT) {
mEglContext = null;
throwEglException("createContext");
}
if (LOG_EGL) {
Log.w("EglHelper", "createContext " + mEglContext + " tid=" + Thread.currentThread().getId());
}
mEglSurface = null;
}
public boolean createSurface() {
if (LOG_EGL) {
Log.w("EglHelper", "createSurface() tid=" + Thread.currentThread().getId());
}
if (mEgl == null) {
throw new RuntimeException("egl not initialized");
}
if (mEglDisplay == null) {
throw new RuntimeException("eglDisplay not initialized");
}
if (mEglConfig == null) {
throw new RuntimeException("mEglConfig not initialized");
}
destroySurfaceImp();
GLSurfaceView view = mGLSurfaceViewWeakRef.get();
if (view != null) {
mEglSurface = view.mEGLWindowSurfaceFactory.createWindowSurface(mEgl,
mEglDisplay, mEglConfig, view.getHolder());
} else {
mEglSurface = null;
}
if (mEglSurface == null || mEglSurface == EGL10.EGL_NO_SURFACE) {
int error = mEgl.eglGetError();
if (error == EGL10.EGL_BAD_NATIVE_WINDOW) {
Log.e("EglHelper", "createWindowSurface returned EGL_BAD_NATIVE_WINDOW.");
}
return false;
}
if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
logEglErrorAsWarning("EGLHelper", "eglMakeCurrent", mEgl.eglGetError());
return false;
}
return true;
}
GL createGL() {
GL gl = mEglContext.getGL();
GLSurfaceView view = mGLSurfaceViewWeakRef.get();
if (view != null) {
if (view.mGLWrapper != null) {
gl = view.mGLWrapper.wrap(gl);
}
if ((view.mDebugFlags & (DEBUG_CHECK_GL_ERROR | DEBUG_LOG_GL_CALLS)) != 0) {
int configFlags = 0;
Writer log = null;
if ((view.mDebugFlags & DEBUG_CHECK_GL_ERROR) != 0) {
configFlags |= GLDebugHelper.CONFIG_CHECK_GL_ERROR;
}
if ((view.mDebugFlags & DEBUG_LOG_GL_CALLS) != 0) {
log = new LogWriter();
}
gl = GLDebugHelper.wrap(gl, configFlags, log);
}
}
return gl;
}
public int swap() {
if (! mEgl.eglSwapBuffers(mEglDisplay, mEglSurface)) {
return mEgl.eglGetError();
}
return EGL10.EGL_SUCCESS;
}
public void destroySurface() {
if (LOG_EGL) {
Log.w("EglHelper", "destroySurface() tid=" + Thread.currentThread().getId());
}
destroySurfaceImp();
}
private void destroySurfaceImp() {
if (mEglSurface != null && mEglSurface != EGL10.EGL_NO_SURFACE) {
mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE,
EGL10.EGL_NO_SURFACE,
EGL10.EGL_NO_CONTEXT);
GLSurfaceView view = mGLSurfaceViewWeakRef.get();
if (view != null) {
view.mEGLWindowSurfaceFactory.destroySurface(mEgl, mEglDisplay, mEglSurface);
}
mEglSurface = null;
}
}
public void finish() {
if (LOG_EGL) {
Log.w("EglHelper", "finish() tid=" + Thread.currentThread().getId());
}
if (mEglContext != null) {
GLSurfaceView view = mGLSurfaceViewWeakRef.get();
if (view != null) {
view.mEGLContextFactory.destroyContext(mEgl, mEglDisplay, mEglContext);
}
mEglContext = null;
}
if (mEglDisplay != null) {
mEgl.eglTerminate(mEglDisplay);
mEglDisplay = null;
}
}
//省略部分代码。。。。
private WeakReference<GLSurfaceView> mGLSurfaceViewWeakRef;
EGL10 mEgl;
EGLDisplay mEglDisplay;
EGLSurface mEglSurface;
EGLConfig mEglConfig;
@UnsupportedAppUsage
EGLContext mEglContext;
}
- GLThread中guardRun()通过synchronized同步阻塞锁的方式,对线程生命周期内的所有状态进行管理。其中pause/pausing等状态太过于繁杂,现在我们重点在着手分析程序时的setRenderer()传入mRenderer的onSurfaceCreated()、onSurfaceChanged()、onDrawFrame()三个回调函数。
java
if(createEglContext)
{
if (LOG_RENDERER) {
Log.w("GLThread", "onSurfaceCreated");
}
GLSurfaceView view = mGLSurfaceViewWeakRef.get();
if (view != null) {
try {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "onSurfaceCreated");
//mRenderer回调的onSurfaceCreated()
view.mRenderer.onSurfaceCreated(gl, mEglHelper.mEglConfig);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}
createEglContext = false;
}
if(sizeChanged) {
if (LOG_RENDERER) {
Log.w("GLThread", "onSurfaceChanged(" + w + ", " + h + ")");
}
GLSurfaceView view = mGLSurfaceViewWeakRef.get();
if (view != null) {
try {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "onSurfaceChanged");
//mRenderer回调的 onSurfaceChanged()
view.mRenderer.onSurfaceChanged(gl, w, h);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}
sizeChanged = false;
}
if(LOG_RENDERER_DRAW_FRAME)
{
Log.w("GLThread", "onDrawFrame tid=" + getId());
}
{
GLSurfaceView view = mGLSurfaceViewWeakRef.get();
if (view != null) {
try {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "onDrawFrame");
//mRenderer回调的 onDrawFrame()
view.mRenderer.onDrawFrame(gl);
if (finishDrawingRunnable != null) {
finishDrawingRunnable.run();
finishDrawingRunnable = null;
}
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}
}
所以得出结论:onSurfaceCreated()、onSurfaceChanged()、onSurfaceCreated()、onDrawFrame()都是在GLThread线程中执行的,这样才能回调出来之后维护同一个EGLConetxt。
二、自定义WyyGLSurfaceView类似于GLSurfaceView的类:
由上GLSurfaceView源码分析可知,内部维护着一个GLThread子线程,用来保持GL的上下文GLConext一致性的操作。接下来仿造GLSurfaceView创建一个同样继承于SurfaceView的自定义类WyyGLSurfaceView。
WyyGLSurfaceView是在OpenGL Texture C++ 预览Camera视频-CSDN博客中GLTextureCPlusVideoPlayView进行改造的,有兴趣的可翻看我的这一篇的博客。
1、创建一个MyGLRendererThread子线程:
维护着onSurfaceCreated()、onSurfaceChanged()、onFrame()三个调用。
java
class MyGLRendererThread extends Thread {
private volatile RenderHandler mRenderHandler;
private WeakReference<WyyGLSurfaceView> mGLSurfaceViewWeakRef;
MyGLRendererThread(WeakReference<WyyGLSurfaceView> glSurfaceViewWeakRef) {
super();
mWidth = 0;
mHeight = 0;
mGLSurfaceViewWeakRef = glSurfaceViewWeakRef;
}
@Override
public void run() {
Looper.prepare();
mRenderHandler = new RenderHandler(this);
Looper.loop();
}
public RenderHandler getHandler() {
return mRenderHandler;
}
public void surfaceCreated() {
Log.e(TAG, "onSurfaceChanged: " + Thread.currentThread().getName());
String fragPath = OpenGLPlayFileUtils.getModelFilePath(mContext
, "texture_video_play_frament.glsl");
String vertexPath = OpenGLPlayFileUtils.getModelFilePath(mContext
, "texture_video_play_vert.glsl");
mJniCall.glSurfaceViewCreate(0, vertexPath, fragPath);
}
public void surfaceChanged(int width, int height) {
if (mJniCall != null) {
mJniCall.glSurfaceViewInit(mSurface, null, width, height);
}
}
public void doFrame(long timestamp) {
if (mJniCall != null)
mJniCall.glSurfaceViewRender();
}
public void setRecordingEnabled(boolean b) {
}
public void setRecordMethod(int arg1) {
}
public void shutdown() {
if (mJniCall != null) {
mJniCall.glTextureVideoPlayDestroy();
}
Looper.myLooper().quit();
}
}
2、在主线程中维持一个RenderHandler:
RenderHandler是对onSurfaceCreated()、onSurfaceChanged()、onFrame()从主线程到子线程的切换。
java
private static class RenderHandler extends Handler {
private static final int MSG_SURFACE_CREATED = 0;
private static final int MSG_SURFACE_CHANGED = 1;
private static final int MSG_DO_FRAME = 2;
private static final int MSG_RECORDING_ENABLED = 3;
private static final int MSG_RECORD_METHOD = 4;
private static final int MSG_SHUTDOWN = 5;
private WeakReference<MyGLRendererThread> mWeakRenderThread;
public RenderHandler(MyGLRendererThread rt) {
mWeakRenderThread = new WeakReference<MyGLRendererThread>(rt);
}
public void sendSurfaceCreated() {
sendMessage(obtainMessage(RenderHandler.MSG_SURFACE_CREATED));
}
public void sendSurfaceChanged(@SuppressWarnings("unused") int format,
int width, int height) {
// ignore format
sendMessage(obtainMessage(RenderHandler.MSG_SURFACE_CHANGED, width, height));
}
public void sendDoFrame(long frameTimeNanos) {
sendMessage(obtainMessage(RenderHandler.MSG_DO_FRAME,
(int) (frameTimeNanos >> 32), (int) frameTimeNanos));
}
public void setRecordingEnabled(boolean enabled) {
sendMessage(obtainMessage(MSG_RECORDING_ENABLED, enabled ? 1 : 0, 0));
}
public void setRecordMethod(int recordMethod) {
sendMessage(obtainMessage(MSG_RECORD_METHOD, recordMethod, 0));
}
public void sendShutdown() {
sendMessage(obtainMessage(RenderHandler.MSG_SHUTDOWN));
}
@Override // runs on RenderThread
public void handleMessage(Message msg) {
int what = msg.what;
//Log.d(TAG, "RenderHandler [" + this + "]: what=" + what);
MyGLRendererThread renderThread = mWeakRenderThread.get();
if (renderThread == null) {
Log.w(TAG, "RenderHandler.handleMessage: weak ref is null");
return;
}
switch (what) {
case MSG_SURFACE_CREATED:
renderThread.surfaceCreated();
break;
case MSG_SURFACE_CHANGED:
renderThread.surfaceChanged(msg.arg1, msg.arg2);
break;
case MSG_DO_FRAME:
long timestamp = (((long) msg.arg1) << 32) |
(((long) msg.arg2) & 0xffffffffL);
renderThread.doFrame(timestamp);
break;
case MSG_RECORDING_ENABLED:
renderThread.setRecordingEnabled(msg.arg1 != 0);
break;
case MSG_RECORD_METHOD:
renderThread.setRecordMethod(msg.arg1);
break;
case MSG_SHUTDOWN:
renderThread.shutdown();
break;
default:
throw new RuntimeException("unknown message " + what);
}
}
}
3、WyyGLSurfaceView的完整代码:
java
package com.wangyongyao.glplay.view;
import android.content.Context;
import android.graphics.Point;
import android.opengl.GLSurfaceView;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.AttributeSet;
import android.util.Log;
import android.util.Size;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import androidx.annotation.NonNull;
import com.wangyongyao.glplay.OpenGLPlayCallJni;
import com.wangyongyao.glplay.camera.Camera2Helper2;
import com.wangyongyao.glplay.camera.GLCamera2Listener;
import com.wangyongyao.glplay.render.WyyRenderer;
import com.wangyongyao.glplay.utils.OpenGLPlayFileUtils;
import java.lang.ref.WeakReference;
/**
* author : wangyongyao https://github.com/wangyongyao1989
* Create Time : 2024/9/30 20:08
* Descibe : MyyFFmpeg com.wangyongyao.glplay.view
*/
public class WyyGLSurfaceView extends SurfaceView implements SurfaceHolder.Callback, GLCamera2Listener {
private static String TAG = WyyGLSurfaceView.class.getSimpleName();
private OpenGLPlayCallJni mJniCall;
private Context mContext;
private int mWidth;
private int mHeight;
private Camera2Helper2 camera2Helper;
private SurfaceHolder mHolder;
private final WeakReference<WyyGLSurfaceView> mThisWeakRef =
new WeakReference<WyyGLSurfaceView>(this);
private MyGLRendererThread mMyGLRendererThread;
private Surface mSurface;
public WyyGLSurfaceView(Context context, OpenGLPlayCallJni jniCall) {
super(context);
Log.e(TAG, "GLSurfaceViewManger");
mContext = context;
mJniCall = jniCall;
init();
}
public WyyGLSurfaceView(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
init();
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
}
private void init() {
//获取SurfaceHolder对象
mHolder = getHolder();
//注册SurfaceHolder的回调方法
mHolder.addCallback(this);
mMyGLRendererThread = new MyGLRendererThread(mThisWeakRef);
mMyGLRendererThread.start();
}
private void stopCameraPreview() {
if (camera2Helper != null) {
camera2Helper.stop();
}
}
@Override
public void surfaceCreated(@NonNull SurfaceHolder holder) {
Log.e(TAG, "surfaceCreated");
mMyGLRendererThread.getHandler().sendSurfaceCreated();
}
@Override
public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) {
mSurface = holder.getSurface();
Log.e(TAG, "onSurfaceChanged width:" + width + ",height" + height
+ "===surface:" + mSurface.toString());
// Log.e(TAG, "surfaceChanged: "+Thread.currentThread().getName());
mMyGLRendererThread.getHandler().sendSurfaceChanged(format, width, height);
mWidth = width;
mHeight = height;
startCameraPreview(width, height);
}
@Override
public void surfaceDestroyed(@NonNull SurfaceHolder holder) {
mMyGLRendererThread.getHandler().sendShutdown();
}
private void startCameraPreview(int width, int height) {
if (camera2Helper == null) {
camera2Helper = new Camera2Helper2.Builder()
.cameraListener(this)
.specificCameraId(Camera2Helper2.CAMERA_ID_BACK)
.context(mContext)
.previewViewSize(new Point(width, height))
.rotation(90)
.build();
}
camera2Helper.start();
}
@Override
public void onPreviewFrame(byte[] yuvData, int width, int height) {
// Log.e(TAG, "onPreviewFrame" );
// Log.e(TAG, "onPreviewFrame: "+Thread.currentThread().getName());
if (mJniCall != null) {
mJniCall.glSurfaceViewDraw(yuvData, width, height, 90);
}
mMyGLRendererThread.getHandler().sendDoFrame(1000);
}
@Override
public void onCameraOpened(Size previewSize, int displayOrientation) {
Log.e(TAG, "onCameraOpened previewSize:" + previewSize.toString()
+ "==displayOrientation:" + displayOrientation);
}
@Override
public void onCameraClosed() {
Log.e(TAG, "onCameraClosed:");
}
@Override
public void onCameraError(Exception e) {
Log.e(TAG, "onCameraError:" + e.toString());
}
public void destroyRender() {
mJniCall.glSurfaceViewDestroy();
stopCameraPreview();
}
class MyGLRendererThread extends Thread {
private volatile RenderHandler mRenderHandler;
private WeakReference<WyyGLSurfaceView> mGLSurfaceViewWeakRef;
MyGLRendererThread(WeakReference<WyyGLSurfaceView> glSurfaceViewWeakRef) {
super();
mWidth = 0;
mHeight = 0;
mGLSurfaceViewWeakRef = glSurfaceViewWeakRef;
}
@Override
public void run() {
Looper.prepare();
mRenderHandler = new RenderHandler(this);
Looper.loop();
}
public RenderHandler getHandler() {
return mRenderHandler;
}
public void surfaceCreated() {
Log.e(TAG, "onSurfaceChanged: " + Thread.currentThread().getName());
String fragPath = OpenGLPlayFileUtils.getModelFilePath(mContext
, "texture_video_play_frament.glsl");
String vertexPath = OpenGLPlayFileUtils.getModelFilePath(mContext
, "texture_video_play_vert.glsl");
mJniCall.glSurfaceViewCreate(0, vertexPath, fragPath);
}
public void surfaceChanged(int width, int height) {
if (mJniCall != null) {
mJniCall.glSurfaceViewInit(mSurface, null, width, height);
}
}
public void doFrame(long timestamp) {
if (mJniCall != null)
mJniCall.glSurfaceViewRender();
}
public void setRecordingEnabled(boolean b) {
}
public void setRecordMethod(int arg1) {
}
public void shutdown() {
if (mJniCall != null) {
mJniCall.glTextureVideoPlayDestroy();
}
Looper.myLooper().quit();
}
}
//
private static class RenderHandler extends Handler {
private static final int MSG_SURFACE_CREATED = 0;
private static final int MSG_SURFACE_CHANGED = 1;
private static final int MSG_DO_FRAME = 2;
private static final int MSG_RECORDING_ENABLED = 3;
private static final int MSG_RECORD_METHOD = 4;
private static final int MSG_SHUTDOWN = 5;
private WeakReference<MyGLRendererThread> mWeakRenderThread;
public RenderHandler(MyGLRendererThread rt) {
mWeakRenderThread = new WeakReference<MyGLRendererThread>(rt);
}
public void sendSurfaceCreated() {
sendMessage(obtainMessage(RenderHandler.MSG_SURFACE_CREATED));
}
public void sendSurfaceChanged(@SuppressWarnings("unused") int format,
int width, int height) {
// ignore format
sendMessage(obtainMessage(RenderHandler.MSG_SURFACE_CHANGED, width, height));
}
public void sendDoFrame(long frameTimeNanos) {
sendMessage(obtainMessage(RenderHandler.MSG_DO_FRAME,
(int) (frameTimeNanos >> 32), (int) frameTimeNanos));
}
public void setRecordingEnabled(boolean enabled) {
sendMessage(obtainMessage(MSG_RECORDING_ENABLED, enabled ? 1 : 0, 0));
}
public void setRecordMethod(int recordMethod) {
sendMessage(obtainMessage(MSG_RECORD_METHOD, recordMethod, 0));
}
public void sendShutdown() {
sendMessage(obtainMessage(RenderHandler.MSG_SHUTDOWN));
}
@Override // runs on RenderThread
public void handleMessage(Message msg) {
int what = msg.what;
//Log.d(TAG, "RenderHandler [" + this + "]: what=" + what);
MyGLRendererThread renderThread = mWeakRenderThread.get();
if (renderThread == null) {
Log.w(TAG, "RenderHandler.handleMessage: weak ref is null");
return;
}
switch (what) {
case MSG_SURFACE_CREATED:
renderThread.surfaceCreated();
break;
case MSG_SURFACE_CHANGED:
renderThread.surfaceChanged(msg.arg1, msg.arg2);
break;
case MSG_DO_FRAME:
long timestamp = (((long) msg.arg1) << 32) |
(((long) msg.arg2) & 0xffffffffL);
renderThread.doFrame(timestamp);
break;
case MSG_RECORDING_ENABLED:
renderThread.setRecordingEnabled(msg.arg1 != 0);
break;
case MSG_RECORD_METHOD:
renderThread.setRecordMethod(msg.arg1);
break;
case MSG_SHUTDOWN:
renderThread.shutdown();
break;
default:
throw new RuntimeException("unknown message " + what);
}
}
}
}
4、EGLConetxt、EglDisplay、EGLWindowSurface创建:
以上的1、2、3是对GLSurefaceView中GLThread的维护处理,但没有创建出EGLConetxt、EglDisplay、EGLWindowSurface的运行环境的必要对象。
接下来我把EGLConetxt、EglDisplay、EGLWindowSurface运行环境必要对象的初始化放在C++层。代码如下:
cpp
///EGL
//1 EGL display创建和初始化
display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
if (display == EGL_NO_DISPLAY) {
LOGE("eglGetDisplay failed!");
return;
}
if (EGL_TRUE != eglInitialize(display, 0, 0)) {
LOGE("eglInitialize failed!");
return;
}
//2 surface
//2-1 surface窗口配置
//输出配置
EGLConfig config;
EGLint configNum;
EGLint configSpec[] = {
EGL_RED_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_BLUE_SIZE, 8,
EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_NONE
};
if (EGL_TRUE != eglChooseConfig(display, configSpec, &config, 1, &configNum)) {
LOGE("eglChooseConfig failed!");
return;
}
//创建surface
ANativeWindow_acquire(window);
ANativeWindow_setBuffersGeometry(window, 0, 0, AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM);
winsurface = eglCreateWindowSurface(display, config, window, 0);
if (winsurface == EGL_NO_SURFACE) {
LOGE("eglCreateWindowSurface failed!");
return;
}
//3 context 创建关联的上下文
const EGLint ctxAttr[] = {
EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE
};
EGLContext context = eglCreateContext(display, config, EGL_NO_CONTEXT, ctxAttr);
if (context == EGL_NO_CONTEXT) {
LOGE("eglCreateContext failed!");
return;
}
if (EGL_TRUE != eglMakeCurrent(display, winsurface, winsurface, context)) {
LOGE("eglMakeCurrent failed!");
return;
}
5、创建OpenglesSurfaceViewVideoRender.cpp传入surface通过texture纹理渲染出视频:
OpenglesSurfaceViewVideoRender.cpp是在OpenGL Texture C++ 预览Camera视频-CSDN博客中OpenlesTextureVideoRender.cpp进行改造而成,有兴趣的可翻看我的这一篇的博客。
在 init()函数中加入了EGLConetxt、EglDisplay、EGLWindowSurface运行环境必要对象的初始化,并在渲染时eglSwapBuffers()窗口显示。
完整代码如下:
cpp
// Author : wangyongyao https://github.com/wangyongyao1989
// Created by MMM on 2024/10/05.
//
#include "OpenglesSurfaceViewVideoRender.h"
#include "OpenGLShader.h"
void
OpenglesSurfaceViewVideoRender::init(ANativeWindow *window, AAssetManager *assetManager,
size_t width,
size_t height) {
LOGI("OpenglesSurfaceViewVideoRender init==%d, %d", width, height);
m_backingWidth = width;
m_backingHeight = height;
///EGL
//1 EGL display创建和初始化
display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
if (display == EGL_NO_DISPLAY) {
LOGE("eglGetDisplay failed!");
return;
}
if (EGL_TRUE != eglInitialize(display, 0, 0)) {
LOGE("eglInitialize failed!");
return;
}
//2 surface
//2-1 surface窗口配置
//输出配置
EGLConfig config;
EGLint configNum;
EGLint configSpec[] = {
EGL_RED_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_BLUE_SIZE, 8,
EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_NONE
};
if (EGL_TRUE != eglChooseConfig(display, configSpec, &config, 1, &configNum)) {
LOGE("eglChooseConfig failed!");
return;
}
//创建surface
ANativeWindow_acquire(window);
ANativeWindow_setBuffersGeometry(window, 0, 0, AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM);
winsurface = eglCreateWindowSurface(display, config, window, 0);
if (winsurface == EGL_NO_SURFACE) {
LOGE("eglCreateWindowSurface failed!");
return;
}
//3 context 创建关联的上下文
const EGLint ctxAttr[] = {
EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE
};
EGLContext context = eglCreateContext(display, config, EGL_NO_CONTEXT, ctxAttr);
if (context == EGL_NO_CONTEXT) {
LOGE("eglCreateContext failed!");
return;
}
if (EGL_TRUE != eglMakeCurrent(display, winsurface, winsurface, context)) {
LOGE("eglMakeCurrent failed!");
return;
}
useProgram();
createTextures();
}
void OpenglesSurfaceViewVideoRender::render() {
// LOGI("OpenglesSurfaceViewVideoRender render");
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
if (!updateTextures() /*|| !useProgram()*/) return;
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
//窗口显示
eglSwapBuffers(display, winsurface);
}
void OpenglesSurfaceViewVideoRender::updateFrame(const surface_video_frame &frame) {
m_sizeY = frame.width * frame.height;
m_sizeU = frame.width * frame.height / 4;
m_sizeV = frame.width * frame.height / 4;
if (m_pDataY == nullptr || m_width != frame.width || m_height != frame.height) {
m_pDataY = std::make_unique<uint8_t[]>(m_sizeY + m_sizeU + m_sizeV);
m_pDataU = m_pDataY.get() + m_sizeY;
m_pDataV = m_pDataU + m_sizeU;
isProgramChanged = true;
}
m_width = frame.width;
m_height = frame.height;
if (m_width == frame.stride_y) {
memcpy(m_pDataY.get(), frame.y, m_sizeY);
} else {
uint8_t *pSrcY = frame.y;
uint8_t *pDstY = m_pDataY.get();
for (int h = 0; h < m_height; h++) {
memcpy(pDstY, pSrcY, m_width);
pSrcY += frame.stride_y;
pDstY += m_width;
}
}
if (m_width / 2 == frame.stride_uv) {
memcpy(m_pDataU, frame.u, m_sizeU);
memcpy(m_pDataV, frame.v, m_sizeV);
} else {
uint8_t *pSrcU = frame.u;
uint8_t *pSrcV = frame.v;
uint8_t *pDstU = m_pDataU;
uint8_t *pDstV = m_pDataV;
for (int h = 0; h < m_height / 2; h++) {
memcpy(pDstU, pSrcU, m_width / 2);
memcpy(pDstV, pSrcV, m_width / 2);
pDstU += m_width / 2;
pDstV += m_width / 2;
pSrcU += frame.stride_uv;
pSrcV += frame.stride_uv;
}
}
isDirty = true;
}
void
OpenglesSurfaceViewVideoRender::draw(uint8_t *buffer, size_t length, size_t width, size_t height,
float rotation) {
m_length = length;
m_rotation = rotation;
surface_video_frame frame{};
frame.width = width;
frame.height = height;
frame.stride_y = width;
frame.stride_uv = width / 2;
frame.y = buffer;
frame.u = buffer + width * height;
frame.v = buffer + width * height * 5 / 4;
updateFrame(frame);
}
void OpenglesSurfaceViewVideoRender::setParameters(uint32_t params) {
m_params = params;
}
uint32_t OpenglesSurfaceViewVideoRender::getParameters() {
return m_params;
}
bool OpenglesSurfaceViewVideoRender::createTextures() {
auto widthY = (GLsizei) m_width;
auto heightY = (GLsizei) m_height;
glActiveTexture(GL_TEXTURE0);
glGenTextures(1, &m_textureIdY);
glBindTexture(GL_TEXTURE_2D, m_textureIdY);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, widthY, heightY, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE,
nullptr);
if (!m_textureIdY) {
LOGE("OpenGL Error Create Y texture");
return false;
}
GLsizei widthU = (GLsizei) m_width / 2;
GLsizei heightU = (GLsizei) m_height / 2;
glActiveTexture(GL_TEXTURE1);
glGenTextures(1, &m_textureIdU);
glBindTexture(GL_TEXTURE_2D, m_textureIdU);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, widthU, heightU, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE,
nullptr);
if (!m_textureIdU) {
LOGE("OpenGL Error Create U texture");
return false;
}
GLsizei widthV = (GLsizei) m_width / 2;
GLsizei heightV = (GLsizei) m_height / 2;
glActiveTexture(GL_TEXTURE2);
glGenTextures(1, &m_textureIdV);
glBindTexture(GL_TEXTURE_2D, m_textureIdV);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, widthV, heightV, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE,
nullptr);
if (!m_textureIdV) {
LOGE("OpenGL Error Create V texture");
return false;
}
return true;
}
bool OpenglesSurfaceViewVideoRender::updateTextures() {
if (!m_textureIdY && !m_textureIdU && !m_textureIdV /*&& !createTextures()*/) return false;
// LOGI("OpenglesSurfaceViewVideoRender updateTextures");
LOGE("updateTextures m_textureIdY:%d,m_textureIdU:%d,m_textureIdV:%d,===isDirty:%d",
m_textureIdY,
m_textureIdU, m_textureIdV, isDirty);
if (isDirty) {
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, m_textureIdY);
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, (GLsizei) m_width, (GLsizei) m_height, 0,
GL_LUMINANCE, GL_UNSIGNED_BYTE, m_pDataY.get());
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, m_textureIdU);
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, (GLsizei) m_width / 2, (GLsizei) m_height / 2,
0,
GL_LUMINANCE, GL_UNSIGNED_BYTE, m_pDataU);
glActiveTexture(GL_TEXTURE2);
glBindTexture(GL_TEXTURE_2D, m_textureIdV);
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, (GLsizei) m_width / 2, (GLsizei) m_height / 2,
0,
GL_LUMINANCE, GL_UNSIGNED_BYTE, m_pDataV);
isDirty = false;
return true;
}
return false;
}
int
OpenglesSurfaceViewVideoRender::createProgram() {
m_program = openGlShader->createProgram();
m_vertexShader = openGlShader->vertexShader;
m_pixelShader = openGlShader->fraShader;
LOGI("OpenglesSurfaceViewVideoRender createProgram m_program:%d", m_program);
if (!m_program) {
LOGE("Could not create program.");
return 0;
}
//Get Uniform Variables Location
m_vertexPos = (GLuint) glGetAttribLocation(m_program, "position");
m_textureYLoc = glGetUniformLocation(m_program, "s_textureY");
m_textureULoc = glGetUniformLocation(m_program, "s_textureU");
m_textureVLoc = glGetUniformLocation(m_program, "s_textureV");
m_textureLoc = (GLuint) glGetAttribLocation(m_program, "texcoord");
m_textureSize = glGetUniformLocation(m_program, "texSize");
return m_program;
}
GLuint OpenglesSurfaceViewVideoRender::useProgram() {
if (!m_program && !createProgram()) {
LOGE("Could not use program.");
return 0;
}
if (isProgramChanged) {
glUseProgram(m_program);
glVertexAttribPointer(m_vertexPos, 2, GL_FLOAT, GL_FALSE, 0, kVerticekSurface);
glEnableVertexAttribArray(m_vertexPos);
glUniform1i(m_textureYLoc, 0);
glUniform1i(m_textureULoc, 1);
glUniform1i(m_textureVLoc, 2);
glVertexAttribPointer(m_textureLoc, 2, GL_FLOAT, GL_FALSE, 0, kTextureCoordSurface);
glEnableVertexAttribArray(m_textureLoc);
if (m_textureSize >= 0) {
GLfloat size[2];
size[0] = m_width;
size[1] = m_height;
glUniform2fv(m_textureSize, 1, &size[0]);
}
isProgramChanged = false;
}
return m_program;
}
bool
OpenglesSurfaceViewVideoRender::setSharderPath(const char *vertexPath, const char *fragmentPath) {
openGlShader->getSharderPath(vertexPath, fragmentPath);
return 0;
}
bool OpenglesSurfaceViewVideoRender::setSharderStringPath(string vertexPath, string fragmentPath) {
openGlShader->getSharderStringPath(vertexPath, fragmentPath);
return 0;
}
OpenglesSurfaceViewVideoRender::OpenglesSurfaceViewVideoRender() {
openGlShader = new OpenGLShader();
}
OpenglesSurfaceViewVideoRender::~OpenglesSurfaceViewVideoRender() {
deleteTextures();
delete_program(m_program);
}
void OpenglesSurfaceViewVideoRender::delete_program(GLuint &program) {
if (program) {
glUseProgram(0);
glDeleteProgram(program);
program = 0;
}
}
void OpenglesSurfaceViewVideoRender::deleteTextures() {
if (m_textureIdY) {
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, 0);
glDeleteTextures(1, &m_textureIdY);
m_textureIdY = 0;
}
if (m_textureIdU) {
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, 0);
glDeleteTextures(1, &m_textureIdU);
m_textureIdU = 0;
}
if (m_textureIdV) {
glActiveTexture(GL_TEXTURE2);
glBindTexture(GL_TEXTURE_2D, 0);
glDeleteTextures(1, &m_textureIdV);
m_textureIdV = 0;
}
}
void OpenglesSurfaceViewVideoRender::printGLString(const char *name, GLenum s) {
const char *v = (const char *) glGetString(s);
LOGI("OpenGL %s = %s\n", name, v);
}
void OpenglesSurfaceViewVideoRender::checkGlError(const char *op) {
for (GLint error = glGetError(); error; error = glGetError()) {
LOGI("after %s() glError (0x%x)\n", op, error);
}
}