8.基於SDL2播放YUV視頻

參考資料:
1.雷博博客

前面講video解碼爲YUV原始數據,接下來則需要將原始的yuv數據通過SDL進行顯示。。。

一.簡介

摘抄自百度百科:

SDL(Simple DirectMedia Layer)是一套開放源代碼的跨平臺多媒體開發庫,使用C語言寫成。SDL提供了數種控制圖像、聲音、輸出入的函數,讓開發者只要用相同或是相似的代碼就可以開發出跨多個平臺(Linux、Windows、Mac OS X等)的應用軟件。目前SDL多用於開發遊戲、模擬器、媒體播放器等多媒體應用領域。
從上面介紹可以得知,SDL是一套開源的多媒體開發庫,對內封裝了與底層硬件交互的接口,對外提供統一的接口,我們使用者只需要調用接口,而不需要關注平臺、底層硬件等參數,即可正常進行的編碼工作。

SDL除了用於音視頻的播放外,還提供了其他的功能,例如:搖桿、光盤驅動器、視窗管理等等,而我們現在只需要關注的則是音視頻的播放,其他的暫且不提。

目前,使用的是SDL2。

二、流程及函數

1)視頻播放流程

大致流程如下;
初始化—>創建窗口—>創建渲染器—>創建紋理—>讀取一幀數據—>設置紋理數據—->將紋理數據拷貝給渲染器—>顯示—>退出

2)常用函數

1.SDL_Init()

函數原型: int SDLCALL SDL_Init(Uint32 flags);

初始化SDL系統,其中flag可以選擇的選項有:

/**
 *  \name SDL_INIT_*
 *
 *  These are the flags which may be passed to SDL_Init().  You should
 *  specify the subsystems which you will be using in your application.
 */
/* @{ */
#define SDL_INIT_TIMER          0x00000001
#define SDL_INIT_AUDIO          0x00000010
#define SDL_INIT_VIDEO          0x00000020  /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */
#define SDL_INIT_JOYSTICK       0x00000200  /**< SDL_INIT_JOYSTICK implies SDL_INIT_EVENTS */
#define SDL_INIT_HAPTIC         0x00001000
#define SDL_INIT_GAMECONTROLLER 0x00002000  /**< SDL_INIT_GAMECONTROLLER implies SDL_INIT_JOYSTICK */
#define SDL_INIT_EVENTS         0x00004000
#define SDL_INIT_NOPARACHUTE    0x00100000  /**< Don't catch fatal signals */
#define SDL_INIT_EVERYTHING ( \
                SDL_INIT_TIMER | SDL_INIT_AUDIO | SDL_INIT_VIDEO | SDL_INIT_EVENTS | \
                SDL_INIT_JOYSTICK | SDL_INIT_HAPTIC | SDL_INIT_GAMECONTROLLER \
            )
/* @} */

可以根據自己代碼裏面的需求進行選擇,例如只有音頻播放,則可以選擇SDL_INIT_AUDIO,只有視頻播放,則可以選擇SDL_INIT_VIDEO

2.SDL_CreateWindow
函數原型:

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

此函數的主要作用是根據提供的參數創建一個窗口。
各個參數含義:
2.1 title
窗口的標題

2.2 x y
窗口位置的座標,有兩種選項:SDL_WINDOWPOS_CENTERED 和 SDL_WINDOWPOS_UNDEFINED

2.3 w h
窗口的長寬

2.4 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.

3.SDL_CreateRenderer()
函數原型:

SDL_Renderer * SDLCALL SDL_CreateRenderer(SDL_Window * window,
                                               int index, Uint32 flags);

此函數的作用是創建一個渲染器。
各個參數含義:
3.1 window
要渲染的窗口

3.2 index
打算初始化的渲染設備的索引。設置“-1”則初始化默認的渲染設備。

3.3 flags
支持以下參數:

typedef enum
{
    SDL_RENDERER_SOFTWARE = 0x00000001,         /**< The renderer is a software fallback */
    SDL_RENDERER_ACCELERATED = 0x00000002,      /**< The renderer uses hardware
                                                     acceleration */
    SDL_RENDERER_PRESENTVSYNC = 0x00000004,     /**< Present is synchronized
                                                     with the refresh rate */
    SDL_RENDERER_TARGETTEXTURE = 0x00000008     /**< The renderer supports
                                                     rendering to texture */
} SDL_RendererFlags;

