用Windows Media API抓屏
Windows Media 9.0 支持用Windows Media Encoder 9 API来抓屏。它有一个编码器叫Windows Media Video 9 Screen codec,特别为抓屏优化过。Windows Media Encoder API提供了一个IWMEncoder2接口可以用来高效地捕捉屏幕图像。
用这种技术进行抓屏也很简单,首先我们用CoCreateInstance()创建一个IWMEncoder2对象:
IWMEncoder2* g_pEncoder=NULL;
CoCreateInstance(CLSID_WMEncoder,NULL,CLSCTX_INPROC_SERVER,
IID_IWMEncoder2,(void**)&g_pEncoder);
这个Encoder对象包含了捕捉屏幕的所需的全部操作,然而为了正确地工作,编码器对象的行为取决于被称作profile的设置。一个profile只是一个包含了所有控制编码操作设置的文件,我们可以根据被捕捉的数据的特性在运行时创建包含自定义设置的profile。为了在你的抓屏程序中使用profile,我们基于Windows Media Video 9 Screen codec来创建自定义的profile。自定义的profile对象从IWMEncProfile2开始就被支持了。我们可以用CoCreateInstance来创建自定义profile
IWMEncProfile2* g_pProfile=NULL;
CoCreateInstance(CLSID_WMEncProfile2,NULL,CLSCTX_INPROC_SERVER,
IID_IWMEncProfile2,(void**)&g_pProfile);
我需要在profile里指定编码器的听众(audience)。每个profile可以包含多个听众配置,它们是IWMEncAudienceObj接口对象。这里我们为profile使用一个听众。我们可以通过IWMEncProfile::AddAudience()为我们的profile创建听众,这个函数返回一个IWMEncAudienceObj指针,可以用来配置视频编码器(IWMEncAudienceObj::put_VideoCodec()),视频帧对象(IWMEncAudienceObj::put_VideoHeight() 和IWMEncAudienceObj::put_VideoWidth())我们用下面的代码来配置视频编码器:
extern IWMEncAudienceObj* pAudience;
#define VIDEOCODEC MAKEFOURCC('M','S','S','2')
//MSS2 is the fourcc for the screen codec
long lCodecIndex=-1;
g_pProfile->GetCodecIndexFromFourCC(WMENC_VIDEO,VIDEOCODEC,
&lCodecIndex); //Get the Index of the Codec
pAudience->put_VideoCodec(0,lCodecIndex);
fourcc是针对每个编码器的唯一的标识,Windows Media Video 9 Screen codec的fourcc为MSS2。IWMEncAudienceObj::put_VideoCodec()接受profile索引来组织一个profile,索引可以用IWMEncProfile::GetCodecIndexFromFourCC()取得。
一旦我们配置完毕一个profile对象,我们就可以用IWMEncSourceGroup :: put_Profile()选择这个profile到我们的编码器。一个源组(SourceGruop)是一组视频流来源或音频流来源,或html来源。每个编码器可以使用许多源组,并从中取得输入数据。由于我们的程序仅仅使用视频流中是视频来源。这个视频来源需要用IWMEncVideoSource2::SetInput(BSTR) Screen Device来配置为输入来源:
extern IWMEncVideoSource2* pSrcVid;
pSrcVid->SetInput(CComBSTR("ScreenCap://ScreenCapture1");
目的输出可以用IWMEncFile::put_LocalFileName()配置为保存到视频文件(wmv文件)。IWMEncFile对象可以用IWMEncoder::get_File()得到:
IWMEncFile* pOutFile=NULL;
g_pEncoder->get_File(&pOutFile);
pOutFile->put_LocalFileName(CComBSTR(szOutputFileName);
现在,一旦编码器对象的一切所需配置都完成后,我们就可以用IWMEncoder::Start()开始抓屏。IWMEncoder::Stop() 和 IWMEncoder::Pause可以用来停止和暂停捕捉。
这些适用于全屏捕捉,我们也可以通过调整输入视频来源流的属性来选择一个区域进行捕捉。我们可以用IWmEnVideoSource2的IPropertyBag 接口来实现:
#define WMSCRNCAP_WINDOWLEFT CComBSTR("Left")
#define WMSCRNCAP_WINDOWTOP CComBSTR("Top")
#define WMSCRNCAP_WINDOWRIGHT CComBSTR("Right")
#define WMSCRNCAP_WINDOWBOTTOM CComBSTR("Bottom")
#define WMSCRNCAP_FLASHRECT CComBSTR("FlashRect")
#define WMSCRNCAP_ENTIRESCREEN CComBSTR("Screen")
#define WMSCRNCAP_WINDOWTITLE CComBSTR("WindowTitle")
extern IWMEncVideoSource2* pSrcVid;
int nLeft, nRight, nTop, nBottom;
pSrcVid->QueryInterface(IID_IPropertyBag,(void**)&pPropertyBag);
CComVariant varValue = false;
pPropertyBag->Write(WMSCRNCAP_ENTIRESCREEN,&varValue);
varValue = nLeft;
pPropertyBag->Write( WMSCRNCAP_WINDOWLEFT, &varValue );
varValue = nRight;
pPropertyBag->Write( WMSCRNCAP_WINDOWRIGHT, &varValue );
varValue = nTop;
pPropertyBag->Write( WMSCRNCAP_WINDOWTOP, &varValue );
varValue = nBottom;
pPropertyBag->Write( WMSCRNCAP_WINDOWBOTTOM, &varValue );
本文附带的源码实现此中技术的抓屏。除去生成的动画质量很好外,一个有意思的地方是鼠标指针也被抓到了(GDI和DirectX默认是不抓取鼠标指针的)。
注意,为了适用WindowMedia9.0 API,你的电脑必须安装Windows Media9.0 SDK,你可以用下面地址下载:
http://msdn.microsoft.com/library/default.asp?url=/downloads/list/winmedia.asp
最终用户必须安装Windows Media Encoder 9 系列才能运行你的程序。在发布基于Windows Media Encoder SDK的程序时,Windows Media Encoder软件也必须附带上去,要么在你的软件安装时自动安装Windows Media Encoder要么让用户自己下载安装。
Windows Encoder 9.0可以从下面地址下载:
http://www.microsoft.com/windows/windowsmedia/ 9series/encoder/default.aspx
结论
上面讨论的各种方法都是基于一个目标-抓取屏幕的内容。然而适用不同的技术,得到的结果也不一样。如果我们需要的只是偶尔的抓屏,GDI方式是个好的选择,因为它简单。然而如果你想得到更专业的结果,可以使用Windows Media。一个可能没有意义的要点是,这些技术捕捉到的内容的质量很大程度上决于你的系统设置,比如进制硬件加速会大大提高抓屏的质量和程序的运行效率。
|