2008年6月9日 星期一

SurfaceView 如何使用



import Android.view.*;

SurfaceView sv;
SurfaceHolder holder;

sv = (SurfaceView) findViewById ( R.id.sv1 );
holder = sv.getHolder();

Canvas cv = holder.lockCanvas();
cv.drawBitmap ( bitmap , left , top , new Paint() );
holder.unlockCanvasAndPost(cv);

這一段 code 會比使用 ImageView 觀看 Bitmap 來的更快, 因為我們利用的 Canvas 直接繪圖, 但是 ImageView 在到 android.graphics.drawable.BitmapDrawable 的 onDraw (Canvas canvas); 之前還需要作很多判斷, 所以速度上來說會比直接使用 Canvas 慢上許多.

另外 drawBitmap 其實是呼叫 JNI native_drawBitmap 來作實際上得繪圖動作.
public void drawBitmap(Bitmap bitmap, float left, float top, Paint paint)
{
native_drawBitmap(mNativeCanvas, bitmap.ni(), left, top, paint == null ? 0 : paint.mNativePaint);
}

2008年6月7日 星期六

測量 CLOCK and Times

自己寫的在 linux 下面測量時間的方式 , 跟大家分享一下 。

typedef struct clock_info {

char name[20];
clock_t start;
clock_t end;
clock_t total;
clock_t times;
clock_t max;
clock_t min;
clock_t tmp_dur;
} clock_info;

void clock_info_Init( clock_info *ci , char *name)
{
strncpy(ci->name, name, 20);
ci->start = 0 ; ci->end = 0; ci->total = 0;
ci->times = 0 ; ci->max = 0; ci->min = 0;
ci->tmp_dur = 0;
}
void clock_info_sample_start ( clock_info *ci, clock_t sample )
{
ci->start = sample;
}
void clock_info_sample_end ( clock_info *ci , clock_t sample )
{
ci->end = sample;
ci->tmp_dur = ci->end - ci->start;
ci->total += ci->tmp_dur;
ci->times ++;
if ( ci->times == 1 )
{
ci->max = ci->tmp_dur;
ci->min = ci->tmp_dur;
}
if ( ci->tmp_dur > ci->max )
ci->max = ci->tmp_dur;

if ( ci->tmp_dur <>min )
ci->min = ci->tmp_dur;
}
void clock_info_print ( clock_info *ci )
{
char str[255];
sprintf(str," CLOCK Measurement report CLOCKS_PER_SEC = %d\n", CLOCKS_PER_SEC);
printf(str);

double avg = (double)( (double) ci->total / (double) ci->times ) ;
double max = (double) ci->max;
double min = (double) ci->min;
sprintf(str," <%s>\t max : %7.5f (clk) \t min : %7.5f (clk) \t avg : %7.5f (clk) \n",
ci->name, max , min , avg);
printf(str);

avg = ( avg / (double)CLOCKS_PER_SEC );
max = ( max / (double)CLOCKS_PER_SEC );
min = ( min / (double)CLOCKS_PER_SEC );
sprintf(str," <%s>\t max : %7.5f (s) \t min : %7.5f (s) \t avg : %7.5f (s)\n",
ci->name, max , min , avg );
printf(str);

avg = avg * 1000.0;
max = max * 1000.0;
min = min * 1000.0;
sprintf(str," <%s>\t max : %7.5f (ms) \t min : %7.5f (ms) \t avg : %7.5f (ms) \n",
ci->name, max , min , avg);
prinf(str);
}

Android上面呼叫 ffmpeg 解壓縮的播放程式


這禮拜五完成一支 可以在Android上面呼叫 ffmpeg 解壓縮的播放程式,這支程式的架構是目前計畫的目標,在 Android 上打出一條路徑可以讓硬體 執行解壓縮。

雖然之前就有做出雛形,但是因為 IPC 控制機制設計不好,所以程式容易 Crash ,另外也容易產生 memory leaking。

最後在本週五下班之前修改出來,每 110ms 可以解壓縮完一張 H.264的 Frame,程式不會 Crash。也能夠控制程式的關閉暫停等等,實在非常感動 努力了三個月終於有一支可以上檯面交差的程式了。( 備註一下 110ms 的時間測量方式 我是採用 clock() 這個 system call 測量得知的 )

在這段撰寫程式的這段時間中, 我遇到了 java threading、同步等兩大難題, 要在 java 裡面做 thread 控制,要在 jni 與 ffmpeg decoder 之間做 ipc 控制。

java threading 算是比較簡單且容易撰寫,但需要閱讀的文件也是最多的。不習慣 synchronized 這種 coding 方式。另外 Thread 要呼叫某個 class的方法必須透過 Android Handler,但是 Handler 沒有 message filter 的功能所以必須自己設計。這大概是我覺得重點的地方。

同步通訊的部份,我用 FIFO ( mkfifo ) 的方式,開通兩個 fifo 讓兩支程式做溝通,傳控制訊息。 至於解開後的 raw data ( RGB32 ) 則適用 memory mapping i/o 的方式,讓 jni 與 ffmpeg decoder 共用同一個 page 降低搬移與傳輸資料的成本。

這邊不得不提一下為何要用 RGB32,因為 FFmpeg 解壓縮的單位是 byte,如果你用 RGB16 就是佔兩個 bytes,RGB24 就是三個。但是 Android 的 Bitmap 可讀取資料來源 array 是 int 格式, 在 java 的定義中是佔了 4 bytes ,所以用 RGB32 可以避免掉 Align data 的時間。

如果加上 SetIntArrayRegion(env, iarr, 0, size, tmp); 就可以輕鬆的把解壓縮完後的 data 傳完 java (但是 Bitmap 那邊要選用 ARGB888 的 config )。

另外還有一件事情就是論文被通知 accept 。
最後感謝跟我一起工作的同事們,沒有你們互相切磋 這隻程式沒法完成,謝謝你們。
這一周真的既勞累又充實, 疲勞都被成功的喜悅給淹沒了。
(加班三天~~ 囧!!)

2008年5月27日 星期二

幫android換掉 sh

android的 init是 google自己寫的, 它會讀取 /etc/init.rc 作一些動作
我不喜歡它原本 sh , 因為不方便使用沒有 history的功能,

我修改如下

console {
exec /system/bin/busybox
args {
0 sh
}
console yes
}

這樣 run emulator的時候加上 -console 就會直接執行 busybox sh
但是 adb shell 則是直接 run /system/bin/sh 這要怎麼辦呢
先把原本的sh改名, 然後 ln -s busybox sh.

對了 android的 sh可不是由 toolbox link過來的喔 是一隻獨立的程式.

Busybox testing on android emulator

busybox 是一套 balabala 不多說了 有些功能可以動作 有些會造成 segmentation fault 以下是我測試目前ok的功能

補充一下 ln -s busybox cat 會產生 symbol file cat 這樣就可以執行cat的功能

cat , cmp ,chmod ,date , dd ,df , dmesg, mkdir, netstat, ps , printenv, rm, rmdir, route, sleep, sync, umount, mount, kill, ls

目前已知會造成 segmentation fault的功能, vi, lsmod, insmod, rmmmod

ifdown , ifup, ifconfig 會說需要 /etc/network/interfaces 這個檔案 我在模擬器上面就沒有多加測試了.

Android 的 一些小心得

目前因為版子還不能 run android 的關係 (只有 shell 出現 沒有 gui ),
所以我就只能模擬器上面作一些工作, 這邊是我的一些小心得.

ramdisk.img 是一個 gzip + cpio 的檔案
先用 gunzip 解壓縮後 得到一個 cpio file ramdisk

我們可以用這一行命令解開它

cpio -iv < ../ramdisk

這一行命令會把 ramdisk 的內容解開到當前的目錄當中.


修改完後要壓回去, 可以執行下面的命令
find | cpio -H newc -o | gzip -9 > ../ramdisk.img

這樣就會還原成原來的 gzip + cpio 的 ramdisk.img

當然這些動作只有在 emulator 上面執行才需要
如果要在 target board 上面執行時 只要像一般的處理 rootfs 那樣即可
不過我只會 NFS mount 的方式.

android JNI debug method

這是一篇小小的心得, 當時我正在寫 android的 jni 準備放在 /system/lib 裡面讓 java的 application 呼叫.

但是苦於沒有辦法知道 android's java application 是否呼叫到了 jni 所以一直很苦惱,
後來靈機一動, 直覺的認為可能是 權限的問題所以想到的解法. 請各位不要笑我 因為我剛開始接觸 linux也不超過一年.

  • adb shell
  • chmod 777 /dev/pts/0

in c code write below this to generate debug message

  • char message[] = "hello android jni interface.";
  • int tty = open ("/dev/pts/0", O_RDWR);
  • write ( tty, message, sizeof(message) );
補充一下 /dev/pts/x 是在使用模擬器的時候使用 adb shell 會自動建立的 device node , 第一次使用時編號為0 第二次為1 以此類推.

所以這個 debug 方法只是用在 emulator 上面.
另外前面可以加上 access("/dev/pts/0", R_OK | W_OK | X_OK ) 作判斷看看檔案是否存在

chm2pdf on linux(在 fedora上面 )

這個是今天在網路上看到的轉檔方法 (在 fedora7 上面 )

首先下載 http://code.google.com/p/chm2pdf/
上面是網頁上說所需要的檔案.

我自己在 fedora 7裡面安裝的經驗是
使用更新搜尋尋找 libchm 全選, python-chm , htmldoc 全選
最後解開 chm2pdf-x.tar

進入解壓縮後的chm2pdf目錄
執行
python setup.py install


產生
/usr/bin/chm2pdf

接下來就可以直接在 shell底下使用
轉檔的方式很簡單 我自己喜歡的閱讀方式是
上下個留 1 inch 的空白 左右 1.2
執行下面的命令即可

chm2pdf --book --left 1.2in --right 1.2in --top 1in --down 1in --size a4 --textfont helvetica --no-overflow --jpeg 100 "filename.chm"

有時後轉檔會失敗 這個時候請把 --book 替換成 --webpage 即可