Surfaceflinger进程提供一个入口来实现对各进程的surface画面进行合并处理,也就是说,Surfaceflinger进程中保存有各进程的图像,(即layer),这里尝试获取各layer图像数据,来加深对surface的理解。
图像保存方法,参考screencap里的截屏实现,
int writePNG(char* fileName, char*base, int w, int h, int f, int s)
{
int fd = open(fileName, O_WRONLY | O_CREAT | O_TRUNC, 0664);
if (fd == -1)
{
fprintf(stderr, "Error opening file: %s (%s)\n", fileName, strerror(errno));
return 1;
}
cout << "writePNG called" << endl;
const SkImageInfo info =
SkImageInfo::Make(w, h, flinger2skia(f), kPremul_SkAlphaType,
dataSpaceToColorSpace(HAL_DATASPACE_UNKNOWN));
cout << "3" << endl;
SkPixmap pixmap(info, base, s * bytesPerPixel(f));
cout << "4" << endl;
struct FDWStream final : public SkWStream {
size_t fBytesWritten = 0;
int fFd;
FDWStream(int f) : fFd(f) {cout << "4.1" << endl;}
size_t bytesWritten() const override {cout << "4.2" << endl; return fBytesWritten; }
bool write(const void* buffer, size_t size) override {
fBytesWritten += size;
cout << "4.3" << endl;
// cout << "fBytesWritten=" << fBytesWritten << ", size=" << size << endl;
return size == 0 || ::write(fFd, buffer, size) > 0;
}
} fdStream(fd);
cout << "5" << endl;
/*
enum class SkEncodedImageFormat {
#ifdef GOOGLE3
kUnknown,
#endif
kBMP,
kGIF,
kICO,
kJPEG,
kPNG,
kWBMP,
kWEBP,
kPKM,
kKTX,
kASTC,
kDNG,
kHEIF,
};
*/
//so we can save the data as BMP
(void)SkEncodeImage(&fdStream, pixmap, SkEncodedImageFormat::kPNG, 100);
cout << "6" << endl;
close(fd);
return 0;
}
使用dumpsys SurfaceFlinger可以查看到所有可见的layer的信息,如
Visible layers (count = 6)
+ Layer 0x7787e84000 (com.android.systemui.ImageWallpaper#0)
尝试把slots中的画面获取显示出来
BufferQueueCore.cpp
+ Layer 0x73c42e7000 (testsurface#0)
Region transparentRegion (this=0x73c42e7380, count=1)
[ 0, 0, 0, 0]
Region visibleRegion (this=0x73c42e7010, count=1)
[100, 100, 420, 800]
Region surfaceDamageRegion (this=0x73c42e7088, count=1)
[ 0, 0, 0, 0]
layerStack= 0, z= 100000, pos=(100,100), size=( 320, 700), crop=( 0, 0, -1, -1), finalCrop=( 0, 0, -1, -1), isOpaque=0, invalidate=0, dataspace=Default (0), pixelformat=RGBA_8888 alpha=1.000, flags=0x00000000, tr=[1.00, 0.00][0.00, 1.00]
client=0x73c55a9180
format= 1, activeBuffer=[ 320x 700: 320, 1], queued-frames=0, mRefreshPending=0
mSecure=0, mProtectedByApp=0, mFiltering=0, mNeedsFiltering=0 mDestroyCalled=0
mTexName=9 mCurrentTexture=0
mCurrentCrop=[0,0,0,0] mCurrentTransform=0
mAbandoned=0
- BufferQueue mMaxAcquiredBufferCount=1 mMaxDequeuedBufferCount=2
mDequeueBufferCannotBlock=0 mAsyncMode=0
default-size=[320x700] default-format=1 transform-hint=00 frame-counter=1
FIFO(0):
this=0x73c4275800 (mConsumerName=testsurface#0, mConnectedApi=2, mConsumerUsageBits=2304, mId=168, mPid=362, producer=[17635:./testSurface], consumer=[362:/system/bin/surfaceflinger])
Slots:
>[00:0x73c8e40960] state=ACQUIRED 0x73c8e40b20 frame=1 [ 320x 700: 320, 1]
[01:0x0] state=FREE
[02:0x0] state=FREE
*BufferQueueDump mIsBackupBufInited=0, mAcquiredBufs(size=1)
简单一点,dumpsys可以调用到获取layer数据的地方,我们就在 那里进行处理。
+ Layer 0x73c43cb000 (testsurface#0)
Region transparentRegion (this=0x73c43cb380, count=1)
[ 0, 0, 0, 0]
Region visibleRegion (this=0x73c43cb010, count=1)
[100, 100, 420, 520]
Region surfaceDamageRegion (this=0x73c43cb088, count=1)
[ 0, 0, 0, 0]
layerStack= 0, z= 100000, pos=(100,100), size=( 320, 420), crop=( 0, 0, -1, -1), finalCrop=( 0, 0, -1, -1), isOpaque=0, invalidate=0, dataspace=Default (0), pixelformat=RGBA_8888 alpha=1.000, flags=0x00000000, tr=[1.00, 0.00][0.00, 1.00]
client=0x73c55a9480
format= 1, activeBuffer=[ 320x 420: 320, 1], queued-frames=0, mRefreshPending=0
mSecure=0, mProtectedByApp=0, mFiltering=0, mNeedsFiltering=0 mDestroyCalled=0
mTexName=7 mCurrentTexture=1
mCurrentCrop=[0,0,0,0] mCurrentTransform=0
mAbandoned=0
- BufferQueue mMaxAcquiredBufferCount=1 mMaxDequeuedBufferCount=2
mDequeueBufferCannotBlock=0 mAsyncMode=0
default-size=[320x420] default-format=1 transform-hint=00 frame-counter=2
FIFO(0):
this=0x73c4275800 (mConsumerName=testsurface#0, mConnectedApi=2, mConsumerUsageBits=2304, mId=59, mPid=362, producer=[9070:./testSurface], consumer=[362:/system/bin/surfaceflinger])
Slots:
>[01:0x73c8e41300] state=ACQUIRED 0x73c8e414c0 frame=2 [ 320x 420: 320, 1]
[00:0x73c8e40260] state=FREE 0x73c8e40c00 frame=1 [ 720x1280: 720, 1]
[02:0x0] state=FREE
*BufferQueueDump mIsBackupBufInited=0, mAcquiredBufs(size=1)
[00] handle=0x73c8e414c0, fence=0x73c8e14230, time=0x45cff12d2788, xform=0x00
FPS ring buffer:
(0) 12:25:46.646 fps=0.33 dur=3027.87 max=3027.87 min=3027.87
+ Layer 0x73c4221000 (StatusBar#0)
调用到Layer.cpp中的dumpsys
if (mSurfaceFlingerConsumer != 0) {
mSurfaceFlingerConsumer->dumpState(result, " ");
}
void GLConsumer::dumpLocked(String8& result, const char* prefix) const
{
result.appendFormat(
"%smTexName=%d mCurrentTexture=%d\n"
"%smCurrentCrop=[%d,%d,%d,%d] mCurrentTransform=%#x\n",
prefix, mTexName, mCurrentTexture, prefix, mCurrentCrop.left,
mCurrentCrop.top, mCurrentCrop.right, mCurrentCrop.bottom,
mCurrentTransform);
ConsumerBase::dumpLocked(result, prefix);
}
void ConsumerBase::dumpLocked(String8& result, const char* prefix) const {
result.appendFormat("%smAbandoned=%d\n", prefix, int(mAbandoned));
if (!mAbandoned) {
String8 consumerState;
mConsumer->dumpState(String8(prefix), &consumerState);
result.append(consumerState);
}
}
android$ find . -name *.cpp | xargs grep 'Slots:'
./frameworks/native/libs/gui/BufferQueueCore.cpp: outResult->appendFormat("%sSlots:\n", prefix.string());
调用到
void BufferQueueCore::dumpState(const String8& prefix, String8* outResult) const {
outResult->appendFormat("%sSlots:\n", prefix.string());
for (int s : mActiveBuffers) {
const sp
// A dequeued buffer might be null if it's still being allocated
if (buffer.get()) {
outResult->appendFormat("%s %s[%02d:%p] ", prefix.string(),
(mSlots[s].mBufferState.isAcquired()) ? ">" : " ", s,
buffer.get());
outResult->appendFormat("state=%-8s %p frame=%" PRIu64, mSlots[s].mBufferState.string(),
buffer->handle, mSlots[s].mFrameNumber);
这里,使用到了GraphicBuffer
初步思路
参考screencap的code,写一个服务,用来接收fd,进行mmap后保存图片
LINUX/android/frameworks/native$ find . -name BufferQueueCore.cpp
./libs/gui/BufferQueueCore.cpp
在里面传送fd