说明
记录一下SDL方式来渲染图像的操作。这里封装成了类,导入SDL库后,复制代码实例化对象直接可用。
SDL是跨平台的多媒体开发库,封装了不同系统中复杂的底层渲染细节,提供了统一的接口,极大提升了开发效率。音视频开发肯定离不开FFmpeg,而FFmpeg解码后的图像数据一般都是包含AVFrame中(主要为yuv420p格式)。所以对于解码后的AVFrame数据,线程内直接调用SDL相关接口即可实现图像显示,简单便捷。另外还有很重要的一点是,SDL可选择硬件渲染,对于多路视频将极大减少CPU的占用。
代码
已经封装成了类,具体逻辑不复杂,直接看代码即可。需要注意以下两点:
- 使用SDK库需要开始运行时先初始化库,程序运行结束后释放库。SDL分为多个子系统,如音频、视频、事件、定时器等,这里是视频即先 SDL_Init(SDL_INIT_VIDEO); 结束后 SDL_Quit();
- Qt 环境开发,显示的窗口句柄通过winId()方法获得,格式为WId。其它环境只要得到正确的窗口句柄是一样的,SDL实际需要的是(void *)的一个句柄,自行修改转换即可。
头文件
//SDL渲染类
class VideoOutput
{
public:
VideoOutput();
~VideoOutput();
//只显示不负责释放 AVFrame为YUV420P格式
void startDisplay(AVFrame *videoFrame);
void stopDisplay();
//设置渲染窗口句柄
void setShowHandle(void *id);
//设置视频宽、高、渲染方式
bool setVideoParm(int width, int height, bool cpuMode = true);
//释放
void release();
private:
//用到的相关变量
SDL_Window *sdlWindow;
SDL_Renderer *sdlRender;
SDL_Texture *sdlTexture;
//显示的句柄
void *showId;
};
实现文件
VideoOutput::VideoOutput()
{
sdlWindow = nullptr;
sdlRender = nullptr;
sdlTexture = nullptr;
showId = nullptr;
hasInit = false;
}
VideoOutput::~VideoOutput()
{
release();
}
void VideoOutput::startDisplay(AVFrame *videoFrame)
{
if(videoFrame == nullptr || !hasInit)
{
qDebug() << "视频帧无效或参数未设置";
return;
}
//句柄尺寸变化 那么SDL渲染的窗口也会跟着变化
SDL_Surface *sdlSurface = SDL_GetWindowSurface(sdlWindow);
if (!sdlSurface)
{
qDebug() << "SDL_GetWindowSurface fail" << SDL_GetError();
return;
}
//渲染
int ret = SDL_UpdateYUVTexture(sdlTexture, NULL, videoFrame->data[0], videoFrame->linesize[0], videoFrame->data[1], videoFrame->linesize[1], videoFrame->data[2], videoFrame->linesize[2]);
if (ret != 0)
qDebug() << "SDL_UpdateYUVTexture fail" << SDL_GetError();
ret = SDL_RenderClear(sdlRender);
if (ret != 0)
qDebug() << "SDL_RenderClear fail" << SDL_GetError();
ret = SDL_RenderCopy(sdlRender, sdlTexture, NULL, NULL);
if (ret != 0)
qDebug() << "SDL_RenderCopy fail" << SDL_GetError();
SDL_RenderPresent(sdlRender);
}
void VideoOutput::stopDisplay()
{
if(!hasInit)
return;
SDL_SetRenderDrawColor(sdlRender, 240, 240, 240, 100);
SDL_RenderClear(sdlRender);
SDL_RenderPresent(sdlRender);
}
void VideoOutput::setShowHandle(void *id)
{
this->showId = id;
}
bool VideoOutput::setVideoParm(int width, int height, bool cpuMode)
{
if(this->showId == nullptr)
{
qDebug() << "video showId is nullptr";
return false;
}
//SDL存在界面伸缩有时画面卡住 查阅资料应该是与SDL内部逻辑处理冲突 多次测试采用重置参数方法更为直接有效
release();
//默认cpu软件渲染模式 相比gpu硬件渲染兼容性更高
SDL_RendererFlags flag;
if(cpuMode)
flag = SDL_RENDERER_SOFTWARE;
else
flag = SDL_RENDERER_ACCELERATED;
sdlWindow = SDL_CreateWindowFrom(showId);
if(!sdlWindow)
{
qDebug() << "SDL_CreateWindowFrom error" << SDL_GetError();
return false;
}
sdlRender = SDL_CreateRenderer(sdlWindow, -1, flag);
if (!sdlRender)
{
qDebug() << "SDL_CreateRenderer error" << SDL_GetError();
return false;
}
sdlTexture = SDL_CreateTexture(sdlRender, SDL_PIXELFORMAT_IYUV, SDL_TEXTUREACCESS_STREAMING, width, height);
if (!sdlTexture)
{
qDebug() << "SDL_CreateRenderer error" << SDL_GetError();
return false;
}
SDL_ShowWindow(sdlWindow);
hasInit = true;
return true;
}
void VideoOutput::release()
{
if(sdlTexture)
{
SDL_DestroyTexture(sdlTexture);
sdlTexture = nullptr;
}
if(sdlRender)
{
SDL_DestroyRenderer(sdlRender);
sdlRender = nullptr;
}
if(sdlWindow)
{
SDL_DestroyWindow(sdlWindow);
sdlWindow = nullptr;
}
hasInit = false;
}