1. 程式人生 > >SDL2---編譯SDL庫、測試播放簡單畫素資料(YUV、RGB等)

SDL2---編譯SDL庫、測試播放簡單畫素資料(YUV、RGB等)

本篇博文整理自雷神(雷霄驊https://blog.csdn.net/leixiaohua1020/article/list/3)多篇博文,多謝分享,在此致敬!

SDL簡介:

SDL庫的作用說白了就是封裝了複雜的視音訊底層操作,簡化了視音訊處理的難度。

以下轉自WiKi:

SDL(Simple DirectMedia Layer)是一套開放原始碼的跨平臺多媒體開發庫,使用C語言寫成。SDL提供了數種控制影象、聲音、輸出入的函式,讓開發者只要用相同或是相似的程式碼就可以開發出跨多個平臺(Linux、Windows、Mac OS X等)的應用軟體。目前SDL多用於開發遊戲、模擬器、媒體播放器等多媒體應用領域。
SDL使用GNU寬通用公共許可證為授權方式,意指動態連結(dynamic link)其庫並不需要開放本身的原始碼。因此諸如《雷神之錘4》等商業遊戲也使用SDL來開發。

結構
雖然SDL時常被比較為‘跨平臺的DirectX’,然而事實上SDL是定位成以精簡的方式來完成基礎的功能,它大幅度簡化了控制影象、聲音、輸出入等工作所需撰寫的程式碼。但更高階的繪圖功能或是音效功能則需搭配OpenGL和OpenAL等API來達成。另外它本身也沒有方便建立圖形使用者介面的函式。
SDL在結構上是將不同作業系統的庫再包裝成相同的函式,例如SDL在Windows平臺上其實是DirectX的再包裝,舊版本包裝的是DirectX 5,現時的版本(SDL 1.2)則是DirectX 7。而在使用X11的平臺上(包括Linux),SDL則是與Xlib庫溝通來輸出影象。
雖然SDL本身是使用C語言寫成,但是它幾乎可以被所有的程式語言所使用,例如:C++、Perl、Python(藉由pygame庫)、Pascal等等,甚至是Euphoria、Pliant這類較不流行的程式語言也都可行。
SDL庫分為 Video、Audio、CD-ROM、Joystick 和 Timer 等若干子系統,除此之外,還有一些單獨的官方擴充函式庫。這些庫由官方網站提供,幷包含在官方文件中,共同組成了SDL的“標準庫”:
SDL_image—支援時下流行的影象格式:BMP、PPM、XPM、 PCX、GIF、JPEG、PNG、TGA。
SDL_mixer—更多的聲音輸出函式以及更多的聲音格式支援。
SDL_net—網路支援。
SDL_ttf—TrueType字型渲染支援。
SDL_rtf—簡單的RTF渲染支援。


子系統
SDL將功能分成下列數個子系統(subsystem):
Video(影象)—影象控制以及執行緒(thread)和事件管理(event)。
Audio(聲音)—聲音控制
Joystick(搖桿)—遊戲搖桿控制
CD-ROM(光碟驅動器)—光碟媒體控制
Window Management(視窗管理)-與視窗程式設計整合
Event(事件驅動)-處理事件驅動

 

SDL播放器開發流程:

SDL播放視訊的程式碼流程大致如下:
初始化:  

SDL_Init(): 初始化SDL。 
SDL_CreateWindow(): 建立視窗(Window)。 
SDL_CreateRenderer(): 基於視窗建立渲染器(Render)。 
SDL_CreateTexture(): 建立紋理(Texture)。 

迴圈渲染資料: 

SDL_UpdateTexture(): 設定紋理的資料。 
SDL_RenderCopy(): 紋理複製給渲染器。 
SDL_RenderPresent(): 顯示。

各函式的原型及引數分析: 

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_CreateWindow()

函式簡介

SDL_CreateWindow()用於建立一個視訊播放的視窗。SDL_CreateWindow()的原型如下。

SDL_Window * SDLCALL SDL_CreateWindow(const char *title,
                                                      int x, int y, int w,
                                                      int h, Uint32 flags);

引數含義如下。 

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。
 

SDL_CreateRenderer()

函式簡介

SDL中使用SDL_CreateRenderer()基於視窗建立渲染器。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。

SDL_CreateTexture()

函式簡介

使用SDL_CreateTexture()基於渲染器建立一個紋理。SDL_CreateTexture()的原型如下。

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。
 

 

SDL_UpdateTexture()

函式簡介

SDL使用SDL_UpdateTexture()設定紋理的畫素資料。SDL_UpdateTexture()的原型如下。

int SDLCALL SDL_UpdateTexture(SDL_Texture * texture,
                                          const SDL_Rect * rect,
                                              const void *pixels, int pitch);

引數的含義如下。

texture:目標紋理。

rect:更新畫素的矩形區域。設定為NULL的時候更新整個區域。

pixels:畫素資料。

pitch:一行畫素資料的位元組數。

成功的話返回0,失敗的話返回-1。

SDL_RenderCopy()

函式簡介

SDL使用SDL_RenderCopy()將紋理資料複製給渲染目標。SDL_RenderCopy()的原型如下。

int SDLCALL SDL_RenderCopy(SDL_Renderer * renderer,
                                           SDL_Texture * texture,
                                           const SDL_Rect * srcrect,
                                           const SDL_Rect * dstrect);

 引數的含義如下。 

renderer:渲染目標。
texture:輸入紋理。
srcrect:選擇輸入紋理的一塊矩形區域作為輸入。設定為NULL的時候整個紋理作為輸入。
dstrect:選擇渲染目標的一塊矩形區域作為輸出。設定為NULL的時候整個渲染目標作為輸出。


成功的話返回0,失敗的話返回-1。

SDL_RenderPresent()

函式簡介

SDL使用SDL_RenderPresent()顯示畫面。SDL_RenderPresent()的原型如下。

void SDLCALL SDL_RenderPresent(SDL_Renderer * renderer);

 引數renderer用於指定渲染器。

 

環境準備:

(由於我測試得系統環境是在虛擬機器上的ubuntu12.04下,系統安裝包裡面只有SDL1.2的包,所以需要下載SDL2-2.0的原始碼包進行手動編譯,從而生成SDL庫)

編譯SDL庫:

1.下載SDL-691c32a30fb9.tar.gz (官網獲取即可)

2.解壓,進入主目錄

   tar zvxf SDL-691c32a30fb9.tar.gz

  cd  SDL-691c32a30fb9.tar.gz

3.配置:

  mkdir _install

 ./configure --prefix=${PWD}/_install  --enable-shared

4.編譯:

 make -j4

5.安裝:

 make install

(編譯完成,在_install目錄下可找到生成的庫檔案及標頭檔案)

簡單測試

SDL畫素資料播放器,程式碼如下:

/**
 * 最簡單的SDL2播放視訊的例子(SDL2播放RGB/YUV)
 * Simplest Video Play SDL2 (SDL2 play RGB/YUV) 
 *
 * 本程式使用SDL2播放RGB/YUV視訊畫素資料。SDL實際上是對底層繪圖
 * API(Direct3D,OpenGL)的封裝,使用起來明顯簡單于直接呼叫底層
 * API。
 *
 * 函式呼叫步驟如下: 
 *
 * [初始化]
 * SDL_Init(): 初始化SDL。
 * SDL_CreateWindow(): 建立視窗(Window)。
 * SDL_CreateRenderer(): 基於視窗建立渲染器(Render)。
 * SDL_CreateTexture(): 建立紋理(Texture)。
 *
 * [迴圈渲染資料]
 * SDL_UpdateTexture(): 設定紋理的資料。
 * SDL_RenderCopy(): 紋理複製給渲染器。
 * SDL_RenderPresent(): 顯示。
 *
 * This software plays RGB/YUV raw video data using SDL2.
 * SDL is a wrapper of low-level API (Direct3D, OpenGL).
 * Use SDL is much easier than directly call these low-level API.  
 *
 * The process is shown as follows:
 *
 * [Init]
 * SDL_Init(): Init SDL.
 * SDL_CreateWindow(): Create a Window.
 * SDL_CreateRenderer(): Create a Render.
 * SDL_CreateTexture(): Create a Texture.
 *
 * [Loop to Render data]
 * SDL_UpdateTexture(): Set Texture's data.
 * SDL_RenderCopy(): Copy Texture to Render.
 * SDL_RenderPresent(): Show.
 */
 
#include <stdio.h>

#include "SDL.h"

 
const int bpp=12;
 
int screen_w=800,screen_h=800;
const int pixel_w=640,pixel_h=480;
 
unsigned char buffer[pixel_w*pixel_h*bpp/8];
 
 
//Refresh Event
#define REFRESH_EVENT  (SDL_USEREVENT + 1)
 
#define BREAK_EVENT  (SDL_USEREVENT + 2)
 
int thread_exit=0;
 
int refresh_video(void *opaque)
{
	thread_exit=0;
	while (!thread_exit) {
		SDL_Event event;
		event.type = REFRESH_EVENT;
		SDL_PushEvent(&event);
		SDL_Delay(40);
	}
	thread_exit=0;
	//Break
	SDL_Event event;
	event.type = BREAK_EVENT;
	SDL_PushEvent(&event);
 
	return 0;
}
 
int main(int argc, char* argv[])
{
	if(SDL_Init(SDL_INIT_VIDEO)) {  
		printf( "Could not initialize SDL - %s\n", SDL_GetError()); 
		return -1;
	} 
 
	SDL_Window *screen; 
	//SDL 2.0 Support for multiple windows
	screen = SDL_CreateWindow("Simplest Video Play SDL2", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
		screen_w, screen_h,SDL_WINDOW_OPENGL|SDL_WINDOW_RESIZABLE);
	if(!screen) {  
		printf("SDL: could not create window - exiting:%s\n",SDL_GetError());  
		return -1;
	}
	SDL_Renderer* sdlRenderer = SDL_CreateRenderer(screen, -1, 0);  
 
	Uint32 pixformat=0;
 
	//IYUV: Y + U + V  (3 planes)
	//YV12: Y + V + U  (3 planes)
	pixformat= SDL_PIXELFORMAT_IYUV;  
 
	SDL_Texture* sdlTexture = SDL_CreateTexture(sdlRenderer,pixformat, SDL_TEXTUREACCESS_STREAMING,pixel_w,pixel_h);
 
	FILE *fp=NULL;
	fp=fopen("yuv420p_640x480.yuv","rb+");
 
	if(fp==NULL){
		printf("cannot open this file\n");
		return -1;
	}
 
	SDL_Rect sdlRect;  
    
    //Create thread
	SDL_Thread *refresh_thread = SDL_CreateThread(refresh_video,NULL,NULL);
	SDL_Event event;
	while(1){
		//Wait event
		SDL_WaitEvent(&event);
		if(event.type==REFRESH_EVENT){
			if (fread(buffer, 1, pixel_w*pixel_h*bpp/8, fp) != pixel_w*pixel_h*bpp/8){
				// Loop
				fseek(fp, 0, SEEK_SET);
				fread(buffer, 1, pixel_w*pixel_h*bpp/8, fp);
			}
 
			SDL_UpdateTexture( sdlTexture, NULL, buffer, pixel_w);  
 
			//FIX: If window is resize
			sdlRect.x = 0;  
			sdlRect.y = 0;  
			sdlRect.w = screen_w;  
			sdlRect.h = screen_h;  
			
			SDL_RenderClear( sdlRenderer );   
			SDL_RenderCopy( sdlRenderer, sdlTexture, NULL, &sdlRect);  
			SDL_RenderPresent( sdlRenderer );  
			
		}else if(event.type==SDL_WINDOWEVENT){
			//If Resize
			SDL_GetWindowSize(screen,&screen_w,&screen_h);
		}else if(event.type==SDL_QUIT){
			thread_exit=1;
		}else if(event.type==BREAK_EVENT){
			break;
		}
	}
	SDL_Quit();
	return 0;
}

建立標頭檔案、庫檔案目錄:

mkdir include lib

chmod 777 include lib

拷貝SDL標頭檔案、庫檔案:

cp   (SDL庫安裝路徑)/lib/lib*    lib/ -r      (記得加入許可權:chmod 777 lib/*)    

cp    (SDL庫安裝路徑) /include/SDL2/* include  (記得加入許可權:chmod 777 include/*)    

 

編寫Makefile:

BIN = player_SDL
IDIR = include/SDL2
SRC = $(wildcard *.c)
OBJS = $(patsubst %.c,%.o,$(SRC))

LDIR = lib
CFLAGS = -I$(IDIR) -g
LIBS = -L$(LDIR) -lSDL2 -lpthread

CC = gcc

#[email protected]:所有的目標檔案
#$^:所有的依賴檔案

$(BIN): $(OBJS)
	$(CC) -o [email protected] $^ $(LIBS)


.PHONY: clean

clean:
	rm -rf $(OBJS) $(BIN)

 編譯:

          make

          (若出現提示找不到libsdl2-2.0.so....錯誤,原因出在gcc編譯器只能在系統目錄下尋找連結庫,所以解決辦法是:

              拷貝所有sdl動態庫到系統目錄lib下,cp  lib/*  /lib)

執行:

        ./player_SDL

         (若執行出錯,提示Could not initialize OpenGL / GLES library

         原因在於mesa-common-dev

         解決辦法:嘗試刪除它 

          apt-get remove mesa-common-dev

          重新編譯SDL庫:./configure --prefix=${PWD}/_install  --enable-shared   

                                      make  -j4 

                                      make install

         完成後拷貝至相應位置即可,重新編譯,並執行player_SDL。 .

         效果如下圖(截圖截得不是很好,不過結果正確了就行,將就看吧^^):