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

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

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

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。如下图所示:android

 

\

 

也就是说,虽然在App端它仍在View hierachy中,但在Server端(WMS和SF)中,它与宿主窗口是分离的。这样的好处是对这个Surface的渲染能够放到单独线程去作,渲染时能够有本身的GL context。这对一些游戏、视频等性能相干的利用很是有益,因为它不会影响主线程对事件的响应。但它也有缺点,因为这个Surface不在View hierachy中,它的显示也不受View的属性控制,因此不能进行平移,缩放等变换,也不能放在其它ViewGroup中,一些View中的特性也无法使用。json

GLSurfaceView从Android 1.5(API level 3)开始加入,做为SurfaceView的补充。它能够看作是SurfaceView的一种典型使用模式。在SurfaceView的基础上,它加入了EGL的管理,并自带了渲染线程。另外它定义了用户须要实现的Render接口,提供了用Strategy pattern更改具体Render行动的灵活性。做为GLSurfaceView的Client,只须要将实现了渲染函数的Renderer的实现类设置给GLSurfaceView即可。如:app

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接口。异步

 

\

 

SurfaceTexture从Android 3.0(API level 11)加入。和SurfaceView不一样的是,它对图象流的处理其实不直接显示,而是转为GL外部纹理,所以可用于图象流数据的2次处理(如Camera滤镜,桌面殊效等)。好比Camera的预览数据,变成纹理后能够交给GLSurfaceView直接显示,也可以经过SurfaceTexture交给TextureView做为View heirachy中的1个硬件加速层来显示。首先,SurfaceTexture从图象流(来自Camera预览,视频解码,GL绘制场景等)中取得帧数据,当调用updateTexImage()时,根据内容流中最近的图象更新SurfaceTexture对应的GL纹理对象,接下来,就可以像操做普通GL纹理同样操做它了。从下面的类图中能够看出,它核心管理着1个BufferQueue的Consumer和Producer两端。Producer端用于内容流的源输出数据,Consumer端用于拿GraphicBuffer并生成纹理。SurfaceTexture.OnFrameAvailableListener用于让SurfaceTexture的使用者知道有新数据到来。JNISurfaceTextureContext是OnFrameAvailableListener从Native到Java的JNI跳板。其中SurfaceTexture中的attachToGLContext()和detachToGLContext()可以让多个GL context同享同一个内容源。ide

 

\

 

Android 5.0中将BufferQueue的核心功能分离出来,放在BufferQueueCore这个类中。BufferQueueProducer和BufferQueueConsumer分别是它的生产者和消费者实现基类(分别实现了IGraphicBufferProducer和IGraphicBufferConsumer接口)。它们都是由BufferQueue的静态函数createBufferQueue()来建立的。Surface是生产者真个实现类,提供dequeueBuffer/queueBuffer等硬件渲染接口,和lockCanvas/unlockCanvasAndPost等软件渲染接口,使内容流的源能够往BufferQueue中填graphic buffer。GLConsumer继承自ConsumerBase,是消费者真个实现类。它在基类的基础上添加了GL相干的操做,如将graphic buffer中的内容转为GL纹理等操做。到此,以SurfaceTexture为中心的1个pipeline大致是这样的:函数

 

\

 

TextureView在4.0(API level 14)中引入。它能够将内容流直接投影到View中,能够用于实现Live preview等功能。和SurfaceView不一样,它不会在WMS中单首创建窗口,而是做为View hierachy中的1个普通View,所以能够和其它普通View1样进行移动,旋转,缩放,动画等变化。值得注意的是TextureView必须在硬件加速的窗口中。它显示的内容流数据能够来自App进程或是远端进程。从类图中能够看到,TextureView继承自View,它与其它的View1样在View hierachy中管理与绘制。TextureView重载了draw()方法,其中主要把SurfaceTexture中收到的图象数据做为纹理更新到对应的HardwareLayer中。SurfaceTexture.OnFrameAvailableListener用于通知TextureView内容流有新图象到来。SurfaceTextureListener接口用于让TextureView的使用者知道SurfaceTexture已准备好,这样就可以把SurfaceTexture交给相应的内容源。Surface为BufferQueue的Producer接口实现类,使生产者能够经过它的软件或硬件渲染接口为SurfaceTexture内部的BufferQueue提供graphic buffer。oop

 

\

 

下面以VideoDumpView.java(位于/frameworks/base/media/tests/MediaDump/src/com/android/mediadump/)为例分析下SurfaceTexture的使用。这个例子的效果是从MediaPlayer中拿到视频帧,而后显示在屏幕上,接着把屏幕上的内容dump到指定文件中。因为SurfaceTexture自己只产生纹理,因此这里还须要GLSurfaceView配合来作最后的渲染输出。post

