天天看點

SDL2播放YUV

本文記錄SDL播放視訊的技術。在這裡使用的版本是SDL2。實際上SDL本身并不提供視音頻播放的功能,它隻是封裝了視音頻播放的底層API。在Windows平台下,SDL封裝了Direct3D這類的API用于播放視訊;封裝了DirectSound這類的API用于播放音頻。因為SDL的編寫目的就是簡化視音頻播放的開發難度,是以使用SDL播放視訊(YUV/RGB)和音頻(PCM)資料非常的容易。下文記錄一下使用SDL播放視訊資料的技術。

SDL2播放YUV

SDL簡介

SDL(Simple DirectMedia Layer)是一套開放源代碼的跨平台多媒體開發庫,使用C語言寫成。SDL提供了數種控制圖像、聲音、輸出入的函數,讓開發者隻要用相同或是相似的代碼就可以開發出跨多個平台(Linux、Windows、Mac OS X等)的應用軟體。目前SDL多用于開發遊戲、模拟器、媒體播放器等多媒體應用領域。用下面這張圖可以很明确地說明SDL的位置。

SDL2播放YUV

SDL實際上并不限于視音頻的播放,它将功能分成下列數個子系統(subsystem):

Video(圖像):圖像控制以及線程(thread)和事件管理(event)。

Audio(聲音):聲音控制

Joystick(搖杆):遊戲搖杆控制

CD-ROM(CD光牒驅動器):CD光牒媒體控制

Window Management(視窗管理):與視窗程式設計內建

Event(事件驅動):處理事件驅動

在Windows下,SDL與DirectX的對應關系如下。

SDL DirectX
SDL_Video、SDL_Image DirectDraw、Direct3D
SDL_Audio、SDL_Mixer DirectSound
SDL_Joystick、SDL_Base DirectInput
SDL_Net DirectPlay

SDL播放視訊的流程

SDL播放視訊的技術在此前做的FFmpeg的示例程式中已經多次用到。在這裡重新總結一下流程。

1.       初始化

1)         初始化SDL

2)         建立視窗(Window)

3)         基于視窗建立渲染器(Render)

4)         建立紋理(Texture)

2.       循環顯示畫面

1)       設定紋理的資料

2)       紋理複制給渲染目标

3)       顯示

下面詳細分析一下上文的流程。

1.       初始化

1)         初始化SDL

使用SDL_Init()初始化SDL。該函數可以确定希望激活的子系統。SDL_Init()函數原型如下:

[cpp]  view plain copy

SDL2播放YUV
SDL2播放YUV
  1. 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()有一點需要注意:初始化的時候盡量做到“夠用就好”,而不要用SDL_INIT_EVERYTHING。因為有些情況下使用SDL_INIT_EVERYTHING會出現一些不可預知的問題。例如,在MFC應用程式中播放純音頻,如果初始化SDL的時候使用SDL_INIT_EVERYTHING,那麼就會出現聽不到聲音的情況。後來發現,去掉了SDL_INIT_VIDEO之後,問題才得以解決。

2)         建立視窗(Window)

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

[cpp]  view plain copy

SDL2播放YUV
SDL2播放YUV
  1. SDL_Window * SDLCALL SDL_CreateWindow(const char *title,  
  2.                                                       int x, int y, int w,  
  3.                                                       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。

3)         基于視窗建立渲染器(Render)

使用SDL_CreateRenderer()基于視窗建立渲染器。SDL_CreateRenderer()原型如下。

[cpp]  view plain copy

SDL2播放YUV
SDL2播放YUV
  1. SDL_Renderer * SDLCALL SDL_CreateRenderer(SDL_Window * window,  
  2.                                                int index, Uint32 flags);  

參數含義如下。

window    : 渲染的目标視窗。

index         :打算初始化的渲染裝置的索引。設定“-1”則初始化預設的渲染裝置。

flags          :支援以下值(位于SDL_RendererFlags定義中)

    SDL_RENDERER_SOFTWARE :使用軟體渲染

    SDL_RENDERER_ACCELERATED :使用硬體加速

    SDL_RENDERER_PRESENTVSYNC:和顯示器的重新整理率同步

    SDL_RENDERER_TARGETTEXTURE :不太懂

傳回建立完成的渲染器的ID。如果建立失敗則傳回NULL。

4)         建立紋理(Texture)

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

[cpp]  view plain copy

