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