4.SDL_CreateTexturev()
函數原型:

SDL_Texture * SDLCALL SDL_CreateTexture(SDL_Renderer * renderer,
                                                        Uint32 format,
                                                        int access, int w,
                                                        int h);

此函數的作用是爲渲染器創建紋理
各個參數含義:
4.1renderer
渲染器

4.2 format
紋理的格式

4.3access
可以取以下枚舉值

typedef enum
{
    SDL_TEXTUREACCESS_STATIC,    /**< Changes rarely, not lockable */
    SDL_TEXTUREACCESS_STREAMING, /**< Changes frequently, lockable */
    SDL_TEXTUREACCESS_TARGET     /**< Texture can be used as a render target */
} SDL_TextureAccess;

4.4 w h
紋理的寬高

5.SDL_UpdateTexture()
函數原型:

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

此函數的作用是更新紋理

各個參數含義:
5.1 texture
要更新的紋理

5.2 rect
指向要更新的像素矩形的指針,爲NULL時更新整個紋理區域

5.3 pixels
像素數據

5.4 pitch
像素數據行之間的字節數。如果更新整個紋理區域,則是區域的.

6.SDL_RenderCopy()
函數原型:

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

將紋理複製到渲染器。
各個參數含義:
6.1 renderer
渲染目標。

6.2 texture
輸入紋理。

6.3 srcrect
選擇輸入紋理的一塊矩形區域作爲輸入。設置爲NULL的時候整個紋理作爲輸入。

6.4 dstrect
選擇渲染目標的一塊矩形區域作爲輸出。設置爲NULL的時候整個渲染目標作爲輸出。

7.SDL_RenderPresent()
函數原型:

void SDLCALL SDL_RenderPresent(SDL_Renderer * renderer);

渲染到屏幕上,即顯示。

3)代碼


#include <stdio.h>

extern "C"
{
#include "sdl/SDL.h"
//#include "sdl/SDL_thread.h"
}; 

const int bpp=12;//像素深度:指存儲每個像素所用的位數(bit)

int screen_w=650,screen_h=180;
const int pixel_w=320,pixel_h=180;

unsigned char buffer[pixel_w*pixel_h*bpp/8];

//Refresh Event
#define REFRESH_EVENT  (SDL_USEREVENT + 1)
//Break
#define BREAK_EVENT  (SDL_USEREVENT + 2)

int thread_exit=0;

int refresh_video(void *opaque)
{
    thread_exit=0;
    SDL_Event event;

    while (thread_exit==0) 
    {
        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 | SDL_INIT_TIMER)) 
    {  
        printf( "Could not initialize SDL - %s\n", SDL_GetError()); 
        return -1;
    } 

    /*創建窗口*/
    SDL_Window *screen;     
    screen = SDL_CreateWindow("SDL2_Play_Video", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
        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("test_yuv420p_320x180.yuv","rb+");
    if(fp==NULL)
    {
        printf("cannot open this file\n");
        return -1;
    }

    SDL_Rect sdlRect;  
    SDL_Rect sdlRect1; 

    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);
            }

            SDL_UpdateTexture( sdlTexture, NULL, buffer, pixel_w);  

            //FIX: If window is resize
            sdlRect.x = 0;  
            sdlRect.y = 0;  
            sdlRect.w = screen_w/2;  
            sdlRect.h = screen_h;  

            sdlRect1.x = screen_w/2;  
            sdlRect1.y = 0;  
            sdlRect1.w = screen_w/2;  
            sdlRect1.h = screen_h;  


            SDL_RenderClear( sdlRenderer );   
            SDL_RenderCopy( sdlRenderer, sdlTexture, NULL, &sdlRect);  
            SDL_RenderCopy( sdlRenderer, sdlTexture, NULL, &sdlRect1);  
            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;
}

4)工程

建了個工程,在visual studio2010上運行正常,已經上傳到了CSDN上:
基於SDL2播放yuv視頻

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章