chipset: MSM8x25Q
codebase: Android4.1
Screencap和screenshot大同小异,不过前者是直接用build好的一个可执行文件来操作的,文件位于/system/bin/screencap。看下code:
int main(int argc, char** argv)
{
const char* pname = argv[0];
bool png = false;
int c;
while ((c = getopt(argc, argv, "ph")) != -1) {
switch (c) {
case 'p':
png = true;
break;
case '?':
case 'h':
usage(pname);
return 1;
}
}
argc -= optind;
argv += optind;
int fd = -1;
if (argc == 0) {
fd = dup(STDOUT_FILENO);
} else if (argc == 1) {
/*获取参数argv[0],为要保存screencap的path*/
const char* fn = argv[0];
fd = open(fn, O_WRONLY | O_CREAT | O_TRUNC, 0664);
if (fd == -1) {
fprintf(stderr, "Error opening file: %s (%s)\n", fn, strerror(errno));
return 1;
}
const int len = strlen(fn);
/*path file是否为png*/
if (len >= 4 && 0 == strcmp(fn+len-4, ".png")) {
png = true;
}
}
if (fd == -1) {
usage(pname);
return 1;
}
void const* mapbase = MAP_FAILED;
ssize_t mapsize = -1;
void const* base = 0;
uint32_t w, h, f;
size_t size = 0;
ScreenshotClient screenshot;
/*最终通过SF的update去重画*/
if (screenshot.update() == NO_ERROR) {
/*获得显示buffer的address*/
base = screenshot.getPixels();
w = screenshot.getWidth();
h = screenshot.getHeight();
f = screenshot.getFormat();
size = screenshot.getSize();
ALOGE("screencap w:%d, h:%d, f:%d, size:%d", w, h, f, size);
} else {
/*上面用SF更新buffer失败的话就直接从fb中获得。*/
const char* fbpath = "/dev/graphics/fb0";
int fb = open(fbpath, O_RDONLY);
if (fb >= 0) {
struct fb_var_screeninfo vinfo;
if (ioctl(fb, FBIOGET_VSCREENINFO, &vinfo) == 0) {
uint32_t bytespp;
if (vinfoToPixelFormat(vinfo, &bytespp, &f) == NO_ERROR) {
size_t offset = (vinfo.xoffset + vinfo.yoffset*(((vinfo.xres + 31) >> 5) << 5)) * bytespp;
w = ((vinfo.xres + 31) >> 5) << 5;
h = vinfo.yres;
size = w*h*bytespp;
ALOGE("screencap offset:%d, w:%d, h:%d, size:%d", offset, w, h, size);
mapsize = offset + size;
mapbase = mmap(0, mapsize, PROT_READ, MAP_PRIVATE, fb, 0);
if (mapbase != MAP_FAILED) {
base = (void const *)((char const *)mapbase + offset);
}
}
}
close(fb);
}
}
if (base) {
/*如果是png格式就需要压缩、编码*/
if (png) {
SkBitmap b;
b.setConfig(flinger2skia(f), w, h);
b.setPixels((void*)base);
SkDynamicMemoryWStream stream;
SkImageEncoder::EncodeStream(&stream, b,
SkImageEncoder::kPNG_Type, SkImageEncoder::kDefaultQuality);
SkData* streamData = stream.copyToData();
write(fd, streamData->data(), streamData->size());
streamData->unref();
} else {
/*将宽、高,数据格式以及显示数据写到fd中*/
write(fd, &w, 4);
write(fd, &h, 4);
write(fd, &f, 4);
write(fd, base, size);
}
}
close(fd);
if (mapbase != MAP_FAILED) {
munmap((void *)mapbase, mapsize);
}
return 0;
}
整个流程比较简单,不过要注意从fb中直接获取,对于当camera preview或者video playback
的数据时YUV的情况,screencap就不能显示了。
screenshot.update()是利用OpenGL来重新render,看下这部分:
status_t ScreenshotClient::update() {
sp<ISurfaceComposer> s(ComposerService::getComposerService());
if (s == NULL) return NO_INIT;
mHeap = 0;
/*s是指SurfacFlinger*/
return s->captureScreen(0, &mHeap,
&mWidth, &mHeight, &mFormat, 0, 0,
0, -1UL);
}
status_t SurfaceFlinger::captureScreen(DisplayID dpy,
sp<IMemoryHeap>* heap,
uint32_t* width, uint32_t* height, PixelFormat* format,
uint32_t sw, uint32_t sh,
uint32_t minLayerZ, uint32_t maxLayerZ)
{
// only one display supported for now
if (CC_UNLIKELY(uint32_t(dpy) >= DISPLAY_COUNT))
return BAD_VALUE;
if (!GLExtensions::getInstance().haveFramebufferObject())
return INVALID_OPERATION;
class MessageCaptureScreen : public MessageBase {
SurfaceFlinger* flinger;
DisplayID dpy;
sp<IMemoryHeap>* heap;
uint32_t* w;
uint32_t* h;
PixelFormat* f;
uint32_t sw;
uint32_t sh;
uint32_t minLayerZ;
uint32_t maxLayerZ;
status_t result;
public:
MessageCaptureScreen(SurfaceFlinger* flinger, DisplayID dpy,
sp<IMemoryHeap>* heap, uint32_t* w, uint32_t* h, PixelFormat* f,
uint32_t sw, uint32_t sh,
uint32_t minLayerZ, uint32_t maxLayerZ)
: flinger(flinger), dpy(dpy),
heap(heap), w(w), h(h), f(f), sw(sw), sh(sh),
minLayerZ(minLayerZ), maxLayerZ(maxLayerZ),
result(PERMISSION_DENIED)
{
}
status_t getResult() const {
return result;
}
virtual bool handler() {
Mutex::Autolock _l(flinger->mStateLock);
// if we have secure windows, never allow the screen capture
if (flinger->mSecureFrameBuffer)
return true;
result = flinger->captureScreenImplLocked(dpy,
heap, w, h, f, sw, sh, minLayerZ, maxLayerZ);
return true;
}
};
sp<MessageBase> msg = new MessageCaptureScreen(this,
dpy, heap, width, height, format, sw, sh, minLayerZ, maxLayerZ);
/*会调用自己的handle()函数*/
status_t res = postMessageSync(msg);
if (res == NO_ERROR) {
res = static_cast<MessageCaptureScreen*>( msg.get() )->getResult();
}
return res;
}
status_t SurfaceFlinger::captureScreenImplLocked(DisplayID dpy,
sp<IMemoryHeap>* heap,
uint32_t* w, uint32_t* h, PixelFormat* f,
uint32_t sw, uint32_t sh,
uint32_t minLayerZ, uint32_t maxLayerZ)
{
ATRACE_CALL();
status_t result = PERMISSION_DENIED;
// only one display supported for now
if (CC_UNLIKELY(uint32_t(dpy) >= DISPLAY_COUNT))
return BAD_VALUE;
if (!GLExtensions::getInstance().haveFramebufferObject())
return INVALID_OPERATION;
// get screen geometry
const DisplayHardware& hw(graphicPlane(dpy).displayHardware());
const uint32_t hw_w = hw.getWidth();
const uint32_t hw_h = hw.getHeight();
/*ap申请的宽高是否合法*/
if ((sw > hw_w) || (sh > hw_h))
return BAD_VALUE;
sw = (!sw) ? hw_w : sw;
sh = (!sh) ? hw_h : sh;
const size_t size = sw * sh * 4;
//ALOGD("screenshot: sw=%d, sh=%d, minZ=%d, maxZ=%d",
// sw, sh, minLayerZ, maxLayerZ);
// make sure to clear all GL error flags
while ( glGetError() != GL_NO_ERROR ) ;
// create a FBO
GLuint name, tname;
/*创建Renderbuffer对象*/
glGenRenderbuffersOES(1, &tname);
/*将Renderbuffer绑定到opengl库中*/
glBindRenderbufferOES(GL_RENDERBUFFER_OES, tname);
/*Renderbuffer本身不含有内存空间,需要分配*/
glRenderbufferStorageOES(GL_RENDERBUFFER_OES, GL_RGBA8_OES, sw, sh);
/*创建非屏幕显示的framebuffer 对象*/
glGenFramebuffersOES(1, &name);
/* 将Framebuffer绑定到Opengl库中*/
glBindFramebufferOES(GL_FRAMEBUFFER_OES, name);
/*将Renderbuffer链接到Framebuffer中*/
glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES,
GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, tname);
/*在上面的操作之后,对应的操作都是重定向到framebuffer对象中,而不是可见屏幕。*/
GLenum status = glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES);
if (status == GL_FRAMEBUFFER_COMPLETE_OES) {
// invert everything, b/c glReadPixel() below will invert the FB
glViewport(0, 0, sw, sh);
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
glOrthof(0, hw_w, hw_h, 0, 0, 1);
glMatrixMode(GL_MODELVIEW);
// redraw the screen entirely...
glClearColor(0,0,0,1);
glClear(GL_COLOR_BUFFER_BIT);
const LayerVector& layers(mDrawingState.layersSortedByZ);
const size_t count = layers.size();
for (size_t i=0 ; i<count ; ++i) {
const sp<LayerBase>& layer(layers[i]);
const uint32_t flags = layer->drawingState().flags;
if (!(flags & ISurfaceComposer::eLayerHidden)) {
const uint32_t z = layer->drawingState().z;
if (z >= minLayerZ && z <= maxLayerZ) {
layer->drawForSreenShot();
}
}
}
// check for errors and return screen capture
if (glGetError() != GL_NO_ERROR) {
// error while rendering
result = INVALID_OPERATION;
} else {
// allocate shared memory large enough to hold the
// screen capture
sp<MemoryHeapBase> base(
new MemoryHeapBase(size, 0, "screen-capture") );
void* const ptr = base->getBase();
if (ptr) {
// capture the screen with glReadPixels()
ScopedTrace _t(ATRACE_TAG, "glReadPixels");
glReadPixels(0, 0, sw, sh, GL_RGBA, GL_UNSIGNED_BYTE, ptr);
if (glGetError() == GL_NO_ERROR) {
*heap = base;
*w = sw;
*h = sh;
*f = PIXEL_FORMAT_RGBA_8888;
result = NO_ERROR;
}
} else {
result = NO_MEMORY;
}
}
glViewport(0, 0, hw_w, hw_h);
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
} else {
result = BAD_VALUE;
}
// release FBO resources
glBindFramebufferOES(GL_FRAMEBUFFER_OES, 0);
glDeleteRenderbuffersOES(1, &tname);
glDeleteFramebuffersOES(1, &name);
hw.compositionComplete();
// ALOGD("screenshot: result = %s", result<0 ? strerror(result) : "OK");
return result;
}
其中的layer->drawForSreenShot();会调用layerbase.cpp中的函数,然后相应地调用各种type的layer的ondraw函数,
一般情况下都是调用layer.cpp中的ondraw函数,最终调用drawWithOpenGL来render。
所以如果发现画面180°旋转的问题, 我们就可以在drawWithOpenGL这个函数里直接对mVertices做手脚了!
对于Framebuffer object,可参考如下文章,,解释的很好:
http://www.imobilebbs.com/wordpress/archives/3051
20130208