You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在GPU端无需拷贝获取Android当前Activity窗口Surface的帧缓冲区?

如何在GPU端无需拷贝获取Android当前Activity窗口Surface的帧缓冲区?

嘿,我完全懂你的痛点!要30-60FPS的帧率获取Activity画面当OpenGL ES纹理,转Bitmap再上传确实太拖性能了,毕竟多了一次CPU-GPU的数据往返。下面给你俩靠谱的方案,都是GPU内部流转,几乎没拷贝开销:

方案一:用MediaProjection + SurfaceTexture(跨App/通用场景)

这是最通用的方案,Android 5.0(API 21)以上支持,不管是捕获自己App还是其他App的窗口都能用,全程GPU内操作,完全绕开Bitmap:

  1. 先搞定屏幕录制权限
    得先通过MediaProjectionManager向用户申请屏幕录制权限,代码大概是这样:

    MediaProjectionManager manager = (MediaProjectionManager) getSystemService(Context.MEDIA_PROJECTION_SERVICE);
    startActivityForResult(manager.createScreenCaptureIntent(), REQUEST_CODE_SCREEN_CAPTURE);
    

    在onActivityResult里拿到用户授权的MediaProjection实例。

  2. 创建SurfaceTexture并绑定VirtualDisplay
    在OpenGL渲染线程里,先创建一个SurfaceTexture(可以传一个你OpenGL上下文里生成的纹理ID,或者让系统自动生成):

    // 假设你已经有了GL纹理id:textureId
    SurfaceTexture surfaceTexture = new SurfaceTexture(textureId);
    

    然后用MediaProjection创建一个VirtualDisplay,把屏幕输出定向到这个SurfaceTexture:

    DisplayMetrics metrics = getResources().getDisplayMetrics();
    MediaProjection mediaProjection = ...; // 从onActivityResult拿到的实例
    VirtualDisplay virtualDisplay = mediaProjection.createVirtualDisplay(
        "ScreenCapture",
        metrics.widthPixels,
        metrics.heightPixels,
        metrics.densityDpi,
        DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
        new Surface(surfaceTexture),
        null, // 回调可选
        null  // handler可选
    );
    
  3. 在OpenGL里直接用这个纹理
    每次SurfaceTexture有新帧的时候,在GL线程调用surfaceTexture.updateTexImage(),然后就能用你之前传入的textureId直接渲染了——这一步完全是GPU内部的纹理更新,没有任何CPU参与的拷贝,帧率轻松达标。

方案二:用PixelCopy直接拷贝到OpenGL纹理(仅限自己App窗口)

如果你只需要捕获自己App的Activity窗口,也可以用PixelCopy直接把窗口Surface拷贝到你的OpenGL纹理里,虽然是拷贝,但也是GPU内部的操作,比转Bitmap快得多:

// 假设你有GL纹理id:glTextureId,以及Activity的窗口
Window window = getWindow();
PixelCopy.request(window, null, glTextureId, new PixelCopy.OnPixelCopyFinishedListener() {
    @Override
    public void onPixelCopyFinished(int copyResult) {
        if (copyResult == PixelCopy.SUCCESS) {
            // 纹理已经更新完成,可以在GL线程里使用了
        }
    }
}, new Handler(Looper.getMainLooper()));

不过要注意,PixelCopy是异步的,而且每次拷贝是一次操作,如果你要持续获取帧的话,得在每次拷贝完成后发起下一次请求,帧率可能不如MediaProjection的实时绑定稳定。

一些注意事项

  • 线程安全:SurfaceTexture的updateTexImage()必须在OpenGL渲染线程调用,别在主线程瞎搞。
  • 分辨率匹配:创建VirtualDisplay的时候尽量用和原窗口一致的分辨率,避免GPU额外的缩放计算,影响帧率。
  • 资源回收:用完MediaProjection记得调用mediaProjection.stop(),VirtualDisplay也要release(),不然会占系统资源。

备注:内容来源于stack exchange,提问作者dragonfly

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.13 17:09:51