编程爱好者电子杂志 2001年第四期
出版日期: 2001年6月17日
欢迎光临编程爱好者网站: http://www.programfan.com
【杂志网站】【编辑信箱】【提问信箱】【过刊查询】【杂志订退】
本 期 内 容
编程爱好者网站最新更动
编程爱好者网站自6月1日开始启用新的域名 http://www.programfan.com 本站将以全新的面貌来欢迎每一位编程爱好者的到来。
本站的建设离不开广大的编程爱好者的帮助,新版的网站将更侧重于互动交流,能为广大的编程爱好者提供资料解决问题是本站的目标。
本站尚有很多栏目尚在改版中,如果有不能访问的栏目的话,敬请等待一两天。
网站本周最新更动
本周更新文章50篇,下面是最新加入的文章
● Delphi趣味编程实例三则 2001-6-15
● 用Delphi打造RealPlayer播放器 2001-6-15
● DELPHI环境中组件的创建技巧 2001-6-15
● 关闭所有正在运行的程序 2001-6-15
● 保护ASP脚本源代码 2001-6-15
● 使用PHP进行用户身份认证 2001-6-15
● 巧妙实现BROWSE语句左右翻屏的方法 2001-6-14
● Java代码编写的一般性指导 2001-6-14
● 在VFP中发送电子邮件 2001-6-14
● 用VFP表单制作帮助文件 2001-6-14
● 在VFP中用伪代码开发数据表维护屏幕 2001-6-14
● VFP中的加密算法 2001-6-14
● 把FoxPro数据库转变成HTML表格 2001-6-14
● 获得ASP的中文日期字符串 2001-6-14
● VFP中实现超链接 2001-6-14
类似OICQ自动滑动的隐藏的实现,以及源代码
OICQ以及东方快车一些软件可以由用户设置隐藏到屏幕上端,鼠标划过时显示
实现起来十分简单,窗口滑动利用MoveWindow( RECT* rc, BOOL bReDraw);
检测鼠标是否划过窗口上方有三种方法
一,利用_TrackMouseEvent
具体实现:
//填加消息映射
//.......
ON_MESSAGE(WM_MOUSELEAVE,OnMouseLeave)
//.......
//消息映射结束
void CSpDlg::OnMouseMove(UINT nFlags, CPoint point)
{
TRACKMOUSEEVENT stTRACKMOUSEEVENT;
stTRACKMOUSEEVENT.cbSize = sizeof(stTRACKMOUSEEVENT);
stTRACKMOUSEEVENT.hwndTrack=m_hWnd;
stTRACKMOUSEEVENT.dwFlags=TME_LEAVE;
_TrackMouseEvent(&stTRACKMOUSEEVENT);
CDialog::OnMouseMove(nFlags, point);
}
LRESULT CSpDlg::OnMouseLeave(WPARAM wParam, LPARAM lParam)
{
/*这里检查鼠标位置
是为了避免鼠标滑过控件时候产生的WM_MOUSELEAVE干扰正常应用*/
POINT pt;
RECT rcWindow;
GetWindowRect( &rcWindow );
GetCursorPos( &pt );
if(pt.x > rcWindow.right || pt.x || pt.y rcWindow.bottom)/*当鼠标移动到窗口矩形外*/
MessageBox("鼠标离开了对话框",NULL,MB_OK);
return 0;
}
二,SetCapture();函数,参见MSDN,我们详细说下面的方法
三,利用WM_TIMER随时检查鼠标位置,判断是否在窗口上方。
void CAutoHideDlg::OnTimer(UINT nIDEvent)
{
POINT pt;
RECT rcWindow;
GetWindowRect( &rcWindow );
GetCursorPos( &pt );
if(pt.x > rcWindow.right || pt.x || pt.y rcWindow.bottom)
{
/*鼠标在窗口外*/
}
else
{
/*在窗口内*/
}
CDialog::OnTimer(nIDEvent);
}
算了,不说了,这部分实现起来实在是太简单,没什么好说的,想琢磨看代码吧
http://263.csdn.net/FileBBS/files/2001_6/T_150_1.zip
directshow的中文资料之综述&配置篇
综述篇
DirectShow的出现,给windows下的多媒体编程提供了强大的支持.他提供了多媒体应用程序的最新技术和工具.为广大的程序员提供了一整套的应用程序接口API,使程序员能够设计高性能实时的应用程序。
最新的DirectMedia SDK,提供于DirectX8.0中.其中DirectShow加入了许多新的特性.DirectShow就是原来的Microsoft ActiveMovie SDK.新的特性包括:音视频的捕捉和压缩,设备的列举,DVD的支持,设备的控制和多媒体流格式等的支持.
Microsoft DirectShow SDK 提供从本地文件和Internet上回放和捕捉媒体流的能力。他支持的文件格式非常的多样,包括MPEG,Apple QuickTime,AVI,WAV,MIDI,Advanced Streaming Format (ASF), and Windows Media Audio (WMA).
再你开始之前,你需要的背景知识:
1.你应该熟悉C\C++或是VB.
2.另外让人头痛的是部件对象模型COM技术.下面我先简单的介绍一下COM的技术.
一、DirectX和部件对象模型COM
1.1、 部件对象模型COM(Component Object Model)
DirectShow中的大部分API都由基于COM的对象和接口组成。COM是接口重利用的基于对象的系统的基础,是COM编程的核心模型,它也是一种接口规范。在操作系统级别上,它又是一个对象模型。 许多DirectShow API都创建为COM对象的实例。你可以将一个对象看做一个黑盒子,对象通过接口与对象通信,通过COM接口发送给对象或从对象接收的命令称为方法。例如
IVideoWindow->SetWindowPosition(grc.left, grc.top, grc.right, grc.bottom);
就是通过IVideoWindow的接口的方法设置显示窗口的位置.对象在运行时可以同其他对象捆绑在一起,并且能够使用这些对象的接口。如果你已知道某个对象是COM对象,并且知道该对象支持的接口,你的应用程序或其他对象就能够确定第一个对象所能执行的服务。所有COM对象都继承的一个方法是Query接口方法,它让你确定一个对象支持的接口和创建这些接口的指针。
1.2、IUnknown 接口
所有的COM 接口都由一个称为Iunknown的接口衍生而来,该接口为DirectShow提供了对象生存期的控制和操作多接口发能力。Iunknown含有三个方法:
.AddRef 当一个接口或另一个应用捆绑到一个对象上时,就使用AddRef方法将该对象的索引值加1。
.QueryInterface 通过指向特定接口的指针查询对象所支持的特性。
.Release 将对象的索引值减1,当索引值变为0时,该对象就从内存中释放。
其中AddRef和Release方法负责维护对象的索引值。例如,如果创建一个IGraphBuilder 对象,该对象的索引值就被设定为1,每
次当有一个函数返回该对象的指针时,该函数必须通过返回发指针调用AddRef方法将该对象的索引值加1。每一个AddRef的调用都必须有一个Release的调用与其对应。当对象的索引值达到0时,该对象就被撤消,该对象的所有接口都不可再用。
QueryInterface方法测定一个对象是否支持指定的接口,如果支持,QueryInterface就返回指向该接口的指针。然后你可以使用该接口包含的方法同对象通信。如果QueryInterface成功地返回接口的指针,它会自动调用AddRef方法增加对象的索引值。在撤消接口指针之前必须
调用Release来减少对象的索引值。
1.3、 DirectShow COM 接口
DirectShow中的接口是用相当基本的COM编程创建的。对象的每个接口都由IUnknown COM接口派生而来,如IGraphBuilder 、IMediaControl 和IMediaEventEx 都是这样。基本对象的创建工作由动态链接库DLL中的特殊函数来处理。当然还有很多的其他接口,会在以后一一说明。
1.4、 C++和COM 接口
对C++程序员来说,COM接口就象一个抽象的基类。在C++的基类中,所有的方法都定义为纯虚的,这就意味着没有任何代码同方法关联在一起。纯虚的C++函数和COM接口都使用一种称为虚表(vtable)的设备。一个虚表包含了应用于所给接口的所有函数的声明。如果你想让程序或对象使用这些函数,可以先用QueryInterface方法检查对象存在的接口,获取该接口的指针,调QueryInterface后,应用或对象实际上就从对象中接收到了虚表的指针,通过该指针就可以调用应用于该对象的所有接口方法。
COM对象和C++的另一个相似之处是方法的第一个参数就是接口或类的名字,C++中称为this参数。因为COM对象和C++对象是完全
二进兼容的,编译器就将COM接口同C++抽象类同样处理,并且具有相同的语法,这会使得代码简单一些。
DirectShow基础指南
一.体系结构
DirectShow的体系结构定义了如何用叫做Filters的控制和处理多媒体数据流。Filters(过滤器)有输入或输出Pins(实在是不好翻译...腿?脚?),或者俩者都有。用一个教Filter Graph的把他们互相连接。应用程序用Filter Graph Manager去装配和移动数据。缺省的,filter graph manager自动为你处理数据的流程。例如,自动插入适当的codec, 自动连接传输filter的输出pin到缺省的渲染filter。如果你不想要缺省的,你可以指定你自己的filter。
Filter graph manager 提供了COM接口,这样应用程序就能访问filter graph了。应用程序能直接调用filter graph manager的接口去控制媒体流或是返回filter事件,他们也能用ActiveMovie控件去回放媒体文件。
因此,你可以通过COM访问DirectShow,ActiveMovie控件,或是media control interfaces(MCI),如下图:
二.下面详细介绍一下Filter Graph Manager and Filter Graphs
A.一个Filter Graphs由很多不同类型的filter组成。大部分的filter可以归为三个类:
1.source filter(源过滤器),获得数据的地方,例如从硬盘,因特网服务器,或是VCR(录像机),把他们引入filter graph。
2.transform filter(传输过滤器),得到数据并处理他,然后向前传递。
3.rendering filter(渲染过滤器),渲染数据;通常是一个设备,能够渲染本地的和接受的媒体输入(象内存或是硬盘文件)。
另外,除了这三种,还有其他的过滤器,举个例子,effect filter,他不改变数据的类型,只是处理他,还有parser filter(解析过滤器),他能够理解源数据的格式,知道如何正确的读数据,执行seek等。
例如,一个filter graph想要回放一个MPEG的视频文件,就要用到下面的filter。
1.source filter,从磁盘读数据。
2.MPEG filter,解析数据流,并且分离出MPEG音视频数据流。
3.transform filter,解压视频数据。
4.transform filter,解压音频数据。
5.video renderer filter,显示视频数据到屏幕中。
6.audip renderer filter,把音频数据送达声卡。
如下图:
B.要想一个filter graph工作,filter必须要正确的顺序连接,并且数据流也要以正确的顺序开始和结束。filter graph manager就是起这作用的。他负责连接filter并控制媒体流。他也能为一个特别的媒体文件配置filter去渲染他,并建立filter graph。filter graph也可以重新配置通过filter graph manager。
当配置时,filter graph manager使用filter mapper,首先读注册信息,决定有用的过滤器数据类型。然后企图连接过滤器,直到找到了render 过滤器。每一个过滤器都有一个注册变量。
filter graph manager允许应用程序或ActiveX控件控制媒体流的开始,暂停,停止,和播放特定的时间段,指定特定的播放位置。通过调用适当的过滤器方法。也允许过滤器传递事件给应用程序。因此,一个应用程序能收到一个特别的过滤器的状态信息等。
三.Filters and Pins
在流体系当中,有两个基本的组件,filter和pin。filter是一个COM对象,他至少有一个pin。pin也是COM对象。他通过filter建立。他是各个filter的连接点。
如下图:
输入pin接受数据到filter中。输出pin提供数据到其他的filter中。通常的传输过滤器,象压缩,解压(codec)过滤器,提供了一个输入,一个输出pin。
一个过滤器有IBaseFilter接口。这个接口提供的方法允许列举在他上的pin和返回过滤器的信息。
也提供从IMediaFilter继承的方法:这些方法允许控制状态(运行,暂停,停止)和同步。另外,filter可以有其他的几个接口,这依赖他支持的媒体类型。
pin有责任提供接口连接其他的pin并传输数据。pin的接口支持:
1.使用共享内存或别的资源传输数据。
2.在每个pin-to-pin的连接中,商议数据的具体格式。
3.缓冲管理和缓冲分配,达到最小的数据拷贝,最大的数据吞吐量。
pin的接口也有一些细微的差别,分为输入和输出pin。
输出pin通常有下面一些接口:
1.IPin方法的调用运行查询pin,连接,和数据类型信息,当filter graph停止的时候,还可以发送通知给下游。
2.IMediaSeeking 允许传播关于流的开始时间,停止时间等信息给renderer。然后,把媒体位置信息传给上游的过滤器(一般是源过滤器)。
3.IQualityControl 传送quality-control消息给上游。
输入pin通常有下面一些接口:
1.IPin 允许pin连接输出pin,和提供信息给输出pin。
2.IMemInputPin 提供共享内存缓冲传输数据。
配置篇
VC的环境设置
在进行任何DirectShow有关的编程之前,你得先设置VC的环境变量值.主要是方便以后的项目设计.
1.选择Options.
2.选择Directories Tab.
3.在Show directories for框中选择include files
4.在Directories框中选择一项新项,键入dxmedia\Include 和 dxmedia\Classes\Base,此目录将含有所有有关的头文件。
5.在Show directories for框中选择Library files
6.在Directories框中选择一项新项,键入C:\dxmedia\Lib,此目录将含有所有有关的库文件。
7.选择OK
建立GUID
如果你建立自己的filter(过滤器),你需要建立一个GUID。
你可以:
选择工具菜单,选择Create GUID,GUID默认的是DEFINE_GUID,把得到数据的粘贴到你的头文件中。
如果你的工具菜单中没有,你可以用你的VC目录下的\Common\Tools下的GUIDGEN.EXE。
例如:
// {3FA5D260-AF2F-11d0-AE9C-00A0C91F0841}
DEFINE_GUID(CLSID_MyFilter,
0x3fa5d260, 0xaf2f, 0x11d0, 0xae, 0x9c, 0x0, 0xa0, 0xc9, 0x1f, 0x8, 0x41);
综述篇
DirectShow的出现,给windows下的多媒体编程提供了强大的支持.他提供了多媒体应用程序的最新技术和工具.为广大的程序员提供了一整套的应用程序接口API,使程序员能够设计高性能实时的应用程序。
最新的DirectMedia SDK,提供于DirectX8.0中.其中DirectShow加入了许多新的特性.DirectShow就是原来的Microsoft ActiveMovie SDK.新的特性包括:音视频的捕捉和压缩,设备的列举,DVD的支持,设备的控制和多媒体流格式等的支持.
Microsoft DirectShow SDK 提供从本地文件和Internet上回放和捕捉媒体流的能力。他支持的文件格式非常的多样,包括MPEG,Apple QuickTime,AVI,WAV,MIDI,Advanced Streaming Format (ASF), and Windows Media Audio (WMA).
再你开始之前,你需要的背景知识:
1.你应该熟悉C\C++或是VB.
2.另外让人头痛的是部件对象模型COM技术.下面我先简单的介绍一下COM的技术.
一、DirectX和部件对象模型COM
1.1、 部件对象模型COM(Component Object Model)
DirectShow中的大部分API都由基于COM的对象和接口组成。COM是接口重利用的基于对象的系统的基础,是COM编程的核心模型,它也是一种接口规范。在操作系统级别上,它又是一个对象模型。 许多DirectShow API都创建为COM对象的实例。你可以将一个对象看做一个黑盒子,对象通过接口与对象通信,通过COM接口发送给对象或从对象接收的命令称为方法。例如
IVideoWindow->SetWindowPosition(grc.left, grc.top, grc.right, grc.bottom);
就是通过IVideoWindow的接口的方法设置显示窗口的位置.对象在运行时可以同其他对象捆绑在一起,并且能够使用这些对象的接口。如果你已知道某个对象是COM对象,并且知道该对象支持的接口,你的应用程序或其他对象就能够确定第一个对象所能执行的服务。所有COM对象都继承的一个方法是Query接口方法,它让你确定一个对象支持的接口和创建这些接口的指针。
1.2、IUnknown 接口
所有的COM 接口都由一个称为Iunknown的接口衍生而来,该接口为DirectShow提供了对象生存期的控制和操作多接口发能力。Iunknown含有三个方法:
.AddRef 当一个接口或另一个应用捆绑到一个对象上时,就使用AddRef方法将该对象的索引值加1。
.QueryInterface 通过指向特定接口的指针查询对象所支持的特性。
.Release 将对象的索引值减1,当索引值变为0时,该对象就从内存中释放。
其中AddRef和Release方法负责维护对象的索引值。例如,如果创建一个IGraphBuilder 对象,该对象的索引值就被设定为1,每
次当有一个函数返回该对象的指针时,该函数必须通过返回发指针调用AddRef方法将该对象的索引值加1。每一个AddRef的调用都必须有一个Release的调用与其对应。当对象的索引值达到0时,该对象就被撤消,该对象的所有接口都不可再用。
QueryInterface方法测定一个对象是否支持指定的接口,如果支持,QueryInterface就返回指向该接口的指针。然后你可以使用该接口包含的方法同对象通信。如果QueryInterface成功地返回接口的指针,它会自动调用AddRef方法增加对象的索引值。在撤消接口指针之前必须
调用Release来减少对象的索引值。
1.3、 DirectShow COM 接口
DirectShow中的接口是用相当基本的COM编程创建的。对象的每个接口都由IUnknown COM接口派生而来,如IGraphBuilder 、IMediaControl 和IMediaEventEx 都是这样。基本对象的创建工作由动态链接库DLL中的特殊函数来处理。当然还有很多的其他接口,会在以后一一说明。
1.4、 C++和COM 接口
对C++程序员来说,COM接口就象一个抽象的基类。在C++的基类中,所有的方法都定义为纯虚的,这就意味着没有任何代码同方法关联在一起。纯虚的C++函数和COM接口都使用一种称为虚表(vtable)的设备。一个虚表包含了应用于所给接口的所有函数的声明。如果你想让程序或对象使用这些函数,可以先用QueryInterface方法检查对象存在的接口,获取该接口的指针,调QueryInterface后,应用或对象实际上就从对象中接收到了虚表的指针,通过该指针就可以调用应用于该对象的所有接口方法。
COM对象和C++的另一个相似之处是方法的第一个参数就是接口或类的名字,C++中称为this参数。因为COM对象和C++对象是完全
二进兼容的,编译器就将COM接口同C++抽象类同样处理,并且具有相同的语法,这会使得代码简单一些。
DirectShow基础指南
一.体系结构
DirectShow的体系结构定义了如何用叫做Filters的控制和处理多媒体数据流。Filters(过滤器)有输入或输出Pins(实在是不好翻译...腿?脚?),或者俩者都有。用一个教Filter Graph的把他们互相连接。应用程序用Filter Graph Manager去装配和移动数据。缺省的,filter graph manager自动为你处理数据的流程。例如,自动插入适当的codec, 自动连接传输filter的输出pin到缺省的渲染filter。如果你不想要缺省的,你可以指定你自己的filter。
Filter graph manager 提供了COM接口,这样应用程序就能访问filter graph了。应用程序能直接调用filter graph manager的接口去控制媒体流或是返回filter事件,他们也能用ActiveMovie控件去回放媒体文件。
因此,你可以通过COM访问DirectShow,ActiveMovie控件,或是media control interfaces(MCI),如下图:
二.下面详细介绍一下Filter Graph Manager and Filter Graphs
A.一个Filter Graphs由很多不同类型的filter组成。大部分的filter可以归为三个类:
1.source filter(源过滤器),获得数据的地方,例如从硬盘,因特网服务器,或是VCR(录像机),把他们引入filter graph。
2.transform filter(传输过滤器),得到数据并处理他,然后向前传递。
3.rendering filter(渲染过滤器),渲染数据;通常是一个设备,能够渲染本地的和接受的媒体输入(象内存或是硬盘文件)。
另外,除了这三种,还有其他的过滤器,举个例子,effect filter,他不改变数据的类型,只是处理他,还有parser filter(解析过滤器),他能够理解源数据的格式,知道如何正确的读数据,执行seek等。
例如,一个filter graph想要回放一个MPEG的视频文件,就要用到下面的filter。
1.source filter,从磁盘读数据。
2.MPEG filter,解析数据流,并且分离出MPEG音视频数据流。
3.transform filter,解压视频数据。
4.transform filter,解压音频数据。
5.video renderer filter,显示视频数据到屏幕中。
6.audip renderer filter,把音频数据送达声卡。
如下图:
B.要想一个filter graph工作,filter必须要正确的顺序连接,并且数据流也要以正确的顺序开始和结束。filter graph manager就是起这作用的。他负责连接filter并控制媒体流。他也能为一个特别的媒体文件配置filter去渲染他,并建立filter graph。filter graph也可以重新配置通过filter graph manager。
当配置时,filter graph manager使用filter mapper,首先读注册信息,决定有用的过滤器数据类型。然后企图连接过滤器,直到找到了render 过滤器。每一个过滤器都有一个注册变量。
filter graph manager允许应用程序或ActiveX控件控制媒体流的开始,暂停,停止,和播放特定的时间段,指定特定的播放位置。通过调用适当的过滤器方法。也允许过滤器传递事件给应用程序。因此,一个应用程序能收到一个特别的过滤器的状态信息等。
三.Filters and Pins
在流体系当中,有两个基本的组件,filter和pin。filter是一个COM对象,他至少有一个pin。pin也是COM对象。他通过filter建立。他是各个filter的连接点。
如下图:
输入pin接受数据到filter中。输出pin提供数据到其他的filter中。通常的传输过滤器,象压缩,解压(codec)过滤器,提供了一个输入,一个输出pin。
一个过滤器有IBaseFilter接口。这个接口提供的方法允许列举在他上的pin和返回过滤器的信息。
也提供从IMediaFilter继承的方法:这些方法允许控制状态(运行,暂停,停止)和同步。另外,filter可以有其他的几个接口,这依赖他支持的媒体类型。
pin有责任提供接口连接其他的pin并传输数据。pin的接口支持:
1.使用共享内存或别的资源传输数据。
2.在每个pin-to-pin的连接中,商议数据的具体格式。
3.缓冲管理和缓冲分配,达到最小的数据拷贝,最大的数据吞吐量。
pin的接口也有一些细微的差别,分为输入和输出pin。
输出pin通常有下面一些接口:
1.IPin方法的调用运行查询pin,连接,和数据类型信息,当filter graph停止的时候,还可以发送通知给下游。
2.IMediaSeeking 允许传播关于流的开始时间,停止时间等信息给renderer。然后,把媒体位置信息传给上游的过滤器(一般是源过滤器)。
3.IQualityControl 传送quality-control消息给上游。
输入pin通常有下面一些接口:
1.IPin 允许pin连接输出pin,和提供信息给输出pin。
2.IMemInputPin 提供共享内存缓冲传输数据。
配置篇
VC的环境设置
在进行任何DirectShow有关的编程之前,你得先设置VC的环境变量值.主要是方便以后的项目设计.
1.选择Options.
2.选择Directories Tab.
3.在Show directories for框中选择include files
4.在Directories框中选择一项新项,键入dxmedia\Include 和 dxmedia\Classes\Base,此目录将含有所有有关的头文件。
5.在Show directories for框中选择Library files
6.在Directories框中选择一项新项,键入C:\dxmedia\Lib,此目录将含有所有有关的库文件。
7.选择OK
建立GUID
如果你建立自己的filter(过滤器),你需要建立一个GUID。
你可以:
选择工具菜单,选择Create GUID,GUID默认的是DEFINE_GUID,把得到数据的粘贴到你的头文件中。
如果你的工具菜单中没有,你可以用你的VC目录下的\Common\Tools下的GUIDGEN.EXE。
例如:
// {3FA5D260-AF2F-11d0-AE9C-00A0C91F0841}
DEFINE_GUID(CLSID_MyFilter,
0x3fa5d260, 0xaf2f, 0x11d0, 0xae, 0x9c, 0x0, 0xa0, 0xc9, 0x1f, 0x8, 0x41);
directshow的中文资料之播放影片
DirectShow编程指南
我们终于开始了我们的真正旅程!Let's Go!
由于DirectX和VC++的紧密联系,所有的代码都用C++写的。
一.播放影片
通过一个简单的C++程序示范如何播放影片。本节包括:
1.播放一个媒体文件--回放媒体文件的基本代码。
2.添加媒体seek功能--提供在媒体文件中如何seek一个特定的的位置的代码。(seek就是...你用过CFile::Seek么?嗯...就是他了)。
因为只是个演示很多都是定了的。例如:
TCHAR *szFilename = "c:\\dxmedia\\movie\\movie.avi";
当然你可以用各种方法得到你使用的文件信息。
另外就是定义了自己的响应消息和一个释放宏:
#define WM_GRAPHNOTIFY WM_USER+13
#define HELPER_RELEASE(x) { if (x) x->Release(); x = NULL; }
需要的头文件:
#include
#include
#include
申明变量:
HWND ghApp;
HINSTANCE ghInst;
HRESULT hr;
LONG evCode;
LONG evParam1;
LONG evParam2;
其中ghApp是一个graph产生的事件的响应窗口句柄。ghInst是窗口的HINSTANCE。evCode将保存事件代码,evParam1和evParam2保存事件的参数。
申明和初始化必须的接口。由于接口的索引值是自动的加1,所以你不要调用IUnknown::AddRef方法(如果你觉得陌生,你可以参考综述篇的"一、DirectX和部件对象模型COM")。
IGraphBuilder *pigb = NULL;
IMediaControl *pimc = NULL;
IMediaEventEx *pimex = NULL;
IVideoWindow *pivw = NULL;
定义一个函数:szFile参数是播放的媒体文件名
void PlayFile(LPSTR szFile)
{
HRESULT hr;
建立一个Unicode(wide character)字符串。
WCHAR wFile[MAX_PATH];
MultiByteToWideChar( CP_ACP, 0, szFile, -1, wFile, MAX_PATH );
实例化一个filter graph manager。
hr = CoCreateInstance(CLSID_FilterGraph,
NULL,
CLSCTX_INPROC_SERVER,
IID_IGraphBuilder,
(void **)&pigb);
查询IMediaControl接口(提供run,pause and stop methods),IMediaEventEx接口(你可以接收事件响应),IVideoWindow接口。
pigb->QueryInterface(IID_IMediaControl, (void **)&pimc);
pigb->QueryInterface(IID_IMediaEventEx, (void **)&pimex);
pigb->QueryInterface(IID_IVideoWindow, (void **)&pivw);
让filter graph manager建立filter graph去渲染输入文件。现在还没播放文件(当你用run函数播放时,filter graph会自动渲染输入文件的媒体类型,你不必指定渲染过滤器)。
hr = pigb->RenderFile(wFile, NULL);
用一个窗口捕捉graph的通知事件。可以改善性能,但是允许你的应用程序运行在另一个线程。
pimex->SetNotifyWindow((OAHWND)ghApp, WM_GRAPHNOTIFY, 0);
在ghApp处理消息去响应从graph传来的所有事件。如果事件发生了,DirectShow会post一个WM_GRAPHNOTIFY消息给ghApp。
开始播放文件。
hr = pimc->Run();
同样的,你也可以有
hr = pimc->Pause();
hr = pimc->Stop();
当然了,你可以用一个对话框的按钮来响应Pause 和 Stop。这样就实现了简单的回放。
记住了,在你的代码里,要释放你用的接口,可以用HELPER_RELEASE 宏。
例:HELPER_RELEASE(pigb);
现在加入seek功能。
在你的媒体文件中,你可以用IMediaPosition or IMediaSeeking 接口seek到一个特定的位置播放。IMediaPostion::put_CurrentPosition方法可以指定开始时间,例如你可以用下面的代码实现重放:
IMediaPosition *pimp;
hr = pigb->QueryInterface(&IID_IMediaPosition, (void **)&pimp);
hr = pimp->put_CurrentPosition(0);
时间的单位是100呐秒,下面的代码seek到文件的一秒处:
hr = pimp->put_CurrentPosition(10000000);
你也可以用IMediaPosition::put_StopTime 方法去设置文件的回放停止时间。
然而,用IMediaPosition只能用seek时间,如果你用 IMediaSeeking接口,你能用多种格式seek,用100呐秒单位,frame(帧),字节,media samples,或者是interlaced video fields。你可以用IMediaSeeking::SetTimeFormat 设置你需要的格式。注意,确信你不在播放媒体文件,当你设置格式的时候。
格式如下:
TIME_FORMAT_MEDIA_TIME 单位是100呐秒
TIME_FORMAT_BYTE 单位是字节
TIME_FORMAT_FIELD 单位是interlaced video field(我不太清楚这格式,所有只好用英文)
TIME_FORMAT_FRAME 单位是帧
TIME_FORMAT_SAMPLE 单位是sample (我不太清楚这格式,所有只好用英文)
举个例子吧,下面的代码设置格式为帧。
IMediaSeeking *pims;
hr = pigb->QueryInterface(IID_IMediaSeeking, (void **)&pims);
hr = pims->SetTimeFormat(&TIME_FORMAT_SAMPLE);
应用程序可以用多样的seek模式,而不需要考虑 时间/速率的转换。有时候这是很有用的。
下面示例如何用帧的格式开始和结束播放。如在15帧开是播放影片。你可以把代码插入到PlayFile函数的任何地方,注意的是,一定要在RenderFile函数的后面(还记得hr = pigb->RenderFile(wFile, NULL);这段代码么?)。
IMediaSeeking *pims;
hr = pigb->QueryInterface(IID_IMediaSeeking, (void **)&pims);
设置时间格式。
hr = pims->SetTimeFormat(&TIME_FORMAT_FRAME);
申明并初始化开始和结束变量:
LONGLONG start = 5L;
LONGLONG stop = 15L;
通过IMediaSeeking::SetPositions方法设置开始和结束时间, AM_SEEKING_AbsolutePositioning标志意味着这是一个绝对的位置(不是相对于媒体文件现在的位置)。在这个例子中,媒体文件就在第5帧开始,在15帧结束,持续时间是10帧。具体的时间长度要看视频帧的播放速率了。
pims->SetPositions(&start, AM_SEEKING_AbsolutePositioning, &stop,
AM_SEEKING_AbsolutePositioning);
最后释放接口。
pims->Release();
当然你也可以设置别的格式,和别的开始结束的信息。例如5秒到7秒。
hr = pims->SetTimeFormat(&TIME_FORMAT_FRAME);
LONGLONG start = 50000000L;
LONGLONG stop = 70000000L;
其他就看你自己喜欢了......(我没有任何权利干涉)
directshow的中文资料之控制视频回放窗口
主要讲解如何控制视频回放窗口,所有有些东西没有说,你可以参考"一.播放影片"中的代码。
需要的头文件:
#include
#include
#include
定义一个窗口消息和HELPER_RELEASE 宏。
#define WM_GRAPHNOTIFY WM_USER+13
#define HELPER_RELEASE(x) { if (x) x->Release(); x = NULL; }
申明变量:
HWND ghApp;
HINSTANCE ghInst;
HRESULT hr;
LONG evCode;
LONG evParam1;
LONG evParam2;
RECT grc;
参数的含义和"一.播放影片"的一样,唯一要说明的是grc,将保存父窗口的客户区。
申明和初始化必须的接口。由于接口的索引值是自动的加1,所以你不要调用IUnknown::AddRef方法(如果你觉得陌生,你可以参考综述篇的"一、DirectX和部件对象模型COM")。
IGraphBuilder *pigb = NULL;
IMediaControl *pimc = NULL;
IMediaEventEx *pimex = NULL;
IVideoWindow *pivw = NULL;
定义一个函数:szFile参数是播放的媒体文件名
void PlayMovieInWindow (LPCTSTR szFile)
{
实例化一个filter graph manager。
hr = CoCreateInstance(CLSID_FilterGraph,
NULL,
CLSCTX_INPROC_SERVER,
IID_IGraphBuilder,
(void **)&pigb);
查询IMediaControl接口(提供run,pause and stop methods),IMediaEventEx接口(你可以接收事件响应),IVideoWindow接口。
pigb->QueryInterface(IID_IMediaControl, (void **)&pimc);
pigb->QueryInterface(IID_IMediaEventEx, (void **)&pimex);
pigb->QueryInterface(IID_IVideoWindow, (void **)&pivw);
让filter graph manager建立filter graph去渲染输入文件。现在还没播放文件(当你用run函数播放时,filter graph会自动渲染输入文件的媒体类型,你不必指定渲染过滤器)。
hr = pigb->RenderFile(wFile, NULL);
设置回放窗口的句柄:
pivw->put_Owner((OAHWND)ghApp);
设置视频窗口的风格。这步很重要,你必须指定WS_CHILD, WS_CLIPCHILDREN, and WS_CLIPSIBLINGS 标志。
pivw->put_WindowStyle(WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS);
得到父窗口的客户区。
GetClientRect(ghApp, &grc);
在父窗口的客户区设置回放窗口的位置。在这里,回放窗口填满了客户区,当然你也可以取其一部分。如果播放的视频比这小,他会自动的伸展以适应位置的大小,如果比这大,他会缩小来适应。
pivw->SetWindowPosition(grc.left, grc.top, grc.right, grc.bottom);
开始播了:
hr = pimc->Run();
directshow的中文资料之建立一个捕捉程序
DirectShow 提供了用应用程序从适当的硬件中捕捉和预览音/视频的能力。数据源包括:VCR,camera,TV tuner,microphone,或其他的数据源。一个应用程序可以立刻显示捕捉的数据(预览),或是保存到一个文件中。
在这个例子中,ICaptureGraphBuilder 接口是处理捕捉工作的主要接口。你可以在你自己的捕捉程序中使用同样的方法和接口。在这里主要讨论ICaptureGraphBuilder 如何执行音/视频的捕捉。我们假设你已经熟悉了DirectShow的filter graph的体系和一般的capture filter graph的结构(可以参考DirectShow基础指南)。
ICaptureGraphBuilder 接口提供了一个filter graph builder对象,让你的应用程序在建立capture filter graph时,省去处理很多单调乏味的工作,集中精力于捕捉中。他提供的方法满足了基本的捕捉和预览功能的要求。
方法FindInterface ,在filter graph中查找一个于捕捉有关的详细的接口。使的你可以访问一个详细接口的功能,而不需要你去列举在filter graph中的pins 和 filters。
方法RenderStream ,连接源过滤器和渲染过滤器,选择添加一些中间的过滤器。
方法ControlStream ,独立的精确的控制graph的开始和结束帧。
既然是硬件捕捉,当然要和硬件打交道,接着介绍设备列举和捕捉接口。
通过ICreateDevEnum::CreateClassEnumerator方法列举捕捉系统中的设备。之后,实例化一个DirectShow的filter去使用这个设备。接着用ICaptureGraphBuilder::FindInterface去获得于捕捉相关的接口指针IAMDroppedFrames, IAMVideoCompression, IAMStreamConfig, and IAMVfwCaptureDialogs 。因为设备列举和捕捉接口比较长,放在这会打乱结构,所有专门写了一篇(参考设备列举和捕捉接口)。
NOTE:
1.这个示例是DirectShow自带的例子。你可以在DirectShow SDK的目录Sample\DS\Caputre看这个例子代码(AMCap.cpp)。这里只是他的一些片断代码。可以说是他的中文模块的说明。
2.AMCap例子中,把所有的接口指针和一些成员变量保存在一个全局结构gcap中了。
定义如下:
struct _capstuff {
char szCaptureFile[_MAX_PATH];
WORD wCapFileSize; // size in Meg
ICaptureGraphBuilder *pBuilder;
IVideoWindow *pVW;
IMediaEventEx *pME;
IAMDroppedFrames *pDF;
IAMVideoCompression *pVC;
IAMVfwCaptureDialogs *pDlg;
IAMStreamConfig *pASC; // for audio cap
IAMStreamConfig *pVSC; // for video cap
IBaseFilter *pRender;
IBaseFilter *pVCap, *pACap;
IGraphBuilder *pFg;
IFileSinkFilter *pSink;
IConfigAviMux *pConfigAviMux;
int iMasterStream;
BOOL fCaptureGraphBuilt;
BOOL fPreviewGraphBuilt;
BOOL fCapturing;
BOOL fPreviewing;
BOOL fCapAudio;
int iVideoDevice;
int iAudioDevice;
double FrameRate;
BOOL fWantPreview;
long lCapStartTime;
long lCapStopTime;
char achFriendlyName[120];
BOOL fUseTimeLimit;
DWORD dwTimeLimit;
} gcap;
当不在需要保存在gcap中的接口指针是,一定要释放这些接口指针,一般是在程序的析构函数中,或是在别的同等功能函数中。如下:
if (gcap.pBuilder)
gcap.pBuilder->Release();
gcap.pBuilder = NULL;
if (gcap.pSink)
gcap.pSink->Release();
gcap.pSink = NULL;
if (gcap.pConfigAviMux)
gcap.pConfigAviMux->Release();
gcap.pConfigAviMux = NULL;
if (gcap.pRender)
gcap.pRender->Release();
gcap.pRender = NULL;
if (gcap.pVW)
gcap.pVW->Release();
gcap.pVW = NULL;
if (gcap.pME)
gcap.pME->Release();
gcap.pME = NULL;
if (gcap.pFg)
gcap.pFg->Release();
gcap.pFg = NULL;
设置文件名
使用普通的OpenFile dialog获得捕捉文件的信息。通过调用AllocCaptureFile 函数为捕捉文件分配空间。这一点是重要的,因为这是个巨大的空间。这样可以提高捕捉操作的速度。ICaptureGraphBuilder::AllocCapFile 执行实际的文件分配,IFileSinkFilter::SetFileName 指示file writer filter使用用户选择的文件名保存数据。ICaptureGraphBuilder::SetOutputFileName 把file writer filter加入filter graph(后面会介绍,他是ICaptureGraphBuilderd自代的)。
SetCaptureFile 和 AllocCaptureFile 函数如下:
/*
* Put up a dialog to allow the user to select a capture file.
*/
BOOL SetCaptureFile(HWND hWnd)
{
if (OpenFileDialog(hWnd, gcap.szCaptureFile, _MAX_PATH)) {
OFSTRUCT os;
// We have a capture file name
/*
* if this is a new file, then invite the user to
* allocate some space
*/
if (OpenFile(gcap.szCaptureFile, &os, OF_EXIST) == HFILE_ERROR) {
// Bring up dialog, and set new file size
BOOL f = AllocCaptureFile(hWnd);
if (!f)
return FALSE;
}
} else {
return FALSE;
}
SetAppCaption(); // need a new app caption
// Tell the file writer to use the new file name
if (gcap.pSink) {
WCHAR wach[_MAX_PATH];
MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, gcap.szCaptureFile, -1,
wach, _MAX_PATH);
gcap.pSink->SetFileName(wach, NULL);
}
return TRUE;
}
// Preallocate the capture file
//
BOOL AllocCaptureFile(HWND hWnd)
{
// We'll get into an infinite loop in the dlg proc setting a value
if (gcap.szCaptureFile[0] == 0)
return FALSE;
/*
* show the allocate file space dialog to encourage
* the user to pre-allocate space
*/
if (DoDialog(hWnd, IDD_AllocCapFileSpace, AllocCapFileProc, 0)) {
// Ensure repaint after dismissing dialog before
// possibly lengthy operation
UpdateWindow(ghwndApp);
// User has hit OK. Alloc requested capture file space
BOOL f = MakeBuilder();
if (!f)
return FALSE;
WCHAR wach[_MAX_PATH];
MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, gcap.szCaptureFile, -1,
wach, _MAX_PATH);
if (gcap.pBuilder->AllocCapFile(wach,
gcap.wCapFileSize * 1024L * 1024L) != NOERROR) {
MessageBoxA(ghwndApp, "Error",
"Failed to pre-allocate capture file space",
MB_OK | MB_ICONEXCLAMATION);
return FALSE;
}
return TRUE;
} else {
return FALSE;
}
}
建立Graph Builder对象
AMCap的 MakeBuilder函数建立了一个capture graph builer对象,通过调用CoCreateInstance获得了ICaptureGraphBuilder 接口指针。AMCap把他存储到gcap结构的pBuilder中。
// Make a graph builder object we can use for capture graph building
//
BOOL MakeBuilder()
{
// We have one already
if (gcap.pBuilder)
return TRUE;
HRESULT hr = CoCreateInstance((REFCLSID)CLSID_CaptureGraphBuilder,
NULL, CLSCTX_INPROC, (REFIID)IID_ICaptureGraphBuilder,
(void **)&gcap.pBuilder);
return (hr == NOERROR) ? TRUE : FALSE;
}
建立Graph的渲染部分,并告诉他写文件(用先前决定的文件)
这包括一个multiplexer filter 和 file writer。DirectShow 提供了一个AVI MUX(multiplexer)filter。
在这里ICaptureGraphBuilder::SetOutputFileName 是一个关键的方法。他把multiplexer 和 file writer添加到filter graph中,连接他们,并设置文件的名字。第一个参数MEDIASUBTYPE_Avi,指出capture graph builder 对象将插入一个AVI multiplexer filter,因此,file writer将以AVI文件格式记录捕捉的数据。第二个参数(wach)是文件名。最后的两个参数指出multiplexer filter (gcap.pRender) 和file writer filter (gcap.pSink),这两个是通过SetOutputFileName 函数初始化的。AMCap存储这些指针到全局结构gcap中。capture graph builder 对象建立了一个filter graph对象(IGraphBuilder),把这两个filter加入到filter graph中去。他告诉file writer使用指定的文件保存数据。下面的例子演示了如何调用SetOutputFileName。
//
// We need a rendering section that will write the capture file out in AVI
// file format
//
WCHAR wach[_MAX_PATH];
MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, gcap.szCaptureFile, -1, wach,
_MAX_PATH);
GUID guid = MEDIASUBTYPE_Avi;
hr = gcap.pBuilder->SetOutputFileName(&guid, wach, &gcap.pRender,
&gcap.pSink);
if (hr != NOERROR) {
ErrMsg("Error %x: Cannot set output file", hr);
goto SetupCaptureFail;
}
获得当前的Filter Graph
因为在调用SetOutputFileName中,capture graph builder 对象建立了一个filter graph,所有你必须把需要的filter加入同一个filter graph 中。通过ICaptureGraphBuilder::GetFiltergraph获得新建立的filter graph。返回的指针是参数gcap.pFg。
//
// The graph builder created a filter graph to do that. Find out what it is,
// and put the video capture filter in the graph too.
//
hr = gcap.pBuilder->GetFiltergraph(&gcap.pFg);
if (hr != NOERROR) {
ErrMsg("Error %x: Cannot get filtergraph", hr);
goto SetupCaptureFail;
}
添加音/视频过滤器到当前的Filter Graph
hr = gcap.pFg->AddFilter(gcap.pVCap, NULL);
if (hr != NOERROR) {
ErrMsg("Error %x: Cannot add vidcap to filtergraph", hr);
goto SetupPreviewFail;
}
hr = gcap.pFg->AddFilter(gcap.pACap, NULL);
if (hr != NOERROR) {
ErrMsg("Error %x: Cannot add audcap to filtergraph", hr);
goto SetupCaptureFail;
}
渲染视频捕捉过滤器的Capture Pin和音频捕捉的Capture Pin
ICaptureGraphBuilder::RenderStream 连接源过滤器的pin到渲染过滤器。pin的类别是可选的,capture pin (PIN_CATEGORY_CAPTURE) 或 preview pin (PIN_CATEGORY_PREVIEW)。下面的例子演示了连接video capture filter (gcap.pVCap) 的capture pin到渲染gcap.pRender中。
//
// Render the video capture and preview pins - we may not have preview, so
// don't worry if it doesn't work
//
hr = gcap.pBuilder->RenderStream(&PIN_CATEGORY_CAPTURE, NULL, gcap.pVCap,
NULL, gcap.pRender);
// Error checking
再次ICaptureGraphBuilder::RenderStream 连接audio capture filter (gcap.pACap) 到渲染audio renderer 中。
//
// Render the audio capture pin?
//
if (gcap.fCapAudio) {
hr = gcap.pBuilder->RenderStream(&PIN_CATEGORY_CAPTURE, NULL,
gcap.pACap, NULL, gcap.pRender);
// Error checking
渲染Video Capture Filter的 Preview Pin
再次调用ICaptureGraphBuilder::RenderStream,从capture filter的preview pin到video renderer。代码如下:
hr = gcap.pBuilder->RenderStream(&PIN_CATEGORY_PREVIEW, NULL, gcap.pVCap,
NULL, NULL);
获得访问Video Preview Window的接口指针
缺省的,video preview window是一个独立的窗口。如果你想改变默认的行为,先调用 ICaptureGraphBuilder::FindInterface获得IVideoWindow 接口。第二个参数通过gcap.pVCap指定,描述video capture filter,第三个参数是想得到的接口(IVideoWindow),最后的是返回的接口。当你得到IVideoWindow接口后,你可以调用IVideoWindow的方法象put_Owner, put_WindowStyle, or SetWindowPosition 去获得video preview window的handle,设置窗口属性,或把他放到想要的位置。
// This will go through a possible decoder, find the video renderer it's
// connected to, and get the IVideoWindow interface on it
hr = gcap.pBuilder->FindInterface(&PIN_CATEGORY_PREVIEW, gcap.pVCap,
IID_IVideoWindow, (void **)&gcap.pVW);
if (hr != NOERROR) {
ErrMsg("This graph cannot preview");
} else {
RECT rc;
gcap.pVW->put_Owner((long)ghwndApp); // We own the window now
gcap.pVW->put_WindowStyle(WS_CHILD); // you are now a child
// give the preview window all our space but where the status bar is
GetClientRect(ghwndApp, &rc);
cyBorder = GetSystemMetrics(SM_CYBORDER);
cy = statusGetHeight() + cyBorder;
rc.bottom -= cy;
gcap.pVW->SetWindowPosition(0, 0, rc.right, rc.bottom); // be this big
gcap.pVW->put_Visible(OATRUE);
}
现在你已经建立完整的capture filter graph了,你可以预览音频,视频,或捕捉数据。
控制 Capture Filter Graph
因为通过ICaptureGraphBuilder接口构造的capture filter graph 只是一个简单的专门用途的filter graph,所有,控制他就象控制其他类型的filter graph一样。你可以使用IMediaControl interface的 Run, Pause, and Stop方法,你也可以使用CBaseFilter::Pause的方法。另外ICaptureGraphBuilder提供了ControlStream方法去控制capture filter graph的streams的开始和结束时间。ControlStream调用IAMStreamControl::StartAt 和 IAMStreamControl::StopAt控制filter graph的捕捉和预览的开始和结束的位置。
注意:不是所有的capture filter都可以,因为不是每一个capture filter都支持IAMStreamControl。
ICaptureGraphBuilder::ControlStream方法的第一个参数(pCategory)是一个输出pin类的GUID。这个变量通常是PIN_CATEGORY_CAPTURE 或 PIN_CATEGORY_PREVIEW。指定为NULL则控制所有的capture filter。
第二个参数在(pFilter)指出那个filter控制。NULL说明为控制所有的filter graph。
如果只是预览(防止捕捉)的话,可以调用ICaptureGraphBuilder::ControlStream,参数用capture pin类型,MAX_TIME作为开始时间(第三个参数,pstart)。再次调用该方法,参数用preview pin类型,NULL作为开始时间则立即开始预览。第四参数指出结束的时间(pstop),含义和第三个参数一样(NULL意味着立刻)。MAX_TIME在DirectShow中定义为最大的参考时间。在这里意味着忽略或取消指定的操作。
最后的参数,wStartCookie和wStopCookie分别是开始和结束的cookies(不知道该怎么翻译,因为我也不理解这个参数的含义)。
下面的代码设置立刻开始预览,但是忽略捕捉。
// Let the preview section run, but not the capture section
// (There might not be a capture section)
REFERENCE_TIME start = MAX_TIME, stop = MAX_TIME;
// show us a preview first? but don't capture quite yet...
hr = gcap.pBuilder->ControlStream(&PIN_CATEGORY_PREVIEW, NULL,
gcap.fWantPreview ? NULL : &start,
gcap.fWantPreview ? &stop : NULL, 0, 0);
if (SUCCEEDED(hr))
hr = gcap.pBuilder->ControlStream(&PIN_CATEGORY_CAPTURE, NULL, &start,
NULL, 0, 0);
同样的,如果你只想要捕捉而不要预览,设置捕捉的开始时间为NULL,设置捕捉的结束时间为MAX_TIME。设置预览的开始时间为MAX_TIME,NULL为结束时间。
下面的例子告诉filter graph开始预览(第三个参数:开始时间为NULL)。结束时间指定为MAX_TIME意味着忽视停止时间(永远放下去)。
gcap.pBuilder->ControlStream(&PIN_CATEGORY_PREVIEW, NULL, NULL, MAX_TIME, 0, 0);
调用IMediaControl::Run 运行 graph
// Run the graph
IMediaControl *pMC = NULL;
HRESULT hr = gcap.pFg->QueryInterface(IID_IMediaControl, (void **)&pMC);
if (SUCCEEDED(hr)) {
hr = pMC->Run();
if (FAILED(hr)) {
// Stop parts that ran
pMC->Stop();
}
pMC->Release();
}
if (FAILED(hr)) {
ErrMsg("Error %x: Cannot run preview graph", hr);
return FALSE;
如果graph已经运行,通过调用ICaptureGraphBuilder::ControlStream立刻开始捕捉。例如下面的代码,控制整个的filter graph(第二个参数为NULL),立刻开始(第三个参数是NULL),并且永不停止(第四个参数是MAX_TIME)。
// NOW!
gcap.pBuilder->ControlStream(&PIN_CATEGORY_CAPTURE, NULL, MAX_TIME, &stop, 0, 0);
停止预览或捕捉操作,调用IMediaControl::Stop,就同你调用IMediaControl::Run一样。
// Stop the graph
IMediaControl *pMC = NULL;
HRESULT hr = gcap.pFg->QueryInterface(IID_IMediaControl, (void **)&pMC);
if (SUCCEEDED(hr)) {
hr = pMC->Stop();
pMC->Release();
}
获得捕捉的信息
通过IAMDroppedFrames接口获得。测试丢失帧的数量(IAMDroppedFrames::GetNumDropped),捕捉的数量(IAMDroppedFrames::GetNumNotDropped)。IAMDroppedFrames::GetAverageFrameSize方法提供了捕捉帧的平均尺寸(单位:byte)。使用这些信息可以知道总的捕捉字节和每秒的帧数(速率)。
保存文件
最初分配的捕捉文件只是临时的保存数据,所有你可以尽可能快的捕捉。当你想把捕捉的数据保存到硬盘中时,调用ICaptureGraphBuilder::CopyCaptureFile。这个方法从先前得到的捕捉文件输出数据到你选择的另一个文件中。这个新的储存文件的大小是和实际捕捉的数据匹配的,而不是和先前的文件大小匹配。
ICaptureGraphBuilder::CopyCaptureFile方法的第一个参数是复制源,第二个是目标文件。第三个参数设为TRUE指出用户允许用ESC键中断复制操作。最后参数是可选的。允许你提供一个进程指示器。如果想要的化,通过执行 IAMCopyCaptureFileProgress 接口。下面示例了如何调用CopyCaptureFile。
hr = pBuilder->CopyCaptureFile(wachSrcFile, wachDstFile,TRUE,NULL);
通过普通的Open File dialog得到新的文件名。用MultiByteToWideChar 函数把文件名转成wide string,使用ICaptureGraphBuilder::CopyCaptureFile把捕捉的数据保存到指定的文件中。
/*
* Put up a dialog to allow the user to save the contents of the capture file
* elsewhere
*/
BOOL SaveCaptureFile(HWND hWnd)
{
HRESULT hr;
char achDstFile[_MAX_PATH];
WCHAR wachDstFile[_MAX_PATH];
WCHAR wachSrcFile[_MAX_PATH];
if (gcap.pBuilder == NULL)
return FALSE;
if (OpenFileDialog(hWnd, achDstFile, _MAX_PATH)) {
// We have a capture file name
MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, gcap.szCaptureFile, -1,
wachSrcFile, _MAX_PATH);
MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, achDstFile, -1,
wachDstFile, _MAX_PATH);
statusUpdateStatus(ghwndStatus, "Saving capture file - please wait...");
// We need our own graph builder because the main one might not exist
ICaptureGraphBuilder *pBuilder;
hr = CoCreateInstance((REFCLSID)CLSID_CaptureGraphBuilder,
NULL, CLSCTX_INPROC, (REFIID)IID_ICaptureGraphBuilder,
(void **)&pBuilder);
if (hr == NOERROR) {
// Allow the user to press ESC to abort... don't ask for progress
hr = pBuilder->CopyCaptureFile(wachSrcFile, wachDstFile,TRUE,NULL);
pBuilder->Release();
}
if (hr == S_OK)
statusUpdateStatus(ghwndStatus, "Capture file saved");
else if (hr == S_FALSE)
statusUpdateStatus(ghwndStatus, "Capture file save aborted");
else
statusUpdateStatus(ghwndStatus, "Capture file save ERROR");
return (hr == NOERROR ? TRUE : FALSE);
} else {
return TRUE; // They canceled or something
}
}
关于捕捉媒体文件和获得捕捉信息的详细内容,可以参考AMCap例子的Amcap.cpp 和 Status.cpp 。
directshow的中文资料之设备列举和捕捉接口
这篇解释和示例如何通过DirectShow的接口去初始化和访问系统的硬件设备。代表性的,DirectShow应用程序使用下面类型的硬件。
音/视频捕捉卡
音频或视频回放卡
音频或视频压缩或解压卡(象MPEG解码器)
下面将以AV设备作参考。
如何列举设备
包括在DirectShow SDK中的接口,类,和例子提供了音/视频捕捉和回放的功能。因为文件源过滤器和filter graph manager处理了内在的工作,所有,添加捕捉功能到一个应用程序中,只需添加很少的代码。你可以通过列举系统硬件设备和得到设备列表完成特别的任务(例如:所有的视频捕捉卡的列表)。DirectShow自动为win32和Video for Windows 设备实例化过滤器。
要AV设备工作,首先,你必须检测当前系统存在的设备。ICreateDevEnum接口建立指定类型的列表。提供你需要的检测和设置硬件的功能。访问一个指定的设备有三步,详细的说明和代码如下:
建立系统硬件设备的列表
首先,申明一个列表指针,然后通过 CoCreateInstance 建立。CLSID_SystemDeviceEnum是我们想建立对象的类型,IID_ICreateDevEnum是接口的GUID。
ICreateDevEnum *pCreateDevEnum ;
CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER,
IID_ICreateDevEnum, (void**)&pCreateDevEnum) ;
其次,建立一个特别类型的硬件设备的列表(例如视频捕捉卡)
申明一个IEnumMoniker接口,并把他传给ICreateDevEnum::CreateClassEnumerator 方法。你就可以使用他访问新得到的列表了。
IEnumMoniker *pEnumMon ;
pCreateDevEnum->CreateClassEnumerator(
[specify device GUID here]
&pEnumMon, 0);
最后,列举列表直到你得到你想要的设备为止。
如果先前的CreateClassEnumerator调用成功了,你可以用IEnumMoniker::Next得到设备。调用IMoniker::BindToObject建立一个和选择的device联合的filter,并且装载filter的属性(CLSID,FriendlyName, and DevicePath)。不需要为if语句的(1 == cFetched) 困惑,在测试合法性之前,pEnumMon->Next(1, &pMon, &cFetched)方法会设置他为返回对象的数字(如果成功了为1)。
ULONG cFetched = 0;
IMoniker *pMon ;
if (S_OK == (pEnumMon->Next(1, &pMon, &cFetched)) && (1 == cFetched))
{
pMon->BindToObject(0, 0, IID_IBaseFilter, (void **)&[desired interface here]) ;
好,现在你有了一个IMoniker指针,你可以添加设备的filter到filter graph。一旦你添加了filter,你就不需要IMoniker指针,设备列表,或系统设备列表。
pGraph->AddFilter([desired interface here], L"[filter name here]") ;
pMon->Release() ; // Release moniker
}
pEnumMon->Release() ; // Release the class enumerator
}
pCreateDevEnum->Release();
实例:AMCap中的设备列表代码
AMCap例子中,把所有的接口指针和一些成员变量保存在一个全局结构gcap中了。
定义如下:
struct _capstuff {
char szCaptureFile[_MAX_PATH];
WORD wCapFileSize; // size in Meg
ICaptureGraphBuilder *pBuilder;
IVideoWindow *pVW;
IMediaEventEx *pME;
IAMDroppedFrames *pDF;
IAMVideoCompression *pVC;
IAMVfwCaptureDialogs *pDlg;
IAMStreamConfig *pASC; // for audio cap
IAMStreamConfig *pVSC; // for video cap
IBaseFilter *pRender;
IBaseFilter *pVCap, *pACap;
IGraphBuilder *pFg;
IFileSinkFilter *pSink;
IConfigAviMux *pConfigAviMux;
int iMasterStream;
BOOL fCaptureGraphBuilt;
BOOL fPreviewGraphBuilt;
BOOL fCapturing;
BOOL fPreviewing;
BOOL fCapAudio;
int iVideoDevice;
int iAudioDevice;
double FrameRate;
BOOL fWantPreview;
long lCapStartTime;
long lCapStopTime;
char achFriendlyName[120];
BOOL fUseTimeLimit;
DWORD dwTimeLimit;
} gcap;
例子用uIndex变量循环列举系统的硬件设备。
BOOL InitCapFilters()
{
HRESULT hr;
BOOL f;
UINT uIndex = 0;
MakeBuilder函数建立了一个filter graph builder(参考建立一个捕捉程序)。
f = MakeBuilder();
建立设备列表对象,得到ICreateDevEnum接口
ICreateDevEnum *pCreateDevEnum;
hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER,
IID_ICreateDevEnum, (void**)&pCreateDevEnum);
建立一个特别类型的硬件设备的列表,类的ID是CLSID_VideoInputDeviceCategory。现在有了一个IEnumMoniker指针,可以访问捕捉设备的列表了。
IEnumMoniker *pEm;
hr = pCreateDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pEm, 0);
pCreateDevEnum->Release(); // We don't need the device enumerator anymore
pEm->Reset(); // Go to the start of the
enumerated list
现在需要实际的设备了,调用IEnumMoniker::Next ,然后用得到的指针pM调用IMoniker::BindToObject,绑定filter到设备。如果你不想建立联合的filter,使用IMoniker::BindToStorage 代替IMoniker::BindToObject。
ULONG cFetched;
IMoniker *pM; // This will access the actual devices
gcap.pVCap = NULL;
while(hr = pEm->Next(1, &pM, &cFetched), hr==S_OK)
{
if ((int)uIndex == gcap.iVideoDevice) { // This is the one we want. Instantiate it.
hr = pM->BindToObject(0, 0, IID_IBaseFilter, (void**)&gcap.pVCap);
pM->Release(); // We don't need the moniker pointer anymore
break;
}
pM->Release();
uIndex++;
}
pEm->Release(); // We've got the device; don't need the
enumerator anymore
当有了设备后,通过接口指针去测量帧数,得到driver的名字,得到捕捉的尺寸(size)。在例子中,把每个指针都存储才gcap全局结构中了。
, and get the capture size. AMCap stores each pointer in the gcap global structure.
// We use this interface to get the number of captured and dropped frames
gcap.pBuilder->FindCaptureInterface(gcap.pVCap,
IID_IAMDroppedFrames, (void **)&gcap.pDF);
// We use this interface to get the name of the driver
gcap.pBuilder->FindCaptureInterface(gcap.pVCap,
IID_IAMVideoCompression, (void **)&gcap.pVC);
// We use this interface to set the frame rate and get the capture size
gcap.pBuilder->FindCaptureInterface(gcap.pVCap,
IID_IAMVideoStreamConfig, (void **)&gcap.pVSC);
然后得到媒体的类型和显示窗口的大小去匹配视频格式的尺寸。
AM_MEDIA_TYPE *pmt;
gcap.pVSC->GetFormat(&pmt); // Current capture format
ResizeWindow(HEADER(pmt->pbFormat)->biWidth,
HEADER(pmt->pbFormat)->biHeight);
DeleteMediaType(pmt);
现在,已经有了视频设备和他的相关信息,重复这个过程,得到音频设和他的信息并存储到全局机构中去。注意,这次是用参数CLSID_AudioInputDeviceCategory 调用ICreateDevEnum::CreateClassEnumerator 。
hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER,
IID_ICreateDevEnum, (void**)&pCreateDevEnum);
uIndex = 0;
hr = pCreateDevEnum->CreateClassEnumerator(CLSID_AudioInputDeviceCategory,
&pEm, 0);
pCreateDevEnum->Release();
pEm->Reset();
gcap.pACap = NULL;
while(hr = pEm->Next(1, &pM, &cFetched), hr==S_OK)
{
if ((int)uIndex == gcap.iAudioDevice) { // this is the one we want
hr = pM->BindToObject(0, 0, IID_IBaseFilter, (void**)&gcap.pACap);
pM->Release();
break;
}
pM->Release();
uIndex++;
}
pEm->Release();
AMCap also repeats the process of retrieving the format interface, this time for the audio device.
hr = gcap.pBuilder->FindCaptureInterface(gcap.pACap,
IID_IAMAudioStreamConfig, (void **)&gcap.pASC);
}
如何保持DirectShow Filter (Properties) 道具
IPropertyBag 和 IPersistPropertyBag 接口存储和返回Properties的"bags"组。通过这些接口存储的Properties是可以持久保持的。同一个对象在不同的实例之间,他们保持一致。Filter可以存储他们的Properties(CLSID, FriendlyName, and DevicePath)。当一个filter存储完他的Properties之后,实例一个filter时,DirectShow会自动得到他们。添加功能到你的filter中,执行IPersistPropertyBag接口和他的方法。你可以用IPropertyBag::Read 方法装载filter Properties 到Win32 VARIANT 变量中,然后初始化输入输出pin。
下面的代码演示DirectShow的VfWCapture filter如何执行IPersistPropertyBag::Load方法的。记住:在执行期间,你的filter必须提供一个有效的IPropertyBag指针。
STDMETHODIMP CVfwCapture::Load(LPPROPERTYBAG pPropBag, LPERRORLOG pErrorLog)
{
HRESULT hr;
CAutoLock cObjectLock(m_pLock); // Locks the object; automatically unlocks it in the destructor.
if (m_pStream) // If the filter already exists for this stream
return E_UNEXPECTED;
VARIANT var; // VARIANT from Platform SDK
var.vt = VT_I4; // four-byte integer (long)
hr = pPropBag->Read(L"VFWIndex", &var, 0); // VFWIndex is the private name used by the Vidcap Class Manager to refer to the VFW Capture filter
if(SUCCEEDED(hr)) // If it read the properties successfully
{
hr = S_OK; // Defaults return value to S_OK
m_iVideoId = var.lVal; // Stores the specified hardware device number
CreatePins(&hr); // Inits the pins, replacing the return value if necessary
}
return hr; // Returns S_OK or an error value, if CreatePins failed
在Delphi中调用Flash
文 老猫
----本文参考了Delphi世界之大富翁论坛上阿蒙和浦青锋两位大侠的
意见。
----原来我并不打算把这种问题放在网上,我以为大家都会知道。后
来好友流云建议说,她们班好多人都学Flash,很想知道如何在Delphi
中调用Flash。我就讲了一下,并顺便把它放在了主页上。
----如果你的计算机已装了Flash,那就非常简单。如果还没装,那么
光在网上下载flash 插件也可以,然后启动你的Delphi,再Component
菜单项选Import ActiveX Control...,接下来在Import ActiveX
Control项就会有Sockwave Flash (版本号)这项出现,选中它再按
Install...按钮。
----现在你的Component面板有ShockwaveFlash控件了吧,它的使用方
法和其他控件没有大的差别,做一个简单示例。
----新建一个Project,在Form上放一个button和一个opendialog 控
件,再放上ShockwaveFlash控件。opendialog 用于打开Flash文件(*.
swf),button的 onclick 事件如下:
begin
----opendialog1.execute;
----swf:=opendialog1.filename;
----shockwaveflash1.Movie:=swf;
----shockwaveflash1.play;
end;
----编译通过,你就可以用按钮打开对话框,选择*.swf文件就可以
了。
用VC制作动态弹出的对话框
在“网络蚂蚁”中,如果在下载过程中有错误发生,或是提示下载完成时,你都可以在屏幕中央看到一个由小变大、动态弹出的对话框,那么,这个动态弹出的对话框是如何实现的呢?
一、选择方法
通常,我们可以在对话框的WM_INITDIALOG事件的响应函数中加入对话框的一些初始化设置,比如改变窗口的位置及大小。同样,我们也可以在WM_INITDIALOG事件的响应函数中让对话框由小到大地显示。但是,如何改变对话框的大小呢?在编程过程中,我们通常是利用MoveWindow函数来动态调整对话框的大小,但是MoveWindow函数有其局限性:一是它的速度较慢;二是由于众所周知的MFC内存泄漏的原因,如果重复调用MoveWindow函数来改变对话框的大小,MoveWindow不能够将屏幕清“干净”(这点你可以在实际编程中试一下),这显然不是我们所需要的。能不能找到一种其他的方法来快速地改变窗口大小呢?我们知道,用SetWindowRgn函数能够改变窗口的显示区域,那么,在对话框显示之前,让程序将对话框先隐藏起来,然后让对话框的显示区域不断变大,这样就实现了对话框的动态显示。
在继续介绍之前,我们先来介绍一下SetWindowRgn的具体用法,以下是其调用格式:
int SetWindowRgn( HRGN hRgn, BOOL bRedraw );
hRgn:指向一区域的句柄,通常该参数是由CRgn对象创建的。
bRedraw:指明在设定窗口显示区域之后系统是否进行窗口重绘操作。
二、应用实例
接下来我们以让程序的“帮助对话框”动态弹出为例,介绍如何通过编程的方式来实现对话框的动态弹出显示。在CAboutDlg类中加入对WM_INITDIALOG的映射,并编辑其响应函数OnInitDialog,该函数的具体内容设置如下:
BOOL CAboutDlg::OnInitDialog()
{
CDialog::OnInitDialog();
file://隐藏对话框
ShowWindow(SW_HIDE);
CRect dlgRect;
GetClientRect(&&dlgRect);
CPoint centerPoint;
centerPoint.x=dlgRect.Width()/2;
centerPoint.y=dlgRect.Height()/2;
file://得到对话框的中点坐标
CRgn testrgn;
this-〉ShowWindow(SW_HIDE);
int m=GetSystemMetrics(SM_CYSIZEFRAME);
file://以下代码实现对话框的动态弹出
for (int i=10;i〈dlgRect.Width()/2+2m;i+=1)
{
testrgn.CreateRectRgn(centerPoint.x-i,centerPoint.y-i,centerPoint.x+i,centerPoint.y+i);
SetWindowRgn((HRGN) testrgn,TRUE);
ShowWindow(SW_SHOW);
CenterWindow();
testrgn.DeleteObject();
}
return TRUE;
}
该程序的代码比较简单,编译之后进入程序的帮助选单,你会看见一个由小变大的对话框跃然于屏幕之上。
三、深入讨论
需要说明的是,本文中主要的目的是介绍编程思路,所以在编程中是首先假定了帮助对话框是正方形或基本上是正方形的,如果你的帮助对话框的形状长宽比例较大,该程序在运行时的界面效果会不太理想。这时,你可以在编程中考虑按对话框的长宽比例来动态创建其他形状的显示区域。
///////
《编程爱好者》订退方法
请在下面的文本框内输入您订阅本刊的邮件地址,并按右面的订阅按钮即可。如果您觉得这份刊物还不错的话,欢迎把它推荐给您的朋友。
欢迎订阅
不知道您看了这期刊物有什么想法或者是意见,欢迎向我提出来。
本人感激不尽,我的联系方法如下:
Homepage: http://www.programfan.com
E-mail: pfan2000@163.net
OICQ: 15987743