纯净、安全、绿色的下载网站

首页|软件分类|下载排行|最新软件|IT学院

当前位置:首页IT学院IT技术

Android SurfaceTexture TextureView SurfaceView GLSurfaceView Android中SurfaceTexture TextureView Surf

ariesjzj   2021-09-10 我要评论
想了解Android中SurfaceTexture TextureView SurfaceView GLSurfaceView的区别的相关内容吗ariesjzj在本文为您仔细讲解Android SurfaceTexture TextureView SurfaceView GLSurfaceView的相关知识和一些Code实例欢迎阅读和指正我们先划重点:Android,SurfaceTexture,TextureView,SurfaceView,GLSurfaceView区别下面大家一起来学习吧

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

SurfaceView

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

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

GLSurfaceView

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

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

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

 

SurfaceTexture

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

 

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

 

TextureView

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

 

实例解读

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

首先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<GLConsumer> 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<JNISurfaceTextureContext> 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的构造函数之一带有参数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<IGraphicBufferProducer> producer(SurfaceTexture_getProducer(env, surfaceTextureObj));
...
144    sp<Surface> 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建立起了一条通知链:MediaPlayer -> SurfaceTexture -> VideDumpRenderer在onFrameAvailable()回调函数中将updateSurface标志设为true表示有新的图像到来需要更新Surface了为毛不在这儿马上更新纹理呢因为当前可能不在渲染线程SurfaceTexture对象可以在任意线程被创建(回调也会在该线程被调用)但updateTexImage()只能在含有纹理对象的GL context所在线程中被调用因此一般情况下回调中不能直接调用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)给了一个简洁的例子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();
              ...
      }

看一下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) {

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

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

可以看到其中会调用updateLayer()函数然后通过invalidate()函数申请更新UIupdateLayer()会设置mUpdateLayer标志位这样当下次VSync到来时Choreographer通知App通过重绘View hierachy在UI重绘函数performTranversals()中作为View hierachy的一分子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引入了渲染线程它是一个更大的topic超出本文范围这里只说相关的部分作为背景知识下面只画出了相关的类可以看到ThreadedRenderer作为新的HardwareRenderer替代了Android 4.4中的Gl20Renderer其中比较关键的是RenderProxy类需要让渲染线程干活时就通过这个类往渲染线程发任务RenderProxy中指向的RenderThread就是渲染线程的主体了其中的threadLoop()函数是主循环大多数时间它会poll在线程的Looper上等待当有同步请求(或者VSync信号)过来它会被唤醒然后处理TaskQueue中的任务TaskQueue是RenderTask的队列RenderTask代表一个渲染线程中的任务如DrawFrameTask就是RenderTask的继承类之一它主要用于渲染当前帧而DrawFrameTask中的DeferredLayerUpdater集合就存放着之前对硬件加速层的更新操作申请

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

 

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


相关文章

猜您喜欢

  • C# 多态 C# 最基础知识介绍--多态

    想了解C# 最基础知识介绍--多态的相关内容吗憨憨学设计在本文为您仔细讲解C# 多态的相关知识和一些Code实例欢迎阅读和指正我们先划重点:C#,多态下面大家一起来学习吧..
  • Java 的封装与接口 Java基础第四篇 封装与接口

    想了解Java基础第四篇 封装与接口的相关内容吗Vamei在本文为您仔细讲解Java 的封装与接口的相关知识和一些Code实例欢迎阅读和指正我们先划重点:Java,的封装,Java的接口下面大家一起来学习吧..

网友评论

Copyright 2020 www.sopisoft.net 【绿软下载站】 版权所有 软件发布

声明:所有软件和文章来自软件开发商或者作者 如有异议 请与本站联系 点此查看联系方式