本文主要内容是基于的“Beginning SDL 2.0(3) SDL介绍及BMP渲染”(以下简称BS3)基础上,将BMP加载及渲染修改为YUV420或I420的原始视频格式。阅读完本部分内容相信你可以对SDL2.0中的Renderer机制(支持硬件加速)有一定了解,并可以直接加载显示YUV数据。

SDL 2.0新的支持硬件加速的视频渲染技术——Render

SDL 1.x的版本的视频渲染技术是基于纯软件实现的,效率相对较差,通常很难满足实时渲染的需求。SDL 2.0之后针对这个问题,提供了新的2D加速渲染方案——Renderer。并提供了基于以下类型的机制:

  • 单个像素点(point)
  • 直线(line)
  • 填充矩形(rectangle)
  • 纹理图像(texture image)

为了应用新的渲染技术,我们需要修改下BS3中提供的SDLVideoRender基类,主要是添加SDL_Renderer成员函数:

SDL_Renderer * m_sdl_renderer;

在实现文件中修改SDLVideoRender::Init函数中添加初始化代码,用于创建SDL_Renderer对象

m_sdl_renderer = SDL_CreateRenderer(m_sdl_window, -, SDL_RENDERER_ACCELERATED);
if (nullptr == m_sdl_renderer)
{
return false;
}

参数具体函数可以参考SDL_CreateRenderer。(SDL官网上的函数介绍)。

同时在void SDLVideoRender::Deinit()函数中添加反初始化代码,用于销毁SDL_Renderer对象

    if (nullptr != m_sdl_renderer)
{
SDL_DestroyRenderer(m_sdl_renderer);
m_sdl_renderer = nullptr;
}

YUV图像渲染

首先我们使用YuvRender类来说明,如何实现YUV图像加载及渲染的方法。其定义如下:

#pragma once
#include "sdlvideorender.h" class YuvRender :public SDLVideoRender
{
public:
YuvRender(void);
~YuvRender(void); bool Init(HWND show_wnd, RECT show_rect);
void Deinit(); // width x height resolution
// data[] for Y\U\V, stride is linesize of each raw
void Update(int width, int height, unsigned char *data[], int stride[]){}
bool Render(); private:
void FillTexture1();
void FillTexture2(); private:
int m_yuv_width;
int m_yuv_height;
int m_yuv_frame_size;
unsigned char * m_yuv_data; SDL_Texture * m_show_texture;
};

这里假定要加载的YUV图像分辨率为720x576,构造函数代码如下:

YuvRender::YuvRender(void)
: SDLVideoRender()
, m_yuv_width(), m_yuv_height()
, m_yuv_frame_size()
, m_yuv_data(nullptr)
, m_show_texture(nullptr)
{}

Init函数的实现是,先创建SDL_Texture,之后从创建YUV缓冲,并从文件中加载图像数据。代码如下

bool YuvRender::Init(HWND show_wnd, RECT show_rect)
{
if (!SDLVideoRender::Init(show_wnd, show_rect))
return false; // create texture
m_show_texture = SDL_CreateTexture(m_sdl_renderer, SDL_PIXELFORMAT_IYUV,
SDL_TEXTUREACCESS_STREAMING, m_yuv_width, m_yuv_height);
if (nullptr == m_show_texture)
{
return false;
} // alloc buffer and read yuv file
m_yuv_frame_size = m_yuv_width * m_yuv_height * >> ;
m_yuv_data = new unsigned char[m_yuv_frame_size];
FILE * fin = NULL;
if ( != fopen_s(&fin, "test_720x576.yuv", "rb"))
{
return false;
} fread(m_yuv_data, , m_yuv_frame_size, fin); fclose(fin); return true;
}
注意创建SDL_Texture时使用SDL_TEXTUREACCESS_STREAMING表示我们希望创建的Texture可以频繁的更新,数据能够放到内存和缓存都可以访问的地方。
Deinit函数需要将Init创建的额外资源释放掉。
void YuvRender::Deinit()
{
if (nullptr != m_yuv_data)
{
delete [] m_yuv_data;
m_yuv_data = nullptr;
} if (nullptr != m_show_texture)
{
SDL_DestroyTexture(m_show_texture);
m_show_texture = NULL;
} SDLVideoRender::Deinit();
}

Render渲染函数实现很简单,先填充Texture,之后将Texture渲染到renderer中,就可以提交系统刷新了。代码如下:

bool YuvRender::Render()
{
if (NULL != m_show_texture)
{
//FillTexture1();
FillTexture2(); SDL_RenderCopy(m_sdl_renderer, m_show_texture, NULL, &m_show_rect);
SDL_RenderPresent(m_sdl_renderer);
} return true;
}

YUV数据拷贝及SDL_Texture的更新

上一节中没有说明FillTexture函数的实现。这个函数主要实现将内存中的YUV数据填充到SDL_Texture中。SDL_Texture中的数据存储形式是由创建时第二个参数决定的,SDL_PIXELFORMAT_IYUV(表示I420,常用的planer YUV420格式之一,Y、1/4U、1/4V)。

先看下FillTexture1函数的实现

void YuvRender::FillTexture1()
{
// This is a fairly slow function, intended for use with static textures that do not change often
SDL_UpdateTexture(m_show_texture, NULL, m_yuv_data, m_yuv_width);
}

从SDL官网上可以看到SDL_UpdateTexture可以直接刷新Texture,但是效率上很难保证。

所以FillTexture2应该是我们在常规视频渲染中使用的机制,其代码如下

void YuvRender::FillTexture2()
{
void * pixel = NULL;
int pitch = ;
if( == SDL_LockTexture(m_show_texture, NULL, &pixel, &pitch))
{
// 如果不考虑数据对齐,直接拷贝YUV数据是没有问题的
if (pitch == m_yuv_width)
{
memcpy(pixel, m_yuv_data, m_yuv_frame_size);
}
else // 可能发生pitch > width的情况
{
// 如果有数据对齐的情况,单独拷贝每一行数据
// for Y
int h = m_yuv_height;
int w = m_yuv_width;
unsigned char * dst = reinterpret_cast<unsigned char *>(pixel);
unsigned char * src = m_yuv_data;
for (int i = ; i < h; ++i)
{
memcpy(dst, src, w);
dst += pitch;
src += w;
} h >>= ;
w >>= ;
pitch >>= ;
// for U
for (int i = ; i < h; ++i)
{
memcpy(dst, src, w);
dst += pitch;
src += w;
} // for V
for (int i = ; i < h; ++i)
{
memcpy(dst, src, w);
dst += pitch;
src += w;
}
} SDL_UnlockTexture(m_show_texture);
}
}

这里使用了LockTexture/UnlockTexture的机制。至于SDL_texture的实际内存布局跟Direct 3D的surface类似。

YUV图像渲染验证

我们用vs2010创建基于win32的工程1_Win32_yuv_render,并按照BS3中的修改,将YuvRender提供的接口添加到工程中。

YUV的原图如下:

程序运行后的效果图如下:(注意YUV做了拉伸和显示区域缩减)

相关代码可以从我的git下载,url如下:https://git.oschina.net/Tocy/SampleCode.git,位于TocySDL2VisualTutorial目录下。

Beginning SDL 2.0(4) YUV加载及渲染的更多相关文章

  1. Beginning SDL 2.0(5) 基于MFC和SDL的YuvPlayer

    本文是在“Beginning SDL 2.0(4) YUV加载及渲染”(以下简称BS4)基础上做的功能完善,如果你对之间介绍的内容了解不多,麻烦先阅读之前的内容. 本文主要介绍如何完成一个基于MFC和 ...

  2. Beginning SDL 2.0(3) SDL介绍及BMP渲染

    SDL是一个跨平台的多媒体库.为了实现跨平台,SDL提供了一个简单的界面库抽象,比如提供了SDL_Window用于表示窗口句柄,SDL_Surface.SDL_Texture.SDL_Renderer ...

  3. 【转】Sqlite 混合模式程序集是针对“v2.0.50727”版的运行时生成的,在没有配置其他信息的情况下,无法在 4.0 运行时中加载该...

    开发环境: vs2010+.net framework 4.0+ System.Data.SQLite.DLL (2.0)今天在做Sqlite数据库测试,一运行程序在一处方法调用时报出了一个异常 混合 ...

  4. SQLite.dll混合模式程序集是针对“v2.0.50727”版的运行时生成的,在没有配置其他信息的情况下,无法在 4.0 运行时中加载该程序集。

    其他信息: V5.7.4.4 Can't find the System.Data.SQLite.dll more info : 混合模式程序集是针对"v2.0.50727"版的运 ...

  5. @vue/cli 3.0 使用 svg-sprite-loader 加载本地 SVG 文件

    目录 @vue/cli 3.0 使用 svg-sprite-loader 加载本地 SVG 文件 运行 使用 配置 svg-sprite-loader 调用当前环境下的颜色 props @vue/cl ...

  6. <VS2010>混合模式程序集是针对“v2.0”版的运行时生成的,在没有配置其他信息的情况下,无法在 4.0 运行时中加载该程序集

    在把以前写的代码生成工具从原来的.NET3.5升级到.NET4.0时,将程序集都更新后,一运行程序在一处方法调用时报出了一个异常: 混合模式程序集是针对“v2.0.50727”版的运行时生成的,在没有 ...

  7. VS报错之混合模式程序集是针对“v1.1.4322”版的运行时生成的,在没有配置其他信息的情况下,无法在 4.0 运行时中加载该程序集。

    看到一个kinect大牛编写的一个水果忍者的体感游戏版本,让我为自己一直以来只用现有的网页游戏来模拟kinect体感游戏控制感到惭愧,没办法,我还是菜鸟.学习一段后自己模仿星际大战这个游戏,自己写了一 ...

  8. C#连接Sqlite 出现:混合模式程序集是针对“v2.0.50727”版的运行时生成的,在没有配置其他信息的情况下,无法在 4.0 运行时中加载该程序集。的解决方案

    C#连接Sqlite 出现: 混合模式程序集是针对“v2.0.50727”版的运行时生成的,在没有配置其他信息的情况下,无法在 4.0 运行时中加载该程序集.的解决方案 C#连接sqlite数据库代码 ...

  9. c# .netframwork 4.0 调用 2.0时报错 混合模式程序集是针对“v2.0.50727”版的运行时生成的,在没有配置其他信息的情况下,无法在 4.0 运行时中加载该程序集。

    “System.IO.FileLoadException”类型的未经处理的异常在 XXX.dll 中发生 其他信息: 混合模式程序集是针对“v2.0.50727”版的运行时生成的,在没有配置其他信息的 ...

随机推荐

  1. Tensorflow结点打包和依赖控制

    深度学习库能够充分发挥GPU并行计算的能力,但是有时我们却不得不需要串行.这时就需要用到依赖控制. import tensorflow as tf a = tf.Variable(1) b = tf. ...

  2. ThinkPHP学习(三)

    我们已经将数据保存到了后台数据库,那接下来我们肯定要将数据显示出来看看了. 先建立一个要显示数据的模板formlist.html: <!DOCTYPE HTML PUBLIC "-// ...

  3. MM bound 与 Jensen's inequality

    MM bound 与 Jensen's inequality 简森不等式 在使用最大似然估计方法求解模型最优解的时候,如果使用梯度下降(GD or SGD)或者梯度上升(GA or SGA),可能收敛 ...

  4. 图片标注工具LabelImg使用教程

    1.进入labelImg-master文件夹,在空白处使用 “Shift+鼠标右键” ,选择在此处打开命令窗口,依次输入下面语句即可打开软件. pyrcc4 -o resources.py resou ...

  5. NET MVC全局异常处理(一) 【转载】网站遭遇DDoS攻击怎么办 使用 HttpRequester 更方便的发起 HTTP 请求 C#文件流。 Url的Base64编码以及解码 C#计算字符串长度,汉字算两个字符 2019周笔记(2.18-2.23) Mysql语句中当前时间不能直接使用C#中的Date.Now传输 Mysql中Count函数的正确使用

    NET MVC全局异常处理(一)   目录 .NET MVC全局异常处理 IIS配置 静态错误页配置 .NET错误页配置 程序设置 全局异常配置 .NET MVC全局异常处理 一直知道有.NET有相关 ...

  6. 企业内知识库wiki所存在的问题

    相信很多公司都利用开源的wiki web app搭建了自家的内部wiki服务,比如使用media Wiki, Gollum, doku wiki, jsWiki等 但是,真正可用的企业wiki系统却没 ...

  7. php把采集内容中图片地址下载并替换成本地地址

    把字符串中地址全部获取到一个数组我们利用preg_match_all函数 代码如下 复制代码 <?php$str='<p><img border="0" s ...

  8. [转]Github 简明教程

    如果你是一枚Coder,但是你不知道Github,那么我觉的你就不是一个菜鸟级别的Coder,因为你压根不是真正Coder,你只是一个Code搬运工. 但是你如果已经在读这篇文章了,我觉的你已经知道G ...

  9. [转]同步对象Event的用法

    同步对象Event的用法  首先介绍CreateEvent是创建windows事件的意思,作用主要用在判断线程退出,线程锁定方面.  CreateEvent函数功能描述:创建或打开一个命名的或无名的事 ...

  10. JavaScript面向对象:类、方法、属性

    JavaScript是一种基于对象的语言,与传统面向对象语言(C#.C++)相比,JavaScript中没有类的概念,其继承有两种基本形式:基于对象的继承和基于类型的继承(原型链继承).无论哪种形式的 ...