|
DirectShow是微软公司提供的一套在Windows平台上进行流媒体处理的开发包,与DirectX开发包一起发布。DirectShow为多媒体的捕捉和回放提供了强有力的支持。运用DirectShow我们可以很方便地从支持WDM驱动模型的采集卡上捕获数据,并且进行相应的后期处理乃至存储到文件中。它广泛地支持各种媒体格式,包括Asf、Mpeg、Avi、Dv、Mp3、Wave等等,使得多媒体数据的回放变得轻而易举。 另外,DirectShow还集成了DirectX其它部分(比如DirectDraw、DirectSound)的技术,直接支持DVD播放,视频的非线性编辑,以及与数据摄像机的交换。更值得一提的是,DirectShow提供的是一种开放式的开发环境,我们可以根据自己的需要定制自己的组件。
本文将对DirectShow的应用进行入门级的介绍。
入门
DirectShow使用一种叫做Filter Graph的模型来管理整个数据流的处理过程,参与数据流处理的各个功能模块称做Filter,各个Filter在Filter Graph中按一定的顺序连成一条流水线协调工作,完成一些相对独立的功能,如Filter可以完成如下的一些功能:
读文件
从视频设备中获取视频
对视频流进行解码
将数据送往声卡或显卡
每个Filter都有输入端和输出端,例如一个MPEG-1解码Filter它的输入是MPEG编码的流数据,它的输出端是一解码过的流数据。DirectShow正是通过将不同的Filter连接在一起完成特定的功能的,我们将这些Filter的连接叫做Filter Graph,如下图A给出是播放AVI的Filter Graph:

图A 播放AVI文件的Graph Filter图
上图中每个模块分别代表了不同的Filter,媒体文件Filter从硬盘读取AVI文件,AVI分离Filter将文件分离为音频流和视频流,AVI解码Filter对视频流进行解码并送往Video表现Filter,由后者将各帧在显示器上显示,默认的DirectSound设备用DirectSound将音频流输出。
我们的应用并不需要对这当中的所以的数据流进行管理,在DirectShow提供一个称做Filter Graph管理器的高级组件。在我们的应用中只需要调用它的API即可,如Run、Stop等,如果你想对其中的数据流做更进一步的控制,你可以对这些Filter直接通过COM接口进行存取。
Filter Graph管理器同时也提供了另一个功能:应用程序可以通过管理器控制Filter Graph如何生成。
DirectShow应用
从广义上说,所有的DirectShow应用都必须完成三件事情,如下图B所示:

图B
1. 生成Filter Graph管理器的一个实例。
2. 利用Filter Grapth实例生成Filter Graph,具体应该由哪些Filter组成Filter Graph视我们的应用的需要而定。
3. 通过对Filter Graph管理器的方法调用和来自Filter Graph的消息的响应Filter Graph和数据流进行控制。
DirectShow是基于COM的,Filter Graph管理器和Filter都是COM对象 ,在开始着手之前你应该对COM有个基本的认识。
下面让我们着手开始做一个简单的DirectShow应用,在这个应用中我们实现这样的功能:打开一个媒体文件,并对其进行播放。
设置环境
在利用DirectShow进行流媒体的处理之前,必须正确安装DirectX的SDK,DirectX SDK可以到微软的网站上下载,目前,DirectX最新版本为9.0。
在正确安装好DirectX SDK后,我们必须设置DirectX SDK的头文件和库文件,使其在Visual Studio的搜索路径内。对于Visual Studio .NET 2003可如下进行设置:菜单→工具→选项→项目→VC++目录,在包含文件中加入D:\DXSDK\Include,在库文件中加入D:\DXSDK\lib(我的SDK的安装路径是D:\DXSDK):

头文件
| 文件名称 |
描述 |
| Dshow.h |
所有的DirectShow应用都必须包含 |
库文件
| 文件名称 |
描述 |
| Strmiids.lib |
此库文件中导出类标识(CLSID)和接口标识(IID),所有的DirectShow应用都必须包含此文件。 |
| Quartz.lib |
此库文件中导出函数AMGetErrorText,如果你的程序中调用了此函数,则必须包含此库文件。 |
zmbbs=1; 开始工程
打开Visual Studio .NET 2003,文件→新建→项目
1.生成基于MFC的应用程序,名称PlayWnd。

2.选择应用程序类型基于对话框,点击完成。
3.设置工程属性
项目→PlayWnd属性→配置属性→链接器→输入→附加依赖项,添加库文件Strmiids.lib 和Quartz.lib。
由于Dshow.h头文件是在任何DirectShow工程中都要用到的,因此我们stdafx.h加入如下行:
4.设计对话框,如下:

