SDL2 打造显示矩阵 (一)
Posted qianbo_insist
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SDL2 打造显示矩阵 (一)相关的知识,希望对你有一定的参考价值。
sdl2
sdl2是一个显示工具,Simple DirectMedia Layer is a cross-platform development ,跨平台媒体显示层
sdl2 官网
无论在何种操作系统中,我们都可以使用其良好的特性和高性能。
显示矩阵
我们可以使用一个SDL2 窗口来显示多个画面,而没有必要使用多个sdl2 窗口,以下是显示矩阵的真实画面,画面来自于rtsp协议的各个摄像头,并且叠加了文字。有关于rtsp协议,可以参考我的其他文章。
在渲染的过程中,我们是可以使用一个窗口来分割显示,这样只有一个显示窗口,比起多窗口显示多画面,节省了很多资源。以下为显示分割算法。画面分割为行列,xn 和 yn 为分割的行列,算法求出显示的矩形区域。
//准备显示的区域
void calc_show_rect(int x1, int y1, int x2, int y2, int xn, int yn,
vector<texture_buf*> & txbuf,int pixelx,int pixely)
{
//电影画格个数
bool inflag = false;
if (txbuf.size() != xn*yn){
//清除
auto iter = txbuf.begin();
while (iter != txbuf.end()){
delete *iter;
iter++;
}
txbuf.clear();
//必须插入
inflag = true;
}
size_t movesize = xn * yn;
float cx = x2-x1;
float cy = y2-y1;
if (xn > 0 && yn > 0)
{
float w = cx / (float)xn;
float h = cy / (float)yn;
//int n = -1;
int nx;
int ny;
//auto iter = wins.begin();
for (size_t n = 0; n < movesize; n++)
{
//n++;
ny = n % xn; //列
nx = n / xn; //行
float nxpos = w *ny; //宽度与列求积
float nypos = h *nx; //高度与行求积
if (inflag == true){
texture_buf * tx = new texture_buf();
//txbuf.insert(pair<int, texture_buf *>(n, tx));
txbuf.push_back(tx);
}
texture_buf * tx = txbuf[n];
SDL_Rect canvas;
canvas.x = x1 + nxpos;
canvas.y = y1 + nypos;
canvas.w = w;
canvas.h = h;
calc0(pixelx, pixely, canvas, tx->rect);
//tx->rect.x = x1+nxpos;
//tx->rect.y = y1+nypos;
//tx->rect.w = w;
//tx->rect.h = h;
}
}
}
单个区域成比例显示算法
上面的函数中有一个cal0 函数,是为了计算显示区域的图像成比例,否则拉伸会把图像扭曲。
/*
w: 视频宽度
h: 视频高度
*/
int calc0(int w, int h, SDL_Rect &canvas, SDL_Rect& rect){
if (w <= 0 || h <= 0){
printf("this is %s\\n", " 视频的宽高不能小于或等于0");
return -1;
}
float scale = (float)w / (float)h;
//float w1 = (float)canvas.w;
//float h1 = (float)canvas.h;
if (canvas.w >= w && canvas.h >= h){
rect.w = w;
rect.h = h;
rect.x = canvas.x + (canvas.w - w) / 2;
rect.y = canvas.y + (canvas.w - h) / 2;
}
else if (canvas.w <= w && canvas.h >= h){
rect.w = canvas.w;
rect.h = (float)canvas.w / scale;
rect.x = canvas.x;
rect.y = canvas.y + abs(canvas.h - rect.h) / 2;
}
else if (canvas.w > w && canvas.h < h){
rect.h = canvas.h;
rect.w = scale * (float)canvas.h;
rect.x = canvas.x + abs(canvas.w - rect.w) / 2;
rect.y = canvas.y;
}
else if (canvas.w < w && canvas.h < h){
float scale1 = (float)canvas.w / (float)canvas.h;
if (scale1 >= scale){
rect.h = canvas.h;
rect.w = scale * (float)canvas.h;
rect.x = canvas.x + abs(rect.w - canvas.w ) / 2;
rect.y = canvas.y;
}
else{
rect.w = canvas.w;
rect.h = (float)canvas.w / scale;
rect.x = canvas.x;
rect.y = canvas.y + abs(canvas.h - rect.h) / 2;
}
}
//printf("calc %f-%f\\n", x, y);
return 0;
}
其他
初始化函数, sdl2 可以在其中设定硬件加速,例如使用opengl 或者 direct3d 等,利用硬件加速来画画面,显著减少cpu的使用率。显示使用ffmpeg解码后,直接使用YUV420 来做显示,不用使用RGB ,这一部分让SDL自己去做,其外,还有硬件解码,我们多使用两种方式,探测系统中有nvidia 的 cuda还是只有intel qsv。这样才能打造高性能的系统。其中加入了文字叠加在图像上的功能,做法是先解码,将文字叠加在YUV画面上。性能主要体现在
1 简洁的数据结构,传递数据时多使用指针引用。
2 使用探测硬件解码
3 画面缩小
4 使用单窗口显示多矩阵画面
5 使用加速显示画面
6 传输层尽量使用udp
当然软件工程的各个方面都要考虑,流程非常重要,尽量清晰简单。
int SDLPlayer::InitVideo()// SDL_PIXELFORMAT_IYUV);
{
if (_pRender == NULL){
_pWindow = SDL_CreateWindowFrom(_hWnd);
if (_pWindow == NULL){
printf("SDL_CreateWindowFrom error\\n");
return -1;
}
int winx;
int winy;
SDL_GetWindowSize(_pWindow, &winx, &winy);
calc_show_rect(0, 0, winx, winy, _xn, _yn, _texturebuf,_pixelx,_pixely);
_pRender = SDL_CreateRenderer(_pWindow, 0, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
wchar_t wszChinese[] = L"数据展示";
if(_font == NULL)
_font = TTF_OpenFont("simhei.ttf", 20);
/* 设置字体样式(加粗|斜体)*/
TTF_SetFontStyle(_font, TTF_STYLE_BOLD /*| TTF_STYLE_ITALIC*/);
//_Text = TTF_RenderUNICODE_Blended(_font, (const Uint16 *)wszChinese, RGB_Red);
//_Text = TTF_RenderUNICODE_Shaded(_font, (const Uint16 *)wszChinese, RGB_Red, RGB_White);
//_pTexture = SDL_CreateTextureFromSurface(_pRender, _Text);
//if (NULL != _Text)
//{
// ApplySurface(280, 150, _Text, pScreen);
// SDL_FreeSurface(pText);
//}
return 0;
}
return 1;
}
如何画内容
void SDLPlayer::DrawContent()
{
#if 1
SDL_Event event;
//SDL_bool done = SDL_FALSE;
while (SDL_PollEvent(&event)) {
printf(" e");
switch (event.type) {
case SDL_WINDOWEVENT:
if (event.window.event == SDL_WINDOWEVENT_RESIZED) {
//_sdlRect.w = event.window.data1;
//_sdlRect.h = event.window.data2;
//SDL_RenderSetViewport(_pRender, &_sdlRect);
int w;
int h;
SDL_GetWindowSize(_pWindow, &w, &h);
/*Calculate(_w, _h, _sdlRect.w, _sdlRect.h);
_sdlRect.x = abs(_w - _sdlRect.w) / 2;
_sdlRect.y = abs(_h - _sdlRect.h) / 2;*/
calc_show_rect(0, 0, w, h, _xn, _yn, _texturebuf,_pixelx,_pixely);
//resetDrawRect(_pixelW, _pixelH, _sdlRect.x, _sdlRect.y, _w, _h, -1);
//_sdlRect.w = _w;
//_sdlRect.h = _h;
SDL_RenderClear(_pRender);
}
break;
}
//SDL_Delay(1);
//printf("event");
}
#endif
SDL_RenderClear(_pRender);
auto tex = _texturebuf.begin();
while (tex != _texturebuf.end()){
(*tex)->copy_texture(_pRender, _draw_mode);
(*tex)->copy_text(_pRender);
tex++;
}
}
void SDLPlayer::Present()
{
SDL_RenderPresent(_pRender);
}
三维显示
我们可以利用这种方式在三维中画出显示矩阵,原理是一样的,下一次我们再放出三维的做法。
三维的好处是:
1 、可以更多地利用空间,
2、在需要的时候,我们可以使用vr
3、可以加入三维本地场景。
希望能帮助到大家。
以上是关于SDL2 打造显示矩阵 (一)的主要内容,如果未能解决你的问题,请参考以下文章