1. 程式人生 > >最簡單的視音頻播放演示樣例7:SDL2播放RGB/YUV

最簡單的視音頻播放演示樣例7:SDL2播放RGB/YUV

pro big 更新 沒有 opaque support 解決 控制 mem

=====================================================

最簡單的視音頻播放演示樣例系列文章列表:

最簡單的視音頻播放演示樣例1:總述

最簡單的視音頻播放演示樣例2:GDI播放YUV, RGB

最簡單的視音頻播放演示樣例3:Direct3D播放YUV,RGB(通過Surface)

最簡單的視音頻播放演示樣例4:Direct3D播放RGB(通過Texture)

最簡單的視音頻播放演示樣例5:OpenGL播放RGB/YUV

最簡單的視音頻播放演示樣例6:OpenGL播放YUV420P(通過Texture,使用Shader)

最簡單的視音頻播放演示樣例7:SDL2播放RGB/YUV

最簡單的視音頻播放演示樣例8:DirectSound播放PCM

最簡單的視音頻播放演示樣例9:SDL2播放PCM

=====================================================


本文記錄SDL播放視頻的技術。在這裏使用的版本號是SDL2。實際上SDL本身並不提供視音頻播放的功能,它僅僅是封裝了視音頻播放的底層API。在Windows平臺下。SDL封裝了Direct3D這類的API用於播放視頻;封裝了DirectSound這類的API用於播放音頻。由於SDL的編寫目的就是簡化視音頻播放的開發難度,所以使用SDL播放視頻(YUV/RGB)和音頻(PCM)數據非常的easy。

下文記錄一下使用SDL播放視頻數據的技術。

技術分享

SDL簡單介紹


SDL(Simple DirectMedia Layer)是一套開放源碼的跨平臺多媒體開發庫,使用C語言寫成。

SDL提供了數種控制圖像、聲音、輸出入的函數。讓開發人員僅僅要用同樣或是類似的代碼就能夠開發出跨多個平臺(Linux、Windows、Mac OS X等)的應用軟件。眼下SDL多用於開發遊戲、模擬器、媒體播放器等多媒體應用領域。用以下這張圖能夠非常明白地說明SDL的位置。


技術分享


SDL實際上並不限於視音頻的播放,它將功能分成下列數個子系統(subsystem):
Video(圖像):圖像控制以及線程(thread)和事件管理(event)。


Audio(聲音):聲音控制
Joystick(搖桿):遊戲搖桿控制
CD-ROM(光盤驅動器):光盤媒體控制
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()函數原型例如以下:

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()的原型例如以下。


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。


3) 基於窗體創建渲染器(Render)
使用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。

4) 創建紋理(Texture)
使用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的中的格式非常多,例如以下所列。

    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代表了像素的幾個分量是一起存儲的。內存中存儲方式例如以下: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()的原型例如以下。


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

參數的含義例如以下。
texture:目標紋理。
rect:更新像素的矩形區域。設置為NULL的時候更新整個區域。


pixels:像素數據。
pitch:一行像素數據的字節數。


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



2) 紋理復制給渲染目標
使用SDL_RenderCopy()將紋理數據復制給渲染目標。在使用SDL_RenderCopy()之前,能夠使用SDL_RenderClear()先使用清空渲染目標。實際上視頻播放的時候不使用SDL_RenderClear()也是能夠的,由於視頻的後一幀會全然覆蓋前一幀。
SDL_RenderClear()原型例如以下。


int SDLCALL SDL_RenderClear(SDL_Renderer * renderer);

參數renderer用於指定渲染目標。


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。



3) 顯示
使用SDL_RenderPresent()顯示畫面。

SDL_RenderPresent()原型例如以下。

void SDLCALL SDL_RenderPresent(SDL_Renderer * renderer);

參數renderer用於指定渲染目標。


流程總結


在《最簡單的基於FFMPEG+SDL的視頻播放器 ver2(採用SDL2.0)》中總結過SDL2播放視頻的流程。在這裏簡單復制過來。


使用SDL播放視頻的流程能夠概括為下圖。
技術分享

SDL中幾個關鍵的結構體之間的關系能夠用下圖概述。
技術分享

簡單解釋一下各變量的作用:
SDL_Window就是使用SDL的時候彈出的那個窗體。

在SDL1.x版本號中,僅僅能夠創建一個一個窗體。在SDL2.0版本號中,能夠創建多個窗體。
SDL_Texture用於顯示YUV數據。一個SDL_Texture相應一幀YUV數據。
SDL_Renderer用於渲染SDL_Texture至SDL_Window。
SDL_Rect用於確定SDL_Texture顯示的位置。


代碼

貼出源碼。

/**
 * 最簡單的SDL2播放視頻的樣例(SDL2播放RGB/YUV)
 * Simplest Video Play SDL2 (SDL2 play RGB/YUV) 
 *
 * 雷霄驊 Lei Xiaohua
 * [email protected]
 * 中國傳媒大學/數字電視技術
 * Communication University of China / Digital TV Technology
 * http://blog.csdn.net/leixiaohua1020
 *
 * 本程序使用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> 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; }



執行結果

程序的執行結果例如以下圖所看到的。
技術分享

下載


代碼位於“Simplest Media Play”中


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

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


註:

該項目會不定時的更新並修復一些小問題。最新的版本號請參考該系列文章的總述頁面:

《最簡單的視音頻播放演示樣例1:總述》


上述project包括了使用各種API(Direct3D。OpenGL,GDI。DirectSound,SDL2)播放多媒體樣例。當中音頻輸入為PCM採樣數據。輸出至系統的聲卡播放出來。

視頻輸入為YUV/RGB像素數據。輸出至顯示器上的一個窗體播放出來。
通過本project的代碼剛開始學習的人能夠高速學習使用這幾個API播放視頻和音頻的技術。


一共包括了例如以下幾個子project:
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視頻像素數據。

最簡單的視音頻播放演示樣例7:SDL2播放RGB/YUV