TextureView中的surfaceTexture的作用

它是什么

SurfaceTexture 是一个"消费者 + 纹理桥接器"

  • 作为 BufferQueue 的消费者 接收图像缓冲(GraphicBuffer);

  • 把最新一帧绑定成 OpenGL ES 的外部纹理(GL_TEXTURE_EXTERNAL_OES),供 GPU 采样;

  • 同时可以从它生成一个 Surface (new Surface(surfaceTexture)),给相机、解码器、OpenGL 等 生产者 往里写帧。

TextureView 里它的作用(数据流)

TextureView 内部自带一个 SurfaceTexture,用来把外部视频帧/相机帧"喂"给自己绘制:

scss 复制代码
Producer (Camera/MediaCodec/GL/CPU)
         │  写入
         ▼
      Surface  ──► BufferQueue ──► SurfaceTexture(Consumer)
                                           │ updateTexImage()
                                           ▼
                                   外部纹理(OES)
                                           │ getTransformMatrix()
                                           ▼
                              TextureView 在 GPU 上采样并合成到 View 树
  • Producer:拿到 Surface 后持续输出帧;

  • SurfaceTexture:收到新帧后触发回调,TextureView 在下次绘制时调用 updateTexImage() 把"最新帧"绑定到纹理;

  • TextureView:作为普通 View 参与 UI 合成(支持平移、旋转、缩放、alpha、圆角裁剪、动画等),把那张外部纹理画进自己的内容里。

你能怎么用

典型做法是监听 SurfaceTextureListener,在可用时把 Surface 交给相机或播放器:

kotlin 复制代码
textureView.surfaceTextureListener = object : TextureView.SurfaceTextureListener {
    override fun onSurfaceTextureAvailable(st: SurfaceTexture, width: Int, height: Int) {
        // 建议把默认缓冲区尺寸设为 View 尺寸,避免缩放模糊
        st.setDefaultBufferSize(width, height)

        val surface = Surface(st)
        // 交给 MediaPlayer / ExoPlayer / Camera / MediaCodec 作为输出面
        player.setSurface(surface)
        player.playWhenReady = true
    }

    override fun onSurfaceTextureSizeChanged(st: SurfaceTexture, w: Int, h: Int) {
        st.setDefaultBufferSize(w, h) // 跟随尺寸变化
    }

    override fun onSurfaceTextureDestroyed(st: SurfaceTexture): Boolean {
        // 返回 true 表示我们自己释放;false 表示交给系统
        player.setSurface(null)
        st.release()
        return true
    }

    override fun onSurfaceTextureUpdated(st: SurfaceTexture) {
        // 每帧更新回调,可做轻量统计或截图
    }
}

要点

  • setDefaultBufferSize(w, h):让生产的帧分辨率匹配 View 大小,避免 GPU 额外缩放引起的模糊/开销。

  • Surface 生命周期:停止输出后及时 release();切后台或旋转屏幕会重建。

  • 如自行用 GLES 渲染到这个纹理:配合 attachToGLContext / detachFromGLContext、updateTexImage()、getTransformMatrix()。

和 SurfaceView 的区别(何时选谁)

维度 TextureView (基于 SurfaceTexture) SurfaceView
合成位置 应用进程内作为普通 View 参与 UI 合成 系统合成器(SurfaceFlinger)直接合成,独立表面
变换/裁剪 支持(旋转/缩放/alpha/圆角/动画) 受限,不易做 View 级别动画/裁剪
延迟/性能 多一道应用端合成,灵活换较低延迟的代价 路径更直接,低延迟、高性能(大分辨率/高帧率更有优势)
叠放关系 与其他 View 可正确叠放 Z 顺序独立,容易"盖在最上/下",与 View 叠放受限
典型场景 需要和 UI 动画、圆角、裁剪融合的预览/视频 追求极致性能/低延迟/4K 高帧率播放、相机取景

同步与时序

  • fence 同步 :Producer 写完 → queueBuffer 给到 BufferQueue;TextureView 绘制前调用 updateTexImage() 会等待写入完成的 acquire fence,保证采样的是"已就绪帧"。

  • 变换矩阵:getTransformMatrix(float[16]) 提供采样矩阵,修正裁剪、旋转、Y 翻转等。

常见坑与优化

  1. 尺寸不匹配:没设 setDefaultBufferSize 易导致缩放模糊和额外带宽。

  2. 生命周期泄漏:离开页面未 release(),相机/解码器还在往无主的 Surface 写。

  3. 高刷新/高分辨率卡顿:TextureView 路径多一道应用端合成,4K60/高刷新机型建议评估 SurfaceView。

  4. 频繁重建:切旋转/多窗口导致 SurfaceTexture 重建,注意恢复播放器输出面。

  5. 截图/滤镜:如果要二次处理帧,可在 onSurfaceTextureUpdated 后做轻量工作,重活放到独立渲染线程。

一句话总结

SurfaceTexture 是把 BufferQueue 的图像缓冲"变成一张可采样的外部纹理" 的关键组件;在 TextureView 中,它让视频/相机帧像普通 View 一样参与 UI 动画与合成,从而兼顾渲染灵活性与较低的复制开销。

相关推荐
知其然亦知其所以然6 小时前
面试官最爱问的坑:MySQL 中 FLOAT 和 DOUBLE 你真懂吗?
后端·mysql·面试
web前端1236 小时前
HTML 和 React Native 元素排列方式对比
前端
w_y_fan6 小时前
Flutter中页面拦截器的实现方法
前端·flutter
快起来搬砖了6 小时前
Uniapp 图片前端上传功能实现与详解
前端·uni-app
南北是北北6 小时前
BufferQueue的环形队列是什么设计的
前端·面试
南北是北北6 小时前
Surface中的BufferQueue
前端·面试
willlzq6 小时前
Swift高级特性深度解析:@dynamicMemberLookup与@dynamicCallable在Builder库中的应用
前端
张旭超7 小时前
vue3 上传插件vue-file-agent-next
前端·vue.js
xianxin_7 小时前
CSS Opacity(透明度)
前端