SDL2播放YUV
SDL2播放YUV
  1. SDL_Texture * SDLCALL SDL_CreateTexture(SDL_Renderer * renderer,  
  2.                                                         Uint32 format,  
  3.                                                         int access, int w,  
  4.                                                         int h);  

參數的含義如下。

renderer:目标渲染器。

format      :紋理的格式。後面會詳述。

access      :可以取以下值(定義位于SDL_TextureAccess中)

    SDL_TEXTUREACCESS_STATIC         :變化極少

    SDL_TEXTUREACCESS_STREAMING        :變化頻繁

    SDL_TEXTUREACCESS_TARGET       :暫時沒有了解

w               :紋理的寬

h                :紋理的高

建立成功則傳回紋理的ID,失敗傳回0。

在紋理的建立過程中,需要指定紋理的格式(即第二個參數)。SDL的中的格式很多,如下所列。

[cpp]  view plain copy

SDL2播放YUV
SDL2播放YUV
  1. SDL_PIXELFORMAT_UNKNOWN,  
  2. SDL_PIXELFORMAT_INDEX1LSB =  
  3.     SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_INDEX1, SDL_BITMAPORDER_4321, 0,  
  4.                            1, 0),  
  5. SDL_PIXELFORMAT_INDEX1MSB =  
  6.     SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_INDEX1, SDL_BITMAPORDER_1234, 0,  
  7.                            1, 0),  
  8. SDL_PIXELFORMAT_INDEX4LSB =  
  9.     SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_INDEX4, SDL_BITMAPORDER_4321, 0,  
  10.                            4, 0),  
  11. SDL_PIXELFORMAT_INDEX4MSB =  
  12.     SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_INDEX4, SDL_BITMAPORDER_1234, 0,  
  13.                            4, 0),  
  14. SDL_PIXELFORMAT_INDEX8 =  
  15.     SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_INDEX8, 0, 0, 8, 1),  
  16. SDL_PIXELFORMAT_RGB332 =  
  17.     SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED8, SDL_PACKEDORDER_XRGB,  
  18.                            SDL_PACKEDLAYOUT_332, 8, 1),  
  19. SDL_PIXELFORMAT_RGB444 =  
  20.     SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED16, SDL_PACKEDORDER_XRGB,  
  21.                            SDL_PACKEDLAYOUT_4444, 12, 2),  
  22. SDL_PIXELFORMAT_RGB555 =  
  23.     SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED16, SDL_PACKEDORDER_XRGB,  
  24.                            SDL_PACKEDLAYOUT_1555, 15, 2),  
  25. SDL_PIXELFORMAT_BGR555 =  
  26.     SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED16, SDL_PACKEDORDER_XBGR,  
  27.                            SDL_PACKEDLAYOUT_1555, 15, 2),  
  28. SDL_PIXELFORMAT_ARGB4444 =  
  29.     SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED16, SDL_PACKEDORDER_ARGB,  
  30.                            SDL_PACKEDLAYOUT_4444, 16, 2),  
  31. SDL_PIXELFORMAT_RGBA4444 =  
  32.     SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED16, SDL_PACKEDORDER_RGBA,  
  33.                            SDL_PACKEDLAYOUT_4444, 16, 2),  
  34. SDL_PIXELFORMAT_ABGR4444 =  
  35.     SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED16, SDL_PACKEDORDER_ABGR,  
  36.                            SDL_PACKEDLAYOUT_4444, 16, 2),  
  37. SDL_PIXELFORMAT_BGRA4444 =  
  38.     SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED16, SDL_PACKEDORDER_BGRA,  
  39.                            SDL_PACKEDLAYOUT_4444, 16, 2),  
  40. SDL_PIXELFORMAT_ARGB1555 =  
  41.     SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED16, SDL_PACKEDORDER_ARGB,  
  42.                            SDL_PACKEDLAYOUT_1555, 16, 2),  
  43. SDL_PIXELFORMAT_RGBA5551 =  
  44.     SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED16, SDL_PACKEDORDER_RGBA,  
  45.                            SDL_PACKEDLAYOUT_5551, 16, 2),  
  46. SDL_PIXELFORMAT_ABGR1555 =  
  47.     SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED16, SDL_PACKEDORDER_ABGR,  
  48.                            SDL_PACKEDLAYOUT_1555, 16, 2),  
  49. SDL_PIXELFORMAT_BGRA5551 =  
  50.     SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED16, SDL_PACKEDORDER_BGRA,  
  51.                            SDL_PACKEDLAYOUT_5551, 16, 2),  
  52. SDL_PIXELFORMAT_RGB565 =  
  53.     SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED16, SDL_PACKEDORDER_XRGB,  
  54.                            SDL_PACKEDLAYOUT_565, 16, 2),  
  55. SDL_PIXELFORMAT_BGR565 =  
  56.     SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED16, SDL_PACKEDORDER_XBGR,  
  57.                            SDL_PACKEDLAYOUT_565, 16, 2),  
  58. SDL_PIXELFORMAT_RGB24 =  
  59.     SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_ARRAYU8, SDL_ARRAYORDER_RGB, 0,  
  60.                            24, 3),  
  61. SDL_PIXELFORMAT_BGR24 =  
  62.     SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_ARRAYU8, SDL_ARRAYORDER_BGR, 0,  
  63.                            24, 3),  
  64. SDL_PIXELFORMAT_RGB888 =  
  65.     SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED32, SDL_PACKEDORDER_XRGB,  
  66.                            SDL_PACKEDLAYOUT_8888, 24, 4),  
  67. SDL_PIXELFORMAT_RGBX8888 =  
  68.     SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED32, SDL_PACKEDORDER_RGBX,  
  69.                            SDL_PACKEDLAYOUT_8888, 24, 4),  
  70. SDL_PIXELFORMAT_BGR888 =  
  71.     SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED32, SDL_PACKEDORDER_XBGR,  
  72.                            SDL_PACKEDLAYOUT_8888, 24, 4),  
  73. SDL_PIXELFORMAT_BGRX8888 =  
  74.     SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED32, SDL_PACKEDORDER_BGRX,  
  75.                            SDL_PACKEDLAYOUT_8888, 24, 4),  
  76. SDL_PIXELFORMAT_ARGB8888 =  
  77.     SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED32, SDL_PACKEDORDER_ARGB,  
  78.                            SDL_PACKEDLAYOUT_8888, 32, 4),  
  79. SDL_PIXELFORMAT_RGBA8888 =  
  80.     SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED32, SDL_PACKEDORDER_RGBA,  
  81.                            SDL_PACKEDLAYOUT_8888, 32, 4),  
  82. SDL_PIXELFORMAT_ABGR8888 =  
  83.     SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED32, SDL_PACKEDORDER_ABGR,  
  84.                            SDL_PACKEDLAYOUT_8888, 32, 4),  
  85. SDL_PIXELFORMAT_BGRA8888 =  
  86.     SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED32, SDL_PACKEDORDER_BGRA,  
  87.                            SDL_PACKEDLAYOUT_8888, 32, 4),  
  88. SDL_PIXELFORMAT_ARGB2101010 =  
  89.     SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED32, SDL_PACKEDORDER_ARGB,  
  90.                            SDL_PACKEDLAYOUT_2101010, 32, 4),  
  91. SDL_PIXELFORMAT_YV12 =        
  92.     SDL_DEFINE_PIXELFOURCC('Y', 'V', '1', '2'),  
  93. SDL_PIXELFORMAT_IYUV =        
  94.     SDL_DEFINE_PIXELFOURCC('I', 'Y', 'U', 'V'),  
  95. SDL_PIXELFORMAT_YUY2 =        
  96.     SDL_DEFINE_PIXELFOURCC('Y', 'U', 'Y', '2'),  
  97. SDL_PIXELFORMAT_UYVY =        
  98.     SDL_DEFINE_PIXELFOURCC('U', 'Y', 'V', 'Y'),  
  99. SDL_PIXELFORMAT_YVYU =        
  100.     SDL_DEFINE_PIXELFOURCC('Y', 'V', 'Y', 'U')  

