上篇主要讲了针对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 = );
~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 "sdl\SDL.h"
#include "sdl\SDL_ttf.h"
#include "sdl\SDL_image.h"
}
#pragma comment(lib, "SDL2.lib")
#pragma comment(lib, "SDL2_ttf.lib")
#pragma comment(lib, "SDL2_image.lib") int SDLRenderWnd::m_nRef = ;
_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, -, 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\n");
}
else {
int nTextureWidth = , nTextureHeight = ;
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\n");
}
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{ , height() - , , };
SDL_FreeSurface(pHeaderSurface);
SDL_RenderCopy(m_pRender, pHeaderTexture, nullptr, &dstRect);
SDL_DestroyTexture(pHeaderTexture);
}
}
}
} //Draw UserName
if (!m_pFont) {
printf("Open System Font Failed\n");
}
else
{
SDL_Color color{ , , };
if (SDL_Surface* pTextSurface = TTF_RenderUTF8_Blended(m_pFont, m_strUserName.toUtf8(), color)) {
SDL_Rect dstRect{ , 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 ( == m_nRef++)
{
SDL_Init(SDL_INIT_VIDEO);
TTF_Init();
IMG_Init(IMG_INIT_PNG);
m_pFont = TTF_OpenFont("C:/Windows/Fonts/simfang.ttf", );
}
} void SDLRenderWnd::SDL_Related_Uninit()
{
if ( == --m_nRef)
{
if (m_pFont)
TTF_CloseFont(m_pFont);
SDL_Quit();
TTF_Quit();
IMG_Quit();
}
}

简单说明一下,IMG库的后缀格式支持需要其他三方解码,IMG默认只能支持bmp的非压缩图片,字体ttf库初始化需要指定字体文件的路径(目前没有找到直接用字体名字让库自己寻找的方式),也没有尝试字体动态修改效果的方式。另外这个方案还有一个颇尴尬的bug是,在快速拖动窗口改变大小的时候,字体和图片的更新没有做到同步,可能会出现窗口变小了,图片字体还没有改变,到等待下一帧的时候才会同步大小

SDL其实还有很多强大功能,希望未来有时间整理一份常用功能的系列文章记录也同时方便大家的使用,如果只是为了yuv显示通过gpu渲染上面的代码已经勉强可以使用

SDL结合QWidget的简单使用说明(2)的更多相关文章

  1. SDL结合QWidget的简单使用说明

    SDL(Simple DirectMeida Layer)是一个简单的封装媒体库,功能主要涉及了相关于OpenGL或者DirectX的显卡硬件功能和一些鼠标,键盘等外设访问.这里主要只说明一下它的渲染 ...

  2. Oracle 中 union 和union all 的简单使用说明

    1.刚刚工作不久,经常接触oracle,但是对oracle很多东西都不是很熟.今天我们来了解一下union和union all的简单使用说明.Union(union all): 指令的目的是将两个 S ...

  3. struts 标签<s:ierator>的简单使用说明

    struts 标签<s:ierator>的简单使用说明,只显示<s:ierator> 的前6条数据 <s:iterator value="lstVisitor& ...

  4. Spring依赖注入 --- 简单使用说明

    Spring依赖注入 --- 简单使用说明 本文将对spring依赖注入的使用做简单的说明,enjoy your time! 1.使用Spring提供的依赖注入 对spring依赖注入的实现方法感兴趣 ...

  5. Linux下源码安装ffmpeg及ffmpeg的简单使用说明

    一.编译安装 ffmpeg在安装时依赖的包和版本都很让人头疼,不同编译环境也各不相同.公司之前封装了一个又各种出错. 其实办法很简单,就是到官网一步一步按着做就行了:http://trac.ffmpe ...

  6. Android自定义属性简单使用说明

    原创文章,转载请注明出处:http://www.cnblogs.com/baipengzhan/p/Android_attrs.html 本文从实用角度说明Android自定义属性的基本使用流程,清晰 ...

  7. ViewPager的简单使用说明

    前提:工程中使用ViewPager,需要导入google提供的jar包(android-support-v4.jar). 要学习ViewPager的使用,建议直接看官方文档 Creating Swip ...

  8. jmeter作接口测试入门的简单使用说明

    一.添加接口信息 1.添加线程组 (1)路径如下图: (2)部分内容解释 a.  Number of Threads(users):线程数 b.  Ramp-Up Period(in seconds) ...

  9. GreenDao的简单使用说明(五)多表n:m

    在设计一些比较复杂的数据库结构的时候,我们会遇到表之间是n:m的关系,就是常说的多对多的关系,最常用的情况,就是用户权限这块,日常最常见的就是学生与老师的关系了,哪么我们来看一下GreenDao中如何 ...

随机推荐

  1. C++/CLI中class成员声明与实现分开在不同文件时必须添加namespace

    以下是我的代码: //TaskConfigFile.h #pragma once using namespace System::Collections::Generic; using namespa ...

  2. linux,ubuntu14.04.5下安装搜狗输入法

    参考内容: http://www.linuxidc.com/Linux/2015-03/114347.htm https://jingyan.baidu.com/album/ad310e80ae6d9 ...

  3. VC++ 操作Windows快捷方式

    声明:本文是参考网友博文,然后自己实践整理所得,转载请注明出处! Windows的快捷方式实际上是一个带有扩展名LNK的数据文件,其中包含有用于访问Windows某一对象(即在资源管理器中所能浏览的所 ...

  4. 关于直播学习笔记-003-nginx-rtmp、srs、vlc、obs

    服务器 1.nginx-rtmp:https://github.com/illuspas/nginx-rtmp-win32 2.srs:https://github.com/illuspas/srs- ...

  5. Linux 任务计划:crontab

    (1) 什么是任务计划:也就是设置服务器在某个指定的时间执行某个指定的任务,比如执行一个命令,或执行一个脚本(2) Linux 使用 cron 服务来制定任务计划,cron 是服务名称,crond 是 ...

  6. Python 数据类型:元组

    一.元组介绍 1. 元组可以存储一系列的值,使用括号来定义,每个元素之间用逗号隔开,形如 ('a', 'b', 'c', 'd')2. 元组一旦定义,元组内的元素就不能再修改,除非重新定义一个新的元组 ...

  7. Serlvet学习笔记之二—不同页面共享数据

    一共有四种方法实现共享页面共享数据 1.cookie 2.sendRedirect 3.session 4.隐藏表单提交(form) 5.ServletContex 1.cookie:服务器在客户端保 ...

  8. ResourcesCompat和ContextCompat

    getResources().getDrawable() 过时的解决方法 当你这个Drawable不受主题影响时 ResourcesCompat.getDrawable(getResources(), ...

  9. 是否可以从一个static(静态)方法内部调用非static(非静态)方法?

    不可以.static方法调用时不需要创建对象(可直接调用),当一个static方法被调用时,可能还没有创建任何实例对象,也就不可能调用非静态方法.

  10. x86 体系指令

    FASM 第二章 - 2.1 x86 体系指令 Author: 徐艺波  From: xuyibo.org  Updated: 2008-04-17   官方论坛   本站软件反馈.软件开发交流.   ...