SDL2 打造显示矩阵 (一)

Posted qianbo_insist

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SDL2 打造显示矩阵 (一)相关的知识,希望对你有一定的参考价值。

sdl2

sdl2是一个显示工具,Simple DirectMedia Layer is a cross-platform development ,跨平台媒体显示层
sdl2 官网
无论在何种操作系统中,我们都可以使用其良好的特性和高性能。

显示矩阵

我们可以使用一个SDL2 窗口来显示多个画面,而没有必要使用多个sdl2 窗口,以下是显示矩阵的真实画面,画面来自于rtsp协议的各个摄像头,并且叠加了文字。有关于rtsp协议,可以参考我的其他文章。
show

在渲染的过程中,我们是可以使用一个窗口来分割显示,这样只有一个显示窗口,比起多窗口显示多画面,节省了很多资源。以下为显示分割算法。画面分割为行列,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 打造显示矩阵 (一)的主要内容,如果未能解决你的问题,请参考以下文章

使用 SDL2 和 OpenGL 显示蓝色

SDL2 / Opengl3 无法显示任何内容

SDL2 简明教程:显示图片

SDL2 简明教程:显示图片

用 SDL2 平铺背景并显示前景

用 SDL2 加载PNG平铺背景并显示前景