原文:http://blog.sina.com.cn/s/blog_4a0a39c30100b1n1.html
SDL(Simple DirectMedia
Layer)是一套開放源碼的跨平台多媒體開發庫,使用C語言寫成。SDL提供了多種映像、聲音、鍵盤等的實現,可配置性與移植性非常高,開發人員可以開發出跨多個平台(Linux、Windows、Mac
OS X、Symbian、Widnows
Mobiel等嵌入式系統,當然也包括今天要移植的平台:Android)的應用,目前SDL多用於開發遊戲、模擬器、媒體播放器等多媒體應用。
目前,SDL的穩定版本是 1.2.13,1.3還在開發中,可以通過SVN得到最新的代碼,本次移植以 1.2.13為準,沒有測試
1.3版的源碼。請從 SDL 的官方網站下載 1.2.13 的源碼,檔案名稱為:SDL-1.2.13.zip,並解壓,將得到一個
SDL-1.2.13 目錄。
在Native編譯SDL之前,要先裝 Code
Sourcery公司的arm交叉編譯器,如果是用Windows作業系統,則一定要裝
Cygwin(一個在windows上類比linux的軟體),因為在編譯時間要用到一些 linux命令,具體的步驟請參見:Port SDL/TinySDGL to android with native C,或自已在網上搜一些資料。
因為SDL是用純C寫的一套類庫,所以移植性非常好,官方支援的系統有:Linux, Windows, Windows CE,
BeOS, MacOS, Mac OS X, FreeBSD, NetBSD, OpenBSD, BSD/OS, Solaris,
IRIX, and QNX,非官方支援的有:AmigaOS, Dreamcast, Atari, AIX, OSF/Tru64,
RISC OS, SymbianOS, and OS/2,而且網上也有人將SDL移植到很多其他嵌入式系統,甚至有人將SDL移植到
Moto A1200,如此強大的可移植性,其架構真是值得好好學習。
現在切入正題,如何為Android量身定做一個
SDL,下面就從視頻,音頻,輸入事件,定時器(video,audio,events[key,mouse],timer),多線程等幾個方面來分析:
1.首先講視頻方面,Android是一個定製的Linux作業系統,Linux顯示要麼用
X11,要麼用framebuffer技術,很顯然Android並沒有整合
X11(也許Android的軟體介面是基於X11的?!),那隻有唯一的選擇:framebuffer!
開啟$SDL/src/video目錄,可以發現SDL支援多達30多種的視頻顯示技術,其中包括我們想要的fbcon及directfb,directfb我沒有做測試,也許顯示效果會比linux內建的fbcon好,有興趣的朋友可以試一下,成功了別忘了告訴我;
2.再來談音頻,記得一個廣告詞:沒有聲音,再好的戲也出不來!可見音頻對多媒體應用的重要性。
這次用的是OSS的driver,但用的是dsp及dma的實現,但在開啟Android指定的音頻檔案 /dev/eac
時有誤,所以音頻這一塊只是能編譯通過,不能正常運行,正在考慮用ALSA (Advanced
Linux Sound Architecture) 替代;
關於OSS大家可以參看IBM的文章: OSS--跨平台的音頻介面簡介,寫得比較詳細。
3.輸入事件(鍵盤,滑鼠)中的鍵盤事件不需要任何更改,就能正常處理,用的裝置檔案是 /dev/tty0,
但滑鼠事件卻不能正常處理,加上DEBUG_MOUSE發現用的是PS2的滑鼠,但其實Android用的不是PS2的滑鼠,用的應該是觸控螢幕(TouchScreen)滑鼠,裝置檔案是
/dev/input/event0,詳情請參見本人的blog: Android原生(Native)C開發之三:滑鼠事件篇(捕鼠記),經過改動後,基本能實現滑鼠的處理;
4.定時器用的是unix的實現;
5.多線程用的是pthread的實現,unix系統都是用pthread來實現多線程的,在 ln demo時別忘了加
-lpthread;
6.載入動態庫用的是unix 的 dl庫,同樣,在ln demo時別忘了加 -ldl。
SDL提供了一個最小化的Makefile:Makefile.minimal,所有的實現都是
dummy,就是一個空的實現,編譯能通過,但運行時什麼都不能做,根據上面的分析,將
Makefile.minimal 的內容改成如下:
# Makefile to build the SDL library INCLUDE = -I./include CFLAGS = -g -s -O2 $(INCLUDE) CC = arm-none-linux-gnueabi-gcc AR = arm-none-linux-gnueabi-ar RANLIB = arm-none-linux-gnueabi-ranlib CONFIG_H = include/SDL_config.h TARGET = libSDL.a SOURCES = / src/*.c / src/audio/*.c / src/cdrom/*.c / src/cpuinfo/*.c / src/events/*.c / src/file/*.c / src/joystick/*.c / src/stdlib/*.c / src/thread/*.c / src/timer/*.c / src/video/*.c / src/audio/dsp/*.c / src/audio/dma/*.c / src/video/fbcon/*.c / src/joystick/dummy/*.c / src/cdrom/dummy/*.c / src/thread/pthread/*.c / src/timer/unix/*.c / src/loadso/dlopen/*.c / OBJECTS = $(shell echo $(SOURCES) | sed -e 's,/.c,/.o,g') all: $(TARGET) $(TARGET): $(CONFIG_H) $(OBJECTS) $(AR) crv $@ $^ $(RANLIB) $@ $(CONFIG_H): cp $(CONFIG_H).default $(CONFIG_H) clean: rm -f $(TARGET) $(OBJECTS) |
最後將$SDL/include/SDL_config_minimal.h的內容改成如下:
#ifndef _SDL_config_minimal_h #define _SDL_config_minimal_h #include "SDL_platform.h" #include <stdarg.h> typedef signed char int8_t; typedef unsigned char uint8_t; typedef signed short int16_t; typedef unsigned short uint16_t; typedef signed int int32_t; typedef unsigned int uint32_t; typedef unsigned int size_t; //typedef unsigned long uintptr_t; #define HAVE_LIBC 1 #ifdef HAVE_LIBC #define HAVE_ALLOCA_H 1 #define HAVE_SYS_TYPES_H 1 #define HAVE_STDIO_H 1 #define STDC_HEADERS 1 #define HAVE_STDLIB_H 1 #define HAVE_STDARG_H 1 #define HAVE_MALLOC_H 1 #define HAVE_MEMORY_H 1 //#define HAVE_STRING_H 1 //#define HAVE_STRINGS_H 1 #define HAVE_INTTYPES_H 1 #define HAVE_STDINT_H 1 #define HAVE_CTYPE_H 1 #define HAVE_MATH_H 1 //#define HAVE_ICONV_H 1 #define HAVE_SIGNAL_H 1 #define HAVE_ALTIVEC_H 1 #define HAVE_MALLOC 1 #define HAVE_CALLOC 1 #define HAVE_REALLOC 1 #define HAVE_FREE 1 #define HAVE_ALLOCA 1 #define HAVE_GETENV 1 #define HAVE_PUTENV 1 #define HAVE_UNSETENV 1 #define HAVE_QSORT 1 #define HAVE_ABS 1 //#define HAVE_BCOPY 1 //#define HAVE_MEMSET 1 //#define HAVE_MEMCPY 1 //#define HAVE_MEMMOVE 1 //#define HAVE_MEMCMP 1 //#define HAVE_STRLEN 1 //#define HAVE_STRLCPY 1 //#define HAVE_STRLCAT 1 //#define HAVE_STRDUP 1 #define HAVE__STRREV 1 #define HAVE__STRUPR 1 #define HAVE__STRLWR 1 //#define HAVE_INDEX 1 #define HAVE_RINDEX 1 //#define HAVE_STRCHR 1 #define HAVE_STRRCHR 1 #define HAVE_STRSTR 1 #define HAVE_ITOA 1 #define HAVE__LTOA 1 #define HAVE__UITOA 1 #define HAVE__ULTOA 1 #define HAVE_STRTOL 1 #define HAVE_STRTOUL 1 #define HAVE__I64TOA 1 #define HAVE__UI64TOA 1 #define HAVE_STRTOLL 1 #define HAVE_STRTOULL 1 #define HAVE_STRTOD 1 #define HAVE_ATOI 1 #define HAVE_ATOF 1 #define HAVE_STRCMP 1 #define HAVE_STRNCMP 1 #define HAVE__STRICMP 1 #define HAVE_STRCASECMP 1 #define HAVE__STRNICMP 1 #define HAVE_STRNCASECMP 1 #define HAVE_SSCANF 1 #define HAVE_SNPRINTF 1 #define HAVE_VSNPRINTF 1 //#define HAVE_ICONV #define HAVE_SIGACTION 1 #define HAVE_SETJMP 1 #define HAVE_NANOSLEEP 1 //#define HAVE_CLOCK_GETTIME 1 #define HAVE_DLVSYM 1 #define HAVE_GETPAGESIZE 1 #define HAVE_MPROTECT 1 #else #include <stdarg.h> #endif //#define HAVE_STDIO_H 1 //#define HAVE_STDINT_H 1 //#define SDL_INPUT_TSLIB 1 //touch screen input #define SDL_AUDIO_DRIVER_OSS 1 // SDL_AUDIO_DRIVER_DUMMY #define SDL_CDROM_DISABLED 1 #define SDL_JOYSTICK_DISABLED 1 #define SDL_LOADSO_DLOPEN 1 //SDL_LOADSO_DISABLED 1 //#undef #define SDL_THREAD_PTHREAD 1 //SDL_THREADS_DISABLED //SDL_TIMERS_DISABLED #define SDL_TIMER_UNIX 1 // SDL_VIDEO_DRIVER_DUMMY #define SDL_VIDEO_DRIVER_FBCON 1 #endif |
注意黑體部分,其實就是開啟一些宏定義,將SDL的實現一一開啟。
改完了這些以後,還需要改一些代碼,主要是video方面的,因為Android
Linux的framebuffer裝置檔案與標準Linux不同,Linux的fb裝置檔案一般是
/dev/fb0,但Android的裝置檔案是 /dev/graphics/fb0,開啟
$SDL/src/video/fbcon/SDL_fbvideo.c,將191、499行的
"/dev/fb0" 替換成
"/dev/graphics/fb0",儲存即可。
再修改 $SDL/src/thread/pthread/SDL_sysmutex.c,將第30行改成: #define
FAKE_RECURSIVE_MUTEX
1,就是在後面加一個“1”子,這可能是編譯器的一個bug,define預設應該就是“1”的。
現在可以開始編譯libSDL.a了,在cygwin或Linux下,進入SDL目錄,輸入:
make -f Makefile.minimal
視情況面定,一般幾分鐘後能順利編譯 Android版的 SDL,編譯成功後,就需要編譯幾個SDL的test demo來測試一下
SDL的效能。
進入 test目錄,複製 Makefile.in 檔案,並改名為 Makefile,將前面一點內容改為:
# Makefile to build the SDL tests srcdir = . INCLUDE = -I../include CC = arm-none-linux-gnueabi-gcc EXE = CFLAGS = -g -s -O2 $(INCLUDE) -static LIBS = -L.. -lSDL -lpthread MATHLIB = -lm |
並將所有的 @MATHLIB@ 替換成 $(MATHLIB),儲存後,先編譯一個testsprite
demo,在命令列輸入:
make testsprite
編譯成功後,啟動Android模擬器,將編譯出來的testsprite及icon.bmp上傳至一個目錄,如
/dev/sample,命令如下:
adb push testspirte /dev/sample/testsprite adb push icon.bmp /dev/sample/icon.bmp |
最後進入 android 的shell: adb shell,再進入 /dev/sample目錄,執行testsprite
demo即可:
#cd /dev/sample #chmod 755 testsprite #./testsprite -width 320 -height 480 -bpp 32 |
可以根據模擬器的設定調整 width 及 height,因為程式預設的是
640x480的,初始化時會失敗,如果一切正常的話,模擬器就會出現很多黃色的笑臉!按任意鍵退出,在本人機器上能達到 60
FPS左右,如下:
其他的 demo有興趣的朋友可以自己編譯測試。