上篇主要讲了针对yv12流数据的渲染,但有时候我们显示视频还要求加一些信息,比如头像,昵称等等。一般的想法是在渲染窗口之上做一个小控件来负责;
但是很遗憾,你会发现你的控件被SDL的渲染完全遮住了,渲染的原理并非是在本窗口的刷新做,而是另外有一层负责,这也是为什么上篇强调一定要setUpdateEnabled(false)的原因
那么只有考虑再渲染层直接把图片文字贴出来的办法。这么简单的需求,SDL当然有实现,不过基础库并没有这个功能(基础库可以加载bmp文件),需要去下载两个插件库
图片库:http://www.libsdl.org/projects/SDL_image
文字库:http://www.libsdl.org/projects/SDL_ttf
下载后解压编译和基础库完全一样,取其中以下两个库使用(这两个库本身还依赖一些其它的库比如libpng等等,已经在源码包里包含了注意使用的时候一起拷贝)
#ifndef _SDL_RENDER_WND_H__
#define _SDL_RENDER_WND_H__ /******************************************************************** 文件名 : SDLRenderWnd 作者 : @Kaiming 创建时间: 2018/3/26 11:27 版本 : 1.0 文件描述: SDL YUV420流输出窗口 *********************************************************************/ #include <QWidget> struct SDL_Renderer; struct SDL_Texture; struct SDL_Window; struct _TTF_Font; class SDLRenderWnd : public QWidget { Q_OBJECT public: SDLRenderWnd(QWidget *parent = 0); ~SDLRenderWnd(); void Clear(); QImage Grab(); //当前窗口是否为空 bool IsEmpty() const; void UpdateUserHeader(const QImage& header); void UpdateUserName(const QString& strName);protected: virtual void resizeEvent(QResizeEvent *event);private: static void SDL_Related_Init(); static void SDL_Related_Uninit(); public slots: //根据传入数据流显示视频 void PresentFrame(const unsigned char* pBuffer, int nImageWidth, int nImageHeight); private: SDL_Renderer* m_pRender; SDL_Texture* m_pTexture; SDL_Window* m_pWindow; bool m_bEmpty; QString m_strUserName; QByteArray m_userHeaderData; static int m_nRef; //引用计数来确定SDL全局资源的创建和回收 static _TTF_Font* m_pFont; }; #endif // _SDL_RENDER_H__
#include "SDLRenderWnd.h
#include <QBuffer>
#include <QResizeEvent>
extern "C" { #include "sdlSDL.h" #include "sdlSDL_ttf.h" #include "sdlSDL_image.h" } #pragma comment(lib, "SDL2.lib") #pragma comment(lib, "SDL2_ttf.lib") #pragma comment(lib, "SDL2_image.lib") int SDLRenderWnd::m_nRef = 0; _TTF_Font* SDLRenderWnd::m_pFont = nullptr; SDLRenderWnd::SDLRenderWnd(QWidget *parent) : QWidget(parent), m_pTexture(nullptr) { setUpdatesEnabled(false); SDL_Related_Init(); m_pWindow = SDL_CreateWindowFrom((void*)winId()); m_pRender = SDL_CreateRenderer(m_pWindow, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC); SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "best"); } SDLRenderWnd::~SDLRenderWnd() { if (m_pWindow) SDL_DestroyWindow(m_pWindow); if (m_pRender) SDL_DestroyRenderer(m_pRender); if (m_pTexture) SDL_DestroyTexture(m_pTexture); SDL_Related_Uninit(); } void SDLRenderWnd::PresentFrame(const unsigned char* pBuffer, int nImageWidth, int nImageHeight) { if (!m_pRender) { printf("Render not Create "); } else { int nTextureWidth = 0, nTextureHeight = 0; SDL_QueryTexture(m_pTexture, nullptr, nullptr, &nTextureWidth, &nTextureHeight); if (nTextureWidth != nImageWidth || nTextureHeight != nImageHeight) { if (m_pTexture) SDL_DestroyTexture(m_pTexture); m_pTexture = SDL_CreateTexture(m_pRender, SDL_PIXELFORMAT_IYUV, SDL_TEXTUREACCESS_STREAMING, nImageWidth, nImageHeight); } } if (!m_pTexture) { printf("YUV Texture Create Failed "); } else { SDL_UpdateTexture(m_pTexture, nullptr, pBuffer, nImageWidth); SDL_RenderClear(m_pRender); //Copy YUV Stream SDL_RenderCopy(m_pRender, m_pTexture, nullptr, nullptr); //Draw UserHeader if (SDL_RWops* ops = SDL_RWFromMem((void*)(m_userHeaderData.constData()), m_userHeaderData.size())) { if (SDL_Surface* pHeaderSurface = IMG_LoadPNG_RW(ops)) { SDL_FreeRW(ops); if (SDL_Texture* pHeaderTexture = SDL_CreateTextureFromSurface(m_pRender, pHeaderSurface)) { SDL_Rect dstRect{ 6, height() - 42, 42, 42 }; SDL_FreeSurface(pHeaderSurface); SDL_RenderCopy(m_pRender, pHeaderTexture, nullptr, &dstRect); SDL_DestroyTexture(pHeaderTexture); } } } } //Draw UserName if (!m_pFont) { printf("Open System Font Failed "); } else { SDL_Color color{ 255, 255, 255 }; if (SDL_Surface* pTextSurface = TTF_RenderUTF8_Blended(m_pFont, m_strUserName.toUtf8(), color)) { SDL_Rect dstRect{ 60, height() - pTextSurface->h, pTextSurface->w, pTextSurface->h }; if (SDL_Texture* pTextTexture = SDL_CreateTextureFromSurface(m_pRender, pTextSurface)) { SDL_FreeSurface(pTextSurface); SDL_RenderCopy(m_pRender, pTextTexture, nullptr, &dstRect); SDL_DestroyTexture(pTextTexture); } } } SDL_RenderPresent(m_pRender); } void SDLRenderWnd::Clear() { SDL_RenderClear(m_pRender); } void SDLRenderWnd::UpdateUserHeader(const QImage& header) { QBuffer buffer(&m_userHeaderData); header.save(&buffer, "PNG"); } void SDLRenderWnd::UpdateUserName(const QString& strName) { m_strUserName = strName; } void SDLRenderWnd::resizeEvent(QResizeEvent *event) { QWidget::resizeEvent(event); } void SDLRenderWnd::SDL_Related_Init() { if (0 == m_nRef++) { SDL_Init(SDL_INIT_VIDEO); TTF_Init(); IMG_Init(IMG_INIT_PNG); m_pFont = TTF_OpenFont("C:/Windows/Fonts/simfang.ttf", 18); } } void SDLRenderWnd::SDL_Related_Uninit() { if (0 == --m_nRef) { if (m_pFont) TTF_CloseFont(m_pFont); SDL_Quit(); TTF_Quit(); IMG_Quit(); } }
简单说明一下,IMG库的后缀格式支持需要其他三方解码,IMG默认只能支持bmp的非压缩图片,字体ttf库初始化需要指定字体文件的路径(目前没有找到直接用字体名字让库自己寻找的方式),也没有尝试字体动态修改效果的方式。另外这个方案还有一个颇尴尬的bug是,在快速拖动窗口改变大小的时候,字体和图片的更新没有做到同步,可能会出现窗口变小了,图片字体还没有改变,到等待下一帧的时候才会同步大小
SDL其实还有很多强大功能,希望未来有时间整理一份常用功能的系列文章记录也同时方便大家的使用,如果只是为了yuv显示通过gpu渲染上面的代码已经勉强可以使用