DirectShow学习之二做一个简单的媒体文件播放器

2011年12月04日 分类:学习笔记C++

或许你现在还不知道directshow是什么东西,不过你应该知道它可以用来播放电影,ok,我们 就做一个简单的媒体文件播放器。什么?我还不知道ds的原理呢,我不懂com呢,我甚至没有什么windows程序的经验呢。都没关系,我们先有一点成就 感,再逐步深入。愣是够!

首先,我们需要找两个叫做CDXGraph.h和CDXGraph.cpp的文件。在DX90SDK 安装目录搜索了一遍,没有,在网上搜索一把,嗯,有很多,原来是一个“使用directShow播放mpeg的基类”,是“自定义的Filter Graph Manager的一个封装类”,(不知道Filter Graph Manager?没关系),作者不详,本文后面将列出。

第一步:(本文使用vs2005)建立一个名叫MyPlayer的MFC dialog based工程。

第二步:配置链接库,假设当前工程是debug版本(这都不清楚的话,嗯,还是慢慢来,先掌握基础好了),则打开 project->properties->configuration properties->Linker ->Input->Additional Dependencies增加strmbasd.lib uuid.lib winmm.lib(以后不会说这么详细了)。

第三步:COM库的初始化和清除,直接列出代码了(把系统的注释去掉了)

BOOL CMyPlayerApp::InitInstance()
{
INITCOMMONCONTROLSEX InitCtrls;
InitCtrls.dwSize = sizeof(InitCtrls);
InitCtrls.dwICC = ICC_WIN95_CLASSES;
InitCommonControlsEx(&InitCtrls);
CWinApp::InitInstance();
AfxEnableControlContainer();
SetRegistryKey(_T("Local AppWizard-Generated Applications"));

CoInitialize(NULL);//这是我们要增加的,com库的初始化

CMyPlayerDlg dlg;
m_pMainWnd = &dlg;
INT_PTR nResponse = dlg.DoModal();
if (nResponse == IDOK)
{
}
else if (nResponse == IDCANCEL)
{
}
return FALSE;
}
//友情提示,下面这个函数系统并没有自动添加,在CMyPlayerApp类
//的properties里单击overrides可找到,发现很多用惯了vc6的“老程序员”
//没有了向导很不习惯呢。
int CMyPlayerApp::ExitInstance()
{
CoUninitialize();//这是我们要增加的,清除com库
return CWinApp::ExitInstance();
}

第四步:界面开发。

在对话框上放置一个Picture Control,(此类静态控件默认ID为IDC_STATIC,如果在程序中不对它们进行控制,不用修改ID,如果要控制,必须修改其ID号,才可对它 进行代码控制,包括添加变量),修改ID为IDC_VIDEO_WINDOW,关联一个Control变量mVideoWindow。

添加一个“打开”和“播放”按钮。

第五步:添加CDXGraph.h和CDXGraph.cpp到工程。首先把这两个文件复制到工程源代码所在的地方,然后打开Project->Add Existing Item把它们真正加到工程。

第六步: MyPlayerDlg.h 增加include,如下:

#include "afxwin.h"
#include <streams.h>
#include "CDXGraph.h"
再增加两个成员变量:
CDXGraph * mFilterGraph; // Filter Graph封装
CString mSourceFile; // 源文件
并做一些初始化工作:
CMyPlayerDlg::CMyPlayerDlg(CWnd* pParent /*=NULL*/)
: CDialog(CMyPlayerDlg::IDD, pParent)
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
mFilterGraph = NULL;
mSourceFile = "";
}

此时F7编译,如果出现问题,请参考上一篇DirectShow学习之一在vs2005中配置directshow开发环境 。

第七步:添加成员函数CreateGraph和DestroyGraph,编写代码如下:

void CMyPlayerDlg::CreateGraph(void)
{
DestroyGraph();
mFilterGraph = new CDXGraph();
if (mFilterGraph->Create())
{
mFilterGraph->RenderFile(mSourceFile);
mFilterGraph->SetDisplayWindow(mVideoWindow.GetSafeHwnd());
mFilterGraph->SetNotifyWindow(this->GetSafeHwnd());
mFilterGraph->Pause();
}
}

void CMyPlayerDlg::DestroyGraph(void)
{
if (mFilterGraph)
{
mFilterGraph->Stop();
mFilterGraph->SetNotifyWindow(NULL);
delete mFilterGraph;
mFilterGraph = NULL;
}
}

第八步:实现“打开”功能。 添加一个实现函数:

void CMyPlayerDlg::OnBnClickedButtonOpen()
{
// TODO: Add your control notification handler code here
CString strFilter = "MPEG File (*.mpg;*.mpeg)|*.mpg;*.mpeg|";
strFilter += "AVI File (*.avi)|*.avi|";
strFilter += "Mp3 File (*.mp3)|*.mp3|";
strFilter += "Wave File (*.wav)|*.wav|";
strFilter += "All Files (*.*)|*.*|";
CFileDialog dlgOpen(TRUE, NULL, NULL, OFN_PATHMUSTEXIST | OFN_HIDEREADONLY,
strFilter, this);
if (IDOK == dlgOpen.DoModal())
{
mSourceFile = dlgOpen.GetPathName();
CreateGraph();
}
}

此时,编译运行程序,打开一个mpeg文件,应该能显示第一帧的画面了。

第九步:实现“播放”功能。 添加一个实现函数:

void CMyPlayerDlg::OnBnClickedButtonPlay()
{
// TODO: Add your control notification handler code here
if (mFilterGraph)
{
mFilterGraph->Run();
}
}

基本上,我们现在就可以用它来看电影了,呵呵。不过,还是有点小问题,当画面被其它窗口挡住之后,再重新出来,画面没了,不能更新,这是小问 题:在OnInitDialog()里的return TRUE;前加一句mVideoWindow.ModifyStyle(0, WS_CLIPCHILDREN);然后添加主对话框的WM_ERASEBKGND消息响应:

BOOL CMyPlayerDlg::OnEraseBkgnd(CDC* pDC)
{
// TODO: Add your message handler code here and/or call default
CRect rc;
mVideoWindow.GetWindowRect(&rc);
ScreenToClient(&rc);
pDC->ExcludeClipRect(&rc);
return CDialog::OnEraseBkgnd(pDC);
}

尽管太过于简陋,但是,毕竟是一个播放器了,下一篇将详细分析程序的实现过程,重点是CDXGraph类。

附:CDXGraph.h和CDXGraph.cpp

CDXGraph

作者:wuyuan 本文来自Wuyuan's Blog 转载请注明,谢谢! 文章地址: https://wuyuans.com/2011/12/directshow-learning-media-player