這一看确實給人一種“眼花缭亂”的感覺。簡單分析一下其中的定義吧。例如ARGB8888的定義如下。

[cpp]  view plain copy

SDL2播放YUV
SDL2播放YUV
  1. SDL_PIXELFORMAT_ARGB8888 =  
  2.         SDL_DEFINE_PIXELFORMAT(SDL_PIXELTYPE_PACKED32, SDL_PACKEDORDER_ARGB,  
  3.                                SDL_PACKEDLAYOUT_8888, 32, 4),  

其中用了一個宏SDL_DEFINE_PIXELFORMAT用于将幾種屬性合并到一個格式中。下面我們看看一個格式都包含哪些屬性:

SDL_PIXELTYPE_PACKED32:代表了像素分量的存儲方式。PACKED代表了像素的幾個分量是一起存儲的,記憶體中存儲方式如下:R1|G1|B1,R2|G2|B2…;ARRAY則代表了像素的幾個分量是分開存儲的,記憶體中存儲方式如下:R1|R2|R3…,G1|G2|G3…,B1|B2|B3…

SDL_PACKEDORDER_ARGB:代表了PACKED存儲方式下像素分量的順序。注意,這裡所說的順序涉及到了一個“大端”和“小端”的問題。這個問題在《最簡單的視音頻播放示例2:GDI播放YUV, RGB》中已經叙述,不再重複記錄。對于Windows這樣的“小端”系統,“ARGB”格式在記憶體中的存儲順序是B|G|R|A。

