SurfaceTexture,TextureView,GLsurfaceview的区别与联系详解

折月煮酒 提交于 2019-12-09 15:04:12

Android 中的SurfaceTexture,TextureView, GLsurfaceview的区别与联系详解

SurfaceView, GLSurfaceView, SurfaceTexture和TextureView是Android当中名字比较绕,关系又比较密切的几个类。本文基于Android 5.0(Lollipop)的代码理一下它们的基本原理,联系与区分。

SurfaceView从Android 1.0(API level 1)时就有 。它继承自类View,因此它本质上是一个View。但与普通View不同的是,它有自己的Surface。我们知道,一般的Activity包括的多个View会组成View hierachy的树形结构,只有最顶层的DecorView,也就是根结点视图,才是对WMS可见的。这个DecorView在WMS中有1个对应的WindowState。相应地,在SF中对应的Layer。而SurfaceView自带1个Surface,这个Surface在WMS中有自己对应的WindowState,在SF中也会有自己的Layer。以下图所示:

 

\public class TriangleActivity extends Activity {     protected void onCreate(Bundle savedInstanceState) {         mGLView = new GLSurfaceView(this);         mGLView.setRenderer(new RendererImpl(this));

相干类图以下。其中SurfaceView中的SurfaceHolder主要是提供了1坨操作Surface的接口。GLSurfaceView中的EglHelper和GLThread分别实现了上面提到的管理EGL环境和渲染线程的工作。GLSurfaceView的使用者需要实现Renderer接口。

 

\Java的JNI跳板。其中SurfaceTexture中的attachToGLContext()和detachToGLContext()可让多个GL context同享同一个内容源。

 

\109 public VideoDumpView(Context context) { ... 116 mRenderer = new VideoDumpRenderer(context); 117 setRenderer(mRenderer); 118 }

随后,GLSurfaceView中的GLThread启动,创建EGL环境后回调VideoDumpRenderer中的onSurfaceCreated()。

519 public void onSurfaceCreated(GL10 glUnused, EGLConfig config) {

...

551 // Create our texture. This has to be done each time the surface is created.

552 int[] textures = new int[1];

553 GLES20.glGenTextures(1, textures, 0);

554

555 mTextureID = textures[0];

556 GLES20.glBindTexture(GL_TEXTURE_EXTERNAL_OES, mTextureID);

...

575 mSurface = new SurfaceTexture(mTextureID);

576 mSurface.setOnFrameAvailableListener(this);

577

578 Surface surface = new Surface(mSurface);

579 mMediaPlayer.setSurface(surface);

这里,首先通过GLES创建GL的外部纹理。外部纹理说明它的真正内容是放在ion分配出来的系统物理内存中,而不是GPU中,GPU中只是保护了其元数据。接着根据前面创建的GL纹理对象创建SurfaceTexture。流程以下:

 

\230static void SurfaceTexture_init(JNIEnv* env, jobject thiz, jboolean isDetached, 231 jint texName, jboolean singleBufferMode, jobject weakThiz) 232{ ... 235 BufferQueue::createBufferQueue(&producer, &consumer); ... 242 sp surfaceTexture; 243 if (isDetached) { 244 surfaceTexture = new GLConsumer(consumer, GL_TEXTURE_EXTERNAL_OES, 245 true, true); 246 } else { 247 surfaceTexture = new GLConsumer(consumer, texName, 248 GL_TEXTURE_EXTERNAL_OES, true, true); 249 } ... 256 SurfaceTexture_setSurfaceTexture(env, thiz, surfaceTexture); 257 SurfaceTexture_setProducer(env, thiz, producer); ... 266 sp ctx(new JNISurfaceTextureContext(env, weakThiz, 267 clazz)); 268 surfaceTexture->setFrameAvailableListener(ctx); 269 SurfaceTexture_setFrameAvailableListener(env, thiz, ctx);

由于直接的Listener在Java层,而触发者在Native层,因此需要从Native层回调到Java层。这里通过JNISurfaceTextureContext当了跳板。JNISurfaceTextureContext的onFrameAvailable()起到了Native和Java的桥接作用:

180void JNISurfaceTextureContext::onFrameAvailable()

...

184 env->CallStaticVoidMethod(mClazz, fields.postEvent, mWeakThiz);

其中的fields.postEvent早在SurfaceTexture_classInit()中被初始化为SurfaceTexture的postEventFromNative()函数。这个函数往所在线程的消息队列中放入消息,异步调用VideoDumpRenderer的onFrameAvailable()函数,通知VideoDumpRenderer有新的数据到来。

回到onSurfaceCreated(),接下来创建供外部生产者使用的Surface类。Surface的构造函数之1带有参数SurfaceTexture。

133 public Surface(SurfaceTexture surfaceTexture) {

...

140 setNativeObjectLocked(nativeCreateFromSurfaceTexture(surfaceTexture));

它实际上是把SurfaceTexture中创建的BufferQueue的Producer接口实现类拿出来后创建了相应的Surface类。

135static jlong nativeCreateFromSurfaceTexture(JNIEnv* env, jclass clazz,

136 jobject surfaceTextureObj) {

137 sp producer(SurfaceTexture_getProducer(env, surfaceTextureObj));

...

144 sp surface(new Surface(producer, true));

这样,Surface为BufferQueue的Producer端,SurfaceTexture中的GLConsumer为BufferQueue的Consumer端。当通过Surface绘制时,SurfaceTexture可以通过updateTexImage()来将绘制结果绑定到GL的纹理中。

回到onSurfaceCreated()函数,接下来调用setOnFrameAvailableListener()函数将VideoDumpRenderer(实现SurfaceTexture.OnFrameAvailableListener接口)作为SurfaceTexture的Listener,由于它要监听内容流上是不是有新数据。接着将SurfaceTexture传给MediaPlayer,由于这里MediaPlayer是生产者,SurfaceTexture是消费者。后者要接收前者输出的Video frame。这样,就通过Observer pattern建立起了1条通知链:MediaPlayer -> SurfaceTexture -> VideDumpRenderer。在onFrameAvailable()回调函数中,将updateSurface标志设为true,表示有新的图象到来,需要更新Surface了。为毛不在这儿马上更新纹理呢,由于当前可能不在渲染线程。SurfaceTexture对象可以在任意线程被创建(回调也会在该线程被调用),但updateTexImage()只能在含有纹理对象的GL context所在线程中被调用。因此1般情况下回调中不能直接调用updateTexImage()。

与此同时,GLSurfaceView中的GLThread也在运行,它会调用到VideoDumpRenderer的绘制函数onDrawFrame()。

372 public void onDrawFrame(GL10 glUnused) {
...
377 if (updateSurface) {
...
380 mSurface.updateTexImage();
381 mSurface.getTransformMatrix(mSTMatrix);
382 updateSurface = false;
...
394 // Activate the texture.
395 GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
396 GLES20.glBindTexture(GL_TEXTURE_EXTERNAL_OES, mTextureID);
...
421 // Draw a rectangle and render the video frame as a texture on it.
422 GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
...
429 DumpToFile(frameNumber);

这里,通过SurfaceTexture的updateTexImage()将内容流中的新图象转成GL中的纹理,再进行坐标转换。绑定刚生成的纹理,画到屏幕上。全部流程以下:

 

\protected void onCreate(Bundle savedInstanceState) { ... mTextureView = new TextureView(this); mTextureView.setSurfaceTextureListener(this); ... }

TextureView的构造函数其实不做主要的初始化工作。主要的初始化工作是在getHardwareLayer()中,而这个函数是在其基类View的draw()中调用。TextureView重载了这个函数:

348 HardwareLayer getHardwareLayer() {
...
358 mLayer = mAttachInfo.mHardwareRenderer.createTextureLayer();
359 if (!mUpdateSurface) {
360 // Create a new SurfaceTexture for the layer.
361 mSurface = new SurfaceTexture(false);
362 mLayer.setSurfaceTexture(mSurface);
363 }
364 mSurface.setDefaultBufferSize(getWidth(), getHeight());
365 nCreateNativeWindow(mSurface);
366
367 mSurface.setOnFrameAvailableListener(mUpdateListener, mAttachInfo.mHandler);
368
369 if (mListener != null && !mUpdateSurface) {
370 mListener.onSurfaceTextureAvailable(mSurface, getWidth(), getHeight());371 }
...
390 applyUpdate();
391 applyTransformMatrix();
392
393 return mLayer;
394 }

由于TextureView是硬件加速层(类型为LAYER_TYPE_HARDWARE),它首先通过HardwareRenderer创建相应的HardwareLayer类,放在mLayer成员中。然后创建SurfaceTexture类,具体流程见前文。以后将HardwareLayer与SurfaceTexture做绑定。接着调用Native函数nCreateNativeWindow,它通过SurfaceTexture中的BufferQueueProducer创建Surface类。注意Surface实现了ANativeWindow接口,这意味着它可以作为EGL Surface传给EGL接口从而进行硬件绘制。然后setOnFrameAvailableListener()将监听者mUpdateListener注册到SurfaceTexture。这样,当内容流上有新的图象到来,mUpdateListener的onFrameAvailable()就会被调用。然后需要调用注册在TextureView中的SurfaceTextureListener的onSurfaceTextureAvailable()回调函数,通知TextureView的使用者SurfaceTexture已就绪。全部流程大体以下:

 

\public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { mCamera = Camera.open(); ... mCamera.setPreviewTexture(surface); mCamera.startPreview(); ... }

看1下setPreviewTexture()的实现,其中把SurfaceTexture中初始化时创建的GraphicBufferProducer拿出来传给Camera模块。

576static void android_hardware_Camera_setPreviewTexture(JNIEnv *env,
577 jobject thiz, jobject jSurfaceTexture)
...
585 producer = SurfaceTexture_getProducer(env, jSurfaceTexture);
...
594 if (camera->setPreviewTarget(producer) != NO_ERROR) {

到这里,1切都初始化地差不多了。接下来当内容流有新图象可用,TextureView会被通知到(通过SurfaceTexture.OnFrameAvailableListener接口)。SurfaceTexture.OnFrameAvailableListener是SurfaceTexture有新内容来时的回调接口。TextureView中的mUpdateListener实现了该接口:

755 public void onFrameAvailable(SurfaceTexture surfaceTexture) {
756 updateLayer();
757 invalidate();
758 }

可以看到其中会调用updateLayer()函数,然后通过invalidate()函数申请更新UI。updateLayer()会设置mUpdateLayer标志位。这样,当下次VSync到来时,Choreographer通知App重绘View hierachy。在UI重绘函数performTranversals()中,作为View hierachy的1份子,TextureView的draw()函数被调用,其中便会相继调用applyUpdate()和HardwareLayer的updateSurfaceTexture()函数。

138 public void updateSurfaceTexture() {
139 nUpdateSurfaceTexture(mFinalizer.get());
140 mRenderer.pushLayerUpdate(this);
141 }

updateSurfaceTexture()实际通过JNI调用到android_view_HardwareLayer_updateSurfaceTexture()函数。在其中会设置相应DeferredLayerUpdater的标志位mUpdateTexImage,它表示在渲染线程中需要更新该层的纹理。

 

\

来源:oschina

链接:https://my.oschina.net/u/1270234/blog/853787

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!