screen-capture.cpp
Go to the documentation of this file.
00001 #include <unistd.h>
00002 #include <sys/types.h>
00003 #include <fcntl.h>
00004 #include <linux/fb.h>
00005 #include <stdio.h>
00006 #include <stdlib.h>
00007 #include <stdarg.h>
00008 #include <errno.h>
00009 #include <sys/ioctl.h>
00010 #include <sys/mman.h>
00011 #include <math.h>
00012 #include <err.h>
00013 #include <stdbool.h>
00014 #include <sys/time.h>
00015 #include <time.h>
00016 #include <signal.h>
00017 #include <termios.h>
00018 #include <pthread.h>
00019 #include <linux/input.h>
00020 #include <sys/un.h>
00021 #include <sys/socket.h>
00022 
00023 // hack android OS head file
00024 #include "libinline.h"
00025 #include "libcutils.h"
00026 #include "libgui.h"
00027 
00028 using namespace android;
00029 
00030 struct ASC_PRIV_DATA;
00031 struct ASC {
00032     ASC_PRIV_DATA* priv_data;
00033     char* data;
00034     int size;
00035     int w; //short size
00036     int h; //long size
00037     char pixfmtName[32];
00038 };
00039 
00040 static bool needLog = true;
00041 #define LOG(fmt, arg...)         ({static bool __logged=false; if (needLog||!__logged){_LOG("%s" fmt "%s", needLog?"":"--------rare case--------", ##arg, needLog?"":"\n\n");__logged=true;}})
00042 #define LOGI(fmt, arg...)        LOG("--------" fmt "\n\n", ##arg)
00043 #define ABORT(fmt, arg...)       ({_LOG(fmt ". Now exit", ##arg); exit(0);})
00044 #define ABORT_ERRNO(fmt, arg...) ({_LOG(fmt " [errno %d(%s)] Now exit", errno, strerror(errno), ##arg); exit(0);})
00045 
00046 static void _LOG(const char* format, ...) {
00047     char buf[4096];
00048     int cnt;
00049     va_list va;
00050     struct timespec ct;
00051     struct tm * st;
00052     clock_gettime(CLOCK_REALTIME, &ct);
00053     st = localtime(&ct.tv_sec);
00054     cnt = sprintf(buf, "%02d/%02d %02d:%02d:%02d.%03d [ASC %d] ", st->tm_mon+1, st->tm_mday, st->tm_hour, st->tm_min, st->tm_sec, (int)(ct.tv_nsec/1000000), gettid());
00055     va_start(va, format);
00056     cnt += vsnprintf(buf+cnt, sizeof(buf)-cnt, format, va);
00057     va_end(va);
00058     if (cnt > sizeof(buf)) cnt = sizeof(buf); else if (cnt <= 0) {cnt = 7; strcpy(buf, "LogErr");};
00059     if (buf[cnt-1]==0) cnt--; //always true
00060     if (buf[cnt-1]!='\n') buf[cnt++] = '\n';
00061     write(STDERR_FILENO, buf, cnt);
00062 }
00063 
00064 static bool isPaused = false;
00065 static bool isScreenOff = false;
00066 static char* blackscreen = NULL;
00067 static int blackscreen_extra_count = 0;
00068 
00069 static Mutex mutex;
00070 static Condition cond;
00071 
00072 static bool isFirstTime = true;
00073 
00074 static void chkDev() {
00075     char k[128] = {0};
00076     char _sn[256] = {0};
00077     char *sn = _sn;
00078     char hb[4+1] = {'0','0','0','0', 0};
00079     char now[6+1] = {0};
00080     const char* es;
00081     char* ds;
00082     char* err;
00083     int i=0, esLen, snLen, dsLen;
00084     unsigned int ec, sc, dc;
00085     struct timespec ct;
00086     struct tm * st;
00087 
00088     es=getenv("ASC_");
00089     if (!es || !es[0]) ABORT("!nes");
00090     esLen = strlen(es);
00091     if (esLen%(2*6) != 0) ABORT("!esl");
00092     dsLen = esLen/2;
00093 
00094     //getprop android_id from net.hostname
00095     k[i=0] = 'n'; k[++i] = 'e'; k[++i] = 't'; k[++i] = '.'; k[++i] = 'h'; k[++i] = 'o'; k[++i] = 's'; k[++i] = 't'; k[++i] = 'n'; k[++i] = 'a'; k[++i] = 'm'; k[++i] = 'e'; k[++i] = 0;
00096     property_get(k, sn, " ");
00097     snLen = strlen(sn);
00098     if (snLen > 8) {
00099         sn += 8;
00100         snLen -= 8;
00101     } else {
00102         //getprop ro.serialno
00103         k[i=0] = 'r'; k[++i] = 'o'; k[++i] = '.'; k[++i] = 's'; k[++i] = 'e'; k[++i] = 'r'; k[++i] = 'i'; k[++i] = 'a'; k[++i] = 'l'; k[++i] = 'n'; k[++i] = 'o'; k[++i] = 0;
00104         property_get(k, sn, " ");
00105         snLen = strlen(sn);
00106     }
00107 
00108     if ( dsLen != (snLen+6-1)/6*6 ) ABORT("!esms %d %d %d", snLen, (snLen+6-1)/6*6, dsLen);
00109 
00110     ds = (char*)calloc(dsLen+1, 1);
00111     for(i=0; i < dsLen; i++) {
00112         //hb[0] = hb[1] = '0';
00113         hb[2] = es[i*2];
00114         hb[3] = es[i*2+1];
00115         ec = (unsigned int)strtoul(hb, &err, 16);
00116         if (err&&err[0]) ABORT("!ec", err);
00117         sc = (unsigned int)(unsigned char)sn[i%snLen];
00118         dc = ec ^ sc;
00119         if (dc < '0' || dc > '9') ABORT("!dcd");
00120         ds[i] = (char)dc;
00121     }
00122     for (i = 6; i < dsLen; i += 6)
00123         if ( 0 != memcmp(ds, &ds[i], 6)) ABORT("!dsfd");
00124 
00125     clock_gettime(CLOCK_REALTIME, &ct);
00126     st = localtime(&ct.tv_sec);
00127     sprintf(now, "%02d%02d%02d", (st->tm_year+1900-2000), st->tm_mon+1, st->tm_mday);
00128 
00129     if (memcmp(now, ds, 6) > 0) ABORT("!to");
00130     free(ds);
00131 }
00132 
00133 static void* thread_cmd_socket_server(void* thd_param) {
00134     int socket_server_fd = (int)thd_param;
00135     for(;;) {
00136         LOG("accept");
00137         int connection_fd = accept(socket_server_fd, NULL, NULL);
00138         if (connection_fd == -1) {
00139             LOG("accept err %d", errno);
00140             continue;
00141         }
00142 
00143         for(;;) {
00144             unsigned char cmd;
00145             LOG("read cmd");
00146             if (read(connection_fd, &cmd, sizeof(cmd)) != sizeof(cmd)) {
00147                 LOG("read err %d", errno);
00148                 break;
00149             }
00150             LOGI("handle cmd: %c (%d)", cmd, cmd);
00151 
00152             AutoMutex autoLock(mutex);
00153             switch (cmd) {
00154             case '+': //start
00155                 if (isPaused) {
00156                     isPaused = false;
00157                     cond.signal();
00158                 }
00159                 break;
00160             case '-': //pause
00161                 if (!isPaused) {
00162                     isPaused = true;
00163                     cond.signal();
00164                 }
00165                 break;
00166             case '1': //screen on
00167                 isScreenOff = isPaused = false;
00168                 cond.signal();
00169                 break;
00170             case '0': //screen off
00171                 isScreenOff = isPaused = true;
00172                 cond.signal();
00173                 break;
00174             }
00175         } //end of for(;;)
00176 
00177         LOG("close cmd connection");
00178         close(connection_fd);
00179     }
00180     return 0;
00181 }
00182 
00183 static int touch_dev_fd = -1;
00184 
00185 static void* thread_touch_socket_server(void* thd_param) {
00186     int socket_server_fd = (int)thd_param;
00187     for(;;) {
00188         LOG("accept");
00189         int connection_fd = accept(socket_server_fd, NULL, NULL);
00190         if (connection_fd == -1) {
00191             LOG("accept err %d", errno);
00192             continue;
00193         }
00194 
00195         struct input_event event = {0};
00196         const int event_core_size = (((char*)&event.value) + sizeof(event.value)) - ((char*)&event.type);
00197         for(;;) {
00198             LOG("read touch event");
00199             if (read(connection_fd, &event.type, event_core_size) != event_core_size) {
00200                 LOG("read err %d", errno);
00201                 break;
00202             }
00203             LOGI("handle touch event %d %d %d", event.type, event.code, event.value);
00204             if (write(touch_dev_fd, &event, sizeof(event)) != sizeof(event)) {
00205                 LOG("write err %d", errno);
00206                 break;
00207             }
00208         } //end of for(;;)
00209 
00210         LOG("close touch connection");
00211         close(connection_fd);
00212     }
00213     return 0;
00214 }
00215 
00216 static void create_cmd_socket_server() {
00217     char* socket_name = getenv("ASC_CMD_SOCKET");
00218     if (socket_name && socket_name[0]) {
00219 
00220         LOG("c s r %s", socket_name);
00221         int socket_server_fd = socket(AF_LOCAL, SOCK_STREAM, 0);
00222         if (socket_server_fd == -1) {
00223             LOG("socket err %d", errno);
00224             return;
00225         }
00226 
00227         struct sockaddr_un addr = {0};
00228         addr.sun_family = AF_LOCAL;
00229         int namelen = strlen(socket_name);
00230         if (1/*\0*/+namelen > sizeof(addr.sun_path)) ABORT("socket name too long");
00231         memcpy(&addr.sun_path[1], socket_name, namelen);
00232         int addrlen = offsetof(struct sockaddr_un, sun_path) + 1/*\0*/ + namelen;
00233 
00234         LOG("bnd");
00235         if (bind(socket_server_fd, (struct sockaddr*)&addr, addrlen)) {
00236             LOG("bind err %d", errno);
00237             return;
00238         }
00239 
00240         LOG("lstn");
00241         if (listen(socket_server_fd, 1)) {
00242             LOG("listen err %d", errno);
00243             return;
00244         }
00245 
00246         LOG("cthd");
00247         pthread_t thd;
00248         int err = pthread_create(&thd, NULL, thread_cmd_socket_server, (void*)socket_server_fd);
00249         if (err) {
00250             LOG("pthread_create err %d", err);
00251             return;
00252         }
00253     }
00254 }
00255 
00256 static void create_touch_socket_server() {
00257     char* socket_name = getenv("ASC_TOUCH_SOCKET");
00258     if (socket_name && socket_name[0]) {
00259 
00260         LOG("o t d %s", socket_name);
00261         touch_dev_fd = open(socket_name, O_WRONLY);
00262         if (touch_dev_fd==-1) {
00263             LOG("open err %d", errno);
00264             return;
00265         }
00266 
00267         LOG("c s r %s", socket_name);
00268         int socket_server_fd = socket(AF_LOCAL, SOCK_STREAM, 0);
00269         if (socket_server_fd == -1) {
00270             LOG("socket err %d", errno);
00271             return;
00272         }
00273 
00274         struct sockaddr_un addr = {0};
00275         addr.sun_family = AF_LOCAL;
00276         int namelen = strlen(socket_name);
00277         if (1/*\0*/+namelen > sizeof(addr.sun_path)) ABORT("socket name too long");
00278         memcpy(&addr.sun_path[1], socket_name, namelen);
00279         int addrlen = offsetof(struct sockaddr_un, sun_path) + 1/*\0*/ + namelen;
00280 
00281         LOG("bnd");
00282         if (bind(socket_server_fd, (struct sockaddr*)&addr, addrlen)) {
00283             LOG("bind err %d", errno);
00284             return;
00285         }
00286 
00287         LOG("lstn");
00288         if (listen(socket_server_fd, 1)) {
00289             LOG("listen err %d", errno);
00290             return;
00291         }
00292 
00293         LOG("cthd");
00294         pthread_t thd;
00295         int err = pthread_create(&thd, NULL, thread_touch_socket_server, (void*)socket_server_fd);
00296         if (err) {
00297             LOG("pthread_create err %d", err);
00298             return;
00299         }
00300     }
00301 }
00302 
00303 #if (ANDROID_VER>=400)
00304     static ScreenshotClient screenshot;
00305     #if (ANDROID_VER>=420)
00306         static sp<IBinder> display;
00307     #endif
00308 #else
00309     static int fb;
00310     static char* mapbase;
00311     static size_t lastMapSize;
00312 #endif
00313 
00314 extern "C" void asc_capture(ASC* asc) {
00315     int err, width, height, internal_width, bytesPerPixel, rawImageSize;
00316 
00317     if (isFirstTime) {
00318         chkDev();
00319         #if (ANDROID_VER>=400)
00320             #if (ANDROID_VER>=420)
00321                 ProcessState::self()->startThreadPool();
00322                 display = SurfaceComposerClient::getBuiltInDisplay(0 /*1 is hdmi*/);
00323                 if (display==NULL) ABORT("gbd e");
00324             #endif
00325         #else
00326             LOG("o f");
00327             fb = open("/dev/graphics/fb0", O_RDONLY);
00328             if (fb < 0) ABORT_ERRNO("o f");
00329             mapbase = NULL;
00330             lastMapSize = 0;
00331         #endif
00332 
00333 
00334         if (isatty(STDOUT_FILENO)) {
00335             LOG("iaty");
00336             struct termios term;
00337             if (tcgetattr(STDOUT_FILENO, &term)) ABORT_ERRNO("tga");
00338             LOG("mkr");
00339             cfmakeraw(&term);
00340             LOG("tsa");
00341             if (tcsetattr(STDOUT_FILENO, TCSANOW, &term)) ABORT_ERRNO("tsa");
00342         }
00343 
00344         create_cmd_socket_server();
00345         create_touch_socket_server();
00346     }
00347 
00348     if (isPaused && !isFirstTime) {
00349         AutoMutex autoLock(mutex);
00350         if (isPaused && !isFirstTime) {
00351             if (blackscreen==NULL) {
00352                 asc->data = blackscreen = (char*)malloc(asc->size);
00353                 if (!blackscreen) ABORT("oom");
00354                 memset(blackscreen,  isScreenOff ? 0 : 0x40, asc->size);
00355                 LOG("use blackscreen");
00356                 return;
00357             }
00358             else {
00359                 if (blackscreen_extra_count++ < 3) { //very strange!
00360                     asc->data = blackscreen;
00361                     LOG("use blackscreen");
00362                     return;
00363                 }
00364                 blackscreen_extra_count = 0;
00365                 free(blackscreen);
00366                 blackscreen = NULL;
00367 
00368                 do {
00369                     LOG("wait for resume");
00370                     cond.wait(mutex);
00371                 } while ( isPaused );
00372             }
00373         } //end of if (isPaused)
00374     }
00375 
00376     #if (ANDROID_VER>=400)
00377         LOG("c w %d h %d", asc->w, asc->h);
00378         for(;;) {
00379             #if (ANDROID_VER>=500)
00380                 err = screenshot.update(display, Rect(), asc->w, asc->h, false);
00381             #elif (ANDROID_VER>=420)
00382                 err = screenshot.update(display, asc->w, asc->h);
00383             #else
00384                 err = screenshot.update(asc->w, asc->h);
00385             #endif
00386             if(err) {
00387                 LOGI("c e %d", err);
00388                 usleep(250*1000);
00389                 if (!isFirstTime) {
00390                     if (!blackscreen) {
00391                         blackscreen = (char*)malloc(asc->size);
00392                         if (!blackscreen) ABORT("oom");
00393                     }
00394                     memset(blackscreen,  0x80, asc->size);
00395                     asc->data = blackscreen;
00396                     return;
00397                 }
00398             } else {
00399                 if (blackscreen) {
00400                     free(blackscreen);
00401                     blackscreen = NULL;
00402                 }
00403                 break;
00404             }
00405         }
00406 
00407         rawImageSize = screenshot.getSize();
00408         width = screenshot.getWidth();
00409         height = screenshot.getHeight();
00410         internal_width = screenshot.getStride();
00411         bytesPerPixel = rawImageSize/internal_width/height;
00412 
00413         if (isFirstTime) {
00414             strncpy(asc->pixfmtName, 
00415                 (bytesPerPixel==4) ? "rgb0" :
00416                 (bytesPerPixel==3) ? "rgb24" :
00417                 (bytesPerPixel==2) ? "rgb565le" :
00418                 (bytesPerPixel==5) ? "rgb48le" :
00419                 (bytesPerPixel==6) ? "rgba64le" :
00420                 (LOG("s bbp %d", bytesPerPixel),"unknown"),
00421                 sizeof(asc->pixfmtName)-1);
00422 
00423             LOG("c r %s is %d w %d h %d bbp %d iw %d f %d",
00424                 asc->pixfmtName, width*height*bytesPerPixel, width, height, bytesPerPixel, internal_width, screenshot.getFormat());
00425         }
00426 
00427         asc->data = (char*)screenshot.getPixels();
00428 
00429         if (internal_width > width) {
00430             char* p1 = asc->data;
00431             char* p2 = asc->data;
00432             int size1 = width*bytesPerPixel;
00433             int size2 = internal_width*bytesPerPixel;
00434             for (int h=0; h < height; h++, p2 += size2, p1+= size1)
00435                 memmove(p1, p2, size1);
00436         }
00437     #else
00438         LOG("ic gv");
00439         struct fb_var_screeninfo vinfo;
00440         if (ioctl(fb, FBIOGET_VSCREENINFO, &vinfo) < 0) ABORT_ERRNO("ic gv");
00441 
00442         width = vinfo.xres;
00443         height = vinfo.yres;
00444         internal_width = width;
00445         bytesPerPixel = vinfo.bits_per_pixel/8;
00446         rawImageSize = (width*height) * bytesPerPixel;
00447 
00448         if (isFirstTime) {
00449             if (vinfo.transp.offset==0&&vinfo.bits_per_pixel==32&&vinfo.red.offset==8) strncpy(asc->pixfmtName,"0rgb", sizeof(asc->pixfmtName)-1);
00450             else if (vinfo.transp.offset==0&&vinfo.bits_per_pixel==32&&vinfo.red.offset==24) strncpy(asc->pixfmtName,"0bgr", sizeof(asc->pixfmtName)-1);
00451             else
00452                 strncpy(asc->pixfmtName,
00453                     (vinfo.bits_per_pixel==32&&vinfo.red.offset==0) ? "rgb0" :
00454                     (vinfo.bits_per_pixel==32&&vinfo.red.offset!=0) ? "bgr0" :
00455                     (vinfo.bits_per_pixel==24&&vinfo.red.offset==0) ? "rgb24" :
00456                     (vinfo.bits_per_pixel==24&&vinfo.red.offset!=0) ? "bgr24" :
00457                     (vinfo.bits_per_pixel==16&&vinfo.red.offset==0) ? "rgb565le" :
00458                     (vinfo.bits_per_pixel==16&&vinfo.red.offset!=0) ? "bgr565le" :
00459                     (vinfo.bits_per_pixel==48&&vinfo.red.offset==0) ? "rgb48le" :
00460                     (vinfo.bits_per_pixel==48&&vinfo.red.offset!=0) ? "bgr48le" :
00461                     (vinfo.bits_per_pixel==64&&vinfo.red.offset==0) ? "rgba64le" :
00462                     (vinfo.bits_per_pixel==64&&vinfo.red.offset!=0) ? "bgra64le" :
00463                     (LOG("strange bits_per_pixel:%d", vinfo.bits_per_pixel),"unknown"),
00464                     sizeof(asc->pixfmtName)-1);
00465 
00466             LOG("gv r %s is %d w %d h %d bpp %d vw %d vh %d"
00467                 " bits:%d"
00468                 " R:(offset:%d length:%d msb_right:%d)"
00469                 " G:(offset:%d length:%d msb_right:%d)"
00470                 " B:(offset:%d length:%d msb_right:%d)"
00471                 " A:(offset:%d length:%d msb_right:%d)"
00472                 " grayscale:%d nonstd:%d rotate:%d",
00473                 asc->pixfmtName, rawImageSize, width, height, bytesPerPixel, vinfo.xres_virtual, vinfo.yres_virtual
00474                 ,vinfo.bits_per_pixel
00475                 ,vinfo.red.offset, vinfo.red.length, vinfo.red.msb_right
00476                 ,vinfo.green.offset, vinfo.green.length, vinfo.green.msb_right
00477                 ,vinfo.blue.offset, vinfo.blue.length, vinfo.blue.msb_right
00478                 ,vinfo.transp.offset, vinfo.transp.length, vinfo.transp.msb_right
00479                 ,vinfo.grayscale, vinfo.nonstd, vinfo.rotate );
00480         }
00481 
00482         uint32_t offset =  (vinfo.xoffset + vinfo.yoffset*width) *bytesPerPixel;
00483         int virtualSize = vinfo.xres_virtual*vinfo.yres_virtual*bytesPerPixel;
00484         if (offset+rawImageSize > virtualSize) {
00485             LOG("Strange! offset:%d+rawImageSize:%d > virtualSize:%d", offset, rawImageSize, virtualSize);
00486             virtualSize = offset+rawImageSize;
00487         }
00488 
00489         if (virtualSize > lastMapSize) {
00490             if (mapbase) {
00491                 LOG("remap due to virtualSize %d is bigger than previous %d", virtualSize, lastMapSize);
00492                 munmap(mapbase, lastMapSize);
00493                 mapbase = NULL;
00494             }
00495             lastMapSize = virtualSize;
00496         }
00497 
00498         if (mapbase==NULL) {
00499             mapbase = (char*)mmap(0, virtualSize, PROT_READ, MAP_PRIVATE, fb, 0);
00500             if (mapbase==NULL) ABORT_ERRNO("mmap %d", virtualSize);
00501         }
00502 
00503         asc->data = mapbase + offset;
00504     #endif
00505 
00506     if (isFirstTime) {
00507         asc->w = width;
00508         asc->h = height;
00509         asc->size = width*height*bytesPerPixel;
00510         if (isPaused) {
00511             asc->data = blackscreen = (char*)calloc(asc->size, 1);
00512             if (!blackscreen) ABORT("oom");
00513             memset(blackscreen,  isScreenOff ? 0 : 0x40, asc->size);
00514             LOG("use blackscreen");
00515         }
00516 
00517         if (! (getenv("ASC_LOG_ALL") && atoi(getenv("ASC_LOG_ALL")) > 0) )
00518             needLog = false;
00519         isFirstTime = false;
00520     }
00521 }
00522 
00523 #if MAKE_TEST==1
00524     extern "C" int main(int argc, char** argv) {
00525         ASC asc;
00526         memset(&asc, 0, sizeof(ASC));
00527         asc.width = argc>1 && atoi(argv[1])> 0 ? atoi(argv[1]) : 0;
00528         asc.height = argc>2 && atoi(argv[2])> 0 ? atoi(argv[2]) : 0;
00529 
00530         for(;;) {
00531             asc_capture(&asc);
00532             static int64_t seq = 0;
00533             LOG("o i %lld", ++seq);
00534             write(1, asc.data, asc.size);
00535         }
00536     }
00537 #endif


dji_ronin
Author(s):
autogenerated on Sat Jun 8 2019 20:15:31