GVKun编程网logo

Go语言实现AndroidFramebuffer屏幕截图功能(android go语言)

14

在本文中,您将会了解到关于Go语言实现AndroidFramebuffer屏幕截图功能的新资讯,同时我们还将为您解释androidgo语言的相关在本文中,我们将带你探索Go语言实现AndroidFra

在本文中,您将会了解到关于Go语言实现AndroidFramebuffer屏幕截图功能的新资讯,同时我们还将为您解释android go语言的相关在本文中,我们将带你探索Go语言实现AndroidFramebuffer屏幕截图功能的奥秘,分析android go语言的特点,并给出一些关于Android 5.0及以上编程实现屏幕截图功能的方法、android framebuffer、Android GraphicBuffer-Fence、Android Surface-GraphicBuffer-BufferQueue的实用技巧。

本文目录一览:

Go语言实现AndroidFramebuffer屏幕截图功能(android go语言)

Go语言实现AndroidFramebuffer屏幕截图功能(android go语言)

package main

/***********
作者:红猎人
***********/

import (
    "os"
    "syscall"
    "fmt"
    "unsafe"
    /*
    "image/png"
    "bytes"
    */
)

var (
    FBIOGET_VSCREENINFO = 0x4600
)

type fbBitfield struct {
    offset uint32 /* beginning of bitfield */
    length uint32 /* length of bitfield */
    msb_right uint32 /* != 0 : Most significant bit is */
}

type fbVarInfo struct {
    xres uint32 /* visible resolution */
    yres uint32
    xres_virtual uint32 /* virtual resolution */
    yres_virtual uint32
    xoffset uint32 /* offset from virtual to visible */
    yoffset uint32 /* resolution */

    bits_per_pixel uint32 /* guess what */
    grayscale uint32 /* 0 = color,1 = grayscale,*/
    /* >1 = FOURCC          */
    red fbBitfield     /* bitfield in fb mem if true color,*/
    green fbBitfield   /* else only length is significant */
    blue fbBitfield
    transp fbBitfield  /* transparency         */

    nonstd uint32 /* != 0 Non standard pixel format */

    activate uint32 /* see FB_ACTIVATE_* */

    height uint32 /* height of picture in mm */
    width uint32 /* width of picture in mm */

    accel_flags uint32 /* (OBSOLETE) see fb_info.flags */

    /* Timing: All values in pixclocks,except pixclock (of course) */
    pixclock uint32 /* pixel clock in ps (pico seconds) */
    left_margin uint32 /* time from sync to picture */
    right_margin uint32 /* time from picture to sync */
    upper_margin uint32 /* time from sync to picture */
    lower_margin uint32
    hsync_len uint32 /* length of horizontal sync */
    vsync_len uint32 /* length of vertical sync */
    sync uint32 /* see FB_SYNC_* */
    vmode uint32 /* see FB_VMODE_* */
    rotate uint32 /* angle we rotate counter clockwise */
    colorspace uint32 /* colorspace for FOURCC-based modes */
    reserved [4]uint32 /* Reserved for future compatibility */
}

func main() {
    fb,err := os.Open("/dev/graphics/fb0")
    if err != nil {
        panic("can't open file /dev/fb0")
    }
    defer fb.Close()
    fmt.Printf("file open %v \n",fb.Fd())

    var varInfo fbVarInfo
    _,_,errno := syscall.RawSyscall(syscall.SYS_IOCTL,fb.Fd(),uintptr(FBIOGET_VSCREENINFO),uintptr(unsafe.Pointer(&varInfo)))
    if errno != 0 {
        fmt.Println(errno)
        panic("can't ioctl ... ")
    }
    fmt.Printf("width = %d height = %d\n",varInfo.xres,varInfo.yres)
    fmt.Printf("xoffset = %d yoffset = %d\n",varInfo.xoffset,varInfo.yoffset)
    fmt.Printf("depth = %d\n",varInfo.bits_per_pixel/8)

    // func Mmap(fd int,offset int64,length int,prot int,flags int) (data []byte,err error)
    bpp := varInfo.bits_per_pixel / 8
    size := varInfo.xres * varInfo.yres * bpp
    offset := varInfo.xoffset * bpp + varInfo.xres * bpp * varInfo.yoffset
    mapsize := size + offset
    fmt.Printf("mapsize = %d\n",mapsize)
    data,err := syscall.Mmap(int(fb.Fd()),int(mapsize),syscall.PROT_READ,syscall.MAP_PRIVATE)
    if err != nil {
        panic("map Failed ...")
    }
    defer syscall.Munmap(data)


    /*
    // save as png image.
    //
    // func Encode(w io.Writer,m image.Image) error
    // m := image.NewRGBA(image.Rect(0,640,480))
    screen := data[offset :]
    m,err := png.Decode(bytes.NewBuffer(screen))
    if err != nil {
        panic("decode fail...")
    }
    output,err := os.OpenFile("/sdcard/screenshot.png",os.O_WRONLY | os.O_CREATE,0666)
    if err != nil {
        panic("can't open file /sdcard/screenshot.png")
    }
    defer output.Close()
    err = png.Encode(output,m)
    if err != nil {
        panic("encoding fail...")
    }
    */
}

Android 5.0及以上编程实现屏幕截图功能的方法

Android 5.0及以上编程实现屏幕截图功能的方法

本文实例讲述了Android 5.0及以上编程实现屏幕截图功能的方法。分享给大家供大家参考,具体如下:

在Android 5.0,API 21 之前想要截图系统屏幕必须Root才能完成,5.0之后开放了接口,下面看我们是怎么实现的。

一. 涉及到的相关类

1. mediaprojectionmanager

官方原话: Manages the retrieval of certain types of {@link MediaProjection} tokens.
这个类通过 Context#getSystemService MEDIA_PROJECTION_SERVICE 获取,他的功能就是获取MediaProjection

2. MediaProjection

官方原话:A token granting applications the ability to capture screen contents and/or record system audio. The exact capabilities granted depend on the type of MediaProjection.在这个类中我们能获取到屏幕的内容

3. ImageReader

官方原话:The ImageReader class allows direct application access to image data
rendered into a {@link android.view.Surface}
通过这个类我们可以把Surface转换成图片

二. 上面三个类就可以完成我们截取屏幕图片的操作,那么下面我们将解释他们是怎么合作完成的

1. 首先获取用户授权,截图屏幕需要用户手动授权后才能操作

@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public void requestCapturePermission() {
 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
 //5.0 之后才允许使用屏幕截图
 return;
 }
 mediaprojectionmanager mediaprojectionmanager = (mediaprojectionmanager)
  getSystemService(Context.MEDIA_PROJECTION_SERVICE);
 startActivityForResult(
  mediaprojectionmanager.createScreenCaptureIntent(),REQUEST_MEDIA_PROJECTION);
}

这里必须使用startActivityForResult 因为在createScreenCaptureIntent() 方法中会返回用户授权截取屏幕的结果,用户根据下面弹窗允许或者拒绝

用户选择后在Activity 的onActivityResult 中操作返回的结果data

@Override
protected void onActivityResult(int requestCode,int resultCode,Intent data) {
  super.onActivityResult(requestCode,resultCode,data);
  switch (requestCode) {
   case REQUEST_MEDIA_PROJECTION:
   if (resultCode == RESULT_OK && data != null) {
    FloatwindowsService.setResultData(data);
    startService(new Intent(getApplicationContext(),FloatwindowsService.class));
   } 
  break;
  }
}

这里我是用FloatwindowsService在桌面上显示一个悬浮按钮,点击截屏,下面我们看在FloatwindowsService 是如何实现截图

2. 截取屏幕内容生成Bitmap

首先创建ImageReader实例

private void createImageReader() {
  mImageReader = ImageReader.newInstance(mScreenWidth,mScreenHeight,PixelFormat.RGBA_8888,2); 
}

然后点击事件中触发startScreenShot()

private void startScreenShot() {
  mFloatView.setVisibility(View.GONE);
  Handler handler = new Handler();
  handler.postDelayed(new Runnable() {
  public void run() {
   //获取当前屏幕内容
   startVirtual();
  }
  },5);
  handler.postDelayed(new Runnable() {
  public void run() {
   //生成图片保存到本地
   startCapture();
  }
  },30);
}

startVirtual() 方法中我们做一件事,就是获取当前屏幕内容

public void startVirtual() {
  if (mMediaProjection != null) {
  virtualdisplay();
  } else {
  setUpMediaProjection();
  virtualdisplay();
  }
}

与此同时需要获取MediaProjection 实例,而mResultData 是授权后返回的结果

public void setUpMediaProjection() {
 if (mResultData == null) {
  Intent intent = new Intent(Intent.ACTION_MAIN);
  intent.addCategory(Intent.CATEGORY_LAUNCHER);
  startActivity(intent);
 } else {
  //mResultData是在Activity中用户授权后返回的结果
  mMediaProjection = getmediaprojectionmanager().getMediaProjection(Activity.RESULT_OK,mResultData);
 }
}

最终得到当前屏幕的内容,注意这里mImageReader.getSurface()被传入,屏幕的数据也将会在ImageReader中的Surface中

private void virtualdisplay() {
 mVirtualdisplay = mMediaProjection.createVirtualdisplay("screen-mirror",mScreenWidth,mScreenDensity,displayManager.VIRTUAL_disPLAY_FLAG_AUTO_MIRROR,mImageReader.getSurface(),null,null);
}

最后把mImageReader得到的屏幕内容数据转换成图片,在AsyncTask中处理,

Image.Plane中的 buffer 数据并不是完全是Bitmap所需要的,需要注意下面3点

1. Image 设置的图片格式与Bitmap设置的必须一致

2. 缓冲数据存在行间距,所以我们必须去除这些间距

3. Image 使用后必须调用image.close();关闭,否则再次使用会报错

@Override
protected Bitmap doInBackground(Image... params) {
 if (params == null || params.length < 1 || params[0] == null) {
  return null;
 }
 Image image = params[0];
 int width = image.getWidth();
 int height = image.getHeight();
 final Image.Plane[] planes = image.getPlanes();
 final ByteBuffer buffer = planes[0].getBuffer();
 //每个像素的间距
 int pixelStride = planes[0].getPixelStride();
 //总的间距
 int rowStride = planes[0].getRowStride();
 int rowPadding = rowStride - pixelStride * width;
 Bitmap bitmap = Bitmap.createBitmap(width + rowPadding / pixelStride,height,Bitmap.Config.ARGB_8888);
 bitmap.copyPixelsFromBuffer(buffer);
 bitmap = Bitmap.createBitmap(bitmap,width,height);
 image.close();

最后把生成的bitmap保存起来,就ok了

GitHub源码下载地址:https://github.com/goodbranch/ScreenCapture

APK文件下载地址:https://raw.githubusercontent.com/goodbranch/AndroidNote/master/note/screenshot/ScreenCapture.apk

更多关于Android相关内容感兴趣的读者可查看本站专题:《Android图形与图像处理技巧总结》、《Android开发入门与进阶教程》、《Android调试技巧与常见问题解决方法汇总》、《Android多媒体操作技巧汇总(音频,视频,录音等)》、《Android基本组件用法总结》、《Android视图View技巧总结》、《Android布局layout技巧总结》及《Android控件用法总结》

希望本文所述对大家Android程序设计有所帮助。

您可能感兴趣的文章:

  • Android中通过view方式获取当前Activity的屏幕截图实现方法
  • Android获取常用辅助方法(获取屏幕高度、宽度、密度、通知栏高度、截图)
  • Android屏幕及view的截图实例详解
  • Android给scrollView截图超过屏幕大小形成长图
  • Android中如何获取视频文件的截图、缩略图
  • 详解有关Android截图与录屏功能的学习
  • Android实现截图和分享功能的代码
  • Android实现拍照截图功能
  • android截图事件监听的原理与实现
  • Android实现从相册截图的功能
  • Android截屏截图的几种方法总结

android framebuffer

android framebuffer

struct fb_fix_screeninfo 该结构体保存的是关于屏幕固定的参数

struct fb_var_screeninfo 该结构体保存的是关于屏幕变化的参数,需要获取frame buffer之前,这个结构体需要更新。

fb_var_screeninfo 的参数介绍

xres     可见矩形的width

yres     可见矩形的height


xres_virtual     

yres_virtual


xoffset

yoffset

void copyFromFrameBuffer()
{
     kesyPrintf("copyFromFrameBuffer\n");
     update_fb_info();
     char* vfd = (char*)readBufferFB();
     
     void const* base = 0;
     
     int bytespp = 4;
     size_t offset =  fscrinfo.line_length * scrinfo.yoffset +  (scrinfo.xoffset) * bytespp;
     base = (void const *)((char const *)vfd + offset);

     int w = scrinfo.xres;
     int h = scrinfo.yres;
     int s = scrinfo.xres_virtual;
     
     kesyPrintf("w=%d,h=%d,s=%d\n", w, h, s);

     int y = 0;
     for (y=0 ; y<h ; y++) {
          memcpy(gNewBuf + y*w*bytespp, base, w*bytespp);
          base = (void *)((char *)base + fscrinfo.line_length);
     }
}


Android GraphicBuffer-Fence

Android GraphicBuffer-Fence

这里需要介绍一个伴随着GraphicBuffer的Fence,包括Fence的诞生,Fence的处理。

在前面介绍Surface的时候,提到过Surface会通过BufferQueueProducer申请GraphicBuffer,用作显存,所以我们这里看一下BufferQueueProducer/Consumer是怎么管理GraphicBuffer的。(这里有一个问题,是否可以脱离BufferQueueProducer/Consumer去使用GraphicBuffer?)

BufferQueueProducer(简称producer)和BufferQueueConsumer(简称consumer)公用BufferQueueCore(简称core);core里面有一个mSlots = struct BufferSlot[64];同时mMaxAcquiredBufferCount=1,mMaxDequeuedBufferCount=1,同时getMaxBufferCountLocked 为3或者2:

int BufferQueueCore::getMaxBufferCountLocked() const {
    int maxBufferCount = mMaxAcquiredBufferCount + mMaxDequeuedBufferCount +
            ((mAsyncMode || mDequeueBufferCannotBlock) ? 1 : 0);

    // limit maxBufferCount by mMaxBufferCount always
    maxBufferCount = std::min(mMaxBufferCount, maxBufferCount);

    return maxBufferCount;
}
//BufferQueueCore创建的时候会初始化mFreeSlots(最多3个元素), mUnusedSlots有64-3个元素

   int numStartingBuffers = getMaxBufferCountLocked();
   for (int s = 0; s < numStartingBuffers; s++) {
     mFreeSlots.insert(s);
   }
   for (int s = numStartingBuffers; s < BufferQueueDefs::NUM_BUFFER_SLOTS;  s++){
     mUnusedSlots.push_front(s);
   }

在APP拿到BufferQueueProducer的时候,在绘制之前,会调用BufferQueueProducer.allocateBuffers,预先分配足够的GraphicBuffer,防止在draw的时候分配造成delay;

分配Buffer的数量和mFreeSlots元素数目相同,也就是3个:

void BufferQueueProducer::allocateBuffers(uint32_t width, uint32_t height,
        PixelFormat format, uint64_t usage) {
         newBufferCount = mCore->mFreeSlots.size();
         。。。。。。
     //创建3个GraphicBuffer for (size_t i = 0; i < newBufferCount; ++i) { sp<GraphicBuffer> graphicBuffer = new GraphicBuffer( allocWidth, allocHeight, allocFormat, BQ_LAYER_COUNT, allocUsage, allocName); status_t result = graphicBuffer->initCheck(); if (result != NO_ERROR) { BQ_LOGE("allocateBuffers: Failed to allocate buffer (%u x %u, format" " %u, usage %#" PRIx64 ")", width, height, format, usage); Mutex::Autolock lock(mCore->mMutex); mCore->mIsAllocating = false; mCore->mIsAllocatingCondition.broadcast(); return; } buffers.push_back(graphicBuffer); } 。。。。。
//将新创建的GraphicBuffer添加到mSlots数组中。注意mSlots的index,这个index很重要,mSlots中的数据是和index绑定的,类似于ID;其中的graphicBuffer可能会发生变化。 for (size_t i = 0; i < newBufferCount; ++i) { if (mCore->mFreeSlots.empty()) { BQ_LOGV("allocateBuffers: a slot was occupied while " "allocating. Dropping allocated buffer."); continue; } auto slot = mCore->mFreeSlots.begin(); mCore->clearBufferSlotLocked(*slot); // Clean up the slot first mSlots[*slot].mGraphicBuffer = buffers[i]; mSlots[*slot].mFence = Fence::NO_FENCE; // freeBufferLocked puts this slot on the free slots list. Since // we then attached a buffer, move the slot to free buffer list. mCore->mFreeBuffers.push_front(*slot); BQ_LOGV("allocateBuffers: allocated a new buffer in slot %d", *slot); // Make sure the erase is done after all uses of the slot // iterator since it will be invalid after this point. mCore->mFreeSlots.erase(slot); } }

下面看一下GraphicBuffer的创建流程:

GraphicBuffer经过GrallocService->GrallocHal,在libgralloc里面分配;其中比较常见的方式是使用ION驱动进行分配,分配完毕之后还要为buffer分配Fence。

 

Android Surface-GraphicBuffer-BufferQueue

Android Surface-GraphicBuffer-BufferQueue

 

Android 的 UI 控件最终在 Surface 上进行绘制;Surface 要进行绘制,需要申请显存,绘制,提交显存进行显示。

申请显存

Android 的显存由两个部分表示,对 APP 的接口体现为 Surface(native/libs/gui/Surface.cpp),对 graphics 部分(CPU/GPU/OPENGL) 体现为 GraphicBuffer。

Surface 说明

Surface 本身有两个含义,一个是代表 UI 系统的 Canvas,另一个是代表本地 window 系统,为跨平台的 OPENGL (EGL) 提供接口。

UI 一般基于 Canvas 绘制,参考 UI 的始祖 View 的 draw 函数:

public void draw(Canvas canvas)

所有 UI 控件继承自 View,都会基于 Canvs 来绘制自己;UI 组件的 draw 是谁触发的,canvas 是怎么创建的?这些秘密在 ViewRootImpl 里面,每个 Activity 在 setContentView 之后,系统会为其创建一个 ViewRootImpl 对象,该对象代替 Activity 管理其 view 系统,并和 window 系统建立关联(Activity 的 window 就是在该类中创建的),并且 ViewRootImpl 会建立和 SurfaceFlinger 的连接,监听 SurfaceFlinger 的 VSYNC 信号,一旦 VSYNC 信号发生,ViewRootImpl 就会进入到 framecallback 中进行绘制。其中 ViewRootImpl 拥有 window 对应的 Surface 对象:

private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
            boolean scalingRequired, Rect dirty, Rect surfaceInsets) {

        // Draw with software renderer.
        final Canvas canvas;try {
          ......
            canvas = mSurface.lockCanvas(dirty);
           ......
            if (!canvas.isOpaque() || yoff != 0 || xoff != 0) {
                canvas.drawColor(0, PorterDuff.Mode.CLEAR);
            }
            try {
                canvas.translate(-xoff, -yoff);
          .......
                mView.draw(canvas);
            } finally {
              ......
            }
        } finally {
            try {
                surface.unlockCanvasAndPost(canvas);
            } catch (IllegalArgumentException e) 
            ......
    }    

对于 Canvas 的使用流程:

Surface.lockCanvas->View.draw(Canvas)-> Surface.unlockCanvasAndPost(Canvas)

在 Surface.lockCanvas 中会调用 native 的对象 android_view_Surface.cpp->Surface.dequeueBuffer->BufferQueueProducer.dequeueBuffer 得到 struct ANativeWindowBuffer 的对象,其实就是一个 GraphicBuffer 对象,与此同时还返回了 FenceID。    

    ANativeWindow_Buffer outBuffer;
    status_t err = surface->lock(&outBuffer, dirtyRectPtr);
    if (err < 0) {
        const char* const exception = (err == NO_MEMORY) ?
                OutOfResourcesException :
                "java/lang/IllegalArgumentException";
        jniThrowException(env, exception, NULL);
        return 0;
    }

    SkImageInfo info = SkImageInfo::Make(outBuffer.width, outBuffer.height,
                                         convertPixelFormat(outBuffer.format),
                                         outBuffer.format == PIXEL_FORMAT_RGBX_8888
                                                 ? kOpaque_SkAlphaType : kPremul_SkAlphaType,
                                         GraphicsJNI::defaultColorSpace());

    SkBitmap bitmap;
    ssize_t bpr = outBuffer.stride * bytesPerPixel(outBuffer.format);
    bitmap.setInfo(info, bpr);
    if (outBuffer.width > 0 && outBuffer.height > 0) {
        bitmap.setPixels(outBuffer.bits);
    } else {
        // be safe with an empty bitmap.
        bitmap.setPixels(NULL);
    }

    Canvas* nativeCanvas = GraphicsJNI::getNativeCanvas(env, canvasObj);
    nativeCanvas->setBitmap(bitmap);

由上面的 lockCanvas 代码片段来看,根据 ANativeWindowBuffer 构建了一个 SKBitmap 对象,将该对象设置给 nativeCanvas(SkiaCanvas),然后就返回到 Java 空间了。

上面提到了 Canvas,我们看一下 Canvas 的处理流程。

 

 大部分基于 Canvas 的操作最后会落到 SKCanvas 上面去,这个在 Skia 2D 库里面。如果想搞清楚流程,可以拿 TextView 或者 Android 任意一个 UI 控件,看一下他的 draw 是怎么利用 canvas API 来实现的。也可以看一下 skia 库实现。

提交显存

在 UI 绘制完成后,需要将绘制的内容提交显示,这里用到了 Surface::unlockAndPost:

status_t Surface::unlockAndPost()
{
    if (mLockedBuffer == 0) {
        ALOGE("Surface::unlockAndPost failed, no locked buffer");
        return INVALID_OPERATION;
    }

    int fd = -1;
    status_t err = mLockedBuffer->unlockAsync(&fd);
    ALOGE_IF(err, "failed unlocking buffer (%p)", mLockedBuffer->handle);

    err = queueBuffer(mLockedBuffer.get(), fd);
    ALOGE_IF(err, "queueBuffer (handle=%p) failed (%s)",
            mLockedBuffer->handle, strerror(-err));

    mPostedBuffer = mLockedBuffer;
    mLockedBuffer = 0;
    return err;
}

其中主要就是将 GraphicBuffer 提交到 BufferQueue 上等待 SurfaceFlinger(comsumer)显示出来。

 GraphicBufferProducer 诞生流程

BufferQueue 的基本结构如下:

 GraphicBuffer 就与基于 BufferQueueProducer 产生的,在 Surface.cpp 里面有一个 sp<IGraphicsBufferProducer> mGraphicBufferProducer; 所有对 GraphicBuffer 的 queue/dequeue/cancel 等都是通过 mBufferProducer 产生的,我们看一下这个对象是怎么产生,谁在 server 端为其服务,client 和 server 的连接是怎么建立的。

从前面 Surface 的说明里面我们提到一点,就是 ViewRootImpl;ViewRootImpl 里面的 Surface 为所有 View 的绘制提供 canvas,我们看一下这个 Surface 是怎么创建的就能搞清楚 Surface.mGraphicBufferProducer 是怎么实例化的。ViewRootImpl 是 Activity View 管理者,也是 Activity 对应 window 的创建者,在其中有几个步骤:

create window,就是创建 Activity 对应的 window 对象,是和 WMS 建立通讯创建窗口

try {
                    mOrigWindowType = mWindowAttributes.type;
                    mAttachInfo.mRecomputeGlobalAttributes = true;
                    collectViewAttributes();
                    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(), mWinFrame,
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel);
                } catch (RemoteException e) {
                    mAdded = false;

relayout window,测量窗口大小位置等:

int relayoutResult = mWindowSession.relayout(mWindow, mSeq, params,
                (int) (mView.getMeasuredWidth() * appScale + 0.5f),
                (int) (mView.getMeasuredHeight() * appScale + 0.5f), viewVisibility,
                insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, frameNumber,
                mWinFrame, mPendingOverscanInsets, mPendingContentInsets, mPendingVisibleInsets,
                mPendingStableInsets, mPendingOutsets, mPendingBackDropFrame, mPendingDisplayCutout,
                mPendingMergedConfiguration, mSurface);

在 WMS 里面上面两个接口分别调用 addWindow 以及 relayoutWindow;其中 relayoutWindow 中创建了实际的 surface,也就是说实在 WMS 中显示窗口的时候去创建了实际的 surface,其创建过程如下:

最后是调用 surface.copyFrom (SurfaceControl) 得到真实的 surface;SurfaceControl 是在 WMS 里面创建的,SurfaceControl 创建的时候就会向 SurfaceComposerClient 申请创建 surface:

static jlong nativeCreate(JNIEnv* env, jclass clazz, jobject sessionObj,
        jstring nameStr, jint w, jint h, jint format, jint flags, jlong parentObject,
        jint windowType, jint ownerUid) {
    ScopedUtfChars name(env, nameStr);
    sp<SurfaceComposerClient> client(android_view_SurfaceSession_getClient(env, sessionObj));
    SurfaceControl *parent = reinterpret_cast<SurfaceControl*>(parentObject);
    sp<SurfaceControl> surface;
    status_t err = client->createSurfaceChecked(
            String8(name.c_str()), w, h, format, &surface, flags, parent, windowType, ownerUid);
    if (err == NAME_NOT_FOUND) {
        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
        return 0;
    } else if (err != NO_ERROR) {
        jniThrowException(env, OutOfResourcesException, NULL);
        return 0;
    }

    surface->incStrong((void *)nativeCreate);
    return reinterpret_cast<jlong>(surface.get());
}

 

在 createSurfaceChecked 里面想 surfaceFlinger 申请创建 Surface,并基于创建的 Surface 创建新的 SurfaceControl。然后一步步返回,ViewRootImpl 里面的 Surface 就具备真正的显存了。但是我们前面是要知道 GraphicBufferProducer 是谁创建的,这个秘密就在 ComposerSurfaceClient.createSurfaceChecked 函数里面。ComposerSurfaceClient 有一个成员变量 mClient,这是 SurfaceFlinger.Client 的客户端,通过这个 mClient 和 SurfaceFlinger 建立通讯。

status_t SurfaceComposerClient::createSurfaceChecked(...)
{
    sp<SurfaceControl> sur;
    status_t err = mStatus;

    if (mStatus == NO_ERROR) {
        sp<IBinder> handle;
        sp<IBinder> parentHandle;
        sp<IGraphicBufferProducer> gbp;

        if (parent != nullptr) {
            parentHandle = parent->getHandle();
        }
        err = mClient->createSurface(name, w, h, format, flags, parentHandle,
                windowType, ownerUid, &handle, &gbp);
        ALOGE_IF(err, "SurfaceComposerClient::createSurface error %s", strerror(-err));
        if (err == NO_ERROR) {
            *outSurface = new SurfaceControl(this, handle, gbp, true /* owned */);
        }
    }
    return err;
}

在这里可以看到调用了 mClient 的 createSurface,然后返回了 gbp(也就是 IGraphicBufferProducer);mClient 是 SurfaceFlinger.Client 的客户端,由此可见 GraphicBufferProducer 实际是有 SurfaceFlinger 进程创建的。mClient 和 SurfaceFlinger 的对象关系如下图所示:

 

 那么进入 SurfaceFlinger 看一下到底是怎么创建 GraphicBufferProducer 的;Client.createSurface->SurfaceFlinger.createLayer-> new BufferLayer,实际是在 BufferLayer::onFirstRef 里面创建的:

void BufferLayer::onFirstRef() {
    // Creates a custom BufferQueue for SurfaceFlingerConsumer to use
    sp<IGraphicBufferProducer> producer;
    sp<IGraphicBufferConsumer> consumer;
    BufferQueue::createBufferQueue(&producer, &consumer, true);
    mProducer = new MonitoredProducer(producer, mFlinger, this);
    mConsumer = new BufferLayerConsumer(consumer,
            mFlinger->getRenderEngine(), mTextureName, this);
    mConsumer->setConsumerUsageBits(getEffectiveUsage(0));
    mConsumer->setContentsChangedListener(this);
    mConsumer->setName(mName);

    if (mFlinger->isLayerTripleBufferingDisabled()) {
        mProducer->setMaxDequeuedBufferCount(2);
    }

    const sp<const DisplayDevice> hw(mFlinger->getDefaultDisplayDevice());
    updateTransformHint(hw);
}

 

由上面的创建过程也可以看出来,Surface 提交的 GraphicBuffer 由 BufferLayerConsumer 来消耗。

 

 

 

 

 

关于Go语言实现AndroidFramebuffer屏幕截图功能android go语言的介绍现已完结,谢谢您的耐心阅读,如果想了解更多关于Android 5.0及以上编程实现屏幕截图功能的方法、android framebuffer、Android GraphicBuffer-Fence、Android Surface-GraphicBuffer-BufferQueue的相关知识,请在本站寻找。

本文标签: