引言
在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
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/287411.html
標籤:其他
