[Android] Framebuffer를 이용한 화면 추출

http://hcthemax.tistory.com/6 사이트 참고

 

Framebuffer는 Android 기기의 화면에 출력되는 모든 정보를 가지고 있다. Android의 화면을 capture하거나, 원격 프로그램 같은 application을 만들기 위해서 반드시 필요한 driver이다. 하지만 Android에서는 이 정보를 쉽게 내어주지 않는다.

Android는 permission mechanism을 통해 하나의 application단에서 또 다른 application이나 OS에 대한 접근을 제한해 두고 있다. 그 중에서도 화면에 대한 정보는 사용자의 거의 모든 정보를 가져갈 수 있을 정도로 그 위험도가 크기때문에 Android에서 READ_FRAME_BUFFER라는 permission이 있음에도 불구하고 READ_FRAME_BUFFER를 선언하여도 Framebuffer에는 접근할 수 가 없다. 그래서 본인은 부득이하게 rooting을 사용하여 framebuffer의 정보를 추출하였고, 그마저도  4.2 Jelly Bean에서는 성공을 보장할 수는 없다. 하지만 4.0.4 ICS까지는 문제 없이 동작하며 조만간 4.2도 test하여 posting하겠다.

먼저 framebuffer가 무엇인지 알아보자.

위의 그림이 Android Application에서 Surface를 생성하여 화면에 출력되기까지의 과정을 나타낸것이다. Application이 Surface를 생성하면 이를 surfaceflinger에서 취합하여 framebuffer에 던져준다. 결국 화면에 출력되는 모든 내용은 framebuffer에 들어있다. 그리고  surfaceflinger에서 framebuffer에 화면을 쓰고있는 과정이 LCD에 출력된다면 매 화면이 변경될때마다 매우 불편한 상황이 생기게된다. 그래서 Android에서는 다음과같은 double-buffering을 사용한다.
http://hcthemax.tistory.com/plugin/CallBack_bootstrapperSrc?nil_profile=tistory&nil_type=copied_post

Double-buffering이란 위의 1번째 그림과 같이 기기의 화면의 2배 크기의 buffer를 잡아 놓은 다음 1번 buffer가 화면에 출력되고 있다면 surfaceflinger는 2번 buffer에 출력될 화면을 쓰고 작업이 끝나면 2번 buffer가 화면에 출력이된다. 마찬가지로 다음 화면 변경이 생기면 1번 buffer에 쓰고 작업이 마무리가 되면 1번 buffer가 화면에 출력되는 방식으로 화면 끊김을 막는다.

화면이 어떤 방식으로 출력되는지 알았으니, 이제 화면을 추출하러가보자.

먼저 rooting을 한다 rooting이 되어있지 않다면 테그라크 커널 블로그를 가서 대충 따라해도 손쉽게 rooting할 수 있다.

http://pspmaster.tistory.com/

Rooting이 끝났다면 Java에서 framebuffer의 권한을 열어준다.

위의 code와 같이 660이던 fb0의 권한을 664로 read할 수 있게끔 바꿔준다. 물론 화면 추출이 끝나면 다시 660으로 바꾸어서 권한을 막아주어야 한다.

다음은 JNI를 이용하여 C나 C++로 작업을 하여야한다. 기본적인 JNI 환경설정을 모두 마쳤다면 coding을 시작해보자.

먼저 framebuffer를 open하고 화면 정보를 가지고있는 structure들을 선언한다.

첫 번째 structure인 fb_var_screeninfo는 말그대로 variable한 screen information을 가지고 있고 fb_fix_screenifo는 fixed screen infomation을 나타낸다. 더 자세한 내용은

http://lxr.free-electrons.com/source/include/linux/fb.h

에 들어가보면 알 수 있다. 그 뒤 mmap을 이용하여 framebuffer를 우리가 읽을 만큼 mapping한다. 위에서 말한것처럼 framebuffer는 double-buffer로 되어있기 때문에 2개의 buffer중에 현재 화면에 출력되고있는 buffer의 정보를 추출하는 것이 가장 좋으나 아직 그 방법을 모르기때문에 그냥 buffer를 통체로 긁어서 1번 buffer만 썼다.

vinfo.xres는 가로 해상도를 vinfo.yres는 세로 해상도를 나타낸다. 그리고 bits_per_pixel은 말 그대로 1 pixel당 bits를 나타낸다. 우리에게 필요한건 bits가 아닌 bytes기 때문에 8을 나누어 준다. 첫 번째 줄같이 계산을 하게되면 1번 buffer의 크기가 나오고 그 크기만큼 mapping하여 jByte로 바꾸어 Java로 보내준다. 만일 2번 buffer를 쓰고 싶다면

pFrame = mmap(size+1, size+size, PROT_READ, MAP_PRIVATE, fb,0);

이런 식으로 해주면 된다. 또 주의 해야할 점은 반드시 close(fd)해야 한다는 점이다. close를 해주지 않고 driver를 읽는데는 한계가 있기때문에 반드시 썼으면 닫아주자. 물론 munmap도 잊지 말자!

위 내용은 현재 진행중인 안드로이드 원격 프로젝트 Remoteroid의 source 일부분으로

http://code.google.com/p/remoteroid

에 가면 frame을 추출하는 full source를 확인할 수 있다. Remoteroid는 open source로 GPL2 license를 따르고 있다.

Advertisements
[Android] Framebuffer를 이용한 화면 추출

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s