OpenGL 自定义SurfaceView Texture C++预览Camera视频

embedded/2024/10/19 4:49:33/

      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做了类似封装。

 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尺寸以及至少指定深度和模具尺寸的配置。

    /*** 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;}@Overridepublic 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运行所需的上下文。

 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创建相关联的联系。

 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/4588890Log.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的持有。

static class GLThread extends Thread {GLThread(WeakReference<GLSurfaceView> glSurfaceViewWeakRef) {super();mWidth = 0;mHeight = 0;mRequestRender = true;mRenderMode = RENDERMODE_CONTINUOUSLY;mWantRenderNotification = false;mGLSurfaceViewWeakRef = glSurfaceViewWeakRef;}@Overridepublic 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()。
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;@UnsupportedAppUsageEGLContext mEglContext;}
  •  GLThread中guardRun()通过synchronized同步阻塞锁的方式,对线程生命周期内的所有状态进行管理。其中pause/pausing等状态太过于繁杂,现在我们重点在着手分析程序时的setRenderer()传入mRenderer的onSurfaceCreated()、onSurfaceChanged()、onDrawFrame()三个回调函数。
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()三个调用。

class MyGLRendererThread extends Thread {private volatile RenderHandler mRenderHandler;private WeakReference<WyyGLSurfaceView> mGLSurfaceViewWeakRef;MyGLRendererThread(WeakReference<WyyGLSurfaceView> glSurfaceViewWeakRef) {super();mWidth = 0;mHeight = 0;mGLSurfaceViewWeakRef = glSurfaceViewWeakRef;}@Overridepublic 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()从主线程到子线程的切换。

 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 formatsendMessage(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 RenderThreadpublic 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的完整代码:

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();}@Overrideprotected 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();}}@Overridepublic void surfaceCreated(@NonNull SurfaceHolder holder) {Log.e(TAG, "surfaceCreated");mMyGLRendererThread.getHandler().sendSurfaceCreated();}@Overridepublic 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);}@Overridepublic 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();}@Overridepublic 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);}@Overridepublic void onCameraOpened(Size previewSize, int displayOrientation) {Log.e(TAG, "onCameraOpened previewSize:" + previewSize.toString()+ "==displayOrientation:" + displayOrientation);}@Overridepublic void onCameraClosed() {Log.e(TAG, "onCameraClosed:");}@Overridepublic 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;}@Overridepublic 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 formatsendMessage(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 RenderThreadpublic 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++层。代码如下:

///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;}//创建surfaceANativeWindow_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()窗口显示。

       完整代码如下:

//  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;}//创建surfaceANativeWindow_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 Locationm_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);}
}

        


http://www.ppmy.cn/embedded/128652.html

相关文章

爱快路由器配置腾讯云动态域名DDNS详细说明

直白点说就是让爱快路由器自动配置当前公网IP地址给域名&#xff0c;动态域名DDNS不清楚的请自行百度&#xff0c; 这里就可以看见操作日志&#xff0c;那么我们一步一步来配置它吧&#xff0c;首先登录爱快路由器&#xff0c;如下图&#xff1a; 那么腾讯云我们怎么找到ID和…

【wpf】08 xml文件的存取操作

在使用wpf编程过程中&#xff0c;会用到xml的配置文件&#xff0c;实现对其读取和存储的操作是必须的。 1 xml说明 可扩展标记语言 (Extensible Markup Language, XML) &#xff0c;标准通用标记语言的子集&#xff0c;可以用来标记数据、定义数据类型&#xff0c;是一种允许…

RHCE例行性工作笔记

1、单一执行的例行性工作 单一执行的例行性工作&#xff1a; 仅处理执行一次就结束了 at命令的工作过程 /etc/at.allow &#xff0c;写在该文件的人可以使用 at 命令 /etc/at.deny &#xff0c;黑名单 两个文件如果都不存在&#xff0c;只有 root 能使用 #at 工作调度对应的…

FFMPEG录屏(18)--- 枚举Windows下的窗口列表并获取进程图标、标题、缩略图等

在Windows中获取可进行屏幕共享捕获的窗口列表及其图标、缩略图 在Windows系统中&#xff0c;获取可进行屏幕共享捕获的窗口列表以及它们的图标和缩略图是一个复杂但有趣的过程。本文将详细介绍如何实现这一功能&#xff0c;涉及到的主要技术包括Windows API、C编程和一些第三…

NAT机制

目录 1、NAT机制的定义 2、NAT机制的工作原理 1、NAT机制的定义 如今IP地址的分配已经不够使用&#xff0c;为了解决这一问题&#xff0c;NAT机制起到了很关键的作用。 NAT机制&#xff08;网络地址转换&#xff09;&#xff0c;本质上&#xff0c;让一个IP地址代表一批设备…

【工具】VSCODE下载,配置初次设置

打开 settings.json 文件&#xff0c;包含了 Visual Studio Code (VSCode) 中的各种用户配置。 {"files.associations": {"*.vue": "vue","*.wpy": "vue","*.wxml": "html","*.wxss": "…

C语言笔记20

指针运算 #include <stdio.h>int main() {char ac[] {0,1,2,3,4,5,6,7,8,9,};char *p ac;printf("p %p\n", p);printf("p1%p\n", p1);int ai[] {0,1,2,3,4,5,6,7,8,9,};int *q ai;printf("q %p\n", q);printf("q1%p\n", q1)…

状态模式(C++)

定义&#xff1a;状态模式&#xff08;State Pattern&#xff09;是一种行为设计模式&#xff0c;它允许对象在内部状态改变时改变它的行为&#xff0c;对象看起来似乎修改了它的类。状态模式将状态相关的行为封装到单独的类中&#xff0c;并将这些对象组合成状态模式&#xff…