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 分钟前
解决 Anki 启动器下载错误的完整指南
前端
im_AMBER26 分钟前
Web 开发 27
前端·javascript·笔记·后端·学习·web
聪明的笨猪猪1 小时前
Java Redis “缓存设计”面试清单(含超通俗生活案例与深度理解)
java·经验分享·笔记·面试
蓝胖子的多啦A梦1 小时前
低版本Chrome导致弹框无法滚动的解决方案
前端·css·html·chrome浏览器·版本不同造成问题·弹框页面无法滚动
玩代码1 小时前
vue项目安装chromedriver超时解决办法
前端·javascript·vue.js
訾博ZiBo1 小时前
React 状态管理中的循环更新陷阱与解决方案
前端
StarPrayers.1 小时前
旅行商问题(TSP)(2)(heuristics.py)(TSP 的两种贪心启发式算法实现)
前端·人工智能·python·算法·pycharm·启发式算法
聪明的笨猪猪2 小时前
Java Redis “运维”面试清单(含超通俗生活案例与深度理解)
java·经验分享·笔记·面试
一壶浊酒..2 小时前
ajax局部更新
前端·ajax·okhttp
苏打水com2 小时前
JavaScript 面试题标准答案模板(对应前文核心考点)
javascript·面试