表面视图和纹理视图

SurfaceView和TextureView是Android进行视频开发必须使用的两个控件。它们的特点和使用场景有什么区别?我们总结如下:

以下来自CharonChui。

在说SurfaceView之前,我们需要先说几个相关的部分。

SurfaceView就是在窗口上挖一个洞,它显示在这个洞里,其他视图显示在窗口上,这样视图就可以显式的在SurfaceView上面,也可以在SurfaceView上面加一些层。

显然,当创建每个SurfaceView时,会创建一个Mywindow。new Mywindow中的this(这)就是SurfaceView本身,所以SurfaceView和窗口是绑定在一起的,如上所述,每个窗口对应一个曲面。

所以SurfaceView内嵌了自己的Surface,可以认为是控制Surface的位置和大小。传统视图及其派生类的更新只能在UI线程中完成,但是UI线程也处理其他交互逻辑。

这样无法保证视图的更新速度和帧率,而SurfaceView可以独立线程绘制,所以可以提供更高的帧率。比如游戏、摄像等场景更适合SurfaceView。

SurfaceView的优缺点:

一个通用活动中包含的多个视图将形成视图层次的树形结构,只有顶层的视图对WMS可见。这个Decorview在WMS有对应的WindowState,然后在SurfaceFlinger也有对应的图层。SurfaceView有自己的表面和自己的窗口。它在WMS有相应的WindowState,在SurfaceFlinger有一个图层。虽然它仍然在应用程序端的视图层次结构中,但它在服务器端(WMS和SurfaceFlinger)与主机窗口分离。

这样做的好处是,这个曲面的渲染可以在一个单独的线程中完成,渲染时可以有自己的GL上下文。因为它不影响主线程对时间的响应。所以它的优点是可以在一个独立的线程中绘制,不影响主线程,并且它使用了双缓冲机制,让播放视频时画面更加流畅。

但它也有缺点,因为这个曲面不在View hierachy中,它的显示不受View的属性控制,所以不能做平移、缩放等动画,也不能放在其他ViewGroup中。SurfaceView不能嵌套,不能使用View的某些功能,比如View.setAlpha()。

从Android7.0开始,SurfaceView的窗口位置与其他视图渲染同步更新。这意味着在屏幕上平移和缩放SurfaceView不会导致渲染失真。

显示表面的抽象界面,使您能够控制表面的大小和格式,编辑表面上的像素,并监视表面的变化。该接口通常通过SurfaceView类实现。

简单来说,我们不能直接操作曲面,只能通过SurfaceHolder的接口来获取和操作曲面。

SurfaceHolder中提供了一些lockCanvas():获取一个画布对象并锁定它。产生的画布对象实际上是表面的一个成员。锁定的目的实际上是为了确保在绘制过程中,

表面中的数据不会改变。LockCanvas是为了防止多个线程同时写入同一个画布。

从设计模式的角度来看,Surface、SurfaceView、SurfaceHolder本质上都是MVC(模型-视图-控制器),Model是模型或者数据模型,可以简单理解为数据,这里是Surface。

View就是视图,代表用户交互界面,这里是曲面视图,曲面持有者就是控制器。

——《音视频开发之旅》注:这个MVC理解很精彩。

因为上面提到的SurfaceView不在主窗口中,所以不能动画化,不能使用View的一些特性和方法,所以在Android 4.0中引入了TextureView,这是一个结合了View和SurfaceTexture的View对象。它不是单独在WMS创建一个窗口,而是作为视图层次结构中的一个普通视图,因此它可以像其他普通视图一样执行平移、旋转和其他动画。但是,TextureView必须在硬件加速的窗口中,它显示的内容流数据可以来自App进程,也可以来自远程进程。

TextureView重载draw()方法,在该方法中,主SurfaceTexture中接收的图像数据作为纹理更新到相应的HardwareLayer。

表面纹理。onframeavailablelistener用于通知TextureView内容流新图像已经到达。SurfaceTextureListener接口用于让TextureView的用户知道SurfaceTexture已经准备好了,这样SurfaceTexture就可以交给相应的内容源了。Surface是BufferQueue的生产者接口实现类,生产者通过其软件或硬件渲染接口,在SurfaceTexture内部为BufferQueue提供图形缓冲。

SurfaceTexture可以作为间接输出内容流,为二次处理提供了机会。相对于SurfaceView的直接输出,会有几帧的延迟。同时,因为它自己管理BufferQueue,所以内存消耗会稍微大一些。

TextureView是一个可以将内容流作为外部纹理输出的视图,它需要是一个硬件加速层。

表面纹理是表面和OpenGL ES(GLES)纹理的组合。表面纹理用于向GLES纹理提供表面输出。

SurfaceTexture是从Android 3.0加入的。与SurfaceView不同的是,它对图像流的处理不是直接显示,而是转换成GL外部纹理,所以用于图像流数据的二次处理。

比如相机的预览数据可以在纹理化后交给GLSurfaceView直接显示,也可以通过SurfaceTexture交给TextureView作为硬件加速层显示在View heirachy中。首先,SurfaceTexture从图像流中获取帧数据(从相机预览、视频解码、GL绘制场景等。).调用updateTexImage()时,根据内容流中最新的图像更新SurfaceTexture对应的GL纹理对象。

SurfaceTexture包含一个应用程序使用的BufferQueue。当生产者将一个新的缓冲区排队时,onFrameAvailable()回调通知应用程序。然后,应用程序调用updateTexImage(),它将释放之前占用的缓冲区,从队列中获取一个新的缓冲区并执行EGL调用,这样GLES就可以使用这个缓冲区作为外部纹理。

SurfaceView是一个有自己表面的视图。它的渲染可以放在一个单独的线程中,而不是主线程中。它的缺点是不能变形和动画。

SurfaceTexture可以作为间接输出内容流,为二次处理提供了机会。相对于SurfaceView的直接输出,会有几帧的延迟。同时,因为它自己管理BufferQueue,所以内存消耗会稍微大一些。

TextureView是一个可以将内容流作为外部纹理输出的视图。它本身需要是一个硬件加速层。其实TextureView本身也包含了SurfaceTexture。与表面视图和表面纹理的结合相比,它可以完成类似的功能(即将内容流上的图像转换为纹理,然后输出)。

TextureView是在视图层次中绘制的,所以一般在主线程上完成(Android 5.0引入渲染线程后,在渲染线程中完成)。SurfaceView+SurfaceTexture在单独的表面上绘制,可以是用户提供的线程,而不是系统的主线程或渲染线程。

与SurfaceView相比,TextureView具有更好的Alpha版本和旋转处理能力,但SurfaceView在视频上分层合成界面元素时具有性能优势。当客户端使用SurfaceView呈现内容时,SurfaceView为客户端提供了一个单独的复合层。如果设备支持,SurfaceFlinger会将各个层合成一个硬件覆盖层。当客户端使用TextureView渲染内容时,接口工具包会使用GPU将TextureView的内容合成到视图层次结构中。对内容的更新可能会导致重新绘制其他视图元素,例如,如果其他视图位于TextureView之上。视图渲染完成后,SurfaceFlinger会合成应用程序接口层和所有其他层,这样每个可见像素都会合成两次。

注意:受DRM保护的视频只能在覆盖平面上显示。支持受保护内容的视频播放器必须使用SurfaceView来实现。