在flutter中通过MethodChannel调用java层的OpenGL函数渲染图形

引言

在flutter中如果只是想简单地播放视频,可以使用flutter官方提供的video_player插件,或者其他第三方提供的插件。但是如果想在flutter上渲染实时视频流,添加美颜算法、渲染全景视频等,这些插件可能不一定能满足我们的要求。遇到这种问题,一般要自己开发插件渲染视频或者使用第三方(比如声网)提供的sdk。如果选择自己开发插件,我们可以参考video_player的源码,结合MethodChannel,在java层或者c++层使用OpenGL作渲染。本文选择在java层使用OpenGL,如果要使用C++,在Java层通过NDK调用C++接口即可。

步骤

  • 创建flutter插件
  • 使用MethodChannel与Java层交互

在dart层添加初始化函数,每个渲染窗口都对应一个textureId。

class OpenglPlg {
  static const MethodChannel _channel =
  const MethodChannel('opengl_plg');

  int textureId = -1;

  Future<int> initialize(double width, double height) async {
    textureId = await _channel.invokeMethod('create', {
      'width': width,
      'height': height,
    });
    return textureId;
  }

  Future<Null> dispose() =>
      _channel.invokeMethod('dispose', {'textureId': textureId});

  bool get isInitialized => textureId != null;
}

初始化完成后可以在flutter app中使用,例如:


void main() {
  runApp(MyApp());
}

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  final _controller = new OpenglPlg();
  final _width = 200.0;
  final _height = 200.0;

  @override
  initState() {
    super.initState();
    initializeController();
  }

  @override
  void dispose() {
    _controller.dispose();

    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      home: new Scaffold(
        appBar: new AppBar(
          title: new Text('OpenGL via Texture widget example'),
        ),
        body: new Center(
          child: new Container(
            width: _width,
            height: _height,
            child: _controller.isInitialized
                ? new Texture(textureId: _controller.textureId)
                : null,
          ),
        ),
      ),
    );
  }

  Future<Null> initializeController() async {
    await _controller.initialize(_width, _height);
    setState(() {});
  }
}

完成dart层的工作后,便要开始着手完成java层的代码,在java层创建与dart层中Texture对应的SurfaceTexture。如果要兼容安卓与ios平台,需要分别完成各自平台对应的代码,这里只完成安卓平台的部分。关键代码为使用TextureRegistry类中SurfaceTextureEntry方法,创造一个SurfaceTextureEntry。这个SurfaceTextureEntry可以为Texture Widget提供textureId以及对应的SurfaceTexture。关键代码如下,其中onMethodCall即为使用MethodChannel时,需要重载的函数。

  // my textures
  private  TextureRegistry textures ;
  private LongSparseArray<OpenGLRenderer> renders = new LongSparseArray<>();

  // ...

  @Override
  public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) {

    Map<String, Number> arguments = (Map<String, Number>) call.arguments;
    if (call.method.equals("create")) {
      TextureRegistry.SurfaceTextureEntry entry = textures.createSurfaceTexture();
      SurfaceTexture surfaceTexture = entry.surfaceTexture();

      int width = arguments.get("width").intValue();
      int height = arguments.get("height").intValue();
      surfaceTexture.setDefaultBufferSize(width, height);

      SampleRenderWorker worker = new SampleRenderWorker();
      OpenGLRenderer render = new OpenGLRenderer(surfaceTexture, worker);
      renders.put(entry.id(), render);

      result.success(entry.id());
    } else if (call.method.equals("dispose")) {
      long textureId = arguments.get("textureId").longValue();
      OpenGLRenderer render = renders.get(textureId);
      render.onDispose();

      renders.delete(textureId);
    } else {
      result.notImplemented();
    }
  }

创建完SurfaceTexture后便可以开始完成OpenGL部分:

public class OpenGLRenderer implements Runnable {
    protected final SurfaceTexture texture;
    private EGL10 egl;
    private EGLDisplay eglDisplay;
    private EGLContext eglContext;
    private EGLSurface eglSurface;

    private boolean running;

    private Worker worker;

    // ...

    @Override
    public void run() {
        initGL();
        worker.onCreate();
        Log.d(LOG_TAG, "OpenGL init OK.");

        while (running) {
            long loopStart = System.currentTimeMillis();

            if (worker.onDraw()) {
                if (!egl.eglSwapBuffers(eglDisplay, eglSurface)) {
                    Log.d(LOG_TAG, String.valueOf(egl.eglGetError()));
                }
            }

            long waitDelta = 16 - (System.currentTimeMillis() - loopStart);
            if (waitDelta > 0) {
                try {
                    Thread.sleep(waitDelta);
                } catch (InterruptedException e) {
                }
            }
        }

        worker.onDispose();
        deinitGL();
    }

    private void initGL() {
        egl = (EGL10) EGLContext.getEGL();
        eglDisplay = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
        if (eglDisplay == EGL10.EGL_NO_DISPLAY) {
            throw new RuntimeException("eglGetDisplay failed");
        }

        int[] version = new int[2];
        if (!egl.eglInitialize(eglDisplay, version)) {
            throw new RuntimeException("eglInitialize failed");
        }

        EGLConfig eglConfig = chooseEglConfig();
        eglContext = createContext(egl, eglDisplay, eglConfig);

        eglSurface = egl.eglCreateWindowSurface(eglDisplay, eglConfig, texture, null);

        if (eglSurface == null || eglSurface == EGL10.EGL_NO_SURFACE) {
            throw new RuntimeException("GL Error: " + GLUtils.getEGLErrorString(egl.eglGetError()));
        }

        if (!egl.eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext)) {
            throw new RuntimeException("GL make current error: " + GLUtils.getEGLErrorString(egl.eglGetError()));
        }
    }

    // ...

    public interface Worker {
        void onCreate();

        boolean onDraw();

        void onDispose();
    }
}
class SampleRenderWorker implements OpenGLRenderer.Worker {

    private double _tick = 0;

    @Override
    public boolean onDraw() {
        _tick = _tick + Math.PI / 60;

        float green = (float) ((Math.sin(_tick) + 1) / 2);

        GLES20.glClearColor(0f, green, 0f, 1f);

        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
        return true;
    }
}

至此,完成所有工作,运行app:

完整代码:https://github.com/obweix/opengl_plugin

参考:https://medium.com/@german_saprykin/opengl-with-texture-widget-f919743d25d9

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值