[Android]GraphicBuffer的传送接收例子

这篇博客介绍了如何在Android中创建一个Binder服务端程序,处理客户端传递的GraphicBuffer,并将其保存为PNG图片。程序涉及到关键步骤如内存映射、图片编码及保存。同时,需要注意在测试时需要关闭SELinux以避免服务添加错误。
摘要由CSDN通过智能技术生成

之前的介绍GraphicBuffer传送的内容中只是贴了几句代码,有小伙伴说要看看全部例子,因为代码写的比较乱,以前没有贴完整,现在还是贴出来吧,给需要的同学参考下,

这个程序是一个binder服务端程序,其中myBinder的case 5 是接收客户端发来的GraphicBuffer,再调用图片保存方法,图片保存的方法是仿照screencap源码的,

这里创建了一个名为screenget的binder服务,是一个最基本的binder服务,

注意,测试的时候需要关闭selinux,不然添加服务会出错

 


#define LOG_TAG "bindertest"

#include <stdio.h>
#include <binder/IInterface.h>
#include <binder/Parcel.h>
#include <binder/IBinder.h>
#include <binder/Binder.h>
#include <binder/ProcessState.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>

#include <binder/MemoryBase.h>
#include <binder/MemoryHeapBase.h>

#include <iostream>
#include <iomanip>

#include <unistd.h>

//for ALOGD
#include <log/log.h>

//test socketpair
#include <sys/types.h>
#include <error.h>
#include <errno.h>
#include <sys/socket.h>

#include <pthread.h>

#include <fcntl.h>
#include <stdlib.h>
#include <string.h>

#include <linux/fb.h>
#include <sys/ioctl.h>
#include <sys/mman.h>

#include <binder/ProcessState.h>

#include <gui/SurfaceComposerClient.h>
#include <gui/ISurfaceComposer.h>

#include <ui/DisplayInfo.h>
#include <ui/PixelFormat.h>

#include <ui/Region.h>

// TODO: Fix Skia.
//#pragma GCC diagnostic push
//#pragma GCC diagnostic ignored "-Wunused-parameter"
#include <SkImageEncoder.h>
#include <SkData.h>
#include <SkColorSpace.h>
//#pragma GCC diagnostic pop

using namespace android;

static uint32_t DEFAULT_DISPLAY_ID = ISurfaceComposer::eDisplayIdMain;
using namespace std;
//
MemoryBase* mem;
int32_t* pdata = NULL;
int fd = -1;

int socket_pair[2];

void* newThread(void *ptr)
{
    for (int i = 0; i < 1000; i++)
    {
        write(socket_pair[1], "12345", 5);
        sleep(1);
        cout << "new thread " << endl;
    }
    
    return NULL;
}

int icode = 0;

static SkColorType flinger2skia(PixelFormat f)
{
    switch (f) {
        case PIXEL_FORMAT_RGB_565:
            return kRGB_565_SkColorType;
        default:
            return kN32_SkColorType;
    }
}

static sk_sp<SkColorSpace> dataSpaceToColorSpace(android_dataspace d)
{
    switch (d) {
        case HAL_DATASPACE_V0_SRGB:
            return SkColorSpace::MakeSRGB();
        case HAL_DATASPACE_DISPLAY_P3:
            return SkColorSpace::MakeRGB(
                    SkColorSpace::kSRGB_RenderTargetGamma, SkColorSpace::kDCIP3_D65_Gamut);
        default:
            return nullptr;
    }
}

int writePNG(char* fileName, char*base, int w, int h, int f, int s)
{
//    if(h != 700) return 0;
        
     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);
        SkAutoTUnref<SkData> data(SkImageEncoder::EncodeData(info, base, s*bytesPerPixel(f),
            SkImageEncoder::kPNG_Type, SkImageEncoder::kDefaultQuality));
        if (data.get()) 
        {
            write(fd, data->data(), data->size());
        }
    */
    
            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;
}

class myBinder : public BBinder
{
public:
    status_t dump(int fd2, const Vector<String16>& args)
    {
        write(fd2, "myBinder dump called       ", 22);

        return NO_ERROR;
    }

    status_t onTransact(
    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
    {
        switch (code) 
        {
                        case 1: {
                            printf("onTransact called, case 1\n");
                            int in_data = data.readInt32();
                            
                            reply->writeInt32(in_data * 2);
              return NO_ERROR;
            }
                        case 2:{
              printf("onTransact called, case 2\n");
                       reply->writeFileDescriptor(socket_pair[0]);
                   //    close(socket_pair[0]);
                return NO_ERROR;
            }
            case 3:{
              printf("onTransact called, case 3\n");
                       reply->writeFileDescriptor(fd);
                return NO_ERROR;
            }
            case 4:{
              printf("onTransact called, case 4\n");
                   
                   int fd = data.readFileDescriptor();
                   int w = data.readInt32();
                   int h = data.readInt32();
                   int format = data.readInt32();
                   
                   if (h != 700) return -1;
                       
                   android_dataspace d = HAL_DATASPACE_UNKNOWN;
                   //here we just use width as stride
                   int s = w;
                   printf("fd=%d, w=%d, h=%d, format=%d \n", fd, w, h, format);
                   printf("1\n");
                   void* mappedAddress= mmap(0, w*h*4, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
//                   void* mappedAddress= mmap(0, w*h*4, PROT_READ, MAP_SHARED, fd, 0);

//test read fd
//char* mappedAddress = new char[w*h*4];
//if (read(fd, mappedAddress, w*h*4) == -1)
//    {printf("Could not read, %s \n\n", strerror(errno));}
                   if (mappedAddress == MAP_FAILED) {
                    printf("Could not mmap %s \n\n", strerror(errno));
                    //return -errno;
                    }
                    char*  base = (char*)mappedAddress;
                   if (base != NULL)                       
                   {
                       cout << "====== base not null" << endl;
                       cout << "====== base[2]=" << base[2] << endl;
                       printf("==base[0]=%d, base[1]=%d, base[2]=%d, base[3]=%d, base[4]=%d\n\n",
                       base[0], base[1], base[2], base[3], base[4]);
                   }
 char* base2 = new char[w*h*4];
 //memcpy(base2, base, w*h*4);
 char*p = base;
 int j=0;
 for (int i = 0; i < h; i++)
 {
     for (j = 0; j < w*4; j++)
     {
         base2[i*w*4 + j] = *p;//
         p++;
     }
     printf("i=%d, j=%d\n", i, j);
}
                   printf("2\n");
                   char pname[32] = {0};
                   sprintf(pname, "%02d.png", icode++);
                   
                   writePNG(pname, base2, w, h, format, s);
                   
                return NO_ERROR;
            }
            case 5:{
              printf("onTransact called, case 3\n");
                   int w = data.readInt32();
                   int h = data.readInt32();
                   int format = data.readInt32();
                   int s = w;
                   
                   sp<GraphicBuffer> buf = new GraphicBuffer();;
                   data.read(*buf);
                   
                   Region newDirtyRegion;
                   const Rect bounds(w, h);
                   newDirtyRegion.set(bounds);
                   
                   int fenceFd = -1;
                   void* vaddr;
                   //GRALLOC_USAGE_SW_READ_OFTEN 0x00000003U
                   //GRALLOC_USAGE_SW_WRITE_OFTEN 0x00000030U 
        status_t res = buf->lockAsync(0x00000003U | 0x00000030U,
                newDirtyRegion.bounds(), &vaddr, fenceFd);
                
              printf("==== vaddr=%p\n", vaddr);
              
              char pname[32] = {0};
                   sprintf(pname, "%02d.png", icode++);
                   
                   char* base = (char*)vaddr;
                   
                   writePNG(pname, base, w, h, format, s);
                   
                return NO_ERROR;
            }
        }

        return BBinder::onTransact(code, data, reply, flags);
    }
};

int main(int argc, char** argv)
{         
    sp < ProcessState > proc(ProcessState::self());
    sp < IServiceManager > sm = defaultServiceManager();

    sm->addService(String16("screenget"), new myBinder());
    
            
    printf("add myService\n");
            
    ProcessState::self()->startThreadPool();
    IPCThreadState::self()->joinThreadPool();

    return 0;
}

 

Android.mk

 

LOCAL_PATH := $(call my-dir)

#for service

include $(CLEAR_VARS)


LOCAL_SRC_FILES:= \
        service.cpp 
        

LOCAL_SHARED_LIBRARIES := \
        libbase \
        libutils \
        liblog \
        libbinder \
            libskia \
    libui \
    libgui


        
ifeq ($(TARGET_OS),linux)
        LOCAL_CFLAGS += -DXP_UNIX
        #LOCAL_SHARED_LIBRARIES += librt
endif

LOCAL_MODULE:= screenget

include $(BUILD_EXECUTABLE)

 

Android Studio是一款由Google开发的集成开发环境(IDE),用于开发Android应用程序。在Android Studio的项目源码中,一般包含一些常见的文件,如.gradle和.idea文件夹。在导入项目之前,可以根据需要删除这两个文件夹。此外,还可以通过打开build.gradle文件来查看和修改Gradle的版本号。Gradle是一种用于构建和管理Android项目的工具。可以通过在自己电脑上新建一个Android Studio项目并查看build.gradle文件来了解自己的Gradle版本号。最后,可以打开Android Studio软件并导入已经修改好的项目。导入成功后,就可以开始开发Android应用程序了。\[1\] 在Android Studio项目中,还有一些常见的文件目录。其中,build目录是系统生成的文件目录,最终生成的apk文件就在这个目录中。libs目录用于存放项目需要添加的.jar包或.so包等外部库。src目录是项目的源代码目录,其中包括androidTest用于存放测试包,main用于存放主要的项目目录和代码,test用于存放单元测试代码。\[2\] 此外,Android Studio项目中的values目录用于存储应用程序引用的一些值。其中,colors.xml文件存储了一些颜色样式,dimens.xml文件存储了一些公用的dp值,strings.xml文件存储了引用的字符串值,styles.xml文件存储了应用程序需要用到的一些样式。\[3\] #### 引用[.reference_title] - *1* [android stdio如何导入别人的项目](https://blog.csdn.net/qq_45644671/article/details/112141069)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [Android Studio 目录结构详解](https://blog.csdn.net/Small_Mouse0/article/details/58300419)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值