【Android】从SurfaceFlinger中获取各layer图片(2)

本文探讨了在Android系统中,通过SurfaceFlinger获取各Layer图像数据的过程与挑战。介绍了尝试通过共享内存fd访问图像数据的初步想法,讨论了遇到的权限、映射和访问问题,并分享了成功通过GraphicBuffer对象传送实现目标的经验。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在前面的【Android】从SurfaceFlinger中获取各layer图片(1)里面,提到通过获取surfaceFlinger中的GraphicBuffer结构中

的共享内存的fd来达到访问图像数据的内存地址,进而实现访问对应的图像数据,

理论上是可行的,但是实际处理会有问题,最终需要通过传送GraphicBuffer对象来达到这个目的。

这里记录下探索过程

 

LINUX/android/frameworks/native$ find . -name BufferQueueCore.cpp

./libs/gui/BufferQueueCore.cpp

 

在里面传送fd

 

Unknown:/data/local/tmp # ./screenget

add myService

onTransact called, case 4

Segmentation fault

 

使用char* base = new char[w*h*4];代替mmap操作,

可以保存图像,没有段错误,说明保存图像的方法没有问题

 

 

                        void* mappedAddress= mmap(0, w*h*4, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);

                        if (mappedAddress == MAP_FAILED) {

                       printf("Could not mmap %s \n\n", strerror(errno));

                       //return -errno;

                           }

 

 

Unknown:/data/local/tmp # ./screenget

add myService

onTransact called, case 4

fd=6, w=720, h=1280, format=1

1

Could not mmap Permission denied

 

====== base not null

 

 

 

可能是由于gralloc模块的内存不允许直接访问

 

改成read方法也报错,

if (read(fd, mappedAddress, w*h*4) == -1)

       {printf("Could not read, %s \n\n", strerror(errno));}

 

Could not read, Invalid argument

 

 

