作者:liguisen
或许你现在还不知道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);
}
尽管太过于简陋,但是&