首先,VideoDumpView是GLSurfaceView的继承类。在构造函数VideoDumpView()中会建立VideoDumpRenderer,也就是GLSurfaceView.Renderer的实例,而后调setRenderer()将之设成GLSurfaceView的Renderer。

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。流程如下:

 

\

 

SurfaceTexture的参数为GLES接口函数glGenTexture()获得的纹理对象id。在初始化函数SurfaceTexture_init()中,先建立GLConsumer和相应的BufferQueue,再将它们的指针经过JNI放到SurfaceTexture的Java层对象成员中。

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中的纹理,再进行坐标转换。绑定刚生成的纹理,画到屏幕上。所有流程如下:

 

\

 

最后onDrawFrame()调用DumpToFile()将屏幕上的内容倒到文件中。在DumpToFile()中,先用glReadPixels()从屏幕中把像素数据存到Buffer中,而后用FileOutputStream输出到文件。

上面讲了SurfaceTexture,下面看看TextureView是如何工做的。仍是从例子着手,Android的关于TextureView的官方文档(http://developer.android.com/reference/android/view/TextureView.html)给了1个简洁的例子LiveCameraActivity。它能够将Camera中的内容放在View中进行显示。在onCreate()函数中首先建立TextureView,再将Activity(实现了TextureView.SurfaceTextureListener接口)传给TextureView,用于监听SurfaceTexture准备好的信号。

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已就绪。所有流程大致如下:

 

\

 

注意这里这里为TextureView建立了DeferredLayerUpdater,而不是像Android 4.4(Kitkat)中返回GLES20TextureLayer。因为Android 5.0(Lollipop)中在App端分离出了渲染线程,并将渲染工做放到该线程中。这个线程还能接收VSync信号,所以它还能本身处理动画。事实上,这里DeferredLayerUpdater的建立就是经过同步方式在渲染线程中作的。DeferredLayerUpdater,顾名思义,就是将Layer的更新要求先记录在这,当渲染线程真正要画的时候,再进行真实的操做。其中的setSurfaceTexture()会调用HardwareLayer的Native函数nSetSurfaceTexture()将SurfaceTexture中的surfaceTexture成员(类型为GLConsumer)传给DeferredLayerUpdater,这样之后要更新纹理时DeferredLayerUpdater就知道从哪里更新了。

前面提到初始化中会调用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,它表示在渲染线程中须要更新该层的纹理。

 

\

 

前面提到,Android 5.0引入了渲染线程,它是1个更大的topic,超越本文范围,这里只说相干的部份。做为背景知识,下面只画出了相干的类。能够看到,ThreadedRenderer做为新的HardwareRenderer替换了Android 4.4中的Gl20Renderer。其中比较关键的是RenderProxy类,须要让渲染线程干活时就经过这个类往渲染线程发任务。RenderProxy中指向的RenderThread就是渲染线程的主体了,其中的threadLoop()函数是主循环,大多数时间它会poll在线程的Looper上等待,当有同步要求(或VSync信号)过来,它会被唤醒,而后处理TaskQueue中的任务。TaskQueue是RenderTask的队列,RenderTask表明1个渲染线程中的任务。如DrawFrameTask就是RenderTask的继承类之1,它主要用于渲染当前帧。而DrawFrameTask中的DeferredLayerUpdater集合就寄存着以前对硬件加速层的更新操做申请。

 

\

 

当主线程准备好渲染数据后,会以同步方式让渲染线程完成渲染工做。其中会先调用processLayerUpdate()更新全部硬件加速层中的属性,继而调用到DeferredLayerUpdater的apply()函数,其中检测到标志位mUpdateTexImage被置位,于是会调用doUpdateTexImage()真正更新GL纹理和转换坐标。

 

\

 

最后,总结下这几者的区分和联系。简单地说,SurfaceView是1个有本身Surface的View。它的渲染能够放在单独线程而不是主线程中。其缺点是不能作变形和动画。SurfaceTexture能够用做非直接输出的内容流,这样就提供2次处理的机会。与SurfaceView直接输出相比,这样会有若干帧的延迟。同时,因为它自己管理BufferQueue,所以内存消耗也会略微大1些。TextureView是1个能够把内容流做为外部纹理输出在上面的View。它自己须要是1个硬件加速层。事实上TextureView自己也包括了SurfaceTexture。它与SurfaceView+SurfaceTexture组合相比能够完成相似的功能(即把内容流上的图象转成纹理,而后输出)。区分在于TextureView是在View hierachy中作绘制,所以1般它是在主线程上作的(在Android 5.0引入渲染线程后,它是在渲染线程中作的)。而SurfaceView+SurfaceTexture在单独的Surface上作绘制,能够是用户提供的线程,而不是系统的主线程或是渲染线程。另外,与TextureView相比,它还有个好处是能够用Hardware overlay进行显示。