59struct private_handle_t : public native_handle {

60#else

61struct private_handle_t {

62    struct native_handle nativeHandle;

63#endif

64

65    enum {

66        PRIV_FLAGS_FRAMEBUFFER = 0x00000001

67    };

68

69    // file-descriptors

70    int     fd;

71    // ints

72    int     magic;

73    int     flags;

74    int     size;

75    int     offset;

76

77    // FIXME: the attributes below should be out-of-line

78    uint64_t base __attribute__((aligned(8)));

 

尝试去获取base指针,报错

         //error: cast from 'const int *' to 'uint64_t *' (aka 'unsigned long *')

         // increases required alignment from 4 to 8 [-Werror,-Wcast-align]

 

         //const int* pint = &(buffer->handle->data[5]);

         //uint64_t* ptr = (uint64_t*)pint;

         //void* vaddr = (void*)(*ptr);

  

 

 

 

Memcpy报错

 

01-01 13:24:54.387 15611 15611 F DEBUG   : signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x100000500

01-01 13:24:54.388 15611 15611 F DEBUG   :     x0   000000748617f000  x1   0000000100000500  x2   0000000000384000  x3   000000748617f000

01-01 13:24:54.388 15611 15611 F DEBUG   :     x4   0000000100384500  x5   0000007486503000  x6   000000000000000a  x7   000000000000000a

01-01 13:24:54.388 15611 15611 F DEBUG   :     x8   000000748bb7266c  x9   0000000000000000  x10  0000000000000001  x11  0000000000000000

 

01-01 13:24:54.439 15611 15611 F DEBUG   : backtrace:

01-01 13:24:54.440 15611 15611 F DEBUG   :     #00 pc 000000000001c418  /system/lib64/libc.so (memcpy+232)

01-01 13:24:54.441 15611 15611 F DEBUG   :     #01 pc 000000000005ce1c  /system/lib64/libgui.so (android::BufferQueueCore::dumpState(android::String8 const&, android::String8*) const+1112)

01-01 13:24:54.441 15611 15611 F DEBUG   :     #02 pc 000000000005b1d8  /system/lib64/libgui.so (android::BufferQueueConsumer::dumpState(android::String8 const&, android::String8*) const+172)

01-01 13:24:54.443 15611 15611 F DEBUG   :     #03 pc 0000000000065f64  /system/lib64/libgui.so (android::ConsumerBase::dumpLocked(android::String8&, char const*) const+120)

01-01 13:24:54.443 15611 15611 F DEBUG   :     #04 pc 0000000000065ed4  /system/lib64/libgui.so (android::ConsumerBase::dumpState(android::String8&, char const*) const+60)

 

 

这个base指针的内容有问题?

 

 

查看log

==== the fd = 101, handle numFds=3, size=0, offset=720

 

为什么fd的数量是3,

看来有必要把fd都打印出来,

         outResult->appendFormat("==== the fd = %d, handle numFds=%d, data[1]=%d,data[2]=%d size=%d, offset=%d\n\n",

         buffer->handle->data[0], buffer->handle->numFds,

         buffer->handle->data[1], buffer->handle->data[2],

         buffer->handle->data[3], buffer->handle->data[4]);

 

 

打印结果

==== the fd = 87, handle numFds=3, data[1]=89,data[2]=90 size=0, offset=320

 

查看surfaceflinger进程的fd

Unknown:/ $ ps -ef|grep sur

system         479     1 9 11:58:19 ?     00:00:28 surfaceflinger

shell         6460  6456 7 12:03:29 pts/13 00:00:00 grep sur

 

adb shell ls -l /proc/479/fd > fd_sf.txt

 

 

 

87是anon_inode:gralloc_extra

90是/dev/ashem

难道data[2]才是需要传递的共享内存fd?

 

接下来测试,

writePNG called

Bus error

 

 

传送fd的方式报错,

Unknown:/data/local/tmp # ./screenget

add myService

onTransact called, case 4

onTransact called, case 4

fd=6, w=320, h=700, format=1

1

====== base not null

====== base[2]=

==base[0]=255, base[1]=255, base[2]=255, base[3]=255, base[4]=255

 

i=0

i=1

i=2

Bus error

 

 

 

这个fd的映射空间不能访问

直接在surfaceflinger进程中看看相应的内存是否可以访问

 

导入private_handle_t,可以直接获取到数据

const private_handle_t* pt = (private_handle_t*)buffer->handle;

              uint64_t value=0;

              memcpy(&value, &(buffer->handle->data[5]), sizeof(uint64_t));

                                          void* vaddr = (void*)value;

                                          void* vaddr2 = (void*)pt->base;

              outResult->appendFormat("==== vaddr=%p, vaddr2=%p ", vaddr, vaddr2);

 

vaddr与vaddr2的值一样,说明使用memcpy得到的指针是没有问题的

 

char c;

char* base = (char*)vaddr2;

for (int i = 0; i < h; i++)

 {

      for (int j = 0; j < w*4; j++)

      {

             c = base[i*w*4 + j];

      }

      ALOGD("==== i=%d, \n", i);

 

这段代码执行会出问题,说明这个base指针不能直接使用,可能和gralloc里面的处理有关。

 

5-16

考虑传送GraphicBuffer对象,写入到parcel中

 

                        Parcel data;

                        Parcel reply;

//                     int fd = buffer->handle->data[2];

//                     data.writeFileDescriptor(fd);

                        data.writeInt32(buffer->width);

                        data.writeInt32(buffer->height);

                        data.writeInt32(buffer->format);

     sp<GraphicBuffer> gbuffer = buffer;

           

            if (buffer != 0) {

                data.write(*gbuffer);

            }                  

                        

                    int err = service->transact(5, data, &reply, 0);

 

 

 

接收端

 

                       

                        sp<GraphicBuffer> buf = new GraphicBuffer();;

                        data.read(*buf);

                       

                        Region newDirtyRegion;

                        const Rect bounds(w, h);

                        newDirtyRegion.set(bounds);

                       

                        int fenceFd = -1;

                        void* vaddr;

                        //GRALLOC_USAGE_SW_READ_OFTEN 0x00000003U

                        //GRALLOC_USAGE_SW_WRITE_OFTEN 0x00000030U

        status_t res = buf->lockAsync(0x00000003U | 0x00000030U,

                newDirtyRegion.bounds(), &vaddr, fenceFd);

               

              printf("==== vaddr=%p\n", vaddr);

             

              char pname[32] = {0};

                        sprintf(pname, "%02d.png", icode++);

                        char* base = (char*)vaddr;                

                        writePNG(pname, base, w, h, format, s);

 

通过dumpsys进行触发调用,可以保存各surface的图像了

 

### 如何在 SurfaceFlinger获取 Buffer 在 Android 系统中,`SurfaceFlinger` 是负责管理图形缓冲区的核心组件之一。为了理解 `SurfaceFlinger` 获取 buffer 的过程,可以从其工作流程入手。 当应用程序请求绘制新一帧时,会向 `SurfaceFlinger` 发送消息通知有新的图像数据准备好。此时,`SurfaceFlinger` 将尝试从现有的双缓存池中分配一个可用的 buffer 给应用线程用于填充图像内容[^1]。具体来说: - **Buffer 分配机制** 应用程序和 `SurfaceFliner` 使用共享内存中的缓冲区进行通信。每当需要更新界面时,应用层会先检查是否有空闲的 buffer 可供使用。如果没有,则需等待直到有一个 buffer 被释放出来为止。这一步骤确保了即使是在高负载条件下也能维持稳定的画面输出质量[^3]。 - **VSync 同步** 当检测到垂直同步脉冲 (VSync) 来临时,`SurfaceFlinger` 主线程会被唤醒,并开始准备下一帧的画面合成工作。在此期间,它会查询各个 Layer 是否已有待处理的新 frame 数据。对于那些已经完成了 queueBuffer 操作的应用而言,它们所关联的 layer 就会有最新的 buffer 准备好参与本次合成操作[^2]。 - **queueBuffer 实现细节** 关键在于 `queueBuffer()` 方法的具体实现上,在每次完成一帧渲染之后都会调用此接口来提交当前使用的 buffer 至显示队列中去。这个过程中涉及到与硬件抽象层(HAL)交互以及可能存在的重复提交问题预防措施等复杂逻辑[^4]。 ```cpp // 假设这是简化版的 queueBuffer 接口定义 void GrallocModule::queueBuffer(buffer_handle_t handle, int fenceFd){ // 处理 buffer 提交前后的各种准备工作... } ``` #### 注意事项 值得注意的是,上述描述基于一般情况下的行为模式;不同版本之间可能存在细微差异。此外,由于涉及到底层架构的知识点较多,建议深入研究官方文档及相关源码以获得更详尽的信息。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值