玩转DVRMS.docx
- 文档编号:7359954
- 上传时间:2023-01-23
- 格式:DOCX
- 页数:32
- 大小:564.67KB
玩转DVRMS.docx
《玩转DVRMS.docx》由会员分享,可在线阅读,更多相关《玩转DVRMS.docx(32页珍藏版)》请在冰豆网上搜索。
玩转DVRMS
玩转DVR-MS
发布日期:
6/7/2005|更新日期:
6/7/2005
StephenToub
MicrosoftCorporation
适用于:
MicrosoftWindowsXPMediaCenterEdition2005
MicrosoftDirectShow
DirectX9.0SDK
摘要:
StephenToub讨论了WindowsXPMediaCenter2005生成的DVR-MS文件格式,介绍了DirectShow并展示了如何使用后者处理前者。
下载DVR-MS示例Code.msi。
本页内容
播放DVR-MS文件
DirectShow和GraphEdit简介
DirectShow接口
将编码转换为WMV
调试筛选器图形
非托管资源清理
将WmvConverter投入使用:
WmvTranscoderPlugin
访问DVR-MS元数据
编辑DVR-MS文件
小结
相关书籍
致谢
几年前我拥有一台TiVo。
它已经不知藏在公寓壁橱的哪个角落了,我想现在一定是布满灰尘,诚然,就是现在我也可能这样对待它。
占据电视旁宝贵空位的是一个更漂亮、更复杂的现代化软件和电子产品—MicrosoftWindowsXPMediaCenter2005。
我的家人为该设备取了个既得体又人性化的名字—“米老鼠”,它有许多神奇的功能。
然而,当我建议我的“技术娴熟”的朋友们放弃他们现在使用的任一款数字摄像机(DVR)而转为使用此平台时,只要他们让我说明一个理由,我的回答都很简单:
可以对录制的电视节目进行文件访问。
DVR-MS文件是由WindowsXPServicePack1引入的流缓冲引擎(StreamBufferEngine,SBE)创建的,MediaCenter用它存储录制的电视节目。
在本文中,我将向您演示如何通过托管代码使用DirectShow来处理和操作DVR-MS文件。
在此过程中,我将向您介绍我为处理DVR-MS文件而创建的一些有用的实用工具,并为您提供您在编写自己的代码时需要的工具和库。
所以,请打开VisualStudio.NET,抓一把爆米花,享受这个过程吧。
注本文假定您的系统中有一个正在工作的MPEG2解码器,并且您使用的是NTSC而非HD内容(虽然这里讨论的大多数概念适用于PAL和HD,但示例代码可能无法正确地处理这些格式)。
另外,由于内容所有者或广播公司所设置的策略,一些DVR-MS文件受到复制保护。
这种保护是在生成文件时通过检查广播公司的复制保护标志(CGMS-A)确定的,它会限制您访问特定DVR-MS文件的方式和时间。
例如,在收费台(如HBO)录制的电影可能是加密的,因此本文描述的技术就不适用了。
最后,与本文相关联的代码示例和应用程序是针对.NETFramework1.1编译的。
然而,默认情况下WindowsXPMediaCenter2005并没有附带安装.NETFramework1.1,而是安装1.0。
因此,要在您的MediaCenter中使用这些示例,您必须安装.NETFramework1.1(可通过WindowsUpdate获得)或者重新编译该示例以适用.NETFramework1.0。
1.播放DVR-MS文件
谈到视频文件时,播放或许是可以执行的最重要的操作,所以我将从此入手。
在您自己的应用程序中可以有多种播放DVR-MS文件的方式,这里我将演示其中的一些。
为此,我创建了一个简单的应用程序(如图1所示),您可以在与本文有关的代码下载中获得。
图1.播放DVR-MS文件的示例应用程序
播放DVR-MS文件的第一种方式也是最简单的方式是,使用 System.Diagnostics.Process 类来执行它。
由于 Process.Start 包装了来自shell32.dll的 ShellExecuteEx 非托管函数,因此这种方式利用了与从WindowsExplorer双击一个文件相同的功能来播放DVR-MS文件:
privatevoidbtnProcessStart_Click(objectsender,System.EventArgse){ Process.Start(txtDvrmsPath.Text);}
这也意味着,视频将在一个独立的进程中播放,这个进程在DVR-MS文件的任何默认处理程序中运行;对于大多数机器和我的机器来说,它就是WindowsMediaPlayer(我使用WindowsMediaPlayer10,如果您没有,我建议您从 免费升级到该版本)。
当然,Process.Start 有另一个同时接受可执行路径和参数的重载,可以使用它在任何您想要的播放机中启动DVR-MS文件,不管它是否是.dvr-ms扩展名的默认处理程序:
privatevoidbtnProcessStart_Click(objectsender,System.EventArgse){ Process.Start( @"c:
\\ProgramFiles\\WindowsMediaPlayer\\wmplayer.exe", "\\""+txtDvrmsPath.Text+"\\"");}
您应该注意到,当这样做时,有必要对DVR-MS文件的路径加上引号(正如此处名为txtDvrmsPath的 TextBox 的内容所提供的),因为要使用的内容是wmplayer.exe的一个命令行参数。
否则,路径中的任何空格都会使路径被分隔并解释为多个参数。
Process.Start 返回一个代表启动进程的 Process 实例,这意味着您可以利用 Process 提供的功能来与WindowsMediaPlayer进一步交互。
例如,在您的应用程序中,您可能想先等待视频停止再让用户继续,可以使用 Process.WaitForExit 方法来完成这样的任务:
privatevoidbtnProcessStart_Click(objectsender,System.EventArgse){ using(Processp=Process.Start(txtDvrmsPath.Text)) { p.WaitForExit(); }}
当然,这只是等待MediaPlayer关闭,而不是像初始请求那样播放您指定的文件,因为您的应用程序没有真正的视图可以查看MediaPlayer执行的内容。
当打开MediaPlayer时,按上述方法编码也会冻结应用程序的GUI,这个问题可以通过订阅 Process 的 Exited 事件加以解决,而不是用 WaitForExit 方法阻止。
总而言之,该解决方案编码简单方便,但非常不灵活,而且是在应用程序的外部播放视频。
它可能只在以下情况下才适用,您想允许用户查看指定的文件,不过是在应用程序不必关心视频内容而且应用程序根本不与视频交互的情况下查看。
例如,如果您的应用程序是一个下载代理,而且您想允许用户查看已经复制到本地的视频文件,则可能适合采用这种方式。
由于我们知道WindowsMediaPlayer可以播放DVR-MS文件,因此对于大多数情况,更好的解决方案是在应用程序中宿主WindowsMediaPlayerActiveX控件的一个实例。
在VisualStudio.NET中,只需右键单击工具箱,选择添加控件并选择WindowsMediaPlayerCOM控件。
这样它就会出现在工具箱中,如图2所示。
图2.工具箱中的WindowsMediaPlayerActiveX控件
当窗体中有一个ActiveX控件的实例时,让它播放DVR-MS文件就只需设置播放器的 URL 属性:
player.URL=txtDvrmsPath.Text;
在我的示例应用程序中,我选择让它更进一步。
我创建了一个 System.Windows.Forms.Panel,它位于想要显示视频的窗体中。
当用户请求使用MediaPlayer播放选定的视频时,我就新建一个MediaPlayer控件的实例,将它添加到 Panel 的子控件集合中,使其保持在最大化,并设置其 URL 属性。
这种方案允许我完全控制MediaPlayer的生存期,而且可以轻松管理它在窗体中的位置,而不用担心它的绝对定位值(这种方案也使演示播放视频的其他方法变得轻松,稍后您将看到)。
正在使用的这种方案的屏幕快照如图3所示,下面显示的是我使用的代码:
privatevoidbtnWmp_Click(objectsender,System.EventArgse){ AxWindowsMediaPlayerplayer=newAxWindowsMediaPlayer(); pnlVideo.Controls.Add(player); player.Dock=DockStyle.Fill; player.PlayStateChange+= new_WMPOCXEvents_PlayStateChangeEventHandler( player_PlayStateChange); player.URL=txtDvrmsPath.Text;}privatevoidplayer_PlayStateChange( objectsender,_WMPOCXEvents_PlayStateChangeEvente){ AxWindowsMediaPlayerplayer=(AxWindowsMediaPlayer)sender; if(e.newState==(int)WMPLib.WMPPlayState.wmppsMediaEnded|| e.newState==(int)WMPLib.WMPPlayState.wmppsStopped) { player.Parent=null;//removesthecontrolfromthepanel ThreadPool.QueueUserWorkItem( newWaitCallback(CleanupVideo),sender); }}privatevoidCleanupVideo(objectvideo){ ((IDisposable)video).Dispose();}
图3.使用WMP控件的嵌入式DVR-MS播放
要阻止显示MediaPlayer工具栏,您可以更改控件的 uiMode 属性:
player.uiMode="none";
要在用户右键单击控件时阻止显示MediaPlayer上下文菜单,可以将其 enableContextMenu 属性设置为false:
player.enableContextMenu=false;
您将注意到,在播放DVR-MS文件的前一刻,我为播放器的 PlayStateChange 事件注册了一个事件处理程序。
这可以使我在播放停止时从 Panel 删除播放器。
在 PlayStateChange 事件的处理程序中,我检查播放是否结束,如果结束,就将播放器从其父控件(面板)删除,并将一个工作项排入.NET ThreadPool 队列中。
这个工作项的作用只是处置播放器控件。
我是在后台线程中进行此次处置的,因为无法在 PlayStateChange 事件处理程序中直接处置。
在此事件处理程序中处置控件会在控件本身中引发异常,因为事件处理程序是在控件中引发的,控件在执行完我的处理程序之后还需要进行更多的处理。
在处理程序中处置播放器控件会导致功能被破坏,所以我让该操作在事件处理程序完成之后稍微延迟一会,以便留出必要的时间。
您将看到,在使用所演示的下一个播放机制时,就需要用到同一技术。
宿主WindowsMediaPlayerActiveX控件有许多好处。
它使用起来非常方便,而且提供了大量的功能。
然而,WindowsMediaPlayer使用DirectX(特别是DirectShow)来播放DVR-MS文件(本文后面我将更详细地讨论DirectShow)。
您不是依赖WindowsMediaPlayer与DirectX交互,而是在您的应用程序中使用ManagedDirectX,完全跳过WindowsMediaPlayer。
在写作本文时ManagedDirectX的最新版本是 DirectX9.0SDKUpdateFebruary2005下载的一部分。
(要获得本文后面介绍的内容,您还需要 February2005Extras下载。
)此SDK在您的全局程序集缓存(GAC)中安装了AudioVideoPlayback.dll程序集,使其可用于您的应用程序(DirectX运行库安装也安装了此DLL以使您的最终用户可以访问它)。
AudioVideoPlayback是一个高级包装,它含有您在.NET应用程序中播放视频和音频文件所需要的最少的DirectShow功能。
有了WindowsMediaPlayerActiveX控件后,使用AudioVideoPlayback变得非常简单。
privatevoidbtnManagedDirectX_Click(objectsender,System.EventArgse){ Videov=newVideo(txtDvrmsPath.Text); Sizes=pnlVideo.Size; v.Owner=pnlVideo; v.Ending+=newEventHandler(v_Ending); v.Play(); pnlVideo.Size=s;}privatevoidv_Ending(objectsender,EventArgse){ ThreadPool.QueueUserWorkItem( newWaitCallback(CleanupVideo),sender);}privatevoidCleanupVideo(objectvideo){ ((IDisposable)video).Dispose();}
这段代码首先实例化一个新的 Microsoft.DirectX.AudioVideoPlayback.Video 对象,然后将要播放的DVR-MS文件的路径提供给它。
当播放一段 Video 时,它会自动将自身的大小(更具体地说是将它的所有者控件)调整为所播放视频的合适大小;为了解决这个问题,我存储了父面板控件的原始大小,这样在开始播放后就可以重置其大小。
就像处理ActiveX控件那样,我注册了一个要在播放停止时激发的事件处理程序,然后播放视频。
当播放结束时,我将一个工作项排入要处置 Video 对象的 ThreadPool 队列中,如同使用ActiveX控件一样(原因也相同)。
当您不再使用 Video 对象时,对其进行处置是非常重要的;否则会浪费大量非托管资源,而且由于此对象有一个非常小的托管占地,垃圾回收器(GC)没有重大的动因可以及时进行回收,这样将使这些非托管资源的分配情况不明,除非您手动通过 IDisposable 处置。
图4中的屏幕快照演示了AudioVideoPlayback功能的使用。
图4.采用AudioVideoPlayback 的嵌入式播放
当然,虽然AudioVideoPlayback是一个高级DirectShow包装,但并不意味着您不能创建自己的托管包装(实际上,在本文后面我们将这样做)。
创建托管包装的最简单方式是使用tlbimp.exe(或者采用类似的做法—使用VisualStudio.NET的COM类型库导入功能。
VisualStudio.NET和tlbimp.exe都依赖于Framework中同样的库执行导入)。
DirectShow运行库的核心库是quartz.dll,位于%windir%\\system32\\quartz.dll。
它包含用于音频和视频播放的最重要的COM接口和coclass,本文后面将对此进行更加详细的讨论。
在quartz.dll上运行tlbimp.exe会产生一个interop库—Interop.QuartzTypeLib.dll(此程序集的描述信息为“ActiveMoviecontroltypelibrary”,因为DirectShow的前身名为ActiveMovie),并公开 FilgraphManagerClass(筛选器图形管理器)和 IVideoWindow 接口。
要播放视频,您只需创建该图形管理器的一个新实例并使用 RenderFile 方法,在DVR-MS文件路径中传送,以便初始化该对象以进行播放。
然后可以使用由 FilgraphManagerClass 实现的 IVideoWindow 接口来控制播放选项,例如所有者窗口、视频在父窗口中的位置,以及视频窗口的标题。
要开始播放,可以使用 Run 方法。
WaitForCompletion 方法可以用于等待视频停止播放(或者,可以指定一个正的毫秒数,作为要等待的最长时间),Stop 方法可以用于暂停播放。
要销毁该对象并释放用于播放的所有非托管资源(包括播放窗口本身),System.Runtime.InteropServices.Marshal 类及其 ReleaseComObject 方法就会派得上用场了。
使用quartz.dll的屏幕快照如图5所示。
privatevoidbtnQuartz_Click(objectsender,System.EventArgse){ FilgraphManagerClassfm=newFilgraphManagerClass(); fm.RenderFile(txtDvrmsPath.Text); IVideoWindowvid=(IVideoWindow)fm; vid.Owner=pnlVideo.Handle.ToInt32(); vid.Caption=string.Empty; vid.SetWindowPosition(0,0,pnlVideo.Width,pnlVideo.Height); ThreadPool.QueueUserWorkItem(newWaitCallback(RunQuartz),fm);}privatevoidRunQuartz(objectstate){ FilgraphManagerClassfm=(FilgraphManagerClass)state; fm.Run(); intcode; fm.WaitForCompletion(Timeout.Infinite,outcode); fm.Stop(); while(Marshal.ReleaseComObject(fm)>0);}
图5.使用quartz.dll的嵌入式播放
我刚刚向您介绍了一些在自己的应用程序中播放DVR-MS文件的方法。
虽然我讨论了多个播放DVR-MS文件的方法(而且我还没列举完),但所有这些方法都要依赖于DirectShow才有播放功能。
因此,我们将简要介绍一下DirectShow(或者让那些具有DirectShow经验的人重温一下)。
2.DirectShow和GraphEdit简介
在本质上,使用DirectShow处理视频文件的应用程序是通过一组称为筛选器的组件完成的。
一个筛选器通常只对多媒体数据流执行一种操作。
这样的筛选器很多,每个筛选器执行不同的任务,例如读取DVR-MS文件、写出AVI文件、对MPEG-2压缩视频进行解码、将视频和音频呈现到视频卡和声卡上,等等。
这些筛选器的实例可以连接在一起并组合成一个筛选器图形,然后由DirectShow筛选器图形管理器组件进行管理(在前面介绍quartz.dll时,您已简要地对其进行了了解)。
这些图形是定向的,也是非循环的,这意味着两个筛选器之间的特定连接只允许数据朝一个方向流动,而且只能流经特定筛选器一次。
这种数据流程称为流(stream),而筛选器则用来处理这些流。
筛选器是通过它们公开的针(pin)连接到其他筛选器的,因此,一个筛选器的输出针连接到另一个筛选器的输入针,并按从前者发送到后者的方式发送数据流。
为了对此进行演示并显示本文中所使用的图形,我使用了DirectXSDK中一个名为GraphEdit的实用工具。
GraphEdit可以用来使筛选器图形可视化,当要确定如何构建用于特定目的的图形以及调试您所构建的图形时,这个功能就能派上用场。
稍后,我将介绍如何使用GraphEdit来对在您的应用程序中运行的筛选器图形进行连接和可视化。
现在,我们运行GraphEdit。
在“File”菜单下,选择“RenderMediaFile”,然后选择本地可用的任何有效的DVR-MS文件(请注意,您可能需要在“OpenFile”对话框中将筛选器扩展名更改为“AllFiles”,而不是“AllMediaFiles”,因为最近发布的GraphEdit版本并没有将.dvr-ms扩展名归类为媒体文件)。
您应该能够看到一个图形,它类似于图6所示的图形。
图6.GraphEdit准备播放DVR-MS文件
此时,GraphEdit已构造了一个筛选器图形,它能够播放选定的DVR-MS文件。
这些蓝框中的每一个都是一个筛选器,箭头显示每个筛选器上的输入和输出针如何互相连接以形成图形。
图形中的第一个筛选器是StreamBufferSource筛选器的实例,它由WindowsXPSP1及更高版本的%windir%\\system32\\sbe.dll库公开。
选择这个筛选器是因为它在注册表中配置为.dvr-ms扩展名的源筛选器(HKCL\\MediaType\\Extensions\\.dvr-ms\\SourceFilter)。
它的作用是从磁盘中读取一个文件,并将该文件的数据以流的形式发送到图形的其他部分。
它从一个DVR-MS文件提供三个流。
第一个是音频流。
如果您检查第一个针的针属性(DVROut-1,可以通过右键单击GraphEdit中的针来访问针属性),您可以发现该针的主要类型是Audio,而其子类型是Encrypted/Tagged,这意味我们在对该数据进行任何操作之前必须先对它进行解密和/或取消标记。
这个过程是由Decrypter/Detagger筛选器(由%windir%\\system32\\encdec.dll公开)处理的。
Decrypter/Deta
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- DVRMS
![提示](https://static.bdocx.com/images/bang_tan.gif)