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視訊