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应用对于浏览器插件能不使用的建议尽量不使用,因为其涉及到安全问题以及影响用户安装(或自动下载注册安装)体验问题.在有特殊需求(如涉及数据安全的金融业务 ...
随机推荐
- 关于Ubantu下使用cshell的问题解决
在一个新创建的目录下使用cshell,直接在/etc/passwd 下对应的用户后 bash改为csh; 之后运行发现报错,后来查找发现/bin下没有csh执行脚本,之后安装csh;; 参考了http ...
- Ubuntu下架设FTP服务器(转)
Ubuntu下架设FTP服务器 Linux下提供了很多的ftp服务器,这里我选用了安全,快速,简单的vsftpd作为FTP服务器.本文是我在自己的Ubuntu 10.10 -32 位系统下搭建的.搭建 ...
- linux 文件系统工作原理
转:<http://linuxperf.com/?p=153> 一.概述 文件系统要解决的一个关键问题是怎样防止掉电或系统崩溃造成数据损坏,在此类意外事件中,导致文件系统损坏的根本原因在于 ...
- 【Struts2】Struts2框架的搭建
1,Struts2简介 struts1和struts2都是由Apache组织发布的,但是比较有趣的是struts2和struts1并没有“血缘关系”.在Apache发布struts1之后,当时是还是非 ...
- 【struts2】值栈(后篇)
在值栈(前篇)我们学习了值栈的基本知识,接下来,来看看在程序中具体如何使用值栈. 1 ActionContext的基本使用 1.1 如何获取? 要获取ActionContext有两个基本的方法,如果在 ...
- WCF入门教程(四)通过Host代码方式来承载服务 一个WCF使用TCP协议进行通协的例子 jquery ajax调用WCF,采用System.ServiceModel.WebHttpBinding System.ServiceModel.WSHttpBinding协议 学习WCF笔记之二 无废话WCF入门教程一[什么是WCF]
WCF入门教程(四)通过Host代码方式来承载服务 Posted on 2014-05-15 13:03 停留的风 阅读(7681) 评论(0) 编辑 收藏 WCF入门教程(四)通过Host代码方式来 ...
- 跟我学SharePoint 2013视频培训课程——使用垃圾箱(5)
课程简介 第5天,在SharePoint 2013中 使用垃圾箱 视频 SharePoint 2013 交流群 41032413
- Win7 64bit 安装VisualSVN出现报错:Servic 'VisualSVN Server' failed to start.解决的方法
问题描写叙述: Win7 64bit 安装VisualSVN时出现报错: Servic 'VisualSVN Server' failed to start.Please check VisualSV ...
- GANS 资料
https://blog.csdn.net/a312863063/article/details/83512870 目 录第一章 初步了解GANs 3 1. 生成模型与判别模型. 3 2. 对抗网络思 ...
- 利用nginx搭建RTMP视频点播、直播、HLS服务器
开发环境 Ubuntu 14.04 server nginx-1.8.1 nginx-rtmp-module nginx的服务器的搭建 安装nginx的依赖库 sudo apt-get update ...