SDL_PACKEDLAYOUT_8888:說明了每個分量占據的比特數。例如ARGB格式每個分量分别占據了8bit。

32:每個像素占用的比特數。例如ARGB格式占用了32bit(每個分量占據8bit)。

4:每個像素占用的位元組數。例如ARGB格式占用了4Byte(每個分量占據1Byte)。

2.       循環顯示畫面

1)       設定紋理的資料

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

[cpp]  view plain copy

SDL2播放YUV
SDL2播放YUV
  1. int SDLCALL SDL_UpdateTexture(SDL_Texture * texture,  
  2.                                               const SDL_Rect * rect,  
  3.                                               const void *pixels, int pitch);  

參數的含義如下。

texture:目标紋理。

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

pixels:像素資料。

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

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

2)       紋理複制給渲染目标

使用SDL_RenderCopy()将紋理資料複制給渲染目标。在使用SDL_RenderCopy()之前,可以使用SDL_RenderClear()先使用清空渲染目标。實際上視訊播放的時候不使用SDL_RenderClear()也是可以的,因為視訊的後一幀會完全覆寫前一幀。

SDL_RenderClear()原型如下。

[cpp]  view plain copy

SDL2播放YUV
SDL2播放YUV
  1. int SDLCALL SDL_RenderClear(SDL_Renderer * renderer);  

參數renderer用于指定渲染目标。

SDL_RenderCopy()原型如下。

[cpp]  view plain copy

SDL2播放YUV
SDL2播放YUV
  1. int SDLCALL SDL_RenderCopy(SDL_Renderer * renderer,  
  2.                                            SDL_Texture * texture,  
  3.                                            const SDL_Rect * srcrect,  
  4.                                            const SDL_Rect * dstrect);  

參數的含義如下。

renderer:渲染目标。

texture:輸入紋理。

srcrect:選擇輸入紋理的一塊矩形區域作為輸入。設定為NULL的時候整個紋理作為輸入。

dstrect:選擇渲染目标的一塊矩形區域作為輸出。設定為NULL的時候整個渲染目标作為輸出。

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

3)       顯示

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

[cpp]  view plain copy

SDL2播放YUV
SDL2播放YUV
  1. void SDLCALL SDL_RenderPresent(SDL_Renderer * renderer);  

 參數renderer用于指定渲染目标。

流程總結

在《最簡單的基于FFMPEG+SDL的視訊播放器 ver2(采用SDL2.0)》中總結過SDL2播放視訊的流程,在這裡簡單複制過來。

使用SDL播放視訊的流程可以概括為下圖。

SDL2播放YUV

SDL中幾個關鍵的結構體之間的關系可以用下圖概述。

SDL2播放YUV

簡單解釋一下各變量的作用:

SDL_Window就是使用SDL的時候彈出的那個視窗。在SDL1.x版本中,隻可以建立一個一個視窗。在SDL2.0版本中,可以建立多個視窗。

SDL_Texture用于顯示YUV資料。一個SDL_Texture對應一幀YUV資料。

SDL_Renderer用于渲染SDL_Texture至SDL_Window。

SDL_Rect用于确定SDL_Texture顯示的位置。

代碼

貼出源代碼。

[cpp]  view plain copy