| IDC_STATIC |
控件类型 |
Static Text |
| Caption |
媒体文件名: |
| IDC_MEDIAFILE_EDIT |
控件类型 |
Edit Control |
| IDC_BROWSE_BUTTON |
控件类型 |
Button |
| Caption |
浏览 |
| IDC_VW_FRAME |
控件类型 |
Picture Control |
| Type |
Rectangle |
| IDC_PLAY_BUTTON |
控件类型 |
Button |
| Caption |
播放 |
| IDC_PAUSE_BUTTON |
控件类型 |
Button |
| Caption |
暂停 |
| IDCANCEL |
控件类型 |
Button |
| Caption |
关闭 |
5.COM的初始化和卸载,
修改PlayWnd.cpp添加初始化代码(加入的代码用粗黑体表示,下同)
BOOL CPlayWndApp::InitInstance()
{
// 如果一个运行在 Windows XP 上的应用程序清单指定要
// 使用 ComCtl32.dll 版本 6 或更高版本来启用可视化方式,
//则需要 InitCommonControls()。否则,将无法创建窗口。
InitCommonControls();
//初始化COM接口
HRESULT hr = CoInitialize(NULL);
if (FAILED(hr))
{
TRACE( "ERROR - Could not initialize COM library.\n");
return FALSE;
}
CWinApp::InitInstance();
AfxEnableControlContainer(); |
修改PlayWnd.cpp添加卸载COM代码,注意需要对虚函数ExitInstance进行重载
int CPlayFileApp::ExitInstance()
{
// TODO: 在此添加专用代码和/或调用基类
//关闭COM
CoUninitialize();
return CWinApp::ExitInstance();
}
6.定义媒体控制成员变量
修改PlayWndDlg.h如下:
protected:
HICON m_hIcon;
// 生成的消息映射函数
virtual BOOL OnInitDialog();
afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
DECLARE_MESSAGE_MAP()
//和媒体控制相关的成员变量
private:
IGraphBuilder *m_pGraph; //IGraphBuilder 接口提供了生成Filter Graph相//关的方法
IMediaControl *m_pMediaControl;
//IMediaControl 接口提供了控制流经Filter //Graph数据流的相关方法
IMediaEventEx *m_pEvent;
//IMediaEventEx 继承自IMediaEvent,提供了从
//Filter Graph 管理器获取事件消息的方法
IMediaSeeking *m_pMediaSeeking; //IMediaSeeking 提供了控制流的播放位置和播放//速度的方法
CString m_strMediaFile; //当前播放的媒体文件的名称
BOOL m_isPlaying; //当前的播放状态
}; |
在CPlayWndDlg的构造函数中添加初始化代码。
CPlayWndDlg::CPlayWndDlg(CWnd* pParent /*=NULL*/)
: CDialog(CPlayWndDlg::IDD, pParent)
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
m_pGraph = NULL;
m_pMediaControl = NULL;
m_pEvent = NULL;
m_pMediaSeeking = NULL;
m_strMediaFile = "";
m_isPlaying = FALSE;
} |
由于一些和窗体控制有关的初始化代码不能放在构造函数中进行,我们将其放在CPlayWndDlg::OnInitDialog()中,我们必须在此必须对CPlayWndDlg添加WS_CLIPCHILDREN 的Style,因为在我们的应用中把视频窗体作为CPlayWndDlg的一个子窗体来使用的,这是非常重要的,许多开发人员在刚开始使用DirectShow时,父窗体的Style没有设置正确,造成视频不能正确显示,代码如下:
// 设置此对话框的图标。当应用程序主窗口不是对话框时,框架将自动
// 执行此操作
SetIcon(m_hIcon, TRUE); // 设置大图标
SetIcon(m_hIcon, FALSE); // 设置小图标
// TODO: 在此添加额外的初始化代码
ModifyStyle(0, WS_CLIPCHILDREN);
((CEdit*)GetDlgItem(IDC_MEDIAFILE_EDIT))->SetReadOnly(TRUE);
return TRUE; // 除非设置了控件的焦点,否则返回 TRUE
}
|
添加相应的清除代码,重载CPlayWndDlg的DestoryWindow方法,如下:
BOOL CPlayWndDlg::DestroyWindow()
{
// TODO: 在此添加专用代码和/或调用基类
if(m_pGraph)
m_pGraph->Release();
if(m_pMediaControl)
m_pMediaControl->Release();
if(m_pEvent)
m_pEvent->Release();
if(m_pMediaSeeking)
m_pMediaSeeking->Release();
m_pGraph = NULL;
m_pMediaControl = NULL;
m_pEvent = NULL;
m_pMediaSeeking = NULL;
return CDialog::DestroyWindow();
} |
|
7.修改CPlayWndDlg::OnPaint(),由于现在视频显示区域必须由我们自己进行重画:
void CPlayWndDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // 用于绘制的设备上下文
SendMessage(WM_ICONERASEBKGND, reinterpret_cast <WPARAM>(dc.GetSafeHdc()), 0);
// 使图标在工作矩形中居中
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect( &rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// 绘制图标
dc.DrawIcon(x, y, m_hIcon);
}
else
{
if (m_isPlaying == FALSE)
{
CClientDC dc(GetDlgItem(IDC_VW_FRAME));
dc.SetBkColor(RGB(0,0,0));
CRect rc;
GetDlgItem(IDC_VW_FRAME)- >GetClientRect(rc);
//ClientToScreen(rc);
dc.FillRect(rc, CBrush::FromHandle((HBRUSH)GetStockObject(BLACK_BRUSH)));
GetDlgItem(IDC_VW_FRAME)- >Invalidate();
}
CDialog::OnPaint();
}
} |
添加浏览、播放、暂停、关闭四按钮的相应事件响应函数,同时在CPlayWndDlg中添加如下四个私有方法:
void MoveVideoWindow(void);
void CleanUp(void);
BOOL Stop(void);
BOOL Play(void); |
上述方法的实现如下:
// IDC_VW_FRAME控件Picture Control主要作用是控制Vedio Window的显示位置
void CPlayWndDlg::MoveVideoWindow(void)
{
IVideoWindow* pVideoWinow = NULL;
if(m_pGraph)
{
m_pGraph->QueryInterface(IID_IVideoWindow, (void **)&pVideoWinow);
CRect rc;
GetDlgItem(IDC_VW_FRAME)->GetWindowRect(rc);
ScreenToClient(rc);
pVideoWinow->SetWindowPosition(rc.left, rc.top, rc.Width(), rc.Height());
pVideoWinow->Release();
pVideoWinow = NULL;
}
}
void CPlayWndDlg::CleanUp(void)
{
long levCode;
IVideoWindow *pVidWin = NULL;
if(!m_pGraph)
return;
m_pGraph->QueryInterface(IID_IVideoWindow, (void **)&pVidWin);
m_pEvent->WaitForCompletion(INFINITE, &levCode);
pVidWin->put_Visible(OAFALSE);
pVidWin->Release();
m_pMediaSeeking->Release();
m_pMediaControl->Release();
m_pEvent->Release();
m_pGraph->Release();
m_pMediaSeeking = NULL;
m_pMediaControl = NULL;
m_pEvent = NULL;
m_pGraph = NULL;
UpdateData(FALSE);
CClientDC dc(GetDlgItem(IDC_VW_FRAME));
dc.SetBkColor(RGB(0,0,0));
CRect rc;
GetDlgItem(IDC_VW_FRAME)->GetClientRect(rc);
ClientToScreen(rc);
dc.FillRect(rc, CBrush::FromHandle((HBRUSH)GetStockObject(BLACK_BRUSH)));
Invalidate();
}
BOOL CPlayWndDlg::Stop(void)
{
IVideoWindow *pVidWin = NULL;
HRESULT hr;
if(m_pMediaControl)
{
LONGLONG pos = 0;
hr = m_pMediaControl->Stop();
hr = m_pMediaSeeking->SetPositions(&pos, AM_SEEKING_AbsolutePositioning ,&pos, AM_SEEKING_NoPositioning);
m_pGraph->QueryInterface(IID_IVideoWindow, (void **)&pVidWin);
pVidWin->put_Visible(OAFALSE);
m_isPlaying = FALSE;
GetDlgItem(IDC_PLAY_BUTTON)->EnableWindow(TRUE);
GetDlgItem(IDC_PAUSE_BUTTON)->EnableWindow(FALSE);
pVidWin->Release();
long levCode;
m_pEvent->WaitForCompletion(INFINITE, &levCode);
m_pMediaControl->Release();
return TRUE;
}
return FALSE;
}
BOOL CPlayWndDlg::Play(void)
{
// 运行
IVideoWindow *pVidWin = NULL;
if(m_pGraph)
{
m_pGraph->QueryInterface(IID_IVideoWindow, (void **)&pVidWin);
pVidWin->put_Visible(OATRUE);
m_pGraph->QueryInterface(IID_IMediaControl, (void **)&m_pMediaControl);
m_pMediaControl->Run();
m_isPlaying = TRUE;
GetDlgItem(IDC_PLAY_BUTTON)->EnableWindow(FALSE);
GetDlgItem(IDC_PAUSE_BUTTON)->EnableWindow(TRUE);
return TRUE;
}
return FALSE;
} |
浏览、播放、暂停、关闭四按钮的相应事件响应函数如下:
void CPlayWndDlg::OnBnClickedBrowseButton()
{
CFileDialog dlgFile(TRUE, NULL, NULL,
OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT,
"Movie Files (*.avi;*.mpg;*.mpeg) |\
*.avi;*.mpg;*.mpeg |\
Audio Files (*.wav;*mp3;*.mpa;*.mpu;*.au) |\
*.wav;*.mp3;*.mpa;*.mpu;*.au |\
Midi Files (*.mid;*.midi;*.rmi) |\
*.mid;*.midi;*.rmi| | ", this);
if(dlgFile.DoModal() == IDOK)
{
m_strMediaFile = dlgFile.GetPathName();
GetDlgItem(IDC_MEDIAFILE_EDIT)- >SetWindowText(m_strMediaFile);
}
else
return;
CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC, IID_IGraphBuilder, (void **) &m_pGraph);
HRESULT hr = m_pGraph- >RenderFile(CA2W(m_strMediaFile), NULL);
if(FAILED(hr))
{
char szMsg[200];
AMGetErrorText(hr, szMsg, sizeof(szMsg));
AfxMessageBox(szMsg);
}
//指定父窗体
IVideoWindow* pVidWin = NULL;
m_pGraph- >QueryInterface(IID_IVideoWindow, (void **)&pVidWin);
pVidWin- >put_Owner((OAHWND)m_hWnd);
pVidWin- >put_WindowStyle(WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN);
CRect rc;
GetDlgItem(IDC_VW_FRAME)- >GetWindowRect(rc);
ScreenToClient(rc);
pVidWin- >SetWindowPosition(rc.left, rc.top, rc.Width(), rc.Height());
// 注意此处Filter Graph Manager的事件以WM_GRAPHNOTIFY发出(用户定义的消息).
m_pGraph- >QueryInterface(IID_IMediaEventEx, (void **)&m_pEvent);
m_pEvent- >SetNotifyWindow((OAHWND)m_hWnd, WM_GRAPHNOTIFY, 0);
// 设置Seeking
m_pGraph- >QueryInterface(IID_IMediaSeeking, (void **)&m_pMediaSeeking);
}
void CPlayWndDlg::OnBnClickedPlayButton()
{
Play();
}
void CPlayWndDlg::OnBnClickedPauseButton()
{
m_pMediaControl- >Pause();
m_isPlaying = TRUE;
GetDlgItem(IDC_PLAY_BUTTON)- >EnableWindow(TRUE);
GetDlgItem(IDC_PAUSE_BUTTON)- >EnableWindow(FALSE);
}
void CPlayWndDlg::OnBnClickedCancel()
{
// TODO: 在此添加控件通知处理程序代码
CleanUp();
OnCancel();
} |
8.添加对WM_GRAPHNOTIFY消息,及其响应函数
在PlayWndDlg添加消息ID定义:
| #define WM_GRAPHNOTIFY WM_USER + 101 |
在PlayWndDlg.h中,代码如下:
// 实现
protected:
HICON m_hIcon;
// 生成的消息映射函数
virtual BOOL OnInitDialog();
afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
DECLARE_MESSAGE_MAP()
afx_msg HRESULT OnGraphNotify(WPARAM wParam,LPARAM lParam);
private:
IGraphBuilder *m_pGraph;
IMediaControl *m_pMediaControl;
IMediaEventEx *m_pEvent; |
PlayWndDlg.cpp,如下:
ON_BN_CLICKED(IDC_BROWSE_BUTTON, OnBnClickedBrowseButton)
ON_BN_CLICKED(IDC_PLAY_BUTTON, OnBnClickedPlayButton)
ON_BN_CLICKED(IDC_PAUSE_BUTTON, OnBnClickedPauseButton)
ON_BN_CLICKED(IDCANCEL, OnBnClickedCancel)
ON_MESSAGE(WM_GRAPHNOTIFY, OnGraphNotify)
END_MESSAGE_MAP() |
实现如下:
当下比较流行的即时通信工具,比如MSN,QQ等都实现了视音频的功能,通过视频,音频,我们可以更好的和朋友通过网络进行沟通,本文通过DirectShow技术模拟QQ实现了视频和音频的采集,传输,基本实现了QQ的视音频聊天的功能。
网络视音频系统主要功能就在于视音频的采集,网络传输两个方面,通过Video Capture系列API函数,你就可以轻松的搞定视频捕捉,但是对于视频的网络传输,则要费一番功夫了。 对于视音频数据的传输,只简单地使用数据报套接字传输音视频数据是不可行的,还必须在UDP层上采用RTP(实时传输协议)和RTCP(实时传输控制协议)来改善服务质量。实时传输协议提供具有实时特征的、端到端的数据传输服务。我们在音视频数据前插入包含有载荷标识、序号、时间戳和同步源标识符的RTP包头,然后利用数据报套接字在IP网络上传输RTP包,以此改善连续重放效果和音视频同步。实时传输控制协议RTCP用于RTP的控制,它最基本的功能是利用发送者报告和接收者报告来推断网络的服务质量,若拥塞状况严重,则改用低速率编码标准或降低数据传输比特率,以减少网络负荷,提供较好的Q.S保证。
Directshow对于音视频的采集提供了很好的接口,利用ICaptureGraphBuilder2接口可以很轻松的建立起视频捕捉的graph图,通过枚举音频设备Filter,也可以很轻松的实现音频的捕捉,有点麻烦的是音视频数据的传输,我们可以自己封装RTP和RTCP的协议,来自己实现一个filter,用来发送和接收音视频数据,当然了Directshow也提供了一组支持使用RTP协议的网络传输多媒体流的Filters。你也完全可以用Directshow提供的RTP系列的filter实现数据的传输。
下面分析一下这些RTP Filters。
新定义的Filter包括 RTP Source Filter ,RTP Render Filter,RTP Demux Filter,RTP Receive Playload Handler (RPH) filter,RTP Send Payload (SPH) filter,使用这5个filter构建一个通过RTP协议传输音视频数据的Graph是没有问题的。
RTP Source filter被用来从一个单独的RTP会话中接收RTP和RTCP包。这个filter提供一个指定发送给其它主机RTCP接收器报告和指定网络地址和端口接口来接收RTP会话的接口。
RTP Rend filter是用来将数据发到网络上的一个filter,这个filter也提供了和RTP source Filter 类似的接口。
RTP Demux filter用来多路分离来自 RTP Source filter的RTP 包,这个filter有一个或者多个输出的pin。这个Filter提供了如何控制多路分离和如何分配到特定输出pin的接口。
RTP RPH Filter 是用来网络过来的RTP包还原成原来的数据格式,主要支持H.261,H.263,Indeo,G.711,G.723和G.729和常见的多种音视频负载类型。
RTP SPH filter则和RPH filter的功能相对,它的任务是将音视频 压缩filter输出的 数据分解为RTP包,它提供的接口有指定最大生成包大小和pt值。
下面我们看看如何用这些filter来搭建我们采集和传输的graph图。
图1和图2展示了DirectShow RTP中定义的filters如何运用。图1是一个采集本地多媒体数据并使用RTP协议通过网络发送的filter graph。它包含一个输出原始视频帧的视频采集filter,紧跟一个压缩帧的编码filter。一旦压缩,这些帧就会被发送到RTP SPH filter,分片打包,生成RTP包,对应的发送到 RTP Render filter,通过网络传输这些包。图2展现了一个filter graph,用来接收包含视频流RTP包,播放视频。这个graph由一个用来接收包的RTP Source filter,一个根据源和负载类型进行分类的RTP Demux filter,一个把RTP包转为压缩视频帧的RTP RPH filter组成。这些filter随后的是用来解压帧的解码filter,一个显示未压缩帧的渲染filter。
有了RTP filter的帮助我们就可以完成类似qq的功能了,可以实现在网络上进行视频和音频的交互了,下面我给出在网络上两个客户端A和B进行音频和视频交互的Graph图。这里我对图1和图2中的RTP filter进行了自己封装,将编解码filter直接封装到了RTP Source filter 和RTP Render filter中,这样Graph图就显得很简洁,RTP Source filter只是用来接收网络过来的音视频数据,然后将数据传递给客户程序,RTP Render filter则是将采集到的音视频数据发送到网络上的另一个客户端,编解码则的工作则封装到这两个filter之中。

