本文記錄SDL播放視頻的技術(shù)。在這里使用的版本是SDL2。實際上SDL本身并不提供視音頻播放的功能,它只是封裝了視音頻播放的底層API。在Windows平臺下,SDL封裝了Direct3D這類的API用于播放視頻;封裝了DirectSound這類的API用于播放音頻。因為SDL的編寫目的就是簡化視音頻播放的開發(fā)難度,所以使用SDL播放視頻(YUV/RGB)和音頻(PCM)數(shù)據(jù)非常的容易。下文記錄一下使用SDL播放視頻數(shù)據(jù)的技術(shù)。
SDL簡介
SDL(Simple DirectMedia Layer)是一套開放源代碼的跨平臺多媒體開發(fā)庫,使用C語言寫成。SDL提供了數(shù)種控制圖像、聲音、輸出入的函數(shù),讓開發(fā)者只要用相同或是相似的代碼就可以開發(fā)出跨多個平臺(Linux、Windows、Mac OS X等)的應(yīng)用軟件。目前SDL多用于開發(fā)游戲、模擬器、媒體播放器等多媒體應(yīng)用領(lǐng)域。用下面這張圖可以很明確地說明SDL的位置。
SDL實際上并不限于視音頻的播放,它將功能分成下列數(shù)個子系統(tǒng)(subsystem):
Video(圖像):圖像控制以及線程(thread)和事件管理(event)。 Audio(聲音):聲音控制 Joystick(搖桿):游戲搖桿控制 CD-ROM(光盤驅(qū)動器):光盤媒體控制 Window Management(視窗管理):與視窗程序設(shè)計集成 Event(事件驅(qū)動):處理事件驅(qū)動
在Windows下,SDL與DirectX的對應(yīng)關(guān)系如下。
SDL
|
DirectX
|
SDL_Video、SDL_Image
|
DirectDraw、Direct3D
|
SDL_Audio、SDL_Mixer
|
DirectSound
|
SDL_Joystick、SDL_Base
|
DirectInput
|
SDL_Net
|
DirectPlay
|
SDL播放視頻的流程
SDL播放視頻的技術(shù)在此前做的FFmpeg的示例程序中已經(jīng)多次用到。在這里重新總結(jié)一下流程。 1. 初始化
1) 初始化SDL 2) 創(chuàng)建窗口(Window) 3) 基于窗口創(chuàng)建渲染器(Render) 4) 創(chuàng)建紋理(Texture)
2. 循環(huán)顯示畫面
1) 設(shè)置紋理的數(shù)據(jù) 2) 紋理復(fù)制給渲染目標(biāo) 3) 顯示
下面詳細(xì)分析一下上文的流程。
1. 初始化
1) 初始化SDL
使用SDL_Init()初始化SDL。該函數(shù)可以確定希望激活的子系統(tǒng)。SDL_Init()函數(shù)原型如下:
- 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:不捕獲關(guān)鍵信號(這個沒研究過) SDL_INIT_EVERYTHING:包含上述所有選項
有關(guān)SDL_Init()有一點需要注意:初始化的時候盡量做到“夠用就好”,而不要用SDL_INIT_EVERYTHING。因為有些情況下使用SDL_INIT_EVERYTHING會出現(xiàn)一些不可預(yù)知的問題。例如,在MFC應(yīng)用程序中播放純音頻,如果初始化SDL的時候使用SDL_INIT_EVERYTHING,那么就會出現(xiàn)聽不到聲音的情況。后來發(fā)現(xiàn),去掉了SDL_INIT_VIDEO之后,問題才得以解決。
2) 創(chuàng)建窗口(Window) 使用SDL_CreateWindow()創(chuàng)建一個用于視頻播放的窗口。SDL_CreateWindow()的原型如下。
- SDL_Window * SDLCALL SDL_CreateWindow(const char *title,
- int x, int y, int w,
- int h, Uint32 flags);
參數(shù)含義如下。 title :窗口標(biāo)題 x :窗口位置x坐標(biāo)。也可以設(shè)置為SDL_WINDOWPOS_CENTERED或SDL_WINDOWPOS_UNDEFINED。 y :窗口位置y坐標(biāo)。同上。 w :窗口的寬 h :窗口的高 flags :支持下列標(biāo)識。包括了窗口的是否最大化、最小化,能否調(diào)整邊界等等屬性。 ::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. 返回創(chuàng)建完成的窗口的ID。如果創(chuàng)建失敗則返回0。 3) 基于窗口創(chuàng)建渲染器(Render) 使用SDL_CreateRenderer()基于窗口創(chuàng)建渲染器。SDL_CreateRenderer()原型如下。
- SDL_Renderer * SDLCALL SDL_CreateRenderer(SDL_Window * window,
- int index, Uint32 flags);
參數(shù)含義如下。 window : 渲染的目標(biāo)窗口。 index :打算初始化的渲染設(shè)備的索引。設(shè)置“-1”則初始化默認(rèn)的渲染設(shè)備。 flags :支持以下值(位于SDL_RendererFlags定義中)
SDL_RENDERER_SOFTWARE :使用軟件渲染 SDL_RENDERER_ACCELERATED :使用硬件加速 SDL_RENDERER_PRESENTVSYNC:和顯示器的刷新率同步 SDL_RENDERER_TARGETTEXTURE :不太懂
返回創(chuàng)建完成的渲染器的ID。如果創(chuàng)建失敗則返回NULL。 4) 創(chuàng)建紋理(Texture) 使用SDL_CreateTexture()基于渲染器創(chuàng)建一個紋理。SDL_CreateTexture()的原型如下。
- SDL_Texture * SDLCALL SDL_CreateTexture(SDL_Renderer * renderer,
- Uint32 format,
- int access, int w,
- int h);
參數(shù)的含義如下。 renderer:目標(biāo)渲染器。 format :紋理的格式。后面會詳述。 access :可以取以下值(定義位于SDL_TextureAccess中)
SDL_TEXTUREACCESS_STATIC :變化極少 SDL_TEXTUREACCESS_STREAMING :變化頻繁 SDL_TEXTUREACCESS_TARGET :暫時沒有理解
w :紋理的寬 h :紋理的高 創(chuàng)建成功則返回紋理的ID,失敗返回0。 在紋理的創(chuàng)建過程中,需要指定紋理的格式(即第二個參數(shù))。SDL的中的格式很多,如下所列。
- SDL_PIXELFORMAT_UNKNOWN,
- SDL_PIXELFORMAT_INDEX1LSB =
- SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_INDEX1, SDL_BITMAPORDER_4321, 0,
- 1, 0),
- SDL_PIXELFORMAT_INDEX1MSB =
- SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_INDEX1, SDL_BITMAPORDER_1234, 0,
- 1, 0),
- SDL_PIXELFORMAT_INDEX4LSB =
- SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_INDEX4, SDL_BITMAPORDER_4321, 0,
- 4, 0),
- SDL_PIXELFORMAT_INDEX4MSB =
- SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_INDEX4, SDL_BITMAPORDER_1234, 0,
- 4, 0),
- SDL_PIXELFORMAT_INDEX8 =
- SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_INDEX8, 0, 0, 8, 1),
- SDL_PIXELFORMAT_RGB332 =
- SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED8, SDL_PACKEDORDER_XRGB,
- SDL_PACKEDLAYOUT_332, 8, 1),
- SDL_PIXELFORMAT_RGB444 =
- SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED16, SDL_PACKEDORDER_XRGB,
- SDL_PACKEDLAYOUT_4444, 12, 2),
- SDL_PIXELFORMAT_RGB555 =
- SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED16, SDL_PACKEDORDER_XRGB,
- SDL_PACKEDLAYOUT_1555, 15, 2),
- SDL_PIXELFORMAT_BGR555 =
- SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED16, SDL_PACKEDORDER_XBGR,
- SDL_PACKEDLAYOUT_1555, 15, 2),
- SDL_PIXELFORMAT_ARGB4444 =
- SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED16, SDL_PACKEDORDER_ARGB,
- SDL_PACKEDLAYOUT_4444, 16, 2),
- SDL_PIXELFORMAT_RGBA4444 =
- SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED16, SDL_PACKEDORDER_RGBA,
- SDL_PACKEDLAYOUT_4444, 16, 2),
- SDL_PIXELFORMAT_ABGR4444 =
- SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED16, SDL_PACKEDORDER_ABGR,
- SDL_PACKEDLAYOUT_4444, 16, 2),
- SDL_PIXELFORMAT_BGRA4444 =
- SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED16, SDL_PACKEDORDER_BGRA,
- SDL_PACKEDLAYOUT_4444, 16, 2),
- SDL_PIXELFORMAT_ARGB1555 =
- SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED16, SDL_PACKEDORDER_ARGB,
- SDL_PACKEDLAYOUT_1555, 16, 2),
- SDL_PIXELFORMAT_RGBA5551 =
- SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED16, SDL_PACKEDORDER_RGBA,
- SDL_PACKEDLAYOUT_5551, 16, 2),
- SDL_PIXELFORMAT_ABGR1555 =
- SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED16, SDL_PACKEDORDER_ABGR,
- SDL_PACKEDLAYOUT_1555, 16, 2),
- SDL_PIXELFORMAT_BGRA5551 =
- SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED16, SDL_PACKEDORDER_BGRA,
- SDL_PACKEDLAYOUT_5551, 16, 2),
- SDL_PIXELFORMAT_RGB565 =
- SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED16, SDL_PACKEDORDER_XRGB,
- SDL_PACKEDLAYOUT_565, 16, 2),
- SDL_PIXELFORMAT_BGR565 =
- SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED16, SDL_PACKEDORDER_XBGR,
- SDL_PACKEDLAYOUT_565, 16, 2),
- SDL_PIXELFORMAT_RGB24 =
- SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_ARRAYU8, SDL_ARRAYORDER_RGB, 0,
- 24, 3),
- SDL_PIXELFORMAT_BGR24 =
- SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_ARRAYU8, SDL_ARRAYORDER_BGR, 0,
- 24, 3),
- SDL_PIXELFORMAT_RGB888 =
- SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED32, SDL_PACKEDORDER_XRGB,
- SDL_PACKEDLAYOUT_8888, 24, 4),
- SDL_PIXELFORMAT_RGBX8888 =
- SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED32, SDL_PACKEDORDER_RGBX,
- SDL_PACKEDLAYOUT_8888, 24, 4),
- SDL_PIXELFORMAT_BGR888 =
- SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED32, SDL_PACKEDORDER_XBGR,
- SDL_PACKEDLAYOUT_8888, 24, 4),
- SDL_PIXELFORMAT_BGRX8888 =
- SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED32, SDL_PACKEDORDER_BGRX,
- SDL_PACKEDLAYOUT_8888, 24, 4),
- SDL_PIXELFORMAT_ARGB8888 =
- SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED32, SDL_PACKEDORDER_ARGB,
- SDL_PACKEDLAYOUT_8888, 32, 4),
- SDL_PIXELFORMAT_RGBA8888 =
- SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED32, SDL_PACKEDORDER_RGBA,
- SDL_PACKEDLAYOUT_8888, 32, 4),
- SDL_PIXELFORMAT_ABGR8888 =
- SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED32, SDL_PACKEDORDER_ABGR,
- SDL_PACKEDLAYOUT_8888, 32, 4),
- SDL_PIXELFORMAT_BGRA8888 =
- SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED32, SDL_PACKEDORDER_BGRA,
- SDL_PACKEDLAYOUT_8888, 32, 4),
- SDL_PIXELFORMAT_ARGB2101010 =
- SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED32, SDL_PACKEDORDER_ARGB,
- SDL_PACKEDLAYOUT_2101010, 32, 4),
-
- SDL_PIXELFORMAT_YV12 = /**< Planar mode: Y + V + U (3 planes) */
- SDL_DEFINE_PIXELFOURCC('Y', 'V', '1', '2'),
- SDL_PIXELFORMAT_IYUV = /**< Planar mode: Y + U + V (3 planes) */
- SDL_DEFINE_PIXELFOURCC('I', 'Y', 'U', 'V'),
- SDL_PIXELFORMAT_YUY2 = /**< Packed mode: Y0+U0+Y1+V0 (1 plane) */
- SDL_DEFINE_PIXELFOURCC('Y', 'U', 'Y', '2'),
- SDL_PIXELFORMAT_UYVY = /**< Packed mode: U0+Y0+V0+Y1 (1 plane) */
- SDL_DEFINE_PIXELFOURCC('U', 'Y', 'V', 'Y'),
- SDL_PIXELFORMAT_YVYU = /**< Packed mode: Y0+V0+Y1+U0 (1 plane) */
- SDL_DEFINE_PIXELFOURCC('Y', 'V', 'Y', 'U')
這一看確實給人一種“眼花繚亂”的感覺。簡單分析一下其中的定義吧。例如ARGB8888的定義如下。
- SDL_PIXELFORMAT_ARGB8888 =
- SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED32, SDL_PACKEDORDER_ARGB,
- SDL_PACKEDLAYOUT_8888, 32, 4),
其中用了一個宏SDL_DEFINE_PIXELFORMAT用于將幾種屬性合并到一個格式中。下面我們看看一個格式都包含哪些屬性: SDL_PIXELTYPE_PACKED32:代表了像素分量的存儲方式。PACKED代表了像素的幾個分量是一起存儲的,內(nèi)存中存儲方式如下:R1|G1|B1,R2|G2|B2…;ARRAY則代表了像素的幾個分量是分開存儲的,內(nèi)存中存儲方式如下:R1|R2|R3…,G1|G2|G3…,B1|B2|B3… SDL_PACKEDORDER_ARGB:代表了PACKED存儲方式下像素分量的順序。注意,這里所說的順序涉及到了一個“大端”和“小端”的問題。這個問題在《最簡單的視音頻播放示例2:GDI播放YUV, RGB》中已經(jīng)敘述,不再重復(fù)記錄。對于Windows這樣的“小端”系統(tǒng),“ARGB”格式在內(nèi)存中的存儲順序是B|G|R|A。 SDL_PACKEDLAYOUT_8888:說明了每個分量占據(jù)的比特數(shù)。例如ARGB格式每個分量分別占據(jù)了8bit。 32:每個像素占用的比特數(shù)。例如ARGB格式占用了32bit(每個分量占據(jù)8bit)。 4:每個像素占用的字節(jié)數(shù)。例如ARGB格式占用了4Byte(每個分量占據(jù)1Byte)。
2. 循環(huán)顯示畫面
1) 設(shè)置紋理的數(shù)據(jù)
使用SDL_UpdateTexture()設(shè)置紋理的像素數(shù)據(jù)。SDL_UpdateTexture()的原型如下。
- int SDLCALL SDL_UpdateTexture(SDL_Texture * texture,
- const SDL_Rect * rect,
- const void *pixels, int pitch);
參數(shù)的含義如下。 texture:目標(biāo)紋理。 rect:更新像素的矩形區(qū)域。設(shè)置為NULL的時候更新整個區(qū)域。 pixels:像素數(shù)據(jù)。 pitch:一行像素數(shù)據(jù)的字節(jié)數(shù)。 成功的話返回0,失敗的話返回-1。 2) 紋理復(fù)制給渲染目標(biāo) 使用SDL_RenderCopy()將紋理數(shù)據(jù)復(fù)制給渲染目標(biāo)。在使用SDL_RenderCopy()之前,可以使用SDL_RenderClear()先使用清空渲染目標(biāo)。實際上視頻播放的時候不使用SDL_RenderClear()也是可以的,因為視頻的后一幀會完全覆蓋前一幀。 SDL_RenderClear()原型如下。
- int SDLCALL SDL_RenderClear(SDL_Renderer * renderer);
參數(shù)renderer用于指定渲染目標(biāo)。 SDL_RenderCopy()原型如下。
- int SDLCALL SDL_RenderCopy(SDL_Renderer * renderer,
- SDL_Texture * texture,
- const SDL_Rect * srcrect,
- const SDL_Rect * dstrect);
參數(shù)的含義如下。 renderer:渲染目標(biāo)。 texture:輸入紋理。 srcrect:選擇輸入紋理的一塊矩形區(qū)域作為輸入。設(shè)置為NULL的時候整個紋理作為輸入。 dstrect:選擇渲染目標(biāo)的一塊矩形區(qū)域作為輸出。設(shè)置為NULL的時候整個渲染目標(biāo)作為輸出。 成功的話返回0,失敗的話返回-1。 3) 顯示 使用SDL_RenderPresent()顯示畫面。SDL_RenderPresent()原型如下。
- void SDLCALL SDL_RenderPresent(SDL_Renderer * renderer);
參數(shù)renderer用于指定渲染目標(biāo)。
流程總結(jié)
在《最簡單的基于FFMPEG+SDL的視頻播放器 ver2(采用SDL2.0)》中總結(jié)過SDL2播放視頻的流程,在這里簡單復(fù)制過來。 使用SDL播放視頻的流程可以概括為下圖。
SDL中幾個關(guān)鍵的結(jié)構(gòu)體之間的關(guān)系可以用下圖概述。
簡單解釋一下各變量的作用: SDL_Window就是使用SDL的時候彈出的那個窗口。在SDL1.x版本中,只可以創(chuàng)建一個一個窗口。在SDL2.0版本中,可以創(chuàng)建多個窗口。 SDL_Texture用于顯示YUV數(shù)據(jù)。一個SDL_Texture對應(yīng)一幀YUV數(shù)據(jù)。 SDL_Renderer用于渲染SDL_Texture至SDL_Window。 SDL_Rect用于確定SDL_Texture顯示的位置。
代碼
貼出源代碼。
- /**
- * 最簡單的SDL2播放視頻的例子(SDL2播放RGB/YUV)
- * Simplest Video Play SDL2 (SDL2 play RGB/YUV)
- *
- * 雷霄驊 Lei Xiaohua
- * leixiaohua1020@126.com
- * 中國傳媒大學(xué)/數(shù)字電視技術(shù)
- * Communication University of China / Digital TV Technology
- * http://blog.csdn.net/leixiaohua1020
- *
- * 本程序使用SDL2播放RGB/YUV視頻像素數(shù)據(jù)。SDL實際上是對底層繪圖
- * API(Direct3D,OpenGL)的封裝,使用起來明顯簡單于直接調(diào)用底層
- * API。
- *
- * 函數(shù)調(diào)用步驟如下:
- *
- * [初始化]
- * SDL_Init(): 初始化SDL。
- * SDL_CreateWindow(): 創(chuàng)建窗口(Window)。
- * SDL_CreateRenderer(): 基于窗口創(chuàng)建渲染器(Render)。
- * SDL_CreateTexture(): 創(chuàng)建紋理(Texture)。
- *
- * [循環(huán)渲染數(shù)據(jù)]
- * SDL_UpdateTexture(): 設(shè)置紋理的數(shù)據(jù)。
- * SDL_RenderCopy(): 紋理復(fù)制給渲染器。
- * 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>
-
- extern "C"
- {
- #include "sdl/SDL.h"
- };
-
- //set '1' to choose a type of file to play
- #define LOAD_BGRA 1
- #define LOAD_RGB24 0
- #define LOAD_BGR24 0
- #define LOAD_YUV420P 0
-
- //Bit per Pixel
- #if LOAD_BGRA
- const int bpp=32;
- #elif LOAD_RGB24|LOAD_BGR24
- const int bpp=24;
- #elif LOAD_YUV420P
- const int bpp=12;
- #endif
-
- int screen_w=500,screen_h=500;
- const int pixel_w=320,pixel_h=180;
-
- unsigned char buffer[pixel_w*pixel_h*bpp/8];
- //BPP=32
- unsigned char buffer_convert[pixel_w*pixel_h*4];
-
- //Convert RGB24/BGR24 to RGB32/BGR32
- //And change Endian if needed
- void CONVERT_24to32(unsigned char *image_in,unsigned char *image_out,int w,int h){
- for(int i =0;i<h;i++)
- for(int j=0;j<w;j++){
- //Big Endian or Small Endian?
- //"ARGB" order:high bit -> low bit.
- //ARGB Format Big Endian (low address save high MSB, here is A) in memory : A|R|G|B
- //ARGB Format Little Endian (low address save low MSB, here is B) in memory : B|G|R|A
- if(SDL_BYTEORDER==SDL_LIL_ENDIAN){
- //Little Endian (x86): R|G|B --> B|G|R|A
- image_out[(i*w+j)*4+0]=image_in[(i*w+j)*3+2];
- image_out[(i*w+j)*4+1]=image_in[(i*w+j)*3+1];
- image_out[(i*w+j)*4+2]=image_in[(i*w+j)*3];
- image_out[(i*w+j)*4+3]='0';
- }else{
- //Big Endian: R|G|B --> A|R|G|B
- image_out[(i*w+j)*4]='0';
- memcpy(image_out+(i*w+j)*4+1,image_in+(i*w+j)*3,3);
- }
- }
- }
-
-
- //Refresh Event
- #define REFRESH_EVENT (SDL_USEREVENT + 1)
-
- int thread_exit=0;
-
- int refresh_video(void *opaque){
- while (thread_exit==0) {
- SDL_Event event;
- event.type = REFRESH_EVENT;
- SDL_PushEvent(&event);
- SDL_Delay(40);
- }
- 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;
- #if LOAD_BGRA
- //Note: ARGB8888 in "Little Endian" system stores as B|G|R|A
- pixformat= SDL_PIXELFORMAT_ARGB8888;
- #elif LOAD_RGB24
- pixformat= SDL_PIXELFORMAT_RGB888;
- #elif LOAD_BGR24
- pixformat= SDL_PIXELFORMAT_BGR888;
- #elif LOAD_YUV420P
- //IYUV: Y + U + V (3 planes)
- //YV12: Y + V + U (3 planes)
- pixformat= SDL_PIXELFORMAT_IYUV;
- #endif
-
- SDL_Texture* sdlTexture = SDL_CreateTexture(sdlRenderer,pixformat, SDL_TEXTUREACCESS_STREAMING,pixel_w,pixel_h);
-
-
-
- FILE *fp=NULL;
- #if LOAD_BGRA
- fp=fopen("../test_bgra_320x180.rgb","rb+");
- #elif LOAD_RGB24
- fp=fopen("../test_rgb24_320x180.rgb","rb+");
- #elif LOAD_BGR24
- fp=fopen("../test_bgr24_320x180.rgb","rb+");
- #elif LOAD_YUV420P
- fp=fopen("../test_yuv420p_320x180.yuv","rb+");
- #endif
- if(fp==NULL){
- printf("cannot open this file\n");
- return -1;
- }
-
- SDL_Rect sdlRect;
-
- SDL_Thread *refresh_thread = SDL_CreateThread(refresh_video,NULL,NULL);
- SDL_Event event;
- while(1){
- //Wait
- 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);
- }
-
- #if LOAD_BGRA
- //We don't need to change Endian
- //Because input BGRA pixel data(B|G|R|A) is same as ARGB8888 in Little Endian (B|G|R|A)
- SDL_UpdateTexture( sdlTexture, NULL, buffer, pixel_w*4);
- #elif LOAD_RGB24|LOAD_BGR24
- //change 24bit to 32 bit
- //and in Windows we need to change Endian
- CONVERT_24to32(buffer,buffer_convert,pixel_w,pixel_h);
- SDL_UpdateTexture( sdlTexture, NULL, buffer_convert, pixel_w*4);
- #elif LOAD_YUV420P
- SDL_UpdateTexture( sdlTexture, NULL, buffer, pixel_w);
- #endif
- //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 );
- //Delay 40ms
- SDL_Delay(40);
-
- }else if(event.type==SDL_WINDOWEVENT){
- //If Resize
- SDL_GetWindowSize(screen,&screen_w,&screen_h);
- }else if(event.type==SDL_QUIT){
- break;
- }
- }
-
- return 0;
- }
運行結(jié)果
程序的運行結(jié)果如下圖所示。
下載
代碼位于“Simplest Media Play”中
SourceForge項目地址:https:///projects/simplestmediaplay/
CSDN下載地址:http://download.csdn.net/detail/leixiaohua1020/8054395
上述工程包含了使用各種API(Direct3D,OpenGL,GDI,DirectSound,SDL2)播放多媒體例子。其中音頻輸入為PCM采樣數(shù)據(jù)。輸出至系統(tǒng)的聲卡播放出來。視頻輸入為YUV/RGB像素數(shù)據(jù)。輸出至顯示器上的一個窗口播放出來。 通過本工程的代碼初學(xué)者可以快速學(xué)習(xí)使用這幾個API播放視頻和音頻的技術(shù)。 一共包括了如下幾個子工程: simplest_audio_play_directsound: 使用DirectSound播放PCM音頻采樣數(shù)據(jù)。 simplest_audio_play_sdl2: 使用SDL2播放PCM音頻采樣數(shù)據(jù)。 simplest_video_play_direct3d: 使用Direct3D的Surface播放RGB/YUV視頻像素數(shù)據(jù)。 simplest_video_play_direct3d_texture:使用Direct3D的Texture播放RGB視頻像素數(shù)據(jù)。 simplest_video_play_gdi: 使用GDI播放RGB/YUV視頻像素數(shù)據(jù)。 simplest_video_play_opengl: 使用OpenGL播放RGB/YUV視頻像素數(shù)據(jù)。 simplest_video_play_opengl_texture: 使用OpenGL的Texture播放YUV視頻像素數(shù)據(jù)。 simplest_video_play_sdl2: 使用SDL2播放RGB/YUV視頻像素數(shù)據(jù)。
from:http://blog.csdn.net/leixiaohua1020/article/details/40525591
|