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应用对于浏览器插件能不使用的建议尽量不使用,因为其涉及到安全问题以及影响用户安装(或自动下载注册安装)体验问题.在有特殊需求(如涉及数据安全的金融业务 ...
随机推荐
- 【DeepLearning】Exercise:Softmax Regression
Exercise:Softmax Regression 习题的链接:Exercise:Softmax Regression softmaxCost.m function [cost, grad] = ...
- Rust 之 cargo(项目构建和包管理工具)
如果食用cargo来进行项目构建: 1. 执行 cargo new hello_cargo --bin ,执行完上面的操作之后,我们切换到hell_cargo目录下,可以看到一个文件(Cargo.to ...
- Android事件总线还能怎么玩?
作者简介:何红辉,Android工程师,现任职于友盟. 顾名思义,AndroidEventBus是一个Android平台的事件总线框架,它简化了Activity.Fragment.Service等组件 ...
- AdapterView的使用与getView函数详解
作者:徐冉.文章首发在他的个人博客. ) AdapterView&Adapter家族 adapterview就是和数据有关的控件,如listview,gridview,spinnerview等 ...
- 【struts2】Struts2的异常处理
在Action中execute方法声明为:public String execute() throws Exception,这样,Action可以抛出任何Exception. 1)自己实现异常处理 我 ...
- React icon bak
- SQL 给字符串补0
第一种方法: right('00000'+cast(@count as varchar),5) 其中'00000'的个数为right函数的最后参数,例如这里是5,所以有5个0 @count就是被格式化 ...
- 转:ECharts图表组件入门教程之Theme:ECharts图表的皮肤是什么?如何给图表换主题(皮肤)Theme?
一.什么是ECharts图表的皮肤(主题)? 针对这个问题我只能这样回答,ECharts图表的主题(皮肤)就犹如人的衣服一样,是用来衬托和渲染主体,使其变得更加美观好看的目的.你去过ECharts图表 ...
- 路径不对 导致FileNotFoundError: [WinError 2] 系统找不到指定的文件, 问题解决办法
执行python + selenium 代码 from selenium import webdriver driver = webdriver.Chrome("D:\AutoConf\bi ...
- 请通过vim练习:vim vimtutor
vim vimtutor ================================================================================ W e l ...