一:SDL介紹與安裝
(一)SDL介紹
(二)SDL安裝
1.源碼下載:http://www.libsdl.org/download-2.0.php
2.生成Makefile文件
./configure --prefix=/usr/local
3.安裝
sudo make -j 8 && sudo make install
二:SDL的簡單使用
SDL播放視頻的代碼流程如下所示:
初始化:
SDL_Init(): 初始化SDL。
SDL_CreateWindow(): 創建窗口(Window)。
SDL_CreateRenderer(): 基於窗口創建渲染器(Render)。
SDL_CreateTexture(): 創建紋理(Texture)。
循環渲染數據:
SDL_UpdateTexture(): 設置紋理的數據。
SDL_RenderCopy(): 紋理復制給渲染器。
SDL_RenderPresent(): 顯示。
(一)基本使用步驟
(二)SDL渲染窗口
1.SDL_Init/SDL_Quit 初始化和退出操作
https://blog.csdn.net/leixiaohua1020/article/details/40680907
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:包含上述所有選項
2.SDL_CreateWindow()/SDL_DestroyWindow() 創建窗口(比如將圖片渲染到窗口)
https://blog.csdn.net/leixiaohua1020/article/details/40701203
SDL_Window * SDLCALL SDL_CreateWindow(const char *title, int x, int y, int w, int h, Uint32 flags);
參數含義如下:https://blog.csdn.net/qq_25333681/article/details/89787867
title :窗口標題
x :窗口位置x坐標。也可以設置為SDL_WINDOWPOS_CENTERED或SDL_WINDOWPOS_UNDEFINED。
y :窗口位置y坐標。同上。
w :窗口的寬
h :窗口的高
flags :支持下列標識。包括了窗口的是否最大化、最小化,能否調整邊界等等屬性。
::SDL_WINDOW_FULLSCREEN, ::SDL_WINDOW_OPENGL,
::SDL_WINDOW_HIDDEN, ::SDL_WINDOW_BORDERLESS,
::SDL_WINDOW_RESIZABLE, ::SDL_WINDOW_MAXIMIZED,
::SDL_WINDOW_MINIMIZED, ::SDL_WINDOW_INPUT_GRABBED,
::SDL_WINDOW_ALLOW_HIGHDPI.
返回創建完成的窗口的ID。如果創建失敗則返回0。
3.SDL_CreateRenderer() 創建渲染器(將圖像/視頻幀匯聚到窗口)
SDL_Renderer * SDLCALL SDL_CreateRenderer(SDL_Window * window, int index, Uint32 flags);
參數含義如下:
window : 渲染的目標窗口。 index :打算初始化的渲染設備的索引。設置“-1”則初始化默認的渲染設備。 flags :支持以下值(位於SDL_RendererFlags定義中) SDL_RENDERER_SOFTWARE :使用軟件渲染 SDL_RENDERER_ACCELERATED :使用硬件加速 SDL_RENDERER_PRESENTVSYNC:和顯示器的刷新率同步 SDL_RENDERER_TARGETTEXTURE :不太懂 返回創建完成的渲染器的ID。如果創建失敗則返回NULL。
(三)簡單實例(未渲染)

#include <stdio.h> #include <SDL.h> int main(int argc,char* argv[]){ SDL_Window* wind = NULL; SDL_Init(SDL_INIT_VIDEO); //進行視頻初始化 wind = SDL_CreateWindow("SDL2 Window", 200,200, 640,480, SDL_WINDOW_SHOWN); //顯示、有邊界 if(!wind){ printf("Failed to Create window!\n"); goto __EXIT; } while(1){ sleep(5000); } SDL_DestroyWindow(wind); __EXIT: SDL_Quit(); return 0; }
gcc 01_sdl_sample.c -o 01ss -I /usr/local/include/SDL2/ -L /usr/local/lib/ -lSDL2
可以看到出現窗口,但是沒有渲染,導致出現透明狀態,顯示屏幕原本信息
(四)簡單實例(使用渲染器)
SDL_Renderer表示渲染上下文。這意味着它包含與渲染相關的所有當前設置,以及有關如何渲染當前幀的說明。
要創建渲染上下文,可以使用函數SDL_CreateWindowAndRenderer()或SDL_CreateRenderer()。前者同時創建窗口和渲染器。后者要求我們先創建一個窗口。
在程序中,您將使用SDL_SetRenderDrawColor()等函數更改上下文中的設置,並使用SDL_RenderDrawPoint()等函數執行渲染操作。
int SDL_RenderClear(SDL_Renderer* renderer) ,該函數的作用是用指定的顏色清空緩沖區。renderer是上面創建的渲染器上下文。
void SDL_RenderPresent(SDL_Renderer* renderer),將緩沖區中的內容輸出到目標上,也就是 windows 窗口上。注意:渲染的數據也可以是從GPU渲染到屏幕!!