图3 网络视频和音频交互的Graph图 |
如果你也想自己封装自己的Source 和Render filter,首先你要选择自己的编解码,视频编解码是选择H261,H263,还是 MEPG4,音频是选择G729还是G711,要首先确定好。选好编解码,封装的工作就简单了。
不多说了,下面看看我给出的代码吧。
首先要定义一下用到的四个RTP filter的CLSID。
static const GUID CLSID_FG729Render = { 0x3556f7d8, 0x5b5, 0x4015, { 0xb9, 0x40, 0x65, 0xb8, 0x8, 0x94, 0xc8, 0xf9 } }; //音频发送
static const GUID CLSID_FG729Source = { 0x290bf11a, 0x93b4, 0x4662, { 0xb1, 0xa3, 0xa, 0x53, 0x51, 0xeb, 0xe5, 0x8e } };//音频接收
static const GUID CLSID_FH263Source = { 0xa0431ccf, 0x75db, 0x463e, { 0xb1, 0xcd, 0xe, 0x9d, 0xb6, 0x67, 0xba, 0x72 } };//视频接收
static const GUID CLSID_FH263Render = { 0x787969cf, 0xc1b6, 0x41c5, { 0xba, 0xa8, 0x4e, 0xff, 0xa3, 0xdb, 0xe4, 0x1f } };//视频发送
//发送和接收音视频数据的filter
CComPtr< IBaseFilter > m_pAudioRtpRender ;
CComPtr< IBaseFilter > m_pAudioRtpSource ;
CComPtr< IBaseFilter > m_pVideoRtpRender ;
CComPtr< IBaseFilter > m_pVideoRtpSource ;
char szClientA[100];
int iVideoPort = 9937;
int iAudioPort = 9938;
//构建视频的graph图,并发送数据
CComPtr< IGraphBuilder > m_pVideoGraphBuilder; //视频图形管理器
CComPtr< ICaptureGraphBuilder2 > m_pVideoCapGraphBuilder;
CComPtr< IBaseFilter > m_pFilterVideoCap;
CComPtr< IVideoWindow > m_pVideoWindow;
CComPtr< IMediaControl > m_pVideoMediaCtrl ;
CComPtr< IBaseFilter > m_pVideoRenderFilter;
HRESULT CMyDialog::VideoGraphInitAndSend()
{
HRESULT hr;
hr =m_pVideoGraphBuilder.CoCreateInstance( CLSID_FilterGraph );
if(FAILED(hr))
return hr;
hr =m_pVideoCapGraphBuilder.CoCreateInstance( CLSID_CaptureGraphBuilder2);
if(FAILED (hr))
return hr;
m_pVideoCapGraphBuilder- >SetFiltergraph(m_pVideoGraphBuilder);
m_pVideoGraphBuilder- >QueryInterface(IID_IMediaControl, (void **)&m_pVideoMediaCtrl);
m_pVideoGraphBuilder- >QueryInterface(IID_IVideoWindow,(void**)&m_pVideoWindow)
FindDeviceFilter( &m_pFilterVideoCap,CLSID_VideoInputDeviceCategory);
if(m_pFilterVideoCap)
m_pVideoGraphBuilder- >AddFilter( m_pFilterVideoCap,T2W("VideoCap") ) ;
//创建预览的filter
hr = m_pRenderFilterVideo.CoCreateInstance(CLSID_VideoRenderer);
if(FAILED(hr))
return hr;
m_pVideoGraphBuilder- >AddFilter( m_pRenderFilterVideo, L"VideoRenderFilter" );
Connect(m_pFilterVideoCap ,m_pRenderFilterVideo) ;
//设置预览的窗口
CRect rc ;
GetClientRect(m_hOwnerWnd, &rc );
int iWidth = rc.right - rc.left ;
int iHeight = rc.bottom - rc.top ;
int iLeft, iTop;
if((iHeight*1.0)/(iWidth*1.0) >= 0.75)
{
//按宽度算
int tmpiHeight = iWidth*3/4;
iTop = (iHeight - tmpiHeight)/2;
iHeight = tmpiHeight;
iLeft = 0;
}
else
{
//按高度算
int tmpiWidth = iHeight*4/3;
iLeft = (iWidth - tmpiWidth)/2;
iWidth = tmpiWidth;
iTop = 0;
}
m_pVideoWindow- >put_Owner( (OAHWND) m_hPreviewWnd ) ;
m_pVideoWindow- >put_Visible( OATRUE );
m_pVideoWindow- >put_WindowStyle( WS_CHILD | WS_CLIPSIBLINGS ) ;
//连接到网络并发送
CComPtr < IRtpOption > pRenderOption;
CComPtr < IVideoOption > pVideoOption;
tagVideoInfo vif(160,120,24);
int t=((int)(m_iFrameRate/5)*5)+5;
vif.nBitCount=24;
vif.nWidth=160;
vif.nHeight=120;
hr = ::CoCreateInstance(CLSID_FH263Render, NULL, CLSCTX_INPROC, IID_IBaseFilter, (void **) &m_pVideoRtpRender);
if(FAILED(hr))
return hr;
m_pVideoRtpRender- >QueryInterface(IID_IJRTPOption, (void**)&pRenderOption);
m_pVideoRtpRender- >QueryInterface(IID_IVideoOption,(void**)&pVideoOption);
pVideoOption- >SetProperty(&vif);
pVideoOption- >SetSendFrameRate(m_iFrameRate,1);//1 不发送数据,0 实际发送数据
Connect(m_pFilterVideoCap ,m_pVideoRtpRender) ;
//连接对方
hr= pRenderOption- >Connect(szClientA,iVideoPort,1024);
if(FAILED(hr))
return hr;
m_pVideoMediaCtrl- >Run();
}
//视频的接收
CComPtr< IGraphBuilder > m_pVideoGraphBuilder; //视频图形管理器
CComPtr< IBaseFilter > m_pFilterVideoCap;
CComPtr< IVideoWindow > m_pVideoWindow;
CComPtr< IMediaControl > m_pVideoMediaCtrl ;
CComPtr< IBaseFilter > m_pVideoRenderFilter;
HWND m_hRenderWnd ;
HRESULT VideoRecive()
{
HRESULT hr;
hr=CoCreateInstance(CLSID_FilterGraph,NULL,CLSCTX_INPROC,
IID_IFilterGraph,(void**) &m_pVideoGraphBuilder);
m_pVideoGraphBuilder- >QueryInterface(IID_IMediaControl, (void **)&m_pVideoMediaCtrl);
m_pVideoGraphBuilder- >QueryInterface(IID_IVideoWindow,(void**)&m_pVideoWindow)
hr = ::CoCreateInstance(CLSID_FH263Source, NULL, CLSCTX_INPROC, IID_IBaseFilter, (void **) &m_pVideoRtpSource);
if(FAILED(hr))
return hr;
m_pVideoGraphBuilder- >AddFilter(m_pVideoRtpSource, L"My Custom Source");
CComPtr < IRtpOption > m_pRtpOption;
CComPtr < IVideoOption > m_pVideoOption;
m_pVideoRtpSource- >QueryInterface(IID_IJRTPOption, (void **)&m_pRtpOption);
m_pVideoRtpSource- >QueryInterface(IID_IVideoOption, (void **)&m_pVideoOption);
tagVideoInfo vif(160, 120 ,24);
m_pVideoOption- >SetProperty(&vif);
hr= pRenderOption- >Connect(szClientA,iVideoPort +1,1024);
if(FAILED(hr))
return hr;
//创建预览的filter
hr = m_pRenderFilterVideo.CoCreateInstance(CLSID_VideoRenderer);
if(FAILED(hr))
return hr;
m_pVideoGraphBuilder- >AddFilter( m_pRenderFilterVideo, L"VideoRenderFilter" );
Connect(m_pVideoRtpSource ,m_pRenderFilterVideo) ;
CRect rc ;
GetClientRect(m_hOwnerWnd, &rc );
int iWidth = rc.right - rc.left ;
int iHeight = rc.bottom - rc.top ;
int iLeft, iTop;
if((iHeight*1.0)/(iWidth*1.0) >= 0.75)
{
//按宽度算
int tmpiHeight = iWidth*3/4;
iTop = (iHeight - tmpiHeight)/2;
iHeight = tmpiHeight;
iLeft = 0;
}
else
{
//按高度算
int tmpiWidth = iHeight*4/3;
iLeft = (iWidth - tmpiWidth)/2;
iWidth = tmpiWidth;
iTop = 0;
}
m_pVideoWindow- >put_Owner( (OAHWND) m_hRenderWnd ) ;
m_pVideoWindow- >put_Visible( OATRUE );
m_pVideoWindow- >put_WindowStyle( WS_CHILD | WS_CLIPSIBLINGS ) ;
m_pVideoMediaCtrl- >Run();
return S_OK;
}
//
HRESULT FindDeviceFilter(IBaseFilter ** ppSrcFilter,GUID deviceGUID)
{
HRESULT hr;
IBaseFilter * pSrc = NULL;
CComPtr <IMoniker> pMoniker =NULL;
ULONG cFetched;
if (!ppSrcFilter)
return E_POINTER;
// Create the system device enumerator
CComPtr <ICreateDevEnum> pDevEnum =NULL;
hr = CoCreateInstance (CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC,
IID_ICreateDevEnum, (void **) &pDevEnum);
if (FAILED(hr))
return hr;
// Create an enumerator for the video capture devices
CComPtr <IEnumMoniker> pClassEnum = NULL;
hr = pDevEnum- >CreateClassEnumerator (deviceGUID, &pClassEnum, 0);
if (FAILED(hr))
return hr;
if (pClassEnum == NULL)
return E_FAIL;
if (S_OK == (pClassEnum- >Next (1, &pMoniker, &cFetched)))
{
hr = pMoniker- >BindToObject(0,0,IID_IBaseFilter, (void**)&pSrc);
if (FAILED(hr))
return hr;
}
else
return E_FAIL;
*ppSrcFilter = pSrc;
return S_OK;
}
//构建音频Graph图,并发送
CComPtr< IGraphBuilder > m_pAudioGraphBuilder; //音频图形管理器
CComPtr< ICaptureGraphBuilder2 > m_pCapAudioGraphBuilder;
CComPtr< IBaseFilter > m_pFilterAudioCap;
CComPtr< IMediaControl > m_pAudioMediaCtrl ;
HRESULT AudioGraphInit()
{
HRESULT hr;
hr =m_pAudioGraphBuilder.CoCreateInstance( CLSID_FilterGraph );
if(FAILED(hr))
return hr;
hr =m_pCapAudioGraphBuilder.CoCreateInstance( CLSID_CaptureGraphBuilder2);
if(FAILED (hr))
return hr;
m_pAudioGraphBuilder- >SetFiltergraph(m_pCapAudioGraphBuilder);
m_pAudioGraphBuilder- >QueryInterface(IID_IMediaControl, (void **)&m_pAudioMediaCtrl);
FindDeviceFilter( &m_pFilterVideoCap,CLSID_AudioInputDeviceCategory);
if(m_pFilterAudioCap)
m_pAudioGraphBuilder- >AddFilter( m_pFilterAudioCap,T2W("AudioCap") ) ;
//发送到网络
hr =::CoCreateInstance(CLSID_FG729Render,NULL,CLSCTX_INPROC,
IID_IBaseFilter,(void**) &m_pFilterRtpSendAudio)
if(FAILED(hr))
return hr;
m_pAudioGraphBuilder- >AddFilter(m_pAudioRtpRender, L"FilterRtpSendAudio");
Connect(m_pFilterAudioCap,m_pAudioRtpRender);
CComPtr < IRtpOption > pOption ;
m_pAudioRtpRender- >QueryInterface(IID_IJRTPOption,(void**)&pOption)
hr =pOption- >Connect(szClientA,iAudioPort,1024);
if(FAILED(hr))
return hr;
m_pAudioMediaCtrl- >Run();
return S_OK;
}
//音频的接收
CComPtr< IGraphBuilder > m_pAudioGraphBuilder; //音频图形管理器
CComPtr< ICaptureGraphBuilder2 > m_pCapAudioGraphBuilder;
CComPtr< IBaseFilter > m_pFilterAudioCap;
CComPtr< IMediaControl > m_pAudioMediaCtrl ;
CComPtr<IBaseFilter> m_pAudioRender;
HRESULT AudioRecive()
{
HRESULT hr;
hr =m_pAudioGraphBuilder.CoCreateInstance( CLSID_FilterGraph );
if(FAILED(hr))
return hr;
m_pAudioGraphBuilder- >QueryInterface(IID_IMediaControl, (void **)&m_pAudioMediaCtrl);
hr = m_pAudioRtpSource- >CoCreateInstance(CLSID_FG729Source) ;
if(FAILED(hr))
return hr;
m_pAudioGraphBuilder- >AddFilter(m_pAudioRtpSource,L"AudioRtp");
//创建声卡Renderfilter
FindDeviceFilter( &m_pAudioRender,CLSID_AudioRendererCategory);
m_pAudioGraphBuilder- >AddFilter(m_pAudioRender,L"AudioRender");
CComPtr < IRtpOption > pRtpOption ;
m_pAudioRtpSource- >QueryInterface(IID_IJRTPOption,(void**)&pRtpOption)
hr= pRtpOption- >Connect(szClientA,iAudioPort+2,1024);
if(FAILED (hr))
return hr;
Connect(m_pAudioRtpSource,m_pAudioRender);
m_pAudioMediaCtrl- >Run();
return S_OK;
} |
|