Beginning SDL 2.0(4) YUV加载及渲染
本文主要內容是基于的“Beginning SDL 2.0(3) SDL介紹及BMP渲染”(以下簡稱BS3)基礎上,將BMP加載及渲染修改為YUV420或I420的原始視頻格式。閱讀完本部分內容相信你可以對SDL2.0中的Renderer機制(支持硬件加速)有一定了解,并可以直接加載顯示YUV數據。
SDL 2.0新的支持硬件加速的視頻渲染技術——Render
SDL 1.x的版本的視頻渲染技術是基于純軟件實現的,效率相對較差,通常很難滿足實時渲染的需求。SDL 2.0之后針對這個問題,提供了新的2D加速渲染方案——Renderer。并提供了基于以下類型的機制:
- 單個像素點(point)
- 直線(line)
- 填充矩形(rectangle)
- 紋理圖像(texture image)
為了應用新的渲染技術,我們需要修改下BS3中提供的SDLVideoRender基類,主要是添加SDL_Renderer成員函數:
SDL_Renderer * m_sdl_renderer;在實現文件中修改SDLVideoRender::Init函數中添加初始化代碼,用于創建SDL_Renderer對象
m_sdl_renderer = SDL_CreateRenderer(m_sdl_window, -1, SDL_RENDERER_ACCELERATED); if (nullptr == m_sdl_renderer) {return false; }參數具體函數可以參考SDL_CreateRenderer。(SDL官網上的函數介紹)。
同時在void SDLVideoRender::Deinit()函數中添加反初始化代碼,用于銷毀SDL_Renderer對象
if (nullptr != m_sdl_renderer){SDL_DestroyRenderer(m_sdl_renderer);m_sdl_renderer = nullptr;}YUV圖像渲染
?首先我們使用YuvRender類來說明,如何實現YUV圖像加載及渲染的方法。其定義如下:
#pragma once #include "sdlvideorender.h"class YuvRender :public SDLVideoRender { public:YuvRender(void);~YuvRender(void);bool Init(HWND show_wnd, RECT show_rect);void Deinit();// width x height resolution// data[] for Y\U\V, stride is linesize of each rawvoid Update(int width, int height, unsigned char *data[3], int stride[3]){}bool Render();private:void FillTexture1();void FillTexture2();private:int m_yuv_width;int m_yuv_height;int m_yuv_frame_size;unsigned char * m_yuv_data;SDL_Texture * m_show_texture; };?這里假定要加載的YUV圖像分辨率為720x576,構造函數代碼如下:
YuvRender::YuvRender(void): SDLVideoRender(), m_yuv_width(720), m_yuv_height(576), m_yuv_frame_size(0), m_yuv_data(nullptr), m_show_texture(nullptr) {} View CodeInit函數的實現是,先創建SDL_Texture,之后從創建YUV緩沖,并從文件中加載圖像數據。代碼如下
bool YuvRender::Init(HWND show_wnd, RECT show_rect) {if (!SDLVideoRender::Init(show_wnd, show_rect))return false;// create texturem_show_texture = SDL_CreateTexture(m_sdl_renderer, SDL_PIXELFORMAT_IYUV, SDL_TEXTUREACCESS_STREAMING, m_yuv_width, m_yuv_height);if (nullptr == m_show_texture){return false;}// alloc buffer and read yuv filem_yuv_frame_size = m_yuv_width * m_yuv_height * 3 >> 1;m_yuv_data = new unsigned char[m_yuv_frame_size];FILE * fin = NULL;if (0 != fopen_s(&fin, "test_720x576.yuv", "rb")){return false;}fread(m_yuv_data, 1, m_yuv_frame_size, fin);fclose(fin);return true; } 注意創建SDL_Texture時使用SDL_TEXTUREACCESS_STREAMING表示我們希望創建的Texture可以頻繁的更新,數據能夠放到內存和緩存都可以訪問的地方。Deinit函數需要將Init創建的額外資源釋放掉。
void YuvRender::Deinit() {if (nullptr != m_yuv_data){delete [] m_yuv_data;m_yuv_data = nullptr;}if (nullptr != m_show_texture){SDL_DestroyTexture(m_show_texture);m_show_texture = NULL;}SDLVideoRender::Deinit(); } View Code
Render渲染函數實現很簡單,先填充Texture,之后將Texture渲染到renderer中,就可以提交系統刷新了。代碼如下:
bool YuvRender::Render() {if (NULL != m_show_texture){//FillTexture1(); FillTexture2();SDL_RenderCopy(m_sdl_renderer, m_show_texture, NULL, &m_show_rect); SDL_RenderPresent(m_sdl_renderer); }return true; }YUV數據拷貝及SDL_Texture的更新
上一節中沒有說明FillTexture函數的實現。這個函數主要實現將內存中的YUV數據填充到SDL_Texture中。SDL_Texture中的數據存儲形式是由創建時第二個參數決定的,SDL_PIXELFORMAT_IYUV(表示I420,常用的planer YUV420格式之一,Y、1/4U、1/4V)。
先看下FillTexture1函數的實現
void YuvRender::FillTexture1() {// This is a fairly slow function, intended for use with static textures that do not change often SDL_UpdateTexture(m_show_texture, NULL, m_yuv_data, m_yuv_width); }從SDL官網上可以看到SDL_UpdateTexture可以直接刷新Texture,但是效率上很難保證。
所以FillTexture2應該是我們在常規視頻渲染中使用的機制,其代碼如下
void YuvRender::FillTexture2() {void * pixel = NULL;int pitch = 0;if(0 == SDL_LockTexture(m_show_texture, NULL, &pixel, &pitch)){// 如果不考慮數據對齊,直接拷貝YUV數據是沒有問題的if (pitch == m_yuv_width){memcpy(pixel, m_yuv_data, m_yuv_frame_size);}else // 可能發生pitch > width的情況 {// 如果有數據對齊的情況,單獨拷貝每一行數據// for Yint h = m_yuv_height;int w = m_yuv_width;unsigned char * dst = reinterpret_cast<unsigned char *>(pixel);unsigned char * src = m_yuv_data;for (int i = 0; i < h; ++i){memcpy(dst, src, w);dst += pitch;src += w;}h >>= 1;w >>= 1;pitch >>= 1;// for Ufor (int i = 0; i < h; ++i){memcpy(dst, src, w);dst += pitch;src += w;}// for Vfor (int i = 0; i < h; ++i){memcpy(dst, src, w);dst += pitch;src += w;}}SDL_UnlockTexture(m_show_texture);} }這里使用了LockTexture/UnlockTexture的機制。至于SDL_texture的實際內存布局跟Direct 3D的surface類似。
YUV圖像渲染驗證
我們用vs2010創建基于win32的工程1_Win32_yuv_render,并按照BS3中的修改,將YuvRender提供的接口添加到工程中。
YUV的原圖如下:
程序運行后的效果圖如下:(注意YUV做了拉伸和顯示區域縮減)
相關代碼可以從我的git下載,url如下:https://git.oschina.net/Tocy/SampleCode.git,位于TocySDL2VisualTutorial目錄下。
總結
以上是生活随笔為你收集整理的Beginning SDL 2.0(4) YUV加载及渲染的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: !-- --注释和%-- --%注释的区
- 下一篇: Python中的 optparse模块