#include <stdio.h> #include <SDL.h> int main(int argc,char* argv[]){ SDL_Window* wind = NULL; SDL_Renderer* rend = NULL; SDL_Init(SDL_INIT_VIDEO); //進行視頻初始化 wind = SDL_CreateWindow("SDL2 Window", 200,200, 640,480, SDL_WINDOW_SHOWN); //顯示、有邊界 if(!wind){ printf("Failed to Create window!\n"); goto __EXIT; } rend = SDL_CreateRenderer(wind,-1,0); //創建Render渲染器 if(!rend){ printf("Failed to create render\n"); goto __DWIND; } SDL_SetRenderDrawColor(rend,0,0,0,255); //設置顏色和透明度(可選操作) SDL_RenderClear(rend); //清屏處理(清空render之前的數據) SDL_RenderPresent(rend); //將渲染器結果放入窗口中顯示出來 SDL_Delay(5000); __DWIND: SDL_DestroyWindow(wind); __EXIT: SDL_Quit(); return 0; }
(五)SDL_Surface與SDL_Texture
SDL 渲染的工作原理:
即在SDL_Render對象中有一個視頻緩沖區,該緩沖區我們稱之為SDL_Surface,它是按照像素存放圖像的。
我們一般把真彩色的像素稱為RGB24數據。
也就是說,每一個像素由24位組成,每8位代表一種顏色,像素的最終顏色是由RGB三種顏色混合而成的。
SDL_Texture 與SDL_Surface相似,也是一種緩沖區。
只不過它存放的不是真正的像素數據,而是存放的圖像的描述信息。這些描述信息通過OpenGL、D3D 或 Metal等技術操作GPU,從而繪制出與SDL_Surface一樣的圖形,且效率更高(因為它是GPU硬件計算的)。
(六)SDL_Window與SDL_Render
SDL_Window代表的是窗口的邏輯概念,它是存放在主內存中的一個對象。
SDL_Render 是渲染器,它也是主存中的一個對象。對Render操作時實際上分為兩個階段:
一、渲染階段。在該階段,用戶可以畫各種圖形渲染到SDL_Surface或SDL_Texture 中;
二、顯示階段。參SDL_Texture為數據,通過OpenGL操作GPU,最終將 SDL_Surfce 或SDL_Texture中的數據輸出到顯示器上。
三:SDL事件
(一)SDL事件基本原理
(二)SDL事件種類
(三)SDL事件處理
1.poll 論詢機制,處理不及時,占CPU 2.wait 事件觸發(類似epoll池),處理及時,不占用過多CPU。
對於wait,可能出現線程阻塞,導致無法處理到來的其他事件,應該為每一個時間處理設置Timeout,所以出現了WaitEventTimeOut。
(四)事件機制簡單使用

#include <stdio.h> #include <SDL.h> int main(int argc,char* argv[]){ int exitFlag=1; SDL_Window* wind = NULL; SDL_Renderer* rend = NULL; SDL_Event event; SDL_Init(SDL_INIT_VIDEO); //進行視頻初始化 wind = SDL_CreateWindow("SDL2 Window", 200,200, 640,480, SDL_WINDOW_SHOWN); //顯示、有邊界 if(!wind){ printf("Failed to Create window!\n"); goto __EXIT; } rend = SDL_CreateRenderer(wind,-1,0); //創建Render渲染器 if(!rend){ printf("Failed to create render\n"); goto __DWIND; } SDL_SetRenderDrawColor(rend,0,0,0,255); //設置顏色和透明度(可選操作) SDL_RenderClear(rend); //清屏處理(清空render之前的數據) SDL_RenderPresent(rend); //將渲染器結果放入窗口中顯示出來 while(exitFlag){ SDL_WaitEvent(&event); //如果沒有事件的話,會阻塞在這里的,不會一直while輪詢 switch(event.type){ case SDL_QUIT: exitFlag = 0; break; default: SDL_Log("event type is %d",event.type); break; } } __DWIND: SDL_DestroyWindow(wind); __EXIT: SDL_Quit(); return 0; }
gcc 02_sdl_event.c -o 02se -I /usr/local/include/SDL2/ -L /usr/local/lib/ -lSDL2
四:紋理渲染
依據SDL編程方式,這里又分為兩種情況:
1. 不使用紋理
即由cpu直接繪制一幅畫(cpu需要將最原始的rgb/YUV數據,刷到屏幕上), 相當於學生小A直接在牆上畫畫
2. 使用紋理
相當於小A同學(cpu)指揮畫家(gpu)在紙上畫, 然后把紙貼在牆上。
這個過程中畫是由畫家(gpu)畫的, 小A同學負責發號施令(即告訴畫家畫什么), 紙代表紋理, 畫家代表gpu, 所有繪制的操作都是在紋理上進行。
事實上,紋理的概念並不僅僅是一張紙, 還包括小A同學中對這幅畫的構思,可以理解成畫畫的算法, 而紙相當於是一個載體(內存空間,用於保存這些構思)。
gpu根據紋理就可以計算出這幅圖每個像素點的顏色( 相當於畫家根據小A同學的描述,畫出一幅畫一樣)
可以看出,使用紋理,可以減輕cpu的負擔, cpu處於一個發號施令的角色,圖片的計算過程交給效率更好的gpu來做,可以提高渲染的效率。
(一)紋理渲染基本原理
圖像結果渲染器,變為紋理(即對這張圖像的描述數據),將紋理交給顯卡GPU,GPU經過計算,將圖像顯示在窗口中!
(二)SDL紋理相關API
SDL_Texture * SDLCALL SDL_CreateTexture(SDL_Renderer * renderer, Uint32 format, int access, int w, int h);
參數的含義如下:
renderer:目標渲染器。
format :紋理的格式。后面會詳述。
access :可以取以下值(定義位於SDL_TextureAccess中)
SDL_TEXTUREACCESS_STATIC :變化極少
SDL_TEXTUREACCESS_STREAMING :變化頻繁
SDL_TEXTUREACCESS_TARGET :暫時沒有理解
w :紋理的寬
h :紋理的高
創建成功則返回紋理的ID,失敗返回0。
(三)渲染相關API
SDL_CreateTexture用於創建紋理, 若要使用SDL_SetRenderTarget()設置渲染到紋理時, 必須使用SDL_TEXTUREACCESS_TARGET方式來創建紋理。
函數SDL_SetRenderTarget()用於在渲染到紋理或屏幕之間進行選擇。切換方式如下:
SDL_SetRenderTarget(renderer, texture) // 渲染到紋理 SDL_SetRenderTarget(renderer, NULL) // 渲染到屏幕(默認渲染到窗口/屏幕)
int SDL_RenderCopy(SDL_Renderer* renderer, SDL_Texture* texture, const SDL_Rect* srcrect, const SDL_Rect* dstrect)將紋理拷貝到顯卡GPU中去,通過SDL_RenderPresent進行渲染到屏幕
renderer:渲染目標。
texture:輸入紋理。
srcrect:選擇輸入紋理的一塊矩形區域作為輸入。設置為NULL的時候整個紋理作為輸入。
dstrect:選擇渲染目標的一塊矩形區域作為輸出。設置為NULL的時候整個渲染目標作為輸出。
(四)紋理使用

#include <stdio.h> #include <SDL.h> int main(int argc,char* argv[]){ int exitFlag=1; SDL_Window* wind = NULL; SDL_Renderer* rend = NULL; SDL_Texture* text = NULL; SDL_Event event; SDL_Rect rect; //創建矩形 SDL_Init(SDL_INIT_VIDEO); //進行視頻初始化 wind = SDL_CreateWindow("SDL2 Window", 200,200, 640,480, SDL_WINDOW_SHOWN); //顯示、有邊界 if(!wind){ printf("Failed to Create window!\n"); goto __EXIT; } rend = SDL_CreateRenderer(wind,-1,0); //創建Render渲染器 if(!rend){ printf("Failed to create render\n"); goto __DWIND; } SDL_SetRenderDrawColor(rend,0,0,0,255); //設置顏色和透明度(可選操作) SDL_RenderClear(rend); //清屏處理(清空render之前的數據) SDL_RenderPresent(rend); //將渲染器結果放入窗口中顯示出來 text = SDL_CreateTexture(rend,SDL_PIXELFORMAT_RGBA8888,SDL_TEXTUREACCESS_TARGET, 640,480); //創建紋理 if(!text){ printf("Failed to create texture\n"); goto __DREND; } rect.w = 30; rect.h = 30; while(exitFlag){ SDL_PollEvent(&event); //如果沒有事件的話,會一直while輪詢 switch(event.type){ case SDL_QUIT: exitFlag = 0; break; default: SDL_Log("event type is %d",event.type); break; } rect.x = rand() % 610; rect.y = rand() % 450; SDL_SetRenderTarget(rend,text); //渲染到紋理,下面的操作都會轉為紋理 SDL_SetRenderDrawColor(rend,0,0,0,0); SDL_RenderClear(rend); //繪制矩形 SDL_RenderDrawRect(rend,&rect); SDL_SetRenderDrawColor(rend,255,0,0,0); //為矩形繪制顏色 SDL_RenderFillRect(rend,&rect); //將渲染器設置到矩形中去,使得clear只在矩形中生效 SDL_SetRenderTarget(rend,NULL); //使用默認渲染,到窗口 SDL_RenderCopy(rend,text,NULL,NULL); //拷貝紋理到顯卡 SDL_RenderPresent(rend); //將結果顯示到窗口 } SDL_DestroyTexture(text); __DREND: SDL_DestroyRenderer(rend); __DWIND: SDL_DestroyWindow(wind); __EXIT: SDL_Quit(); return 0; }
gcc 03_sdl_texture.c -o 03st -I /usr/local/include/SDL2/ -L /usr/local/lib/ -lSDL2
SDL_SetRenderTarget(rend,text); //渲染到紋理,下面的操作都會轉為紋理!!!
SDL_SetRenderDrawColor(rend,0,0,0,0); SDL_RenderClear(rend); //繪制矩形 SDL_RenderDrawRect(rend,&rect); SDL_SetRenderDrawColor(rend,255,0,0,0); //為矩形繪制顏色 SDL_RenderFillRect(rend,&rect); //將渲染器設置到矩形中去,使得內部clear只在矩形中生效 SDL_SetRenderTarget(rend,NULL); //使用默認渲染,到窗口;下面的操作會顯示在窗口,不經過紋理轉換 SDL_RenderCopy(rend,text,NULL,NULL); //拷貝紋理到顯卡---在窗口層面處理,而不是紋理 SDL_RenderPresent(rend); //GPU顯卡將計算的結果顯示到窗口
五:實戰YUV播放器
(一)創建線程(提高效率)
創建線程:SDL_Thread* SDL_CreateThread(SDL_ThreadFunction fn, const char* name, void* data)
fn: 線程要運行的函數。
name: 線程名。
data: 函數參數。
等待線程:void SDL_WaitThread(SDL_Thread* thread, int* status) 等待線程結束
創建互斥量:SDL_mutex* SDL_CreateMutex(void) 也就是創建一個稀有資源,這樣大家就去搶這個資源。從而達到為真正資源加鎖的目的。
銷毀互斥量:void SDL_DestroyMutex(SDL_mutex* mutex)
加鎖: int SDL_LockMutex(SDL_mutex* mutex)
解鎖: int SDL_UnlockMutex(SDL_mutex* mutex)
(二)紋理更新
int SDLCALL SDL_UpdateTexture(SDL_Texture * texture, const SDL_Rect * rect, const void *pixels, int pitch);
參數的含義如下:
texture:目標紋理。 rect:更新像素的矩形區域。設置為NULL的時候更新整個區域。 pixels:像素數據。 pitch:一行像素數據的字節數。 成功的話返回0,失敗的話返回-1。
SDL_UpdateTexture()的大致流程如下:
1. 檢查輸入參數的合理性。例如像素格式是否支持,寬和高是否小於等於0等等。 2. 如果是一些特殊的格式,進行一定的處理: a) 如果輸入的像素數據是YUV格式的,則會調用SDL_UpdateTextureYUV()進行處理。 b) 如果輸入的像素數據的像素格式不是渲染器支持的格式,則會調用SDL_UpdateTextureNative()進行處理。 3. 調用SDL_Render的UpdateTexture()方法更新紋理。這一步是整個函數的核心。
通過SDL_CreateTexture中指定紋理格式format為SDL_PIXELFORMAT_IYUV即可處理YUV數據!!
(三)編程實現
ffmpeg -i gfxm.mp4 -an -c:v rawvideo -pix_fmt yuv420p out.yuv

#include <stdio.h> #include <string.h> #include <SDL.h> //下面定義自定義事件,SDL_USEREVENT 0x8000之后,到0xFFFF都可以 #define REFRASH_EVENT (SDL_USEREVENT + 1) #define QUIT_EVENT (SDL_USEREVENT + 2) int thread_exit = 0; //控制線程退出 void refresh_video_timer(void* udata){ thread_exit = 0; while(!thread_exit){ SDL_Event event; event.type = REFRASH_EVENT; SDL_PushEvent(&event); SDL_Delay(40); //25幀/S } thread_exit = 0; //開始退出 SDL_Event event; event.type = QUIT_EVENT; SDL_PushEvent(&event); return 0; } int main(int argc,char* argv[]){ int exitFlag=1; //控制循環 char* filename = NULL; FILE* fp = NULL; int len; uint8_t* src_data = NULL,*src_start=NULL; //獲取視頻幀數據 int v_w,v_h,w_w=1280,w_h=720; //視頻寬高、窗口寬高 unsigned int frame_len,frame_temp_len; SDL_Window* wind = NULL; SDL_Renderer* rend = NULL; SDL_Texture* text = NULL; SDL_Event event; SDL_Rect rect; //創建矩形 SDL_Thread* timer_thread = NULL; //線程 if(argc<4){ printf("The number of parameter must be than 3\n"); return 0; } filename = argv[1]; //獲取YUV文件----YUV為YUV420數據 v_w = atoi(argv[2]); //獲取文件的寬 v_h = atoi(argv[3]); //獲取視頻的高 frame_temp_len = v_h*v_w*1.5; //YUV420數據大小,需要對數據進行對齊操作 frame_len = frame_temp_len; if(frame_temp_len&0xF){ //后4位存在數據,則表示不是16對齊 frame_len = (frame_temp_len&0xFFF0) + 0x10; //進16 } if(SDL_Init(SDL_INIT_VIDEO)){ //進行視頻初始化 printf("Can`t initialize SDL\n"); return -1; } wind = SDL_CreateWindow("YUV Player", SDL_WINDOWPOS_UNDEFINED,SDL_WINDOWPOS_UNDEFINED, //不指定左上位置 w_w,w_h, //設置窗口寬高,與視頻一般或者大些都可以 SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE); //窗口可用於OpenGL上下文 窗口可以調整大小 if(!wind){ printf("Failed to Create window!\n"); goto __EXIT; } rend = SDL_CreateRenderer(wind,-1,0); //創建Render渲染器 if(!rend){ printf("Failed to create render\n"); goto __DWIND; } //創建紋理 text = SDL_CreateTexture(rend, SDL_PIXELFORMAT_IYUV, //< Planar mode: Y + U + V (3 planes) SDL_TEXTUREACCESS_TARGET, v_w,v_h); //創建紋理 if(!text){ printf("Failed to create texture\n"); goto __DREND; } //開始讀取YUV數據 fp = fopen(filename,"rb"); if(!fp){ printf("Failed to open file:%s\n",filename); goto __TEXT; } src_data = (uint8_t*)malloc(frame_len); //分配空間 if(!src_data){ printf("Failed to alloc memory for src_data!\n"); goto __FILE; } src_start = src_data; //開始位置 //開啟線程,處理其他控制事件 timer_thread = SDL_CreateThread(refresh_video_timer,NULL,NULL); //要顯示紋理數據的矩形 rect.x = 0; rect.y = 0; while(exitFlag){ SDL_PollEvent(&event); //如果沒有事件的話,會一直while輪詢 switch(event.type){ case SDL_QUIT: exitFlag = 0; break; case REFRASH_EVENT: //更新紋理 //先讀取一幀數據 if((len=fread(src_data,1,frame_len,fp))<=0){ //數據讀取完成,退出 thread_exit = 1; continue; } SDL_UpdateTexture(text,NULL,src_start,v_w); //內部設置 rect.w = w_w; //因為窗口可以被重置 rect.h = w_h; SDL_RenderClear(rend); SDL_RenderCopy(rend,text,NULL,&rect); //拷貝紋理到顯卡,選擇渲染目標的一塊矩形區域作為輸出 SDL_RenderPresent(rend); //將結果顯示到窗口 break; case QUIT_EVENT: //退出播放 exitFlag = 0; break; default: SDL_Log("event type is %d",event.type); break; } } if(src_data) free(src_data); __FILE: if(fp) fclose(fp); __TEXT: SDL_DestroyTexture(text); __DREND: SDL_DestroyRenderer(rend); __DWIND: SDL_DestroyWindow(wind); __EXIT: SDL_Quit(); return 0; }
gcc 04_sdl_yuv.c -o 04sy -I /usr/local/include/SDL2/ -L /usr/local/lib/ -lSDL2
./04sy out.yuv 864 486
六:實戰PCM播放器
使用 SDL 的音頻 API 來播放聲音。其基本流程是,從 pcm 文件一塊一塊的讀數據。然后通過 read_audio_data 這個回調函數給聲卡喂數據。
如果一次沒用完,SDL會再次調用回調函數讀數據。如果audio_buf中的數據用完了,則再次從文件中讀一塊數據,直到讀到文件尾。
(一)播放音頻基本流程
(二)播放音頻原則
(三)相關API
1.打開音頻設備 int SDL_OpenAudio(SDL_AudioSpec* desired,SDL_AudioSpec* obtained)
desired: 設置音頻參數。
參數 | 說明 |
---|---|
freq | 每秒采頻率 |
SDL_AudioFormat | 音頻數據存儲格式 |
channels | 通道數 |
silence | 靜音值 |
samples | 采樣個數 |
size | 音頻緩沖區大小 |
SDL_AudioCallback | 回調函數 |
userdata | 回調函數參數指針 |
obtained: 返回參數。
2.關閉音頻設備 void SDL_CloseAudio(void)
3.播放與暫停 void SDL_PauseAudio(int pause_on)
pause_on: 0, 暫停播放;1, 播放;
4.喂數據 void SDL_MixAudio(Uint8* dst,const Uint8* src,Uint32 len,int volume)
dst: 目的緩沖區 src: 源緩沖區 len: 音頻數據長度 volume: 音量大小,0-128 之間的數。SDL_MIX_MAXVOLUME代表最大音量。
(四)編程實現
ffmpeg -i out.aac -ar 44100 -ac 2 -f s16le out.pcm

#include <stdio.h> #include <string.h> #include <SDL.h> #define BLOCK_SIZE 4096000 //4096k,設置一個較大的數值空間 //將數據存放在全局 static uint8_t* audio_buf = NULL; static uint8_t* audio_pos = NULL; static int buffer_len = 0; //回調函數 void read_audio_data(void* udata,uint8_t* stream,int len){ //回調數據,目的緩沖區,和緩沖區長度 if(buffer_len == 0) return; SDL_memset(stream,0,len); //初始化緩沖區 len = (len<buffer_len)?len:buffer_len; //獲取空間長度 SDL_MixAudio(stream,audio_pos,len,SDL_MIX_MAXVOLUME); audio_pos += len; buffer_len -= len; } int main(int argc,char* argv[]){ int exitFlag=1; //控制循環 char* filename = NULL; FILE* fp = NULL; SDL_AudioSpec spec; //用來設置音頻參數 if(argc<2){ printf("The number of parameter must be than 1\n"); return 0; } filename = argv[1]; //獲取pcm文件 if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)){ //進行視頻初始化 printf("Can`t initialize SDL\n"); return -1; } fp = fopen(filename,"rb"); if(!fp){ printf("Failed to open file:%s",filename); goto __EXIT; } audio_buf = (uint8_t*)malloc(BLOCK_SIZE); //分配空間 if(!audio_buf){ printf("Failed to alloc memory for audio buffer\n"); goto __FILE; } //開始開啟音頻設備 spec.freq = 44100; spec.format = AUDIO_S16SYS; spec.channels = 2; spec.silence = 0; spec.samples = 1024; //每幀數據,單通道的采樣數量 spec.callback = read_audio_data; spec.userdata = NULL; if(SDL_OpenAudio(&spec,NULL)){ printf("Failed to open audio device:%s\n",SDL_GetError()); goto __BUFFER; } //開始播放 SDL_PauseAudio(0); while(1){ buffer_len = fread(audio_buf,1,BLOCK_SIZE,fp); if(buffer_len<=0) break; printf("read block size:%d \n",buffer_len); audio_pos = audio_buf; while(audio_pos<(audio_buf+buffer_len)){ //數據沒有讀取完成 SDL_Delay(1); //延遲1毫秒,等等設備繼續讀取 } } SDL_CloseAudio(); //關閉音頻設備 __BUFFER: if(audio_buf) free(audio_buf); __FILE: if(fp) fclose(fp); __EXIT: SDL_Quit(); return 0; }
gcc 05_sdl_pcm.c -o 05sp -I /usr/local/include/SDL2/ -L /usr/local/lib/ -lSDL2
./05sp out.pcm