android后台截屏实现(2)--screencap源码修改

         首先找到screencap类在Android源码中的位置,/442/frameworks/base/cmds/screencap/screencap.cpp

源码如下:

  1 /*
  2  * Copyright (C) 2010 The Android Open Source Project
  3  *
  4  * Licensed under the Apache License, Version 2.0 (the "License");
  5  * you may not use this file except in compliance with the License.
  6  * You may obtain a copy of the License at
  7  *
  8  *      http://www.apache.org/licenses/LICENSE-2.0
  9  *
 10  * Unless required by applicable law or agreed to in writing, software
 11  * distributed under the License is distributed on an "AS IS" BASIS,
 12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  * See the License for the specific language governing permissions and
 14  * limitations under the License.
 15  */
 16 
 17 #include <errno.h>
 18 #include <unistd.h>
 19 #include <stdio.h>
 20 #include <fcntl.h>
 21 
 22 #include <linux/fb.h>
 23 #include <sys/ioctl.h>
 24 #include <sys/mman.h>
 25 
 26 #include <binder/ProcessState.h>
 27 
 28 #include <gui/SurfaceComposerClient.h>
 29 #include <gui/ISurfaceComposer.h>
 30 
 31 #include <ui/PixelFormat.h>
 32 
 33 #include <SkImageEncoder.h>
 34 #include <SkBitmap.h>
 35 #include <SkData.h>
 36 #include <SkStream.h>
 37 
 38 using namespace android;
 39 
 40 static uint32_t DEFAULT_DISPLAY_ID = ISurfaceComposer::eDisplayIdMain;
 41 
 42 static void usage(const char* pname)
 43 {
 44     fprintf(stderr,
 45             "usage: %s [-hp] [-d display-id] [FILENAME]
"
 46             "   -h: this message
"
 47             "   -p: save the file as a png.
"
 48             "   -d: specify the display id to capture, default %d.
"
 49             "If FILENAME ends with .png it will be saved as a png.
"
 50             "If FILENAME is not given, the results will be printed to stdout.
",
 51             pname, DEFAULT_DISPLAY_ID
 52     );
 53 }
 54 
 55 static SkBitmap::Config flinger2skia(PixelFormat f)
 56 {
 57     switch (f) {
 58         case PIXEL_FORMAT_RGB_565:
 59             return SkBitmap::kRGB_565_Config;
 60         default:
 61             return SkBitmap::kARGB_8888_Config;
 62     }
 63 }
 64 
 65 static status_t vinfoToPixelFormat(const fb_var_screeninfo& vinfo,
 66         uint32_t* bytespp, uint32_t* f)
 67 {
 68 
 69     switch (vinfo.bits_per_pixel) {
 70         case 16:
 71             *f = PIXEL_FORMAT_RGB_565;
 72             *bytespp = 2;
 73             break;
 74         case 24:
 75             *f = PIXEL_FORMAT_RGB_888;
 76             *bytespp = 3;
 77             break;
 78         case 32:
 79             // TODO: do better decoding of vinfo here
 80             *f = PIXEL_FORMAT_RGBX_8888;
 81             *bytespp = 4;
 82             break;
 83         default:
 84             return BAD_VALUE;
 85     }
 86     return NO_ERROR;
 87 }
 88 
 89 int main(int argc, char** argv)
 90 {
 91     ProcessState::self()->startThreadPool();
 92 
 93     const char* pname = argv[0];
 94     bool png = false;
 95     int32_t displayId = DEFAULT_DISPLAY_ID;
 96     int c;
 97     while ((c = getopt(argc, argv, "phd:")) != -1) {
 98         switch (c) {
 99             case 'p':
100                 png = true;
101                 break;
102             case 'd':
103                 displayId = atoi(optarg);
104                 break;
105             case '?':
106             case 'h':
107                 usage(pname);
108                 return 1;
109         }
110     }
111     argc -= optind;
112     argv += optind;
113 
114     int fd = -1;
115     if (argc == 0) {
116         fd = dup(STDOUT_FILENO);
117     } else if (argc == 1) {
118         const char* fn = argv[0];
119         fd = open(fn, O_WRONLY | O_CREAT | O_TRUNC, 0664);
120         if (fd == -1) {
121             fprintf(stderr, "Error opening file: %s (%s)
", fn, strerror(errno));
122             return 1;
123         }
124         const int len = strlen(fn);
125         if (len >= 4 && 0 == strcmp(fn+len-4, ".png")) {
126             png = true;
127         }
128     }
129 
130     if (fd == -1) {
131         usage(pname);
132         return 1;
133     }
134 
135     void const* mapbase = MAP_FAILED;
136     ssize_t mapsize = -1;
137 
138     void const* base = 0;
139     uint32_t w, s, h, f;
140     size_t size = 0;
141 
142     ScreenshotClient screenshot;
143     sp<IBinder> display = SurfaceComposerClient::getBuiltInDisplay(displayId);
144     if (display != NULL && screenshot.update(display) == NO_ERROR) {
145         base = screenshot.getPixels();
146         w = screenshot.getWidth();
147         h = screenshot.getHeight();
148         s = screenshot.getStride();
149         f = screenshot.getFormat();
150         size = screenshot.getSize();
151     } else {
152         const char* fbpath = "/dev/graphics/fb0";
153         int fb = open(fbpath, O_RDONLY);
154         if (fb >= 0) {
155             struct fb_var_screeninfo vinfo;
156             if (ioctl(fb, FBIOGET_VSCREENINFO, &vinfo) == 0) {
157                 uint32_t bytespp;
158                 if (vinfoToPixelFormat(vinfo, &bytespp, &f) == NO_ERROR) {
159                     size_t offset = (vinfo.xoffset + vinfo.yoffset*vinfo.xres) * bytespp;
160                     w = vinfo.xres;
161                     h = vinfo.yres;
162                     s = vinfo.xres;
163                     size = w*h*bytespp;
164                     mapsize = offset + size;
165                     mapbase = mmap(0, mapsize, PROT_READ, MAP_PRIVATE, fb, 0);
166                     if (mapbase != MAP_FAILED) {
167                         base = (void const *)((char const *)mapbase + offset);
168                     }
169                 }
170             }
171             close(fb);
172         }
173     }
174 
175     if (base) {
176         if (png) {
177             SkBitmap b;
178             b.setConfig(flinger2skia(f), w, h, s*bytesPerPixel(f));
179             b.setPixels((void*)base);
180             SkDynamicMemoryWStream stream;
181             SkImageEncoder::EncodeStream(&stream, b,
182                     SkImageEncoder::kPNG_Type, SkImageEncoder::kDefaultQuality);
183             SkData* streamData = stream.copyToData();
184             write(fd, streamData->data(), streamData->size());
185             streamData->unref();
186         } else {
187             write(fd, &w, 4);
188             write(fd, &h, 4);
189             write(fd, &f, 4);
190             size_t Bpp = bytesPerPixel(f);
191             for (size_t y=0 ; y<h ; y++) {
192                 write(fd, base, w*Bpp);
193                 base = (void *)((char *)base + s*Bpp);
194             }
195         }
196     }
197     close(fd);
198     if (mapbase != MAP_FAILED) {
199         munmap((void *)mapbase, mapsize);
200     }
201     return 0;
202 }

 由源码可以看出,screencap的入口main方法是从命令行获取参数,通过分析后执行相应的操作。我们要想在java层调用这个类,就要把它的入口改成native方法的接口,修改后的代码如下:

  1 #include <jni.h>
  2 #include "com_android_servicescreencap_ScreenCap.h"
  3 
  4 #include <errno.h>
  5 #include <unistd.h>
  6 #include <stdio.h>
  7 #include <fcntl.h>
  8 
  9 #include <linux/fb.h>
 10 #include <sys/ioctl.h>
 11 #include <sys/mman.h>
 12 
 13 #include <binder/ProcessState.h>
 14 
 15 #include <gui/SurfaceComposerClient.h>
 16 #include <gui/ISurfaceComposer.h>
 17 
 18 #include <ui/PixelFormat.h>
 19 
 20 #include <SkImageEncoder.h>
 21 #include <SkBitmap.h>
 22 #include <SkData.h>
 23 #include <SkStream.h>
 24 
 25 #include <android/log.h>
 26 #define LOG_TAG "ServiceScreenCap"
 27 #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
 28 #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
 29 #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
 30 
 31 using namespace android;
 32 
 33 static uint32_t DEFAULT_DISPLAY_ID = ISurfaceComposer::eDisplayIdMain;
 34 
 35 
 36 static status_t vinfoToPixelFormat(const fb_var_screeninfo& vinfo,
 37         uint32_t* bytespp, uint32_t* f)
 38 {
 39 
 40     switch (vinfo.bits_per_pixel) {
 41         case 16:
 42             *f = PIXEL_FORMAT_RGB_565;
 43             *bytespp = 2;
 44             break;
 45         case 24:
 46             *f = PIXEL_FORMAT_RGB_888;
 47             *bytespp = 3;
 48             break;
 49         case 32:
 50             // TODO: do better decoding of vinfo here
 51             *f = PIXEL_FORMAT_RGBX_8888;
 52             *bytespp = 4;
 53             break;
 54         default:
 55             return BAD_VALUE;
 56     }
 57     return NO_ERROR;
 58 }
 59 
 60 static SkBitmap::Config flinger2skia(PixelFormat f)
 61 {
 62     switch (f) {
 63         case PIXEL_FORMAT_RGB_565:
 64             return SkBitmap::kRGB_565_Config;
 65         default:
 66             return SkBitmap::kARGB_8888_Config;
 67     }
 68 }
 69 
 70 
 71 /*
 72  * Class:     com_android_servicescreencap_ScreenCap
 73  * Method:    currentscreen
 74  * Signature: (Ljava/lang/String;)I
 75  */
 76 JNIEXPORT jint
 77 JNICALL ScreenCap_currentscreen(JNIEnv *env,
 78         jclass clazz, jstring jpath) {
 79 
 80     ProcessState::self()->startThreadPool();
 81 
 82     int32_t displayId = DEFAULT_DISPLAY_ID;
 83 
 84     const char* fn = env->GetStringUTFChars(jpath,NULL);
 85     LOGI("=====jpath:%s 
", fn);
 86 
 87     if (fn == NULL) {
 88         LOGE("=====path = %s 
 =====err: %s 
",fn, strerror(errno));
 89         return 1;
 90     }
 91     int fd = -1;
 92     fd = open(fn, O_WRONLY | O_CREAT | O_TRUNC, 0664);
 93     LOGI("=====after open ,fd:%d 
",fd);
 94     if (fd == -1) {
 95         LOGE("=====err: %s 
", strerror(errno));
 96         return 2;
 97     }
 98 
 99     void const* mapbase = MAP_FAILED;
100     ssize_t mapsize = -1;
101 
102     void const* base = 0;
103     uint32_t w, s, h, f;
104     size_t size = 0;
105 
106     ScreenshotClient screenshot;
107     sp < IBinder > display = SurfaceComposerClient::getBuiltInDisplay(displayId);
108     if (display != NULL && screenshot.update(display) == NO_ERROR) {
109         base = screenshot.getPixels();
110         w = screenshot.getWidth();
111         h = screenshot.getHeight();
112         s = screenshot.getStride();
113         f = screenshot.getFormat();
114         size = screenshot.getSize();
115     } else {
116         const char* fbpath = "/dev/graphics/fb0";
117         int fb = open(fbpath, O_RDONLY);
118         LOGI("=====read framebuffer, fb:%d 
", fb);
119         if (fb >= 0) {
120             struct fb_var_screeninfo vinfo;
121             if (ioctl(fb, FBIOGET_VSCREENINFO, &vinfo) == 0) {
122                 uint32_t bytespp;
123                 if (vinfoToPixelFormat(vinfo, &bytespp, &f) == NO_ERROR) {
124                     size_t offset = (vinfo.xoffset + vinfo.yoffset * vinfo.xres)
125                             * bytespp;
126                     w = vinfo.xres;
127                     h = vinfo.yres;
128                     s = vinfo.xres;
129                     size = w * h * bytespp;
130                     mapsize = offset + size;
131                     mapbase = mmap(0, mapsize, PROT_READ, MAP_PRIVATE, fb, 0);
132                     if (mapbase != MAP_FAILED) {
133                         base = (void const *) ((char const *) mapbase + offset);
134                     }
135                 }
136             }
137             close(fb);
138         }else{
139             LOGE("=====fb = %d , err: %s 
",fb, strerror(errno));
140             return 3;
141         }
142     }
143 
144     if (base) {
145         SkBitmap b;
146         b.setConfig(flinger2skia(f), w, h, s * bytesPerPixel(f));
147         b.setPixels((void*) base);
148         SkDynamicMemoryWStream stream;
149         SkImageEncoder::EncodeStream(&stream, b, SkImageEncoder::kPNG_Type,
150                 SkImageEncoder::kDefaultQuality);
151         SkData* streamData = stream.copyToData();
152         write(fd, streamData->data(), streamData->size());
153         streamData->unref();
154     }
155     close (fd);
156     if (mapbase != MAP_FAILED) {
157         munmap((void *) mapbase, mapsize);
158     }
159     return 0;
160 }
161 
162 
163 static JNINativeMethod methods[] = {
164         {"currentscreen","(Ljava/lang/String;)I",(void*)ScreenCap_currentscreen},
165 };
166 
167 static int registerNativeMethods(JNIEnv* env,const char* classname,JNINativeMethod* gMethods,int numMethods ){
168     jclass clazz;
169     clazz = env->FindClass(classname);
170     if(clazz == NULL){
171         return JNI_FALSE;
172     }
173     if(env->RegisterNatives(clazz,gMethods,numMethods) <0 ){
174         return JNI_FALSE;
175     }
176     return JNI_TRUE;
177 }
178 
179 
180 
181 static int registerNatives(JNIEnv* env)
182 {
183   if (!registerNativeMethods(env, "com/android/servicescreencap/ScreenCap",
184                  methods, sizeof(methods) / sizeof(methods[0]))) {
185     return JNI_FALSE;
186   }
187 
188   return JNI_TRUE;
189 }
190 
191 
192 typedef union {
193     JNIEnv* env;
194     void* venv;
195 } UnionJNIEnvToVoid;
196 
197 jint JNI_OnLoad(JavaVM* vm, void* reserved)
198 {
199     UnionJNIEnvToVoid uenv;
200     uenv.venv = NULL;
201     jint result = -1;
202     JNIEnv* env = NULL;
203 
204 
205     if (vm->GetEnv(&uenv.venv, JNI_VERSION_1_6) != JNI_OK) {
206          return result;
207     }
208     env = uenv.env;
209 
210     if (registerNatives(env) != JNI_TRUE) {
211          return result;
212     }
213 
214     result = JNI_VERSION_1_6;
215 
216     return result;
217 }


 修改后的代码入口是ScreenCap_currentscreen,该方法接收一个地址,将当前屏幕截取到该地址下。代码中加入了日志,可以打印native层的错误信息。


此处需采用动态方式注册本地方法,静态方式好像会有问题。



参考链接:

Android: How to Capture Screen in Gingerbread(2.3中实现截屏)

Android: How to Capture Screen in Gingerbread(2.3中实现截屏)(续)

Android系统截屏的实现(附代码)


原文地址:https://www.cnblogs.com/xulingfeng/p/5313197.html