【Android】从SurfaceFlinger中获取各layer图片(1)

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数据的地方,我们就在 那里进行处理。

 

 

观察dumpsys

 

+ 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<GraphicBuffer>& buffer(mSlots[s].mGraphicBuffer);

        // 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

 

### 如何在 SurfaceFlinger获取 Buffer 在 Android 系统中,`SurfaceFlinger` 是负责管理图形缓冲区的核心组件之一。为了理解 `SurfaceFlinger` 获取 buffer 的过程,可以从其工作流程入手。 当应用程序请求绘制新一帧时,会向 `SurfaceFlinger` 发送消息通知有新的图像数据准备好。此时,`SurfaceFlinger` 将尝试从现有的双缓存池中分配一个可用的 buffer 给应用线程用于填充图像内容[^1]。具体来说: - **Buffer 分配机制** 应用程序和 `SurfaceFliner` 使用共享内存中的缓冲区进行通信。每当需要更新界面时,应用层会先检查是否有空闲的 buffer 可供使用。如果没有,则需等待直到有一个 buffer 被释放出来为止。这一步骤确保了即使是在高负载条件下也能维持稳定的画面输出质量[^3]。 - **VSync 同步** 当检测到垂直同步脉冲 (VSync) 来临时,`SurfaceFlinger` 主线程会被唤醒,并开始准备下一帧的画面合成工作。在此期间,它会查询各个 Layer 是否已有待处理的新 frame 数据。对于那些已经完成了 queueBuffer 操作的应用而言,它们所关联的 layer 就会有最新的 buffer 准备好参与本次合成操作[^2]。 - **queueBuffer 实现细节** 关键在于 `queueBuffer()` 方法的具体实现上,在每次完成一帧渲染之后都会调用此接口来提交当前使用的 buffer 至显示队列中去。这个过程中涉及到与硬件抽象层(HAL)交互以及可能存在的重复提交问题预防措施等复杂逻辑[^4]。 ```cpp // 假设这是简化版的 queueBuffer 接口定义 void GrallocModule::queueBuffer(buffer_handle_t handle, int fenceFd){ // 处理 buffer 提交前后的各种准备工作... } ``` #### 注意事项 值得注意的是,上述描述基于一般情况下的行为模式;不同版本之间可能存在细微差异。此外,由于涉及到底层架构的知识点较多,建议深入研究官方文档及相关源码以获得更详尽的信息。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值