SDL(Simple DirectMedia Layer)是一套開放源碼的跨平臺(tái)多媒體開發(fā)庫(kù),使用C語(yǔ)言寫成。SDL提供了多種圖像、聲音、鍵盤等的實(shí)現(xiàn),可配置性與移植性非常高,開發(fā)者可以開發(fā)出跨多個(gè)平臺(tái)(Linux、Windows、Mac OS X、Symbian、Widnows Mobiel等嵌入式系統(tǒng),當(dāng)然也包括今天要移植的平臺(tái):Android)的應(yīng)用,目前SDL多用于開發(fā)游戲、模擬器、媒體播放器等多媒體應(yīng)用。
目前,SDL的穩(wěn)定版本是 1.2.13,1.3還在開發(fā)中,可以通過SVN得到最新的代碼,本次移植以 1.2.13為準(zhǔn),沒有測(cè)試 1.3版的源碼。請(qǐng)從 SDL 的官方網(wǎng)站下載 1.2.13 的源碼,文件名為:SDL-1.2.13.zip,并解壓,將得到一個(gè) SDL-1.2.13 目錄。
在Native編譯SDL之前,要先裝 Code Sourcery公司的arm交叉編譯器,如果是用Windows操作系統(tǒng),則一定要裝 Cygwin(一個(gè)在windows上模擬linux的軟件),因?yàn)樵诰幾g時(shí)要用到一些 linux命令,具體的步驟請(qǐng)參見:Port SDL/TinySDGL to android with native C,或自已在網(wǎng)上搜一些資料。
因?yàn)镾DL是用純C寫的一套類庫(kù),所以移植性非常好,官方支持的系統(tǒng)有: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,而且網(wǎng)上也有人將SDL移植到很多其他嵌入式系統(tǒng),甚至有人將SDL移植到 Moto A1200,如此強(qiáng)大的可移植性,其架構(gòu)真是值得好好學(xué)習(xí)。
現(xiàn)在切入正題,如何為Android量身定做一個(gè) SDL,下面就從視頻,音頻,輸入事件,定時(shí)器(video,audio,events[key,mouse],timer),多線程等幾個(gè)方面來(lái)分析:
1.首先講視頻方面,Android是一個(gè)定制的Linux操作系統(tǒng),Linux顯示要么用 X11,要么用framebuffer技術(shù),很顯然Android并沒有集成 X11(也許Android的軟件界面是基于X11的?!),那只有唯一的選擇:framebuffer!
打開$SDL/src/video目錄,可以發(fā)現(xiàn)SDL支持多達(dá)30多種的視頻顯示技術(shù),其中包括我們想要的fbcon及directfb,directfb我沒有做測(cè)試,也許顯示效果會(huì)比linux自帶的fbcon好,有興趣的朋友可以試一下,成功了別忘了告訴我;
2.再來(lái)談音頻,記得一個(gè)廣告詞:沒有聲音,再好的戲也出不來(lái)!可見音頻對(duì)多媒體應(yīng)用的重要性。
這次用的是OSS的driver,但用的是dsp及dma的實(shí)現(xiàn),但在打開Android指定的音頻文件 /dev/eac 時(shí)有誤,所以音頻這一塊只是能編譯通過,不能正常運(yùn)行,正在考慮用ALSA (Advanced Linux Sound Architecture) 替代;
關(guān)于OSS大家可以參看IBM的文章: OSS--跨平臺(tái)的音頻接口簡(jiǎn)介,寫得比較詳細(xì)。
3.輸入事件(鍵盤,鼠標(biāo))中的鍵盤事件不需要任何更改,就能正常處理,用的設(shè)備文件是 /dev/tty0,
但鼠標(biāo)事件卻不能正常處理,加上DEBUG_MOUSE發(fā)現(xiàn)用的是PS2的鼠標(biāo),但其實(shí)Android用的不是PS2的鼠標(biāo),用的應(yīng)該是觸摸屏(TouchScreen)鼠標(biāo),設(shè)備文件是 /dev/input/event0,詳情請(qǐng)參見本人的blog: Android原生(Native)C開發(fā)之三:鼠標(biāo)事件篇(捕鼠記),經(jīng)過改動(dòng)后,基本能實(shí)現(xiàn)鼠標(biāo)的處理;
4.定時(shí)器用的是unix的實(shí)現(xiàn);
5.多線程用的是pthread的實(shí)現(xiàn),unix系統(tǒng)都是用pthread來(lái)實(shí)現(xiàn)多線程的,在 ln demo時(shí)別忘了加 -lpthread;
6.加載動(dòng)態(tài)庫(kù)用的是unix 的 dl庫(kù),同樣,在ln demo時(shí)別忘了加 -ldl。
SDL提供了一個(gè)最小化的Makefile:Makefile.minimal,所有的實(shí)現(xiàn)都是 dummy,就是一個(gè)空的實(shí)現(xiàn),編譯能通過,但運(yùn)行時(shí)什么都不能做,根據(jù)上面的分析,將 Makefile.minimal 的內(nèi)容改成如下:
# 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的內(nèi)容改成如下:
#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
|
注意黑體部分,其實(shí)就是打開一些宏定義,將SDL的實(shí)現(xiàn)一一打開。
改完了這些以后,還需要改一些代碼,主要是video方面的,因?yàn)锳ndroid Linux的framebuffer設(shè)備文件與標(biāo)準(zhǔn)Linux不同,Linux的fb設(shè)備文件一般是 /dev/fb0,但Android的設(shè)備文件是 /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,就是在后面加一個(gè)“1”子,這可能是編譯器的一個(gè)bug,define默認(rèn)應(yīng)該就是“1”的。
現(xiàn)在可以開始編譯libSDL.a了,在cygwin或Linux下,進(jìn)入SDL目錄,輸入:
make -f Makefile.minimal
視情況面定,一般幾分鐘后能順利編譯 Android版的 SDL,編譯成功后,就需要編譯幾個(gè)SDL的test demo來(lái)測(cè)試一下 SDL的性能。
進(jìn)入 test目錄,復(fù)制 Makefile.in 文件,并改名為 Makefile,將前面一點(diǎn)內(nèi)容改為:
# 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),保存后,先編譯一個(gè)testsprite demo,在命令行輸入:
make testsprite
編譯成功后,啟動(dòng)Android模擬器,將編譯出來(lái)的testsprite及icon.bmp上傳至一個(gè)目錄,如 /dev/sample,命令如下:
adb push testspirte /dev/sample/testsprite
adb push icon.bmp /dev/sample/icon.bmp |
最后進(jìn)入 android 的shell: adb shell,再進(jìn)入 /dev/sample目錄,執(zhí)行testsprite demo即可:
#cd /dev/sample
#chmod 755 testsprite
#./testsprite -width 320 -height 480 -bpp 32 |
可以根據(jù)模擬器的設(shè)置調(diào)整 width 及 height,因?yàn)槌绦蚰J(rèn)的是 640x480的,初始化時(shí)會(huì)失敗,如果一切正常的話,模擬器就會(huì)出現(xiàn)很多黃色的笑臉!按任意鍵退出,在本人機(jī)器上能達(dá)到 60 FPS左右,效果圖如下:

其他的 demo有興趣的朋友可以自己編譯測(cè)試。