SDL2播放YUV
SDL2播放YUV
  1. #include <stdio.h>  
  2. extern "C"  
  3. {  
  4. #include "sdl/SDL.h"  
  5. };  
  6. //set '1' to choose a type of file to play  
  7. #define LOAD_BGRA    1  
  8. #define LOAD_RGB24   0  
  9. #define LOAD_BGR24   0  
  10. #define LOAD_YUV420P 0  
  11. //Bit per Pixel  
  12. #if LOAD_BGRA  
  13. const int bpp=32;  
  14. #elif LOAD_RGB24|LOAD_BGR24  
  15. const int bpp=24;  
  16. #elif LOAD_YUV420P  
  17. const int bpp=12;  
  18. #endif  
  19. int screen_w=500,screen_h=500;  
  20. const int pixel_w=320,pixel_h=180;  
  21. unsigned char buffer[pixel_w*pixel_h*bpp/8];  
  22. //BPP=32  
  23. unsigned char buffer_convert[pixel_w*pixel_h*4];  
  24. //Convert RGB24/BGR24 to RGB32/BGR32  
  25. //And change Endian if needed  
  26. void CONVERT_24to32(unsigned char *image_in,unsigned char *image_out,int w,int h){  
  27.     for(int i =0;i<h;i++)  
  28.         for(int j=0;j<w;j++){  
  29.             //Big Endian or Small Endian?  
  30.             //"ARGB" order:high bit -> low bit.  
  31.             //ARGB Format Big Endian (low address save high MSB, here is A) in memory : A|R|G|B  
  32.             //ARGB Format Little Endian (low address save low MSB, here is B) in memory : B|G|R|A  
  33.             if(SDL_BYTEORDER==SDL_LIL_ENDIAN){  
  34.                 //Little Endian (x86): R|G|B --> B|G|R|A  
  35.                 image_out[(i*w+j)*4+0]=image_in[(i*w+j)*3+2];  
  36.                 image_out[(i*w+j)*4+1]=image_in[(i*w+j)*3+1];  
  37.                 image_out[(i*w+j)*4+2]=image_in[(i*w+j)*3];  
  38.                 image_out[(i*w+j)*4+3]='0';  
  39.             }else{  
  40.                 //Big Endian: R|G|B --> A|R|G|B  
  41.                 image_out[(i*w+j)*4]='0';  
  42.                 memcpy(image_out+(i*w+j)*4+1,image_in+(i*w+j)*3,3);  
  43.             }  
  44.         }  
  45. }  
  46. //Refresh Event  
  47. #define REFRESH_EVENT  (SDL_USEREVENT + 1)  
  48. int thread_exit=0;  
  49. int refresh_video(void *opaque){  
  50.     while (thread_exit==0) {  
  51.         SDL_Event event;  
  52.         event.type = REFRESH_EVENT;  
  53.         SDL_PushEvent(&event);  
  54.         SDL_Delay(40);  
  55.     }  
  56.     return 0;  
  57. }  
  58. int main(int argc, char* argv[])  
  59. {  
  60.     if(SDL_Init(SDL_INIT_VIDEO)) {    
  61.         printf( "Could not initialize SDL - %s\n", SDL_GetError());   
  62.         return -1;  
  63.     }   
  64.     SDL_Window *screen;   
  65.     //SDL 2.0 Support for multiple windows  
  66.     screen = SDL_CreateWindow("Simplest Video Play SDL2", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,  
  67.         screen_w, screen_h,SDL_WINDOW_OPENGL|SDL_WINDOW_RESIZABLE);  
  68.     if(!screen) {    
  69.         printf("SDL: could not create window - exiting:%s\n",SDL_GetError());    
  70.         return -1;  
  71.     }  
  72.     SDL_Renderer* sdlRenderer = SDL_CreateRenderer(screen, -1, 0);    
  73.     Uint32 pixformat=0;  
  74. #if LOAD_BGRA  
  75.     //Note: ARGB8888 in "Little Endian" system stores as B|G|R|A  
  76.     pixformat= SDL_PIXELFORMAT_ARGB8888;    
  77. #elif LOAD_RGB24  
  78.     pixformat= SDL_PIXELFORMAT_RGB888;    
  79. #elif LOAD_BGR24  
  80.     pixformat= SDL_PIXELFORMAT_BGR888;    
  81. #elif LOAD_YUV420P  
  82.     //IYUV: Y + U + V  (3 planes)  
  83.     //YV12: Y + V + U  (3 planes)  
  84.     pixformat= SDL_PIXELFORMAT_IYUV;    
  85. #endif  
  86.     SDL_Texture* sdlTexture = SDL_CreateTexture(sdlRenderer,pixformat, SDL_TEXTUREACCESS_STREAMING,pixel_w,pixel_h);  
  87.     FILE *fp=NULL;  
  88. #if LOAD_BGRA  
  89.     fp=fopen("../test_bgra_320x180.rgb","rb+");  
  90. #elif LOAD_RGB24  
  91.     fp=fopen("../test_rgb24_320x180.rgb","rb+");  
  92. #elif LOAD_BGR24  
  93.     fp=fopen("../test_bgr24_320x180.rgb","rb+");  
  94. #elif LOAD_YUV420P  
  95.     fp=fopen("../test_yuv420p_320x180.yuv","rb+");  
  96. #endif  
  97.     if(fp==NULL){  
  98.         printf("cannot open this file\n");  
  99.         return -1;  
  100.     }  
  101.     SDL_Rect sdlRect;    
  102.     SDL_Thread *refresh_thread = SDL_CreateThread(refresh_video,NULL,NULL);  
  103.     SDL_Event event;  
  104.     while(1){  
  105.         //Wait  
  106.         SDL_WaitEvent(&event);  
  107.         if(event.type==REFRESH_EVENT){  
  108.             if (fread(buffer, 1, pixel_w*pixel_h*bpp/8, fp) != pixel_w*pixel_h*bpp/8){  
  109.                 // Loop  
  110.                 fseek(fp, 0, SEEK_SET);  
  111.                 fread(buffer, 1, pixel_w*pixel_h*bpp/8, fp);  
  112.             }  
  113. #if LOAD_BGRA  
  114.             //We don't need to change Endian  
  115.             //Because input BGRA pixel data(B|G|R|A) is same as ARGB8888 in Little Endian (B|G|R|A)  
  116.             SDL_UpdateTexture( sdlTexture, NULL, buffer, pixel_w*4);    
  117. #elif LOAD_RGB24|LOAD_BGR24  
  118.             //change 24bit to 32 bit  
  119.             //and in Windows we need to change Endian  
  120.             CONVERT_24to32(buffer,buffer_convert,pixel_w,pixel_h);  
  121.             SDL_UpdateTexture( sdlTexture, NULL, buffer_convert, pixel_w*4);    
  122. #elif LOAD_YUV420P  
  123.             SDL_UpdateTexture( sdlTexture, NULL, buffer, pixel_w);    
  124. #endif  
  125.             //FIX: If window is resize  
  126.             sdlRect.x = 0;    
  127.             sdlRect.y = 0;    
  128.             sdlRect.w = screen_w;    
  129.             sdlRect.h = screen_h;    
  130.             SDL_RenderClear( sdlRenderer );     
  131.             SDL_RenderCopy( sdlRenderer, sdlTexture, NULL, &sdlRect);    
  132.             SDL_RenderPresent( sdlRenderer );    
  133.             //Delay 40ms  
  134.             SDL_Delay(40);  
  135.         }else if(event.type==SDL_WINDOWEVENT){  
  136.             //If Resize  
  137.             SDL_GetWindowSize(screen,&screen_w,&screen_h);  
  138.         }else if(event.type==SDL_QUIT){  
  139.             break;  
  140.         }  
  141.     }  
  142.     return 0;  
  143. }  

運作結果

程式的運作結果如下圖所示。

SDL2播放YUV

下載下傳

代碼位于“Simplest Media Play”中

SourceForge項目位址:https://sourceforge.net/projects/simplestmediaplay/

CSDN下載下傳位址:http://download.csdn.net/detail/leixiaohua1020/8054395

上述工程包含了使用各種API(Direct3D,OpenGL,GDI,DirectSound,SDL2)播放多媒體例子。其中音頻輸入為PCM采樣資料。輸出至系統的聲霸卡播放出來。視訊輸入為YUV/RGB像素資料。輸出至顯示器上的一個視窗播放出來。

通過本工程的代碼初學者可以快速學習使用這幾個API播放視訊和音頻的技術。

一共包括了如下幾個子工程:

simplest_audio_play_directsound:         使用DirectSound播放PCM音頻采樣資料。

simplest_audio_play_sdl2:                       使用SDL2播放PCM音頻采樣資料。

simplest_video_play_direct3d:                使用Direct3D的Surface播放RGB/YUV視訊像素資料。

simplest_video_play_direct3d_texture:使用Direct3D的Texture播放RGB視訊像素資料。

simplest_video_play_gdi:                          使用GDI播放RGB/YUV視訊像素資料。

simplest_video_play_opengl:                   使用OpenGL播放RGB/YUV視訊像素資料。

simplest_video_play_opengl_texture:    使用OpenGL的Texture播放YUV視訊像素資料。

simplest_video_play_sdl2:                        使用SDL2播放RGB/YUV視訊像素資料。

from:http://blog.csdn.net/leixiaohua1020/article/details/40525591