Android でのスクリーンキャプチャー

前回の宿題で、Androidでアプリからスクリーンキャプチャーを取る方法です。

Androidでアプリからスクリーンキャプチャーを取るにはroot権限がないとだめです。(一部機種でrootを取らなくてもできるものもあります。)

Android 4.0以降では、root化してあればscreencap,screenshotコマンドで簡単にとれるのですが、Android バージョン2.3では使えません。
フレームバッファー /dev/graphics/fb0から直接取得することになります。
(このデバイスは,rootでないとアクアセスできません。)

Android 4.0以降では、電源ボタン+ボリューム(↓)同時押しで取れますが、アプリでこのキーのエミュレートをしても、「電源ボタン」押下は、やはりrootでないと反応しません。

今回、自分使用している機種:SONY Xperia SO-01C 480×854 Android バージョン2.3.2では、Google Playにアップされているツールはどれも正常にとれないのです。赤みがかかった画像になる。RBGAの配列が違うようです。しかたがないので作ることにした。

自分のスマホはrootを取っているので、今回はうまくいけば、アプリにする予定
(公開は今のところ考えていません。)

1) フレームバッファーから生データを取得
#cp /dev/graphics/fb0 /sdcard/fb0

fbのファイルサイズは、3,410,432で、480×854で1ピクセル4バイトとすると
+,-,x,/を思考錯誤して以下の式にたどりついた。

(3410432-(65536 x 2))/2 = 480 x 854 x 4

で、元サイズから65536×2バイト余分引いて、ダブルバッファリングしているので1/2すると、ちょうど480 x 854 x 4になります。

2) 無理やりBMPにしてみる。

fb0にヘッダー54バイトを加えます。
unsigned char header[54] = {
    0x42, 0x4d, 0x36, 0x05, 0x19, 0x00, 0x00, 0x00,0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x28, 0x00,
  0x00, 0x00, 0xe0, 0x01, 0x00, 0x00, 0x56, 0x03,0x00, 0x00, 0x01, 0x00, 0x20, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x05, 0x19, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};

一応、画像になってますが、上下反転して、色も少しおかしいです。RとBが入れ替わってる。

3) データ変換する。行単位で上下を変換し、BGRA -> RGBAにする。

#include <stdio.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <io.h>

#define BMP_SIZE (3410432-(65536*2))/2

unsigned char srcdata[BMP_SIZE];
unsigned char outdata[BMP_SIZE];

unsigned char header[54] = {
    0x42, 0x4d, 0x36, 0x05, 0x19, 0x00, 0x00, 0x00,0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x28, 0x00,
    0x00, 0x00, 0xe0, 0x01, 0x00, 0x00, 0x56, 0x03,0x00, 0x00, 0x01, 0x00, 0x20, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x05, 0x19, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};

int main(int argc, char *argv[]){

int fd,fd_out;
long i,j;

unsigned char c;
unsigned char *p;

fd = _open(“fb0”,O_RDONLY | O_BINARY );
if(fd == (-1)){
return(-1);
}

p = srcdata;

//_read(fd,srcdata , 3410432 – (65536 * 2));
for(i = 0 ; i < BMP_SIZE ; i+= 53760){
_read(fd,p,53760);
p += 53760;
printf(“%in”,i);
}

fd_out = _open(“fb0-001.bmp”,O_CREAT | O_WRONLY | O_TRUNC | O_BINARY,S_IREAD | S_IWRITE );
if(fd_out == (-1)){
return(-1);
}

//これだと、上下逆さ&色抜け(R<->B)
//_write(fd_out,header,54);
//_write(fd_out,srcdata,(3410432 – (65536 * 2))/2);

//————————————

unsigned int width = 480;
unsigned int height = BMP_SIZE  / 480 / 4;

        //上下反転
for(i = 0; i < height; i++){
for(j = 0; j < width * 4; j++){
outdata[(height – 1 – i) * width * 4 + j] = srcdata[i * width * 4 + j];
}
}
//R,B入れ替え
int cnv = 0;
for(i = 0 ; i < BMP_SIZE ; i += 2){
if(cnv == 0){
p = &outdata[i];
c = outdata[i];
cnv++;
}else{
*p = outdata[i];
outdata[i] = c;
cnv = 0;
}
}

_write(fd_out,header,54);
_write(fd_out,outdata,BMP_SIZE);

_close(fd);
_close(fd_out);
}

こうすると、正常な画面になります。これで、スクリーンキャプチャーツールが作れます。(要root)

*root化状態でパズドラを動かすとデータが初期化されますので注意が必要です。といっても/system/bin/su,/system/xbin/su,/sbin/suのようにsuがなければ大丈夫です。

参考:
ビットマップフォーマット
http://www.kuwalab.net/technics/bitmap/
RGB565の時は、16bit のデータフォーマットなので
少し調整が必要です。
http://android-dev-log.ldblog.jp/archives/11346611.html