Beginning SDL 2.0(5) 基于MFC和SDL的YuvPlayer
本文是在“Beginning SDL 2.0(4) YUV加载及渲染”(以下简称BS4)基础上做的功能完善,如果你对之间介绍的内容了解不多,麻烦先阅读之前的内容。
本文主要介绍如何完成一个基于MFC和SDL 2.0的YUV播放器,基本思路是使用Windows的WM_TIMER消息,定期刷新画面。(正规的播放器通常使用一个独立的线程用于做固定帧率的刷新,这里为了简单期间使用系统提供的定时器实现。)
工程创建
使用vs10创建mfc基于对话框的工程,2_sdl_yuv_player,配置好SDL包含路径,同时包含BS4中提供的YuvRender类。如果不想处理unicode字符,建议将工程属性的字符集设置为多字节编码。
并在主对话框中编辑出如下几个控件:一个Static用于YUV视频显示,一个播放按钮用于选择yuv路径,并开启播放,三个输入框分别用于输入视频宽、高及帧率。效果如下:
YuvRender类更新
由于BS4中的YuvRender是读取本地文件目录下的yuv图像,然后显示视频的,这里需要修改下,以支持动态的YUV画面渲染。
具体接口如下:
#pragma once
#include "sdlvideorender.h" class YuvRender :public SDLVideoRender
{
public:
YuvRender(void);
~YuvRender(void); // Init use parent impl
//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:
bool CreateTexture(int width, int height);
void FillTexture(unsigned char *data[], int stride[]); private:
// texture size
int m_in_width, m_in_height;
SDL_Texture * m_show_texture;
};
相比之前的版本这里最大的区别是Update函数不再是空实现,添加了CreateTexture函数,主要考虑我们事先是不知道需要创建Texture的分辨率。
这里Init函数功能,完全可以直接使用父类提供的实现。
下面是Deinit函数实现代码
void YuvRender::Deinit()
{
if (nullptr != m_show_texture)
{
SDL_DestroyTexture(m_show_texture);
m_show_texture = NULL;
} SDLVideoRender::Deinit();
}
Update函数会调用CreateTexture和FillTexture两个函数,用于创建和填充纹理,其实现代码如下:
bool YuvRender::CreateTexture(int width, int height)
{
if (m_in_height == height && m_in_width == width &&
nullptr != m_show_texture)
{
return true;
} ASSERT(width > && width < );
ASSERT(height > && height < ); m_show_texture = SDL_CreateTexture(m_sdl_renderer, SDL_PIXELFORMAT_IYUV,
SDL_TEXTUREACCESS_STREAMING, width, height);
if (nullptr != m_show_texture)
{
m_in_width = width;
m_in_height = height;
} return NULL != m_show_texture;
}
void YuvRender::FillTexture(unsigned char *data[], int stride[])
{
void * pixel = NULL;
int pitch = ;
if( == SDL_LockTexture(m_show_texture, NULL, &pixel, &pitch))
{
// for Y
int h = m_in_height;
int w = m_in_width;
unsigned char * dst = reinterpret_cast<unsigned char *>(pixel);
unsigned char * src = data[];
for (int i = ; i < h; ++i)
{
memcpy(dst, src, w);
dst += pitch;
src += stride[];
} h >>= ;
w >>= ;
pitch >>= ;
// for U
for (int i = ; i < h; ++i)
{
memcpy(dst, src, w);
dst += pitch;
src += stride[];
} // for V
for (int i = ; i < h; ++i)
{
memcpy(dst, src, w);
dst += pitch;
src += stride[];
}
SDL_UnlockTexture(m_show_texture);
}
} // width x height resolution
// data[] for Y\U\V, stride is linesize of each raw
void YuvRender::Update(int width, int height, unsigned char *data[], int stride[])
{
if (nullptr == m_show_texture)
{
CreateTexture(width, height);
} if (nullptr != m_show_texture)
{
FillTexture(data, stride);
}
}
最后一个函数是Render,实现相对简单,直接将texture复制并提交到显存中。
bool YuvRender::Render()
{
if (NULL != m_show_texture)
{
SDL_RenderCopy(m_sdl_renderer, m_show_texture, NULL, &m_show_rect);
SDL_RenderPresent(m_sdl_renderer);
} return true;
}
主程序中的修改
主要修改位于CMy2_sdl_yuv_playerDlg中,依次添加OnBnClickedButtonPlay、WM_TIMER、WM_DESTORY的消息处理函数,并添加三个输入框的关联变量,m_width、m_height、m_fps。同时定义m_yuv_render用于显示yuv数据。我们将需要的数据通过文件指针的形式保存,每次读取一帧YUV数据。
首先看一下OnBnClickedButtonPlay的功能,需要调用打开对话框,选择指定的yuv,分配资源,启动定时器,相关实现如下:
enum{
DFT_WIDTH = ,
DFT_HEIGHT = ,
DFT_FPS = , SHOW_TIMER_ID = WM_USER + ,
}; bool CMy2_sdl_yuv_playerDlg::InitRender(CString file_path)
{
UpdateData(TRUE);
m_plane_size = (m_width * m_height) >> ;
m_frame_length = m_plane_size * ;
m_frame_data = new unsigned char[m_frame_length];
if (nullptr == m_frame_data)
{
return false;
}
m_plane_size <<= ; m_in_file = nullptr;
if ( != fopen_s(&m_in_file, (LPCTSTR)file_path, "rb"))
{
CString strMsg;
strMsg.Format("open failed! %s", file_path);
AfxMessageBox(strMsg);
return false;
} CRect rect;
CStatic * pStatic = (CStatic *)GetDlgItem(IDC_STATIC_VIDEO);
pStatic->GetClientRect(&rect);
// 因为SDL_DestoryWindow会调用ShowWindow使窗口隐藏
// 为了实现重复使用播放窗口的目的,这里直接将其显示出来
pStatic->ShowWindow(SW_SHOW);
m_yuv_render.Init(pStatic->GetSafeHwnd(), rect); ASSERT( != m_fps);
int interval = / m_fps;
SetTimer(SHOW_TIMER_ID, interval, NULL); return true;
} void CMy2_sdl_yuv_playerDlg::DeinitRender()
{
if (nullptr != m_in_file)
{
KillTimer(SHOW_TIMER_ID);
fclose(m_in_file);
m_in_file = nullptr;
} m_yuv_render.Deinit(); if (nullptr != m_frame_data)
{
delete [] m_frame_data;
m_frame_data = nullptr;
} m_plane_size = ;
} void CMy2_sdl_yuv_playerDlg::OnBnClickedButtonPlay()
{
CString file_name = _T("");
CFileDialog fd(TRUE, NULL,
file_name,
OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT | OFN_NOCHANGEDIR,
NULL, NULL);
if (fd.DoModal() == IDOK)
{
DeinitRender();
InitRender(fd.GetPathName());
}
}
注意这里额外调用了ShowWindow函数,你可以尝试下看看这个到底有什么功能。相关修改是参考SDL2.0的源码中SDL_DestroyWindow实现。
定时消息处理函数的基本功能是读取一帧yuv,渲染,如果文件到头,重置文件指针。
void CMy2_sdl_yuv_playerDlg::OnTimer(UINT_PTR nIDEvent)
{
if (nIDEvent == SHOW_TIMER_ID && nullptr != m_in_file)
{
size_t read_size = fread(m_frame_data, , m_frame_length, m_in_file);
if(read_size == m_frame_length)
{
unsigned char *src[] = {NULL}; //Y、U、V数据首地址
src[] = m_frame_data;
src[] = src[] + m_plane_size;
src[] = src[] + (m_plane_size>>);
int stride[] = {m_width, m_width/, m_width/};
m_yuv_render.Update(m_width, m_height, src, stride);
m_yuv_render.Render();
}
else
{
// 循环播放
fseek(m_in_file, , SEEK_SET);
}
} CDialogEx::OnTimer(nIDEvent);
}
WM_DESTROY函数主要做必要的退出处理,并清理SDL的资源。
void CMy2_sdl_yuv_playerDlg::OnDestroy()
{
CDialogEx::OnDestroy(); DeinitRender(); if (SDL_WasInit())SDL_Quit();
}
最终程序运行效果如下图:
总结
在BS4的基础上实现YUV播放器相对比较简单,整理这篇文章主要目的在于梳理SDL中视频渲染机制,同时提供尽可能直接的YUV渲染方法。
相关代码可以从我的git下载,url如下:https://git.oschina.net/Tocy/SampleCode.git,位于TocySDL2VisualTutorial目录下。
Beginning SDL 2.0(5) 基于MFC和SDL的YuvPlayer的更多相关文章
- Beginning SDL 2.0(4) YUV加载及渲染
本文主要内容是基于的“Beginning SDL 2.0(3) SDL介绍及BMP渲染”(以下简称BS3)基础上,将BMP加载及渲染修改为YUV420或I420的原始视频格式.阅读完本部分内容相信你可 ...
- Beginning SDL 2.0(3) SDL介绍及BMP渲染
SDL是一个跨平台的多媒体库.为了实现跨平台,SDL提供了一个简单的界面库抽象,比如提供了SDL_Window用于表示窗口句柄,SDL_Surface.SDL_Texture.SDL_Renderer ...
- Beginning SDL 2.0(2) TwinklebearDev SDL 2.0 Tutorial
本文整理并简要介绍了TwinklebearDev SDL 2.0 Tutorial相关内容(以下简称TDSDLTutorial). 这是作为我学习并了解SDL2.0功能一篇学习总结. TDSDLTut ...
- 基于MFC和opencv的FFT
在网上折腾了一阵子,终于把这个程序写好了,程序是基于MFC的,图像显示的部分和获取图像的像素点是用到了opencv的一些函数,不过FFT算法没有用opencv的(呵呵,老师不让),网上的二维的FFT程 ...
- 基于MFC的socket编程(异步非阻塞通信)
对于许多初学者来说,网络通信程序的开发,普遍的一个现象就是觉得难以入手.许多概念,诸如:同步(Sync)/异步(Async),阻塞(Block)/非阻塞(Unblock)等,初学者往往迷惑不清, ...
- SDL 开发实战(二):SDL 2.0 核心 API 解析
在上一篇文章 SDL 开发实战(一):SDL介绍及开发环境配置 中,我们配置好了SDL的开发环境,并成功运行了SDL的Hello World 代码.但是可能大部分人还是读不太明白具体Hello Wol ...
- 基于MFC开发的指纹识别系统.
MFC-FingerPrint 基于MFC开发的指纹识别系统. 效果图如下: 在第12步特征入库中,会对当前指纹的mdl数据与databases中所有的mdl进行对比,然后返回识别结果. 一.载入图像 ...
- 最全的基于MFC的ActiveX控件开发教程
浏览器插件之ActiveX开发(一) 一般的Web应用对于浏览器插件能不使用的建议尽量不使用,因为其涉及到安全问题以及影响用户安装(或自动下载注册安装)体验问题.在有特殊需求(如涉及数据安全的金融业务 ...
- 基于MFC的ActiveX控件开发教程------------浏览器插件之ActiveX开发
浏览器插件之ActiveX开发(一) 一般的Web应用对于浏览器插件能不使用的建议尽量不使用,因为其涉及到安全问题以及影响用户安装(或自动下载注册安装)体验问题.在有特殊需求(如涉及数据安全的金融业务 ...
随机推荐
- CSS特效(弧光效果)
代码很简单, 根据需要修改标签class和弧光效果的宽高位置就可以了. <!DOCTYPE html> <html lang="en"> <head& ...
- lua lua_settable
void lua_settable (lua_State *L, int index); Does the equivalent to t[k] = v, where t is the value a ...
- Redability
在写爬虫进行舆情分析.情感分析等内容时,我们经常爬取博客.新闻等长篇大论的文章,在一个网页上,根据网页布局,人眼可以很明确的确定哪些元素是文章主体.人眼既然能够识别,那么机器必然也是能够识别的.如果给 ...
- codeblocks编译pthread问题
默认的编译选项是没有pthread的,所以要自己添加: 参考:http://hi.baidu.com/u_soa/item/9d6cc40b7e9d76eb3499024d 错误: undefined ...
- ipsec在企业网中的应用(IKE野蛮模式)(转)
from:http://lulu1101.blog.51cto.com/4455468/817954 ipsec在企业网中的应用(IKE野蛮模式) 案例: 本实验采用华为三台F100防火墙,和一台s3 ...
- golang 学习笔记 ---make/copy/append
package main import ( "fmt" ) func main() { a := [...]int{0, 1, 2, 3, 4, 5, 6, 7} s := mak ...
- 豆瓣上9分以上的IT书籍-编程技术篇
在豆瓣上9分以上的IT书籍-编程语言篇中,收集了很多优秀的编程语言书籍,也得到了不少读者的喜欢.不过也有一些读者留言说某某书为什么没有,一种是因为某些书并不算讲某种编程语言的,一种是由于豆瓣9分以上这 ...
- 如何在osx的终端下使用字典
因为各种原因我经常要在osx上查英文单词,在osx系统下,查字典其实是一件非常优雅的事情,三指轻触,简单快速.在terminal中其实也是这样,3指轻触需要查询的单词,释义一触即发,用户体验非常好.不 ...
- ES6--变量的声明及解构赋值
ES6的目标是使得JavaScript语言能够用来编写大型的复杂的应用程序.成为企业级开发语言:该标准已于2015年6月17日正式公布. 可是真正的普及我觉得还得须要一段时间.然而这并非理由让我们 ...
- 主流磁盘接口比较(SATA/SCSI/SAS/FC)[转]
数据越来越多,用户对存储容量的要求是越来越高.作为数据存储最基本的介质——硬盘,其种类也越来越多.面对市场上纷繁复杂的硬盘,用户又该如何选择呢?本文就对SATA.FC.SAS三种硬盘进行了比较,希望能 ...