在前面的【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
if (buffer != 0) {
data.write(*gbuffer);
}
int err = service->transact(5, data, &reply, 0);
接收端
sp
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的图像了