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