=====================================================
SDL源代碼分析系列文章列表:
SDL2源代碼分析1:初始化(SDL_Init())
SDL2源代碼分析2:窗口(SDL_Window)
SDL2源代碼分析3:渲染器(SDL_Renderer)
SDL2源代碼分析4:紋理(SDL_Texture)
SDL2源代碼分析5:更新紋理(SDL_UpdateTexture())
SDL2源代碼分析6:複製到渲染器(SDL_RenderCopy())
SDL2源代碼分析7:顯示(SDL_RenderPresent())
SDL2源代碼分析8:視頻顯示總結
=====================================================
打算花一段時間研究一下SDL的內部代碼。前面幾篇文章《最簡單的視音頻播放示例1:總述》中記錄了視頻、音頻播放的技術,文中提及了SDL實際上封裝了Direct3D,DirectSound這類的底層API。但是SDL究竟是如何封裝的呢?這次打算深入其源代碼一探究竟,看看它是如何封裝這些API的。
SDL簡介
有關SDL的簡介在《最簡單的視音頻播放示例7:SDL2播放RGB/YUV》以及《最簡單的視音頻播放示例9:SDL2播放PCM》中已經敘述過了,不再重複。這兩篇文章中也提到了一張SDL的原理圖,如下所示:
從這個圖中可以看出,SDL根據系統的不同調用不同的API完成相應的功能。至於它是如何實現的,將會在後文中詳細敘述。下面不再羅嗦,直接進入正題。
使用SDL播放一個視頻代碼流程大體如下
初始化:
SDL_Init(): 初始化SDL。
SDL_CreateWindow(): 創建窗口(Window)。
SDL_CreateRenderer(): 基於窗口創建渲染器(Render)。
SDL_CreateTexture(): 創建紋理(Texture)。
循環渲染數據:
SDL_UpdateTexture(): 設置紋理的數據。
SDL_RenderCopy(): 紋理複製給渲染器。
SDL_RenderPresent(): 顯示。
本文分析這個流程中最基本的一個函數SDL_Init()。SDL_Init()是SDL運行的初始,通過分析該函數,可以瞭解到SDL內部的架構。
獲取源代碼
SDL的源代碼獲取十分簡單。訪問SDL的官方網站(http://www.libsdl.org/),單擊左側的“Download”進入下載頁面,然後下載“SourceCode”欄目下的文件就可以了。
下載下來的文件只有4MB左右大小,但是解壓縮之後竟然有50MB左右大小,確實不可思議。
解壓縮之後,源代碼目錄如下圖所示。
幾個關鍵的文件夾如下所示:
1. include:存儲SDL的頭文件的文件夾。
2. src:存儲SDL源代碼文件的文件夾。SDL根據功能模塊的不同,將源代碼分成了很多的文件夾。下圖中標出了存儲SDL幾個子系統的源代碼的文件夾。
3. VisualC:存儲VC解決方案的文件夾。從下圖中可以看出,包含了VS2008,VS2010,VS2012,VS2013等各個版本的VC的解決方案。
實際上從文件名稱我們可以看出,其它幾個文件夾中,“Xcode,Xcode-iOS”包含了Xcode的項目文件,“test”包含了一些測試例子程序,“android-project”包含了Android下的項目文件。由於我們暫時不研究這些文件,就不詳細分析了。
SDL_Init()
函數簡介
下面這一部分進入正題,分析SDL的初始化函數SDL_Init()。該函數可以確定希望激活的子系統。SDL_Init()函數原型如下:
-
int SDLCALL SDL_Init(Uint32 flags)
其中,flags可以取下列值:
SDL_INIT_TIMER:定時器
SDL_INIT_AUDIO:音頻
SDL_INIT_VIDEO:視頻
SDL_INIT_JOYSTICK:搖桿
SDL_INIT_HAPTIC:觸摸屏
SDL_INIT_GAMECONTROLLER:遊戲控制器
SDL_INIT_EVENTS:事件
SDL_INIT_NOPARACHUTE:不捕獲關鍵信號(這個不理解)
SDL_INIT_EVERYTHING:包含上述所有選項
函數調用關係圖
SDL_Init()關鍵函數的調用關係可以用下圖表示。
上面的函數調用關係圖本來是一張高清大圖,但是博客不支持這麼大尺寸的圖片。因此把圖片縮小了,看上去比較模糊。相冊裏面上傳了一份原始的大圖片:
http://my.csdn.net/leixiaohua1020/album/detail/1792993
選擇上述相冊裏面的圖片,右鍵選擇“另存爲”即可保存原始大圖片。
源代碼分析
SDL_Init()的實現位於SDL.c中。定義如下。
-
int SDL_Init(Uint32 flags)
-
{
-
return SDL_InitSubSystem(flags);
-
}
可以看出其代碼只有一句,即調用了SDL_InitSubSystem(),下面我們看一下SDL_InitSubSystem()的定義。
SDL_InitSubSystem()函數的定義看上去很長,實際上卻並不複雜。下面簡單闡述一下它的一些關鍵點:
1. 通過將傳入的flag與子系統的宏定義(例如SDL_INIT_VIDEO,SDL_INIT_AUDIO等)相與,判斷是否需要初始化該子系統。
2. 有很多的預定義的宏,用於判斷SDL是否支持這些子系統。例如SDL_EVENTS_DISABLED,SDL_TIMERS_DISABLED,SDL_VIDEO_DISABLED,SDL_AUDIO_DISABLED,SDL_JOYSTICK_DISABLED,SDL_HAPTIC_DISABLED等。這些宏的定義位於SDL_config_minimal.h文件中,如下所示。
-
-
#define SDL_AUDIO_DRIVER_DUMMY 1
-
-
-
-
#define SDL_JOYSTICK_DISABLED 1
-
-
-
-
#define SDL_HAPTIC_DISABLED 1
-
-
-
-
#define SDL_LOADSO_DISABLED 1
-
-
-
-
#define SDL_THREADS_DISABLED 1
-
-
-
-
#define SDL_TIMERS_DISABLED 1
-
-
-
-
#define SDL_VIDEO_DRIVER_DUMMY 1
-
-
-
-
#define SDL_FILESYSTEM_DUMMY 1
如果這些定義取值不爲0,代表該子系統已經被disable了,就不編譯指定子系統的源代碼了。初始化的時候會調用SDL_SetError()函數輸出錯誤信息。例如SDL_VIDEO_DISABLED如果設置爲1的話,初始化視頻子系統的時候會執行以下代碼。
-
SDL_SetError("SDL not built with video support");
3. 在每一個子系統真正初始化之前,都會調用一個函數SDL_PrivateShouldInitSubsystem()。該函數用於檢查目標子系統是否需要初始化。
4. 在一個子系統初始化之後,都會調用一個函數SDL_PrivateSubsystemRefCountIncr()。該函數用於增加子系統的引用計數。
5. 下表列出各個子系統的初始化函數。
子系統名稱
|
函數
|
AUDIO(音頻)
|
SDL_AudioInit()
|
VIDEO(視頻)
|
SDL_VideoInit()
|
TIMERS(定時器)
|
SDL_TicksInit(),SDL_TimerInit()
|
EVENTS(事件)
|
SDL_StartEventLoop()
|
JOYSTICK(搖桿)
|
SDL_GameControllerInit()
|
HAPTIC(觸摸屏)
|
SDL_HapticInit()
|
我們先不看JOYSTICK(搖桿),HAPTIC(觸摸屏)這些方面的代碼,專門關注AUDIO(音頻),VIDEO(視頻)這兩個方面的代碼。
1.
VIDEO(視頻)
視頻子系統的初始化函數是SDL_VideoInit()。它的源代碼位於video\SDL_video.c文件中,如下所示。
下面簡單闡述一下它的大致步驟:
1.
初始化一些子系統,比如EVENTS(事件)子系統。也就是說,就算在調用SDL_Init()的時候不指定初始化EVENTS子系統,在初始化VIDEO子系統的時候,同樣也會初始化EVENTS子系統。
2.
選擇一個合適的SDL_VideoDevice。
在這裏,涉及到兩個重要的結構體:SDL_VideoDevice以及VideoBootStrap。其中SDL_VideoDevice代表了一個視頻驅動程序。VideoBootStrap從字面上理解是“視頻驅動程序的引導程序”,即用於創建一個SDL_VideoDevice。因此,我們先來看看VideoBootStrap這個結構體。它的定義如下(位於video\SDL_sysvideo.h)。
-
typedef struct VideoBootStrap
-
{
-
const char *name;
-
const char *desc;
-
int (*available) (void);
-
SDL_VideoDevice *(*create) (int devindex);
-
} VideoBootStrap;
可以看出它的定義比較簡單,每個字段的含義如下:
name:驅動名稱
desc:描述
available():檢查是否可用的一個函數指針
create():創建SDL_VideoDevice的函數指針
SDL中有一個VideoBootStrap類型的靜態數組bootstrap。用於存儲SDL支持的視頻驅動程序。注意這是SDL“跨平臺”特性中很重要的一部分。該靜態數組定義如下(位於video\SDL_video.c)。
-
-
static VideoBootStrap *bootstrap[] = {
-
#if SDL_VIDEO_DRIVER_COCOA
-
&COCOA_bootstrap,
-
#endif
-
#if SDL_VIDEO_DRIVER_X11
-
&X11_bootstrap,
-
#endif
-
#if SDL_VIDEO_DRIVER_MIR
-
&MIR_bootstrap,
-
#endif
-
#if SDL_VIDEO_DRIVER_DIRECTFB
-
&DirectFB_bootstrap,
-
#endif
-
#if SDL_VIDEO_DRIVER_WINDOWS
-
&WINDOWS_bootstrap,
-
#endif
-
#if SDL_VIDEO_DRIVER_WINRT
-
&WINRT_bootstrap,
-
#endif
-
#if SDL_VIDEO_DRIVER_HAIKU
-
&HAIKU_bootstrap,
-
#endif
-
#if SDL_VIDEO_DRIVER_PANDORA
-
&PND_bootstrap,
-
#endif
-
#if SDL_VIDEO_DRIVER_UIKIT
-
&UIKIT_bootstrap,
-
#endif
-
#if SDL_VIDEO_DRIVER_ANDROID
-
&Android_bootstrap,
-
#endif
-
#if SDL_VIDEO_DRIVER_PSP
-
&PSP_bootstrap,
-
#endif
-
#if SDL_VIDEO_DRIVER_RPI
-
&RPI_bootstrap,
-
#endif
-
#if SDL_VIDEO_DRIVER_WAYLAND
-
&Wayland_bootstrap,
-
#endif
-
#if SDL_VIDEO_DRIVER_DUMMY
-
&DUMMY_bootstrap,
-
#endif
-
NULL
-
};
從代碼中可以看出,SDL通過預編譯宏取值是否非0來判斷是否支持該視頻驅動。我們可以看一下Windows的視頻設備驅動的定義。該設備驅動同樣是一個靜態變量,名稱爲WINDOWS_bootstrap(位於video\windows\SDL_windowsvideo.c)。
-
VideoBootStrap WINDOWS_bootstrap = {
-
"windows", "SDL Windows video driver", WIN_Available, WIN_CreateDevice
-
};
可以看出該視頻驅動名稱爲“windows”,描述爲“SDL Windows video driver”,檢查是否可用的函數爲“WIN_Available()”,創建SDL_VideoDevice的函數爲“WIN_CreateDevice()”。
同樣, Android的視頻設備驅動的名稱爲Android_bootstrap;PSP的視頻設備驅動爲PSP_bootstrap;X11的視頻設備驅動爲X11_bootstrap。不再一一例舉。
下面看一下Windows視頻驅動中那兩個函數的定義。WIN_Available()定義如下。
-
static int WIN_Available(void)
-
{
-
return (1);
-
}
可見該函數沒有做任何的處理。WIN_CreateDevice()定義如下。
-
static SDL_VideoDevice *
-
WIN_CreateDevice(int devindex)
-
{
-
SDL_VideoDevice *device;
-
SDL_VideoData *data;
-
-
-
SDL_RegisterApp(NULL, 0, NULL);
-
-
-
-
device = (SDL_VideoDevice *) SDL_calloc(1, sizeof(SDL_VideoDevice));
-
if (device) {
-
data = (struct SDL_VideoData *) SDL_calloc(1, sizeof(SDL_VideoData));
-
} else {
-
data = NULL;
-
}
-
if (!data) {
-
SDL_free(device);
-
SDL_OutOfMemory();
-
return NULL;
-
}
-
device->driverdata = data;
-
-
-
data->userDLL = SDL_LoadObject("USER32.DLL");
-
if (data->userDLL) {
-
data->CloseTouchInputHandle = (BOOL (WINAPI *)( HTOUCHINPUT )) SDL_LoadFunction(data->userDLL, "CloseTouchInputHandle");
-
data->GetTouchInputInfo = (BOOL (WINAPI *)( HTOUCHINPUT, UINT, PTOUCHINPUT, int )) SDL_LoadFunction(data->userDLL, "GetTouchInputInfo");
-
data->RegisterTouchWindow = (BOOL (WINAPI *)( HWND, ULONG )) SDL_LoadFunction(data->userDLL, "RegisterTouchWindow");
-
}
-
-
-
-
device->VideoInit = WIN_VideoInit;
-
device->VideoQuit = WIN_VideoQuit;
-
device->GetDisplayBounds = WIN_GetDisplayBounds;
-
device->GetDisplayModes = WIN_GetDisplayModes;
-
device->SetDisplayMode = WIN_SetDisplayMode;
-
device->PumpEvents = WIN_PumpEvents;
-
-
-
#undef CreateWindow
-
device->CreateWindow = WIN_CreateWindow;
-
device->CreateWindowFrom = WIN_CreateWindowFrom;
-
device->SetWindowTitle = WIN_SetWindowTitle;
-
device->SetWindowIcon = WIN_SetWindowIcon;
-
device->SetWindowPosition = WIN_SetWindowPosition;
-
device->SetWindowSize = WIN_SetWindowSize;
-
device->ShowWindow = WIN_ShowWindow;
-
device->HideWindow = WIN_HideWindow;
-
device->RaiseWindow = WIN_RaiseWindow;
-
device->MaximizeWindow = WIN_MaximizeWindow;
-
device->MinimizeWindow = WIN_MinimizeWindow;
-
device->RestoreWindow = WIN_RestoreWindow;
-
device->SetWindowBordered = WIN_SetWindowBordered;
-
device->SetWindowFullscreen = WIN_SetWindowFullscreen;
-
device->SetWindowGammaRamp = WIN_SetWindowGammaRamp;
-
device->GetWindowGammaRamp = WIN_GetWindowGammaRamp;
-
device->SetWindowGrab = WIN_SetWindowGrab;
-
device->DestroyWindow = WIN_DestroyWindow;
-
device->GetWindowWMInfo = WIN_GetWindowWMInfo;
-
device->CreateWindowFramebuffer = WIN_CreateWindowFramebuffer;
-
device->UpdateWindowFramebuffer = WIN_UpdateWindowFramebuffer;
-
device->DestroyWindowFramebuffer = WIN_DestroyWindowFramebuffer;
-
device->OnWindowEnter = WIN_OnWindowEnter;
-
-
-
device->shape_driver.CreateShaper = Win32_CreateShaper;
-
device->shape_driver.SetWindowShape = Win32_SetWindowShape;
-
device->shape_driver.ResizeWindowShape = Win32_ResizeWindowShape;
-
-
-
#if SDL_VIDEO_OPENGL_WGL
-
device->GL_LoadLibrary = WIN_GL_LoadLibrary;
-
device->GL_GetProcAddress = WIN_GL_GetProcAddress;
-
device->GL_UnloadLibrary = WIN_GL_UnloadLibrary;
-
device->GL_CreateContext = WIN_GL_CreateContext;
-
device->GL_MakeCurrent = WIN_GL_MakeCurrent;
-
device->GL_SetSwapInterval = WIN_GL_SetSwapInterval;
-
device->GL_GetSwapInterval = WIN_GL_GetSwapInterval;
-
device->GL_SwapWindow = WIN_GL_SwapWindow;
-
device->GL_DeleteContext = WIN_GL_DeleteContext;
-
#endif
-
device->StartTextInput = WIN_StartTextInput;
-
device->StopTextInput = WIN_StopTextInput;
-
device->SetTextInputRect = WIN_SetTextInputRect;
-
-
-
device->SetClipboardText = WIN_SetClipboardText;
-
device->GetClipboardText = WIN_GetClipboardText;
-
device->HasClipboardText = WIN_HasClipboardText;
-
-
-
device->free = WIN_DeleteDevice;
-
-
-
return device;
-
}
該函數首先通過SDL_calloc()的方法爲創建的SDL_VideoDevice分配了一塊內存,接下來爲創建的SDL_VideoDevice結構體中的函數指針賦了一大堆的值。這也是SDL“跨平臺”特性的一個特點:通過調用SDL_VideoDevice中的接口函數,就可以調用不同平臺的具體實現功能的函數。
PS:在這裏補充一個SDL中內存分配函數的知識。在SDL中分配內存使用SDL_malloc(),SDL_calloc(),這些函數實際上就是malloc(),calloc()。它們的定義位於stdlib\SDL_malloc.c文件中。如下所示:
-
#define memset SDL_memset
-
#define memcpy SDL_memcpy
-
#define malloc SDL_malloc
-
#define calloc SDL_calloc
-
#define realloc SDL_realloc
-
#define free SDL_free
下面來看一下SDL_VideoDevice這個結構體的定義(位於video\SDL_sysvideo.h)。
-
struct SDL_VideoDevice
-
{
-
-
-
const char *name;
-
-
-
-
-
-
-
-
-
-
-
int (*VideoInit) (_THIS);
-
-
-
-
-
-
-
void (*VideoQuit) (_THIS);
-
-
-
-
-
-
-
-
-
-
-
-
int (*GetDisplayBounds) (_THIS, SDL_VideoDisplay * display, SDL_Rect * rect);
-
-
-
-
-
-
void (*GetDisplayModes) (_THIS, SDL_VideoDisplay * display);
-
-
-
-
-
-
-
-
-
int (*SetDisplayMode) (_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode);
-
-
-
-
-
-
-
int (*CreateWindow) (_THIS, SDL_Window * window);
-
int (*CreateWindowFrom) (_THIS, SDL_Window * window, const void *data);
-
void (*SetWindowTitle) (_THIS, SDL_Window * window);
-
void (*SetWindowIcon) (_THIS, SDL_Window * window, SDL_Surface * icon);
-
void (*SetWindowPosition) (_THIS, SDL_Window * window);
-
void (*SetWindowSize) (_THIS, SDL_Window * window);
-
void (*SetWindowMinimumSize) (_THIS, SDL_Window * window);
-
void (*SetWindowMaximumSize) (_THIS, SDL_Window * window);
-
void (*ShowWindow) (_THIS, SDL_Window * window);
-
void (*HideWindow) (_THIS, SDL_Window * window);
-
void (*RaiseWindow) (_THIS, SDL_Window * window);
-
void (*MaximizeWindow) (_THIS, SDL_Window * window);
-
void (*MinimizeWindow) (_THIS, SDL_Window * window);
-
void (*RestoreWindow) (_THIS, SDL_Window * window);
-
void (*SetWindowBordered) (_THIS, SDL_Window * window, SDL_bool bordered);
-
void (*SetWindowFullscreen) (_THIS, SDL_Window * window, SDL_VideoDisplay * display, SDL_bool fullscreen);
-
int (*SetWindowGammaRamp) (_THIS, SDL_Window * window, const Uint16 * ramp);
-
int (*GetWindowGammaRamp) (_THIS, SDL_Window * window, Uint16 * ramp);
-
void (*SetWindowGrab) (_THIS, SDL_Window * window, SDL_bool grabbed);
-
void (*DestroyWindow) (_THIS, SDL_Window * window);
-
int (*CreateWindowFramebuffer) (_THIS, SDL_Window * window, Uint32 * format, void ** pixels, int *pitch);
-
int (*UpdateWindowFramebuffer) (_THIS, SDL_Window * window, const SDL_Rect * rects, int numrects);
-
void (*DestroyWindowFramebuffer) (_THIS, SDL_Window * window);
-
void (*OnWindowEnter) (_THIS, SDL_Window * window);
-
-
-
-
-
-
-
SDL_ShapeDriver shape_driver;
-
-
-
-
SDL_bool(*GetWindowWMInfo) (_THIS, SDL_Window * window,
-
struct SDL_SysWMinfo * info);
-
-
-
-
-
-
-
int (*GL_LoadLibrary) (_THIS, const char *path);
-
void *(*GL_GetProcAddress) (_THIS, const char *proc);
-
void (*GL_UnloadLibrary) (_THIS);
-
SDL_GLContext(*GL_CreateContext) (_THIS, SDL_Window * window);
-
int (*GL_MakeCurrent) (_THIS, SDL_Window * window, SDL_GLContext context);
-
void (*GL_GetDrawableSize) (_THIS, SDL_Window * window, int *w, int *h);
-
int (*GL_SetSwapInterval) (_THIS, int interval);
-
int (*GL_GetSwapInterval) (_THIS);
-
void (*GL_SwapWindow) (_THIS, SDL_Window * window);
-
void (*GL_DeleteContext) (_THIS, SDL_GLContext context);
-
-
-
-
-
-
-
void (*PumpEvents) (_THIS);
-
-
-
-
void (*SuspendScreenSaver) (_THIS);
-
-
-
-
void (*StartTextInput) (_THIS);
-
void (*StopTextInput) (_THIS);
-
void (*SetTextInputRect) (_THIS, SDL_Rect *rect);
-
-
-
-
SDL_bool (*HasScreenKeyboardSupport) (_THIS);
-
void (*ShowScreenKeyboard) (_THIS, SDL_Window *window);
-
void (*HideScreenKeyboard) (_THIS, SDL_Window *window);
-
SDL_bool (*IsScreenKeyboardShown) (_THIS, SDL_Window *window);
-
-
-
-
int (*SetClipboardText) (_THIS, const char *text);
-
char * (*GetClipboardText) (_THIS);
-
SDL_bool (*HasClipboardText) (_THIS);
-
-
-
-
int (*ShowMessageBox) (_THIS, const SDL_MessageBoxData *messageboxdata, int *buttonid);
-
-
-
-
-
SDL_bool suspend_screensaver;
-
int num_displays;
-
SDL_VideoDisplay *displays;
-
SDL_Window *windows;
-
Uint8 window_magic;
-
Uint32 next_object_id;
-
char * clipboard_text;
-
-
-
-
-
struct
-
{
-
int red_size;
-
int green_size;
-
int blue_size;
-
int alpha_size;
-
int depth_size;
-
int buffer_size;
-
int stencil_size;
-
int double_buffer;
-
int accum_red_size;
-
int accum_green_size;
-
int accum_blue_size;
-
int accum_alpha_size;
-
int stereo;
-
int multisamplebuffers;
-
int multisamplesamples;
-
int accelerated;
-
int major_version;
-
int minor_version;
-
int flags;
-
int profile_mask;
-
int share_with_current_context;
-
int framebuffer_srgb_capable;
-
int retained_backing;
-
int driver_loaded;
-
char driver_path[256];
-
void *dll_handle;
-
} gl_config;
-
-
-
-
-
-
-
-
SDL_Window *current_glwin;
-
SDL_GLContext current_glctx;
-
SDL_TLSID current_glwin_tls;
-
SDL_TLSID current_glctx_tls;
-
-
-
-
-
void *driverdata;
-
struct SDL_GLDriverData *gl_data;
-
-
#if SDL_VIDEO_OPENGL_EGL
-
struct SDL_EGL_VideoData *egl_data;
-
#endif
-
-
#if SDL_VIDEO_OPENGL_ES || SDL_VIDEO_OPENGL_ES2
-
struct SDL_PrivateGLESData *gles_data;
-
#endif
-
-
-
-
-
void (*free) (_THIS);
-
};
這個結構體包含了一大堆的函數指針。這些指針在前文所說的VideoBootStrap的create()方法調用的時候會被賦值。SDL通過調用這些函數指針,完成視頻顯示的各項內容。
3.
調用選中的SDL_VideoDevice的VideoInit()方法。
選擇了合適的SDL_VideoDevice之後,調用該SDL_VideoDevice的VideoInit()就可以真正的初始化視頻驅動了。以Windows系統爲例。從前文的函數中可以看出,Windows系統的VideoInit()接口實際上調用了WIN_VideoInit()函數。我們來看一下WIN_VideoInit()函數的定義(位於video\windows\SDL_windowsvideo.c)。
-
int WIN_VideoInit(_THIS)
-
{
-
if (WIN_InitModes(_this) < 0) {
-
return -1;
-
}
-
-
-
WIN_InitKeyboard(_this);
-
WIN_InitMouse(_this);
-
-
-
return 0;
-
}
其中有3個函數:WIN_InitModes(),WIN_InitKeyboard(),WIN_InitMouse()。後兩個函數用於初始化鍵盤和鼠標,我們暫且不研究。看一下WIN_InitModes()的函數。
-
int WIN_InitModes(_THIS)
-
{
-
int pass;
-
DWORD i, j, count;
-
DISPLAY_DEVICE device;
-
-
-
device.cb = sizeof(device);
-
-
-
-
for (pass = 0; pass < 2; ++pass) {
-
for (i = 0; ; ++i) {
-
TCHAR DeviceName[32];
-
-
-
if (!EnumDisplayDevices(NULL, i, &device, 0)) {
-
break;
-
}
-
if (!(device.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP)) {
-
continue;
-
}
-
if (pass == 0) {
-
if (!(device.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE)) {
-
continue;
-
}
-
} else {
-
if (device.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) {
-
continue;
-
}
-
}
-
SDL_memcpy(DeviceName, device.DeviceName, sizeof(DeviceName));
-
#ifdef DEBUG_MODES
-
printf("Device: %s\n", WIN_StringToUTF8(DeviceName));
-
#endif
-
count = 0;
-
for (j = 0; ; ++j) {
-
if (!EnumDisplayDevices(DeviceName, j, &device, 0)) {
-
break;
-
}
-
if (!(device.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP)) {
-
continue;
-
}
-
if (pass == 0) {
-
if (!(device.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE)) {
-
continue;
-
}
-
} else {
-
if (device.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) {
-
continue;
-
}
-
}
-
count += WIN_AddDisplay(device.DeviceName);
-
}
-
if (count == 0) {
-
WIN_AddDisplay(DeviceName);
-
}
-
}
-
}
-
if (_this->num_displays == 0) {
-
return SDL_SetError("No displays available");
-
}
-
return 0;
-
}
該函數的作用就是獲得系統中顯示設備的信息。目前還沒有深入研究,待有時間再補上該函數的分析。
2.
AUDIO(音頻)
音頻子系統的初始化函數是SDL_ AudioInit ()。它的源代碼位於audio\SDL_audio.c文件中,如下所示。
-
int SDL_AudioInit(const char *driver_name)
-
{
-
int i = 0;
-
int initialized = 0;
-
int tried_to_init = 0;
-
-
-
if (SDL_WasInit(SDL_INIT_AUDIO)) {
-
SDL_AudioQuit();
-
}
-
-
-
SDL_memset(¤t_audio, '\0', sizeof(current_audio));
-
SDL_memset(open_devices, '\0', sizeof(open_devices));
-
-
-
-
if (driver_name == NULL) {
-
driver_name = SDL_getenv("SDL_AUDIODRIVER");
-
}
-
-
-
for (i = 0; (!initialized) && (bootstrap[i]); ++i) {
-
-
const AudioBootStrap *backend = bootstrap[i];
-
if ((driver_name && (SDL_strncasecmp(backend->name, driver_name, SDL_strlen(driver_name)) != 0)) ||
-
(!driver_name && backend->demand_only)) {
-
continue;
-
}
-
-
-
tried_to_init = 1;
-
SDL_memset(¤t_audio, 0, sizeof(current_audio));
-
current_audio.name = backend->name;
-
current_audio.desc = backend->desc;
-
initialized = backend->init(¤t_audio.impl);
-
}
-
-
-
if (!initialized) {
-
-
if (!tried_to_init) {
-
if (driver_name) {
-
SDL_SetError("Audio target '%s' not available", driver_name);
-
} else {
-
SDL_SetError("No available audio device");
-
}
-
}
-
-
-
SDL_memset(¤t_audio, 0, sizeof(current_audio));
-
return (-1);
-
}
-
-
-
finalize_audio_entry_points();
-
-
-
return (0);
-
}
音頻初始化和視頻很類似,比視頻簡單一些,關鍵在於選擇一個合適的SDL_AudioDriver。
在這裏,涉及到兩個重要的結構體:SDL_AudioDriver以及AudioBootStrap。其中SDL_AudioDriver代表了一個音頻驅動程序。AudioBootStrap從字面上理解是“音頻驅動程序的引導程序”,即用於創建一個SDL_AudioDriver。可以看出音頻子系統中的結構體和視頻子系統中的結構體的格式基本上是一模一樣的。我們先來看看AudioBootStrap這個結構體。它的定義如下(位於audio\SDL_sysaudio.h)。
-
typedef struct AudioBootStrap
-
{
-
const char *name;
-
const char *desc;
-
int (*init) (SDL_AudioDriverImpl * impl);
-
int demand_only;
-
} AudioBootStrap;
可以看出它的定義比較簡單,每個字段的含義如下:
name:驅動名稱
desc:描述
init():創建SDL_AudioDriver的函數指針
demand_only:沒有研究過。
SDL中有一個AudioBootStrap類型的靜態數組bootstrap。用於存儲SDL支持的音頻驅動程序。該靜態數組定義如下(位於audio\SDL_audio.c)。
-
-
static const AudioBootStrap *const bootstrap[] = {
-
#if SDL_AUDIO_DRIVER_PULSEAUDIO
-
&PULSEAUDIO_bootstrap,
-
#endif
-
#if SDL_AUDIO_DRIVER_ALSA
-
&ALSA_bootstrap,
-
#endif
-
#if SDL_AUDIO_DRIVER_SNDIO
-
&SNDIO_bootstrap,
-
#endif
-
#if SDL_AUDIO_DRIVER_BSD
-
&BSD_AUDIO_bootstrap,
-
#endif
-
#if SDL_AUDIO_DRIVER_OSS
-
&DSP_bootstrap,
-
#endif
-
#if SDL_AUDIO_DRIVER_QSA
-
&QSAAUDIO_bootstrap,
-
#endif
-
#if SDL_AUDIO_DRIVER_SUNAUDIO
-
&SUNAUDIO_bootstrap,
-
#endif
-
#if SDL_AUDIO_DRIVER_ARTS
-
&ARTS_bootstrap,
-
#endif
-
#if SDL_AUDIO_DRIVER_ESD
-
&ESD_bootstrap,
-
#endif
-
#if SDL_AUDIO_DRIVER_NAS
-
&NAS_bootstrap,
-
#endif
-
#if SDL_AUDIO_DRIVER_XAUDIO2
-
&XAUDIO2_bootstrap,
-
#endif
-
#if SDL_AUDIO_DRIVER_DSOUND
-
&DSOUND_bootstrap,
-
#endif
-
#if SDL_AUDIO_DRIVER_WINMM
-
&WINMM_bootstrap,
-
#endif
-
#if SDL_AUDIO_DRIVER_PAUDIO
-
&PAUDIO_bootstrap,
-
#endif
-
#if SDL_AUDIO_DRIVER_HAIKU
-
&HAIKUAUDIO_bootstrap,
-
#endif
-
#if SDL_AUDIO_DRIVER_COREAUDIO
-
&COREAUDIO_bootstrap,
-
#endif
-
#if SDL_AUDIO_DRIVER_DISK
-
&DISKAUD_bootstrap,
-
#endif
-
#if SDL_AUDIO_DRIVER_DUMMY
-
&DUMMYAUD_bootstrap,
-
#endif
-
#if SDL_AUDIO_DRIVER_FUSIONSOUND
-
&FUSIONSOUND_bootstrap,
-
#endif
-
#if SDL_AUDIO_DRIVER_ANDROID
-
&ANDROIDAUD_bootstrap,
-
#endif
-
#if SDL_AUDIO_DRIVER_PSP
-
&PSPAUD_bootstrap,
-
#endif
-
NULL
-
};
在這裏我們可以看一下DirectSound的AudioBootStrap的變量DSOUND_bootstrap(audio\directsound\SDL_directsound.c)。
-
AudioBootStrap DSOUND_bootstrap = {
-
"directsound", "DirectSound", DSOUND_Init, 0
-
};
可以看出該音頻驅動名稱爲“directsound”,描述爲“DirectSound”,創建SDL_AudioDriver的函數爲“DSOUND_Init()”。
下面看一下DirectSound初始化函數DSOUND_Init()的定義。
-
static int DSOUND_Init(SDL_AudioDriverImpl * impl)
-
{
-
if (!DSOUND_Load()) {
-
return 0;
-
}
-
-
-
-
impl->DetectDevices = DSOUND_DetectDevices;
-
impl->OpenDevice = DSOUND_OpenDevice;
-
impl->PlayDevice = DSOUND_PlayDevice;
-
impl->WaitDevice = DSOUND_WaitDevice;
-
impl->WaitDone = DSOUND_WaitDone;
-
impl->GetDeviceBuf = DSOUND_GetDeviceBuf;
-
impl->CloseDevice = DSOUND_CloseDevice;
-
impl->Deinitialize = DSOUND_Deinitialize;
-
-
-
return 1;
-
}
和視頻驅動的初始化一樣,音頻驅動初始化也是對SDL_AudioDriver的接口指針進行賦值。在這裏涉及到了一個DirectSound的加載函數DSOUND_Load(),我們可以看一下它的代碼。
-
static int DSOUND_Load(void)
-
{
-
int loaded = 0;
-
-
-
DSOUND_Unload();
-
-
-
DSoundDLL = SDL_LoadObject("DSOUND.DLL");
-
if (DSoundDLL == NULL) {
-
SDL_SetError("DirectSound: failed to load DSOUND.DLL");
-
} else {
-
-
#define DSOUNDLOAD(f) { \
-
p##f = (fn##f) SDL_LoadFunction(DSoundDLL, #f); \
-
if (!p##f) loaded = 0; \
-
}
-
loaded = 1;
-
DSOUNDLOAD(DirectSoundCreate8);
-
DSOUNDLOAD(DirectSoundEnumerateW);
-
DSOUNDLOAD(DirectSoundCaptureEnumerateW);
-
#undef DSOUNDLOAD
-
-
-
if (!loaded) {
-
SDL_SetError("DirectSound: System doesn't appear to have DX8.");
-
}
-
}
-
-
-
if (!loaded) {
-
DSOUND_Unload();
-
}
-
-
-
return loaded;
-
}
從代碼中可以看出,該函數加載了“DSOUND.DLL”的DirectSoundCreate8(),DirectSoundEnumerateW(),DirectSoundCaptureEnumerateW()函數。