- 浏览: 108846 次
- 性别:
- 来自: 南京
-
文章分类
最新评论
使用 Windows Mobile 5.0 中的图片、视频和照相机
适用于:
Windows Mobile version 5.0
Microsoft Visual Studio .NET 2005
Microsoft .NET Compact Framework 版本 2.0
摘要:学习 Windows Mobile 5.0 和 .NET Compact Framework 2.0 中新增的多媒体功能,并了解如何在您的企业应用程序中使用这些功能。本文通过使用一个下载代码示例以及其他用 C# 编写的代码示例,演示新增的功能。
请从 Microsoft Download Center 下载 Working with Multimedia.msi。

本页内容
![]() |
简介 |
![]() |
检查业务过程 |
![]() |
应用程序设计 |
![]() |
示例应用程序演练 |
![]() |
代码演练 |
![]() |
媒体播放器控件 |
![]() |
DirectShow |
![]() |
小结 |
简介
Windows Mobile version 5.0 通过集成的照相机应用程序编程接口 (API) 提供优秀的多媒体支持,这些 API 使开发人员能够将照相机、图片和视频功能直接嵌入到应用程序中。它还包括大量新增的多媒体 API。Microsoft DirectShow 在视频流和音频流的捕获和播放方面为开发人员提供更强的控制和更高的灵活性。Microsoft Windows Media Player 10 Mobile 使开发人员能够将媒体播放器功能(包括媒体库的管理和播放)集成到他们的应用程序中。Microsoft DirectDraw 在呈现高速的二维图形方面为开发人员提供更强的控制和更高的灵活性。Microsoft Direct3D 使开发人员能够交付更丰富的三维游戏环境,该 API 也可以在托管代码中使用。
本文通过一个基于业务方案(检查)的实际示例应用程序,使您了解如何使用集成的照相机 API 来捕获照片和视频。该示例说明新的高级构造(如图片选择和照相机捕获对话框)如何轻松地包括到企业应用程序之中。您将了解如何通过使用本机代码以及新增的托管组件对象模型 (COM) 互操作性功能(具有一些优秀的社区外接程序代码),在应用程序中使用媒体播放器控件。您还将了解到,如果需要对媒体和照相机的交互进行更多控制,如何使用 DirectShow API 中更高级的功能。包括的 DirectShow 示例代码用于说明视频和音频的播放和捕获。
本文首先描述为常见业务方案创建一些多媒体支持的基本任务。
检查业务过程
尽管在许多不同行业中都有检查任务,但这类任务通常是现场执行的。检查员一般是移动工作人员,他们的大部分工作实际上是收集数据。他们所面临的问题是需要收集尽可能多的信息,这样才能将因检查的不同感知而产生差异的风险降至最低,并最大限度地扩大涉及到的各方之间的信任。例如,一辆汽车在变更车主之前,车检员要进行检查,检查的准确性可能是买卖双方最关注的。然而,该工作在实地进行并且需要收集大量信息时,就出现了一个非常棘手的情况。
在检查过程中使用传统的解决方案(如在纸上手写记录)可能看似有效率,但是不以数字方式记录信息在检查之后可能会产生很多问题。手写记录需要输入到信息系统中,这通常由除检查员之外的其他人输入信息。这种情况通常会导致较差的信息质量,有时甚至会丢失信息。
由于文本不可能记录所有的信息,因此检查员在很长一段时间内都使用照片。但是传统的照片也会在检查后引发问题,因为照片的质量可能不合格,而且只有在这些照片由开发过程返回后才会发现质量问题。
对于所有这些问题,使用配备有照相机(能够捕获记录、照片甚至视频)的移动设备这一想法似乎是最佳解决方案。但在诸如 Pocket PC 这样的移动设备上以文本形式捕获信息仍然是个挑战,所以多媒体的作用变得更为重要。将通过集成手段捕获文本、照片、口述内容以及具有音频的视频合为一体,可能是成功的诀窍。本文重点讲述如何使这种愿景成为现实,不过首先要确定需求。用于检查的一般业务过程定义如下:
• |
从后台(服务器)签出必要的检查。 |
• |
利用注释记录检查。 |
• |
收集诸如照片和视频这样的展示内容。 |
• |
将检查签入到后台(服务器)。 |
该业务过程如图 1 所示。

图 1. 检查业务过程
如图 1 所示,业务过程中主要步骤包括了大量作为新解决方案重要需求的活动。即使第二个步骤(记录检查)和第三个步骤(收集展示内容)按顺次进行,这些任务在执行时也可能会反复。要了解更多具体的需求,可通过将每个过程步骤扩展到它自己的子过程流来深入研究业务过程。
当您满意地定义了新过程后,下一步是查看该解决方案的设计。
应用程序设计
Northwind Pocket Service: Field Service for Windows Mobile-based Pocket PCs 一文很好地介绍了移动解决方案中体系结构的工作。Northwind Pocket Inventory: Logistics for Windows Mobile 2003-based PocketPCs 一文包括对该应用程序设计(该设计实际上从用例的定义开始)中最重要的可交付物(构件)的描述。因为本文主要关注多媒体,因此图 2 显示示例应用程序中实现的一些最有趣的用例。

图 2. 用于收集展示的用例模型
设计过程初期要创建的其他重要构件是对话框模型和示例对话框。图 3 显示示例应用程序的对话框模型。

图 3. 对话框模型
该对话框模型概括应用程序中包括的对话框(窗体)以及这些对话框之间的导航。请注意,虚线边框的对话框在本文该示例中不实现。
图 4 显示一些示例对话框。

图 4. 示例对话框
这些对话框示例是用常规的绘图工具(本例使用 Microsoft PowerPoint)绘制的,创建此类对话框时应该使用懂得用户界面设计的人员。及早的将这些应用程序对话框图示化则为用户和其他股东提供一个了解应用程序外观(以及工作方式)的机会,从而在该阶段进行更改时能轻松实现。
下载代码示例将上述所有图片作为一个 PowerPoint 演示文稿提供,以便您在创建自己的关系图时可以重用它们。
示例应用程序演练
该示例客户端方案是通过 Microsoft Visual Studio .NET 2005 用 C# 编写的 Pocket PC 应用程序,它针对的是 Microsoft .NET Compact Framework 版本 2.0。
该应用程序显示如何使用 Pocket PC 支持检查业务过程。为了使该示例更真实,使用了一个具体的检查方案 - 车检员。该应用程序的用户执行不同目的检查。例如,用户可以在发生或者车辆进行修理后进行检查。其他的示例还有,在签订新保险策略之前或者在车辆变更车主时进行的常规检查。该演练涵盖一般类型的检查(用户界面有时称之为"检验")。该方案是在后台创建检查,当检查员与后台服务器同步时,新的检查就下载到设备上。
本文将在该应用程序的演练过程中对一些设计选择进行评论。另请注意,本文在描述该应用程序的用户界面设计之后探究部分代码。
主屏幕
启动该应用程序时,第一个屏幕是可用于搜索和选择检查的主屏幕,如图 5 所示。

图 5. 主屏幕(检查)
首先,可以搜索检查的名称和类型。然后,当按左软键 (Find) 时,该应用程序将搜索可用的检查。请注意可以搜索的可用检查类型。
右软键 (Menu) 提供某些常规应用程序功能,如应用程序选项以及与服务器同步。因为本文重点关注多媒体,所以该示例中没有实现这些功能,但 MSDN 上有许多涉及这些主题的文章(例如,Northwind Pocket Sales: Field Sales for Windows Mobile 2003 Software for Pocket PCs)。
当该应用程序进行搜索时,它将搜索结果添加到检查列表中。点击并按住列表中的某个检查会显示一个快捷菜单,该快捷菜单带有执行所选检查的命令 (Inspect),如图 6 所示。

图 6. 搜索结果
检查
选择 Inspect 命令时(如前面的图 6 所示),该应用程序将显示 Inspection 屏幕,如图 7 所示。

图 7. Inspection 屏幕
在 Inspection 屏幕上,可以编辑有关检查的信息。可以修改名称和类型(例如,如果名称拼写错误),然后即可以自由文本方式输入检查注释。图 7 中输入的注释涉及机动车辆的不同部件,在这些注释中加入实际照片是非常有价值的。这就是屏幕上 Exhibits 部分(图 7 的下面部分)的作用。图 7 还显示以下情况:如果在 Exhibits 部分中点击并按住鼠标,将显示一个快捷菜单,这样就可以对附加到该检查的展示进行添加 (New)、编辑 (Edit) 或删除 (Delete) 操作。
展示
如果在 Exhibits 部分的快捷菜单中选择 New 命令,则显示 Exhibit 屏幕。在此处,可以输入展示的名称。然后,当按下左软键 (New) 时,将显示一个快捷菜单,可从中选择从文件添加展示 (FromFile),方法是拍一张新照片 (Photo),或者用内置照相机(如果该设备上有照相机)制作新视频 (Video),如图 8 所示。

图 8. Exhibit 屏幕
选择 From File 命令将显示一个屏幕,用于选择一个媒体文件,如图 9 中所示。

图 9. 文件选择屏幕。
您可以使用硬件导航台(上、下、左、右)在媒体文件列表中浏览,可以通过按 Action 键(通常在硬件导航台中间)选择一个文件(如照片或视频)。如果选择一张照片,则返回到您可以看见所选照片的 Exhibit 屏幕。然后,当按 Menu 软键时,一个快捷菜单将出现并包含一个 View 命令,如图 10 所示。

图 10. 具有所选照片的 Exhibit 屏幕。
当选择 View 命令时,图片查看器应用程序将启动。在该应用程序中,您可以更详细地查看该图片,甚至可以对该图片进行操作。图 11 中显示的命令(可通过点击 Menu 软键显示)表示您可以将该图片 (Beam picture) 发送到另一个设备(例如,Pocket PC、Smartphone 或桌面计算机),并且能够以限制方式(例如,旋转)Edit 它。

图 11. 图片查看器。
点击 OK,返回到 Exhibit 屏幕。
在文件选择屏幕上(如图 9 所示),如果选择添加一个视频文件,将出现 Exhibit 屏幕。如果随后点击 Menu,将显示一个快捷菜单,如图 12 所示。

图 12. 选择了视频的 Exhibit 屏幕。
当针对一个视频展示选择 View 命令时,媒体播放器将启动并播放该视频,如图 13 所示。

图 13. 媒体播放器中显示的视频。
请注意,因为展示的标识(一个 uniqueidentifier 类型的值)用作该文件的名称,所以媒体播放器中就显示这一名称。
在 Exhibit 屏幕上(如前面的图 8 所示),如果选择 Photo 命令来添加一张新照片,该应用程序将显示一个可在其中捕获新照片的屏幕,如图 14 所示。

图 14. 照片捕获屏幕。
录制新视频时,捕获屏幕如图 14 中显示(然后胶片的符号替换为摄影机的符号)。但请注意,图 14 是一个概念性的屏幕快照,其外观在基于 Windows Mobile 5.0 的实际 Pocket PC 上可能稍有不同。
客户端应用程序的演练到此已经结束,现在该看一些源代码了。
代码演练
本部分将为您介绍示例客户端方案的一些源代码。您将看到如何在托管代码和本机代码中使用选择图片对话框和照相机捕获对话框。不过,本文的附带源代码中不包括本机代码示例。然后,您将看到如何使用其他包含媒体的应用程序,以及如何将媒体存储到数据库中。
选择图片(托管代码)
要将多媒体与企业应用程序集成,最重要的是能够使用已经存储在文件系统中的图片(包括照片)和视频。如果该设备没有配备内置照相机,您仍然可以使用该方法集成多媒体。例如,您可能使用单独的数字照相机来拍照或者捕获视频,然后使用红外线端口或蓝牙将照片或视频传输到 Pocket PC。为了满足该需要,Windows Mobile 5.0 平台中包括了一个现成的对话框,该对话框可以在托管 (.NET Compact Framework) 代码中作为"Microsoft.WindowsMobile.Forms"命名空间中的 SelectPictureDialog 类使用。
要创建前面图 9 中显示的文件选择屏幕,可以使用以下代码示例。
private void fromFileMenuItem_Click(object sender, EventArgs e) { SelectPictureDialog selectPictureDialog = new SelectPictureDialog(); selectPictureDialog.Owner = this; selectPictureDialog.Title = "Select Exhibit Photo or Video"; selectPictureDialog.CameraAccess = false; selectPictureDialog.Filter = "All files|*.*"; if(selectPictureDialog.ShowDialog() == DialogResult.OK && selectPictureDialog.FileName.Length > 0) { fileExtension = Path.GetExtension(selectPictureDialog.FileName); File.Copy(selectPictureDialog.FileName, fileName()); if(fileExtension.ToLower() == ".jpg") pictureBox.Image = new Bitmap(fileName()); else { ComponentResourceManager resources = new ComponentResourceManager(typeof(ExhibitForm)); pictureBox.Image = ((System.Drawing.Image) (resources.GetObject("pictureBox.Image"))); } } }
在上面的代码中,您可以看到该对话框的所有者设置为具有该标题的当前窗口。CameraAccess 属性指示照相机是否应该从该对话框窗口使用。如果该属性设置为 true 且照相机可用,则可以在文件列表中看到照相机符号。当选择照相机符号时,将显示一个 CameraCaptureDialog 对话框,这样就可以照相或者制作视频记录。请注意,因为 SelectPictureDialog 对话框能够链接到 CameraCaptureDialog 对话框(虽然该示例中未显示它),所以这的确就是从文件系统和内置照相机中启用包含的媒体而需进行的最小集成。
Filter 属性定义当应用程序显示文件列表时将应用的搜索筛选器。该属性的默认值为"Image Files(*.BMP;*.JPG;*.GIF)|*.BMP;*.JPG;*.GIF"。
上述代码中接下来要发生的是对话框的实际呈现。呈现通过 ShowDialog 方法进行,该方法直至对话框关闭时才停止执行。通常,ShowDialog 方法返回一个值,指示对话框的关闭方式。如果用户按设备上的 Action 键或点击 OK,该对话框返回 DialogResult.OK。如果选择一张图片,则 FileName 属性包括所选文件的名称。如果这两个条件都满足,则保存所选文件的扩展名,将该文件复制到示例的媒体文件夹。如果所选文件是照片(在本示例中,只有 JPEG 文件可用于照片),使用所选图片更新 Exhibit 屏幕上的图片框 (pictureBox)。如果所选文件是视频,默认图像(图 12 显示的放大的媒体播放器文档符号)从应用程序资源加载。
fileName 方法的代码如下所示。
private string fileName() { return Common.Values.MediaPath + Path.DirectorySeparatorChar + exhibitID.ToString() + fileExtension; }
上述代码创建了展示的完整文件名,该文件名是展示的标识(唯一标识符)和所选文件的文件扩展名的组合。所有媒体文件都存储在一个公共文件夹 (Common.Values.MediaPath) 中。
图 15 显示 SelectPictureDialog 类的完整定义。

图 15. SelectPictureDialog 类
因为 Filter 属性可以包括多个筛选器,所以可以使用 FilterIndex 属性设置默认筛选器索引。例如,如果 Filter 属性设置为"Bitmap Files|*.bmp|JPEG Files|*.jpg|GIF Files|*.gif",FilterIndex 设置为 1,则该对话框将搜索 JPEG 文件。
除了已经提到的属性,还可以用 InitialDirectory 属性选择一个初始文件夹来搜索图片。可以将 LockDirectory 属性设置为 true 以防止用户更改文件夹。可以使用 SortOrder 属性设置找到的文件的初始排序,还可以将该属性设置为根据日期、名称或大小按升序和降序进行排序。可以使用 ShowDrmContent 属性指定受数字版权管理(Digital Rights Management,DRM)保护的文件是否应该显示在对话框中。如果受 DRM 保护的文件得到保护,则它们不会被转发,只有当 ShowForwardLockedContent 属性设置为 true 时,它们才会显示在该对话框中。
选择图片(本机代码)
要在本机代码中使用上一部分中描述的对话框,可以使用具有 OPENFILENAMEEX 结构的 GetOpenFileNameEx 函数,如下所示。
TCHAR szFile[MAX_PATH]; OPENFILENAMEEX ofnex = {0}; ofnex.lStructSize = sizeof(ofnex); ofnex.hwndOwner = g_hWnd; ofnex.lpstrFile = szFile; ofnex.nMaxFile = sizeof(szFile) / sizeof(szFile[0]); ofnex.lpstrFilter = TEXT("All Files (*.*)/0*.*/0"); ofnex.lpstrTitle = TEXT("Select Exhibit Photo or Video"); ofnex.ExFlags = OFN_EXFLAG_THUMBNAILVIEW; ofnex.lpstrInitialDir = NULL; if(GetOpenFileNameEx(&ofnex)) { // The selected file name is in szFile }
上述代码中的所有者窗口是主应用程序窗口(全局定义为 g_hWnd)。请注意,筛选器的各部分用 NULL 字符分隔。使用 OFN_EXFLAG_THUMBNAILVIEW 以缩略图格式显示 ListView 控件,ExFlags 成员可以包括 OFN_EXFLAG_DETAILSVIEW 标志以详细信息格式显示 ListView。其他选项是 OFN_EXFLAG_HIDEDRMPROTECTED 和 OFN_EXFLAG_HIDEDRMFORWARDLOCKED,前者用于排除受 DRM 保护的文件的显示,后者用于排除无法转发的受 DRM 保护的文件。
捕获照片和视频(托管代码)
尽管许多移动设备早已配备了内置照相机,但刚开始时缺乏对使用这些照相机的开发支持。几乎没有制造商制造可以从托管代码使用的照相机,甚至本机 API 都难以找到。对于那些希望将媒体集成到应用程序中的企业开发人员而言,唯一可用的选择是进行文件级集成(如前面图片选择对话框的讨论中描述的那样)。
有了 Windows Mobile 5.0 软件,所有一切都改变了。现在定义了设备制造商将支持的通用照相机 API。此外,为了满足那些使用 .NET Compact Framework 构建应用程序的企业开发人员的需要,Windows Mobile 5.0 软件还包括了一个现成的对话框。该对话框名为 CameraCaptureDialog,可在"Microsoft.WindowsMobile.Forms"命名空间中找到它。
要创建前面图 14 中显示的照片捕获屏幕,可以使用以下代码示例。
private void photoMenuItem_Click(object sender, EventArgs e) { CameraCaptureDialog cameraCaptureDialog = new CameraCaptureDialog(); cameraCaptureDialog.Owner = this; cameraCaptureDialog.Title = "Take Exhibit Photo"; cameraCaptureDialog.Mode = CameraCaptureMode.Still; if(cameraCaptureDialog.ShowDialog() == DialogResult.OK && cameraCaptureDialog.FileName.Length > 0) { fileExtension = Path.GetExtension(cameraCaptureDialog.FileName); File.Copy(cameraCaptureDialog.FileName, fileName()); pictureBox.Image = new Bitmap(fileName()); } }
CameraCaptureDialog 类的工作方式与前面讨论的 SelectPictureDialog 类非常类似。在上述代码中,该对话框的所有者设置为具有该标题的当前窗口。Mode 属性指示照相机捕获对话框应该用于拍照还是进行视频录制。在本例中,用户需要一张照片 (CameraCaptureMode.Still)。对话框通过 ShowDialog 方法显示,该方法返回一个指示对话框关闭方式的值。如果用户点击 OK,则对话框返回 DialogResult.OK。如果拍了一张照片,FileName 属性包括新建的图片(照片)文件的名称。如果这两个条件都满足,则保存所选文件的扩展名,将该文件复制到示例的媒体文件夹,使用所选图片更新 Exhibit 屏幕上的图片框 (pictureBox)。
要使用同一个对话框捕获视频,使用以下代码示例。
private void videoMenuItem_Click(object sender, EventArgs e) { CameraCaptureDialog cameraCaptureDialog = new CameraCaptureDialog(); cameraCaptureDialog.Owner = this; cameraCaptureDialog.Title = "Take Exhibit Video"; cameraCaptureDialog.Mode = CameraCaptureMode.VideoWithAudio; if(cameraCaptureDialog.ShowDialog() == DialogResult.OK && cameraCaptureDialog.FileName.Length > 0) { fileExtension = Path.GetExtension(cameraCaptureDialog.FileName); File.Copy(cameraCaptureDialog.FileName, fileName()); ComponentResourceManager resources = new ComponentResourceManager(typeof(ExhibitForm)); pictureBox.Image = ((System.Drawing.Image) (resources.GetObject("pictureBox.Image"))); ; } }
请注意,上面的代码与捕获照片的代码非常类似,但是 Mode 属性却设置为捕获带有声音的视频记录 (CameraCaptureMode.VideoWithAudio)。Mode 属性也可以设置为捕获没有声音的视频 (CameraCaptureMode.VideoOnly)。此外,默认图像(图 12 中显示的放大的媒体播放器文档符号)从该应用程序的资源加载到图片框中。
图 16 显示 CameraCaptureDialog 类的完整定义。

图 16. CameraCaptureDialog 类
除了已经提到的属性,如果在使用 ShowDialog 方法显示该对话框之前设置 DefaultFileName 属性,该名称将用作新照片或视频的文件名。可以使用 InitialDirectory 属性指定捕获的文件(照片或视频)将存储到何处。要请求捕获的照片或视频的分辨率,将 Resolution 属性设置为一个 Size 实例,如下所示。
cameraCaptureDialog.Resolution = new Size(320, 240);
使用 StillQuality 属性设置该照片的压缩级别。在 CameraCaptureStillQuality 枚举中,High 选项意味着低压缩率用于保持照片的高质量。Low 选项与其相反(结果产生具有较低质量的高压缩)。Normal 选项产生介于 High 和 Low 选项之间的质量。
使用 VideoTimeLimit 属性设置新视频的最大记录时间。默认值为零,即没有时间限制。
最后一个属性是 VideoTypes,它可用于选择要捕获哪种类型的视频。基本上,在基于 Windows Mobile 5.0 的设备上,可以使用两种不同的视频类型 - 多媒体消息传递服务(Multimedia Messaging Service,MMS)和 Windows Media Video (WMV)。CameraCaptureVideoTypes 枚举提供可能的值。使用 Messaging 录制 MMS 视频,使用 Standard 录制 WMV 视频。还有一个 All 枚举值,用于使 Resolution 属性确定要录制的视频类型。如果使用 All 选项且 Resolution 高度和宽度为零,则再次使用上次使用的分辨率。您还需要设置 Messaging 和 Standard 枚举值的 Resolution 属性。
捕获照片和视频(本机代码)
如果想在本机代码中使用相同的对话框(如前所述),可以使用具有 SHCAMERACAPTURE 结构的 SHCameraCapture 函数,如下所示。
HRESULT hResult; SHCAMERACAPTURE shcc; ZeroMemory(&shcc, sizeof(shcc)); shcc.cbSize = sizeof(shcc); shcc.hwndOwner = g_hWnd; shcc.pszTitle = TEXT("Take Exhibit Video"); shcc.Mode = CAMERACAPTURE_MODE_VIDEOWITHAUDIO; shcc.VideoTypes = CAMERACAPTURE_VIDEOTYPE_ALL; if(S_OK == SHCameraCapture(&shcc)) { // The selected file name is in shcc.szFile }
上述代码中的所有者窗口是主应用程序窗口(全局定义为 g_hWnd)。Mode 成员的其他值是针对照片的 CAMERACAPTURE_MODE_STILL(默认值)和针对无声音视频的 CAMERACAPTURE_MODE_VIDEOONLY。VideoTypes 成员的其他值是针对 WMV 视频的 CAMERACAPTURE_VIDEOTYPE_STANDARD 以及针对 MMS 视频的 CAMERACAPTURE_VIDEOTYPE_MESSAGING。当使用后两个值之一时,nResolutionWidth 或 nResolutionHeight 成员均不能为零。
查看照片和视频
要打开前面图 11(图片查看器)和图 13(媒体播放器)中显示的屏幕,可以使用以下代码。
private void viewMenuItem_Click(object sender, EventArgs e) { Process process = new Process(); process.StartInfo.FileName = fileName(); process.StartInfo.Verb = "Open"; process.StartInfo.UseShellExecute = true; process.Start(); }
使用"System.Diagnostics"命名空间的 Process 类为特定文件新建一个进程。请注意,如果 UseShellExecute 属性设置为 true,文件名可以是与具有默认打开操作的可执行文件相关联的任何文件类型。在基于 Windows Mobile 5.0 的 Pocket PC 上,.jpg 扩展名与图片和视频查看器可执行文件 (pimg.exe) 相关联,.wmv 扩展名与媒体播放器可执行文件 (wmplayer.exe) 相关联。
将媒体保存到数据库
在前面的示例中,媒体保存在一个文件中,该文件复制到特定的(媒体)文件夹以便稍后与服务器同步。媒体(文件)信息也可以通过如下代码示例的方式存储在数据库中。
FileStream fs = File.Open(fileName(), FileMode.Open); byte[] imageData = new byte[fs.Length]; fs.Read(imageData, 0, imageData.Length); fs.Close(); SqlCeCommand cmd = cn.CreateCommand(); cmd.CommandText = "UPDATE Exhibit SET Media=?" + " WHERE ExhibitID='" + exhibitID + "'"; SqlCeParameter param = cmd.Parameters.Add("p0", SqlDbType.Image); param.Value = imageData; cmd.ExecuteNonQuery();
将文件读入一个字节数组,然后该数组用于更新数据库中正确的展示行。在上述代码中,保存媒体数据的数据库列 (Media) 被假定为 image 类型。请注意,要使用外部程序(pimg.exe 和 wmplayer.exe)播放媒体,需要将媒体再次写入到一个文件中,然后才能调用外部程序。
将媒体文件存储在文件系统中还是存储在数据库中,这取决于应用程序的要求。这两项技术都有优缺点。将媒体文件存储在文件系统中可以使录制、播放和操作更简单,并且能更容易地分离数据和媒体的存储。例如,数据库可以位于设备上较快的内存中从而提高访问性能,而媒体则存储在存储卡上,这样就可以存储更多信息,但访问速度较慢。然而,当这些文件需要与服务器同步时,就需要自定义解决方案(如 HTTP 上载和 XML Web 服务的附件)。如果媒体存储在数据库中,那么每次需要播放或操作时,都需要将媒体提取到一个文件中。但是这一同步操作较容易,因为数据库同步(如 Microsoft SQL Server Mobile Edition 中的远程数据访问和合并复制)和 XML Web 服务同步(SOAP 上的 DataSet)都支持该数据传输。任何情况下,您都应该考虑在设备上本地压缩媒体,以及当媒体在设备和服务器间传输时压缩媒体这两方面。
多媒体之外的代码重点
既然已经演练了示例应用程序中实现的多媒体功能,那么您可以再演练其他一些代码,因为从企业应用程序角度看,这是值得研究一下的。例如,使用 .NET Compact Framework 2.0 命名空间"Microsoft.Win32"中的 Registry 类获取注册表设置,如下代码示例所示。
string registryKey = @"SOFTWARE/Microsoft/MsdnSamples/Inspection"; RegistryKey key = Registry.LocalMachine.CreateSubKey(registryKey); string userName = key.GetValue("UserName", "<default>").ToString();
要保存相同的注册表设置,使用以下代码。
RegistryKey key = Registry.LocalMachine.OpenSubKey(registryKey, true); key.SetValue("UserName", userName);
该代码示例的另一个非常有趣的部分是数据访问。首先是用户界面实现,接着是用来填充 Type 组合框的代码(如前面的图 5 所示)。
try { DataTable dt; using(InspectionHandler inspectionHandler = new InspectionHandler()) dt = inspectionHandler.GetAllTypes().Tables[0]; DataRow dr = dt.NewRow(); dr["TypeName"] = "<All types>"; dr["InspectionTypeID"] = Guid.Empty; dt.Rows.InsertAt(dr, 0); typeComboBox.DisplayMember = "TypeName"; typeComboBox.ValueMember = "InspectionTypeID"; typeComboBox.DataSource = dt; typeComboBox.SelectedIndex = 0; } catch(Exception) { MessageBox.Show("Could not load inspection types!", this.Text); }
通过检查处理程序类实例 (inspectionHandler) 检索具有所有类型的数据表 (dt)。第一行先添加到数据表中,然后它作为数据源添加到组合框中。该检查处理程序中方法 (GetAllTypes) 的实现如下所示。
public DataSet GetAllTypes() { return SqlHelper.ExecuteDataset(cn, CommandType.Text, "SELECT * FROM InspectionType", "InspectionType"); }
SqlHelper 类实际上可以算是 Data Access Application Block(包括在 OpenNETCF 的 Application Blocks 1.0 中)的增强版。Application Blocks 1.0 是桌面计算机应用程序块的一个端口。方法 (ExecuteDataSet) 获取以下参数:数据库连接(SqlCeConnection 类型的 connection)、命令类型 (commandType)、语句 (commandText) 以及返回的表的名称 (tableName)。此处使用的重载如下代码示例所示。
public static DataSet ExecuteDataset(SqlCeConnection connection, CommandType commandType, string commandText, string tableName) { DataSet ds = ExecuteDataset(connection, commandType, commandText, (SqlCeParameter[])null); ds.Tables[0].TableName = tableName; return ds; }
调用的另一个重载按以下代码示例的方式实现。
public static DataSet ExecuteDataset(SqlCeConnection connection, CommandType commandType, string commandText, params SqlCeParameter[] commandParameters) { SqlCeCommand cmd = new SqlCeCommand(); bool mustCloseConnection = false; PrepareCommand(cmd, connection, (SqlCeTransaction)null, commandType, commandText, commandParameters, out mustCloseConnection ); SqlCeDataAdapter da = new SqlCeDataAdapter(cmd); DataSet ds = new DataSet(); ds.Locale = CultureInfo.InvariantCulture; da.Fill(ds, 0, 100, "Table"); cmd.Parameters.Clear(); if(mustCloseConnection) connection.Close(); return ds; }
创建一个 SQL Mobile 命令 (SqlCeCommand) 实例 (cmd) 并准备执行它。然后,创建一个数据适配器 (da) 和一个数据集 (ds),该数据适配器用于使用数据填充数据集。注意如何才能指定只返回前 100 行。这种限制可能是个好点子;很少需要一个大的结果列表,一方面是因为对性能的影响,也因为在移动设备上浏览大数据集的效率并不太高。所有参数都与该命令对象分离,这样它们才可以再次使用。最后,返回创建的数据集。
您按照以下代码示例所示准备该命令。
private static void PrepareCommand(SqlCeCommand command, SqlCeConnection connection, SqlCeTransaction transaction, CommandType commandType, string commandText, SqlCeParameter[] commandParameters, out bool mustCloseConnection ) { if(connection.State != ConnectionState.Open) { mustCloseConnection = true; connection.Open(); } else mustCloseConnection = false; command.Connection = connection; command.CommandText = commandText; command.CommandType = commandType; if(transaction != null) command.Transaction = transaction; if(commandParameters != null) AttachParameters(command, commandParameters); return; }
如果连接尚未打开,则将其打开,根据这些参数配置该命令。有关 SqlHelper 的实现的更多信息,请参阅本文的下载示例代码以及 Data Access Application Block 文档。
对于任何企业开发人员,大大改进对 .NET Compact Framework 2.0 和 Visual Studio .NET 2005 中的数据访问支持不仅仅是受欢迎这么简单的。您可以直接从开发工具中使用数据库这一事实,将大大提高工作效率。典型的开发过程如下所示:在 SQL Server Management Studio 中创建 SQL Server Mobile Edition 数据库文件(sdf 扩展名)。也可以使用该工具添加所有表和数据。然而,您也可以从 SQL Server Management Studio 中的 Server Explorer 窗口连接到 SQL Server Mobile Edition 数据库,可以在其中添加表和数据、测试查询以及类似的项。进行调试时,您可以部署相同的数据库文件以及该应用程序。还有许多其他新增的数据访问功能有待您去探究。
媒体播放器控件
在许多情况下,将标准媒体播放器作为单独的进程启动(如前面所示)可能会满足业务需求,但如果需要更多地控制视频播放,则可以使用媒体播放器控件。媒体播放器控件(版本 10)包括在 Windows Mobile 5.0 软件中,它使开发人员能够将媒体播放器用作自己应用程序的一个自定义控件。
宿主媒体播放器控件(本机代码)
媒体播放器控件是一个常规 ActiveX 控件(.ocx 文件),熟悉 COM 的本机开发人员使用它时不会有很多问题。首先,您应该查看 Windows Media Player Mobile 代码示例,这些示例包括用于宿主媒体播放器控件的代码。然而,因为这些示例是针对本机智能设备开发的上一代工具 (Microsoft eMbedded Visual C++) 而编写的,所以本文的下载代码示例包括尝试通过 eMbedded Visual C++ Upgrade Wizard for Visual Studio 2005 Beta 2 方法转换媒体播放器示例 (CEWMPHostML) 的结果。
宿主窗口和该控件的创建如下代码示例所示。
CAxWindow m_wndView; CComPtr<IWMPPlayer> m_spWMPPlayer; CComPtr<IConnectionPoint> m_spConnectionPoint; DWORD m_dwAdviseCookie; RECT rcClient; GetClientRect(&rcClient); m_wndView.Create(m_hWnd, rcClient, NULL, WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS); if(NULL == m_wndView.m_hWnd) goto FAILURE; CComPtr<IAxWinHostWindow> spHost; HRESULT hr = m_wndView.QueryHost(&spHost); if(FAILMSG(hr)) goto FAILURE; hr = spHost->CreateControl(CComBSTR( _T("{6BF52A52-394A-11d3-B153-00C04F79FAA6}")), m_wndView, 0); if(FAILMSG(hr)) goto FAILURE; hr = m_wndView.QueryControl(&m_spWMPPlayer); if(FAILMSG(hr)) goto FAILURE; CComWMPEventDispatch *pEventListener = NULL; hr = CComWMPEventDispatch::CreateInstance(&pEventListener); CComPtr<IWMPEvents> spEventListener = pEventListener; if(FAILMSG(hr)) goto FAILURE; CComPtr<IConnectionPointContainer> spConnectionContainer; hr = m_spWMPPlayer->QueryInterface(__uuidof(IConnectionPointContainer), (void**)&spConnectionContainer); if(FAILMSG(hr)) goto FAILURE; hr = spConnectionContainer->FindConnectionPoint(__uuidof(IWMPEvents), &m_spConnectionPoint); if(FAILMSG(hr)) goto FAILURE; m_dwAdviseCookie = 0; hr = m_spConnectionPoint->Advise(spEventListener, &m_dwAdviseCookie); if(FAILMSG(hr)) goto FAILURE;
全局声明(前四行)之后,控件宿主窗口 (m_wndView) 作为主应用程序窗口 (m_hWnd) 的子窗口(并且具有和客户端区域相同的大小)进行创建。然后,检索宿主窗口的接口引用 (spHost) 并用它来创建媒体播放器控件。GUID(对应于与版本无关的 ProgID WMPlayer.OCX)用于创建该控件,从该宿主窗口检索该控件的实际接口引用 (m_spWMPPlayer)。最后,使用侦听所有类型事件的事件调度程序 (pEventHandler) 建立事件处理。请注意每个调用上的大量错误处理,您在调试和测试时将从中大大受益。
下一个主要操作是用户选择使用菜单打开某个文件,如下代码示例所示。
CFileOpenDlg dlgOpen; if(dlgOpen.DoModal(m_hWnd) == IDOK) { HRESULT hr = m_spWMPPlayer->put_URL(_T(dlgOpen.m_bstrName); if(FAILMSG(hr)) return 0; } return 0;
显示一个对话框 (dlgOpen),用户可从中选择文件名。如果用户点击 OK 关闭该对话框,则媒体播放器的 URL 属性设置为在该对话框中输入的名称。然后媒体播放器控件播放该文件。
该事件处理以如下方式实现。
HRESULT CWMPEventDispatch::Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS FAR* pDispParams, VARIANT FAR* pVarResult, EXCEPINFO FAR* pExcepInfo, unsigned int FAR* puArgErr) { if (!pDispParams) return E_POINTER; if (pDispParams->cNamedArgs != 0) return DISP_E_NONAMEDARGS; HRESULT hr = DISP_E_MEMBERNOTFOUND; switch(dispIdMember) { case DISPID_WMPCOREEVENT_OPENSTATECHANGE: OpenStateChange(pDispParams->rgvarg[0].lVal); break; case DISPID_WMPCOREEVENT_PLAYSTATECHANGE: PlayStateChange(pDispParams->rgvarg[0].lVal); break; case DISPID_WMPCOREEVENT_AUDIOLANGUAGECHANGE: AudioLanguageChange(pDispParams->rgvarg[0].lVal); break; case DISPID_WMPCOREEVENT_STATUSCHANGE: StatusChange(); break; // Here comes another 40 events case DISPID_WMPOCXEVENT_MOUSEUP: MouseUp(pDispParams->rgvarg[3].iVal, pDispParams->rgvarg[2].iVal, pDispParams->rgvarg[1].lVal, pDispParams->rgvarg[0].lVal); break; } return( hr); }
对于每个事件调度标识 (dispIdMember),调用一个单独的方法来操作该事件。正如您在上述代码中所看到的,另外 40 个事件在下载代码示例中实现。媒体播放器控件发布的广泛的属性、方法和事件集,使其成为进行媒体播放时的重点考虑对象。
宿主媒体播放器控件(托管代码)
尽管 .NET Compact Framework 2.0 包括对 COM 互操作性的支持,但是没有对 ActiveX 控件的内置支持。然而,多亏我的同事 Alex Feinman,他提供了一种在托管代码中使用 ActiveX 控件的方式,您将在他的文章 (Hosting ActiveX Controls in Compact Framework 2.0 Applications) 中找到在托管代码中使用 ActiveX 控件的方式。通过使用该技术并在前面的示例基础上进行生成,图 17 显示具有集成视频播放的 Exhibit 屏幕在 Smartphone 版本的示例应用程序中可能的外观。

图 17. 通过媒体播放器控件进行的集成播放。
正如在 Pocket PC 示例(如图 10 所示)中一样,可以编辑展示名称,但此处播放可以从同一屏幕启动。
通过下面的设计器代码开始创建窗体的代码。
private AxWMPLib.AxWindowsMediaPlayer windowsMediaPlayer; this.windowsMediaPlayer = new AxWMPLib.AxWindowsMediaPlayer(); this.windowsMediaPlayer.Location = new System.Drawing.Point(0, 28); this.windowsMediaPlayer.Name = "windowsMediaPlayer"; this.windowsMediaPlayer.Size = new System.Drawing.Size(176, 152); this.windowsMediaPlayer.TabIndex = 0; this.windowsMediaPlayer.Text = "windowsMediaPlayer";
正如您所看到的,当 ActiveX 控件的包装准备就绪时,可以像处理任何托管控件那样处理该控件。在前面的代码示例基础上生成,该媒体播放器控件可以按以下方式使用展示视频加载。
windowsMediaPlayer.URL = fileName();
URL 属性可以是指向流式源或远程文件的 URL,也可以是本地文件系统中的文件。Menu 命令(Play、Pause 和 Stop)的后台代码如下代码示例所示。
private void playMenuItem_Click(object sender, EventArgs e) { windowsMediaPlayer.Ctlcontrols.play(); } private void pauseMenuItem_Click(object sender, EventArgs e) { windowsMediaPlayer.Ctlcontrols.pause(); } private void stopMenuItem_Click(object sender, EventArgs e) { windowsMediaPlayer.Ctlcontrols.stop(); }
与该媒体播放器的基本交互不是非常困难,设置事件处理程序的方式也不会更困难。就像任何其他托管控件一样,以如下方式添加事件处理程序。
this.windowsMediaPlayer.StatusChange += new System.EventHandler(windowsMediaPlayer_StatusChange);
然后,该事件处理程序按如下方式实现。
void windowsMediaPlayer_StatusChange(object sender, System.EventArgs e) { string status = windowsMediaPlayer.status; // Do something with status string }
正如在关于从本机代码使用多媒体播放器控件的讨论中提到的一样,该控件有许多可能性。您在独立媒体播放器中可以进行的任何操作在该控件中几乎都可以实现。示例包括使用播放列表、连接、远程媒体、播放器状态以及很多事件(如播放和结束流)。
DirectShow
既然您已经了解了基础知识,现在该看看 Windows Mobile 5.0 软件中多媒体支持的更高级的用法了。一个最有趣的开发是在 Windows Mobile 5.0 软件中包含 DirectShow API。查看该 API 的主要原因是您需要对应用程序中使用的多媒体进行更多的控制。如果前面描述的高级支持不足以满足应用程序的需求,DirectShow 提供更多的控制和更大的灵活性。DirectShow 是一个本机 API,因此可以通过本机工具集 (Visual C++) 使用,如本文后面的代码示例所示。
如果想在托管代码中访问 DirectShow,一个解决方案是将 DirectShow 的使用打包到一个本机 DLL 中,托管代码可以使用平台调用来调用该 DLL。这对于媒体捕获这样的功能而言可能是个很好的解决方案,这些功能并不依赖于用户界面组件(如媒体播放)。另一个解决方案是使用 .NET Compact Framework 2.0 中包括的 COM 互操作性来包装 DirectShow COM 组件。有一个称为 DirectShowNET 库的有趣项目,它为桌面计算机上的 DirectShow 提供托管包装,但目前没有可用于 .NET Compact Framework 的版本。
一个不错的常用方法是利用托管代码来实现用户界面、业务逻辑、数据库访问和基本的照相机交互。然后,由于需要更多的控制和灵活性,您可以使用媒体播放器控件进行媒体播放,使用 DirectShow 进行媒体捕获(功能包装在一个本机 DLL 中,通过平台调用从托管代码调用)。
开发过程中,DirectShow 在内部称为 Quartz,Quartz 还是主 DLL 的名称。1996 年 7 月,它首次作为 Microsoft ActiveMovie 版本 1.0 发布,当时它针对媒体播放提供了一个 ActiveX 控件,该控件除了支持音频文件外,还支持 Motion Picture Experts Group (MPEG) 1、影音交叉存取技术(Audio-Video Interleaved,AVI)和 QuickTime 视频。DirectShow SDK 作为 Microsoft DirectX SDK 的一部分由来已久,但 DirectShow SDK 现在包括在平台 SDK 中,它为开发 DirectShow 筛选器和应用程序提供工具和信息。
Windows Mobile 5.0 软件的 DirectShow 是用于流媒体的一种体系结构,它提供多媒体流的高质量播放和捕获。它支持很多格式,如波形 (WAV)、MP3(MPEG Audio Layer-3)、AVI、高级数据流格式(Advanced Streaming Format,ASF)和 MPEG。
DirectShow 与 Windows Mobile 5.0 支持的其他两种 DirectX 技术(DirectDraw 和 Direct3D)相集成。DirectShow 使用任何可用的视频和音频加速硬件,也支持不使用加速硬件的系统。
DirectShow 简化了媒体播放和格式转换,但对于需要自定义解决方案的应用程序而言,它还提供对基础流控制体系结构的访问。例如,您可以创建自己的组件来支持新媒体格式或自定义效果。您可以使用 DirectShow 编写的应用程序示例包括:AVI 和 MP3 播放器、AVI 到 ASF 的转换器,以及音频/视频捕获和编辑应用程序。DirectShow 基于 COM 并提供大量 COM 组件。要扩展 DirectShow,您需要实现自己的 COM 组件。
筛选器和筛选器图形
DirectShow 的主构造块是一个称为筛选器的组件。筛选器是一个在多媒体流上执行操作的软件(实际上是一个 COM)组件。例如,筛选器可以读取文件,从视频捕获设备获取视频,解码各种流格式,以及将数据传递到图形卡或声卡。
筛选器接收输入和产生输出,信息通过筛选器针在筛选器之间传递。一个针是一个筛选器端口,它可以是输入端口也可以是输出端口。如果筛选器解码 WMV 视频,则输入是 WMV 编码的流,输出是一系列未压缩的视频帧。在 DirectShow 中,一个应用程序通过将筛选器链连接在一起来执行任何任务,这样一个筛选器的输出就成为另一个筛选器的输入。一组连接的筛选器称为一个筛选器图形,图 18 显示一个用于播放带声音的视频文件的筛选器图形。

图 18. 典型视频文件的筛选器图形
筛选器图形必须遵循某些原则,第一个原则是需要一个源筛选器。这是数据的最初来源,无论它是文件、流媒体的 URL,还是诸如内置照相机这样的设备。然后,源筛选器的输出运行通过任意数量的转换筛选器。转换筛选器是这样的中间筛选器:它们接收某种类型的输入数据,修改传入的数据,然后将修改的数据传递到其输出。图的最后一部分是输出程序筛选器。输出程序筛选器是筛选器图形中处理的任何数据的最终目的地。输出程序可以代表以下内容:用于在屏幕上显示视频的窗口、用来发出声音的声卡,或者用来将数据存储到磁盘的筛选器编写器。
在图 18 中,这些筛选器如下所示:
• |
文件源筛选器从文件系统读取视频文件。 |
• |
拆分器筛选器将文件内容解析为两个流:一个压缩的视频流和一个音频流。 |
• |
视频解码器筛选器对视频帧进行解码。 |
• |
视频输出程序筛选器使用 DirectDraw 或图形设备接口 (GDI) 将这些帧绘制到显示器。 |
• |
声音设备筛选器使用 DirectSound 播放音频流。 |
请注意,图 18 中筛选器边缘的小正方形表示每个筛选器的针。
DirectShow SDK 附带一个名为 GraphEdit 的工具,用于处理筛选器图形。图 19 显示 GraphEdit 工具已经呈现的 WMV 视频文件。

图 19. GraphEdit 工具中 .wmv 文件的筛选器图形。
可以使用 GraphEdit 工具呈现文件、生成自定义图形、测试自定义筛选器、逐步(一帧接一帧)显示一个图形,以及处理类似的任务。如果要在应用程序中使用的图形已经确认在 GraphEdit 中运行,使用该工具将节省大量开发时间。(需要牢记的重要一点是,如果筛选器图形无法在 GraphEdit 中工作,它也无法在您的应用程序中工作。)GraphEdit 甚至可以将一个完整的筛选器图形保存为一个文件(.grf 扩展名),应用程序稍后可以加载该文件。
DirectShow 应用程序结构
DirectShow 应用程序进行的第一个操作是使用最重要的 COM 组件,即 Filter Graph Manager。为了使应用程序开发人员免于管理筛选器及其交互的复杂任务,该组件作为一个高级构造可以简化对筛选器图形及其筛选器的控制。您可以通过将筛选器连接在一起来使用 Filter Graph Manager 生成筛选器图形,然后应用程序可以进行诸如 Run、Pause 和 Stop 这样的简单调用,以便通过筛选器图形控制数据流。在筛选器图形的处理过程中,Filter Graph Manager 还将事件通知传递给应用程序。如需对流过程进行更多控制,也可以通过这些筛选器的 COM 接口直接访问它们。在任何情况下,很好地了解 COM 在使用 DirectShow 时都很有帮助。
简言之,DirectShow 应用程序的典型步骤是:
• |
创建 Filter Graph Manager 实例。 |
• |
使用 Filter Graph Manager 实例生成一个筛选器图形,方法是直接使用呈现功能或筛选器。 |
• |
通过对 Filter Graph Manager 实例进行高级调用来控制媒体流,并响应 Filter Graph Manager 实例引发的事件。 |
处理完成后,释放 Filter Graph Manager 实例以及使用的所有筛选器。
当使用 DirectShow 播放媒体时,需要注意的重要一点是,它使用单独的线程运行筛选器图形。在 DirectShow 筛选器图形的执行过程中,您会看到创建并运行了若干个线程,因为 DirectShow 为 Filter Graph Manager 创建一个线程,然后为筛选器图形中的每个筛选器都创建一个单独的线程。因此,应用程序将在 DirectShow 播放该媒体文件时继续运行,在大多数应用程序中,这对用户界面响应是件好事。然而,您需要为 DirectShow 线程留出足够的时间运行。例如,如果应用程序的主线程在播放媒体文件的同时进行大量处理,由于这些线程的优先级较低,媒体播放时将时断时续。
视频播放
简单任务(如显示视频文件)以及一些基本的控制台应用程序代码如下所示:
#include <dshow.h> void __cdecl main(void) { IGraphBuilder *pGraphBuilder; IMediaControl *pMediaControl; CoInitialize(NULL); CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC, IID_IGraphBuilder, (void **)&pGraphBuilder); pGraphBuilder->QueryInterface(IID_IMediaControl, (void **)&pMediaControl); pGraphBuilder->RenderFile(L"//test.wmv", NULL); pMediaControl->Run(); MessageBox(NULL, "Click OK to end playback.", "DirectShow", MB_OK); pMediaControl->Release(); pGraphBuilder->Release(); CoUninitialize(); }
上述代码示例首先声明和初始化 COM 库,然后该 COM 库用于创建 Filter Graph Manager 实例,该实例引用了图形生成器 (IGraphBuilder) 和媒体控件 (IMediaControl) 接口。图形生成器通过呈现(使用 RenderFile 方法)视频文件 (test.wmv) 来创建筛选器图形,然后媒体控件接口 (pMediaControl) 启动(使用 Run 方法)筛选器图形处理。显示一个消息框以防止应用程序关闭,但这不会影响视频的呈现,因为筛选器图形运行在单独的线程上。当该用户在消息框中点击 OK 时,接口引用随 COM 库一起释放。
更复杂的解决方案是使用以下代码侦听来自 Filter Graph Manager 实例的事件。
#include <dshow.h> void __cdecl main(void) { IGraphBuilder *pGraphBuilder; IMediaControl *pMediaControl; IMediaEvent *pMediaEvent; CoInitialize(NULL); CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC, IID_IGraphBuilder, (void **)&pGraphBuilder); pGraphBuilder->QueryInterface(IID_IMediaControl, (void **)&pMediaControl); pGraphBuilder->QueryInterface(IID_IMediaEvent, (void **)&pMediaEvent); pGraphBuilder->RenderFile(L"//test.wmv", NULL); pMediaControl->Run(); long eventCode; pMediaEvent->WaitForCompletion(INFINITE, &eventCode); pMediaControl->Release(); pGraphBuilder->Release(); CoUninitialize(); }
请注意,上述代码除了以粗体显示的新加内容外,几乎与之前的基本代码示例相同。在此处,您创建一个对事件 (IMediaEvent) 接口的引用,它用于等待筛选器图形处理完成。然而,在实际应用程序中,您应该避免使用 INFINITE,因为它可能会导致应用程序无限期阻塞。
如果不指定其他内容,该播放在单独的弹出窗口中进行。但在许多情况中,您可能想使播放窗口成为应用程序的子窗口。要指定播放窗口的所有者、类型和位置,可以使用以下代码(从上面代码示例修改而来)。
#include <dshow.h> void __cdecl main(void) { IGraphBuilder *pGraphBuilder; IMediaControl *pMediaControl; IMediaEvent *pMediaEvent; IVideoWindow *pVideoWindow; CoInitialize(NULL); CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC, IID_IGraphBuilder, (void **)&pGraphBuilder); pGraphBuilder->QueryInterface(IID_IMediaControl, (void **)&pMediaControl); pGraphBuilder->QueryInterface(IID_IMediaEvent, (void **)&pMediaEvent); pGraphBuilder->QueryInterface(IID_IVideoWindow, (void **)&pVideoWindow); pGraphBuilder->RenderFile(L"//test.wmv", NULL); pVideoWindow->put_Owner((OAHWND)g_hwnd); pVideoWindow->put_WindowStyle(WS_CHILD | WS_CLIPSIBLINGS); RECT rect; GetClientRect(g_hWnd, &rect); pVideoWindow->SetWindowPosition(0, 0, rect.right, rect.bottom); pMediaControl->Run(); long eventCode; pMediaEvent->WaitForCompletion(INFINITE, &eventCode); pVideoWindow->Release(); pMediaControl->Release(); pGraphBuilder->Release(); CoUninitialize(); }
改动的内容还是用粗体表示。首先,设置典型子窗口的所有者和样式。然后,将视频播放窗口的大小和位置设置为与主应用程序窗口 (g_hWnd) 的客户端区域(在 rect 中加载)的大小和位置相同。
请注意,在前面的代码示例中,排除了错误处理以便容易阅读。为完整起见,包括错误处理的相同代码如下所示。
#include <dshow.h> void __cdecl main(void) { IGraphBuilder *pGraphBuilder; IMediaControl *pMediaControl; IMediaEvent *pMediaEvent; IVideoWindow *pVideoWindow; HRESULT hr = CoInitialize(NULL); if(FAILED(hr)) { printf("ERROR: Couldn't initialize COM library!"); return; } hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC, IID_IGraphBuilder, (void **)&pGraphBuilder); if(FAILED(hr)) { printf("ERROR: Couldn't create Filter Graph Manager instance!"); return; } pGraphBuilder->QueryInterface(IID_IMediaControl, (void **)&pMediaControl); pGraphBuilder->QueryInterface(IID_IMediaEvent, (void **)&pMediaEvent); pGraphBuilder->QueryInterface(IID_IVideoWindow, (void **)&pVideoWindow); hr = pGraphBuilder->RenderFile(L"//test.wmv", NULL); if(SUCCEEDED(hr)) { pVideoWindow->put_Owner((OAHWND)g_hwnd); pVideoWindow->put_WindowStyle(WS_CHILD | WS_CLIPSIBLINGS); RECT rect; GetClientRect(g_hWnd, &rect); pVideoWindow->SetWindowPosition(0, 0, rect.right, rect.bottom); hr = pMediaControl->Run(); if(SUCCEEDED(hr)) { long eventCode; pMediaEvent->WaitForCompletion(INFINITE, &eventCode); } else printf("ERROR: Couldn't run filter graph!"); } else printf("ERROR: Couldn't render video file!"); pVideoWindow->Release(); pMediaControl->Release(); pGraphBuilder->Release(); CoUninitialize(); }
要接收 Filter Graph Manager 引发的事件,需要以下两个全局声明。
#define WM_FILTERGRAPHNOTIFY WM_APP + 1 IMediaEventEx *g_pMediaEventEx = NULL;
请注意,常量 WM_FILTERGRAPHNOTIFY 可设置为任何值,WM_APP + 1 就是一个示例。在播放该流(运行筛选器图形)之前,请使用以下代码。
g_pGraphBuilder->QueryInterface(IID_IMediaEventEx, (void **)&g_pMediaEventEx); g_pMediaEventEx->SetNotifyWindow((OAHWND)g_hWnd, WM_FILTERGRAPHNOTIFY, 0);
上述代码指示 Filter Graph Manager 使用第二个参数 (WM_FILTERGRAPHNOTIFY) 的消息标识将事件发送到主应用程序窗口 (g_hWnd)。请注意,SetNotifyWindow 调用的第三个参数将作为窗口消息 (WM_FILTERGRAPHNOTIFY) 的 lParam 参数返回到应用程序。在前面的示例代码中不使用该参数,因此它设为零。然而,该参数可用于传递实例数据和事件。
现在,可以将以下代码添加到应用程序的消息循环(通常在 WndProc 函数中)。
case WM_FILTERGRAPHNOTIFY: HandleFilterGraphEvent(); break;
然后,可以使用以下代码处理事件。
void HandleFilterGraphEvent() { if (g_pMediaEventEx == NULL) return; long eventCode; LONG_PTR param1, param2; HRESULT hr; while(SUCCEEDED(g_pMediaEventEx->GetEvent(&eventCode, ¶m1, ¶m2, 0))) { g_pMediaEventEx->FreeEventParams(eventCode, param1, param2); switch(eventCode) { case EC_COMPLETE: case EC_USERABORT: case EC_ERRORABORT: g_pMediaControl->Stop(); long eventCode; g_pMediaEvent->WaitForCompletion(INFINITE, &eventCode); g_pMediaEventEx->SetNotifyWindow(NULL, 0, 0); g_pMediaEventEx->Release(); g_pMediaEventEx = NULL; // Do other clean-up (releases, etc.) PostQuitMessage(0); return; } } }
如果没有设置事件指针,将退出该处理,然后检索该队列上的所有事件。GetEvent 方法的第四个参数是等待事件的时间(以毫秒为单位)。因为来自 Filter Graph Manager 的事件已经在队列中,所以该参数可以设为零,这意味着不等待。请注意,EC_COMPLETE 事件不会自动停止筛选器图形的处理,因此在接收到该事件时停止该筛选器图形是一个好做法。
该介绍将使您能够在自己的应用程序中开始实现媒体播放,因此,现在本文将解决更复杂的捕获视频和声音的任务。
视频捕获
创建针对视频和音频捕获的筛选器图形比创建针对播放的筛选器更复杂。图 20 显示带有声音的视频捕获的典型筛选器图形。

图 20. 带有声音的视频捕获的筛选器图形
为了帮助创建和控制筛选器图形,DirectShow 提供了一个名为 Capture Graph Builder 的组件。就像对播放筛选器图形一样,首先创建一个 Filter Graph Manager 实例。然后,创建 Capture Graph Builder 实例并将两者相连。该本机代码如下所示。
IGraphBuilder *pGraphBuilder; ICaptureGraphBuilder2 *pCaptureGraphBuilder; CoCreateInstance(CLSID_FilterGraph, 0, CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (void**)&pGraphBuilder); CoCreateInstance(CLSID_CaptureGraphBuilder, NULL, CLSCTX_INPROC_SERVER, IID_ICaptureGraphBuilder2, (void**)&pCaptureGraphBuilder); pCaptureGraphBuilder->SetFiltergraph(pGraphBuilder);
因为它将节省一些编码工作(特别是关于 COM 的代码),您可以使用以下活动模板库 (ATL) 代码。
CComPtr<IGraphBuilder> pGraphBuilder; CComPtr<ICaptureGraphBuilder2> pCaptureGraphBuilder; pCaptureGraphBuilder.CoCreateInstance(CLSID_CaptureGraphBuilder); pGraphBuilder.CoCreateInstance(CLSID_FilterGraph); pCaptureGraphBuilder->SetFiltergraph(pGraphBuilder);
继续 ATL,下一步是使用以下代码初始化视频捕获筛选器(如前面的图 20 所示)。
CComPtr<IBaseFilter> pVideoCapture; CComPtr<IPersistPropertyBag> pPropertyBag; DEVMGR_DEVICE_INFORMATION di; CPropertyBag PropBag; CComVariant varCamName; GUID guidCamera = { 0xCB998A05, 0x122C, 0x4166, 0x84, 0x6A, 0x93, 0x3E, 0x4D, 0x7E, 0x3C, 0x86 }; di.dwSize = sizeof(di); HANDLE handle = FindFirstDevice(DeviceSearchByGuid, &guidCamera, &di); FindClose(handle); pVideoCapture.CoCreateInstance(CLSID_VideoCapture)); pVideoCapture.QueryInterface(&pPropertyBag)); varCamName = di.szLegacyName; PropBag.Write(L"VCapName", &varCamName); pPropertyBag->Load(&PropBag, NULL); pPropertyBag.Release(); pGraphBuilder->AddFilter(pVideoCapture, L"Video capture source");
第一个照相机捕获设备通过 FindFirstDevice 函数检索,该函数的第二个参数设置为 DEVCLASS_CAMERA_GUID(对应于在前面的代码中硬编码的 GUID [CB998A05-122C-4166-846A-933E4D7E3C86]),该检索方式是查找捕获设备的最可靠方式。属性包实例(PropBag 是实现 IPropertyBag 接口的自定义类 [CPropertyBag] 的实例)用于将捕获设备名称信息传递到捕获筛选器,然后该视频捕获筛选器添加到筛选器图形。
下一步是初始化音频捕获筛选器,为此您可以使用以下代码。
CComPtr<IBaseFilter> pAudioCaptureFilter; pAudioCaptureFilter.CoCreateInstance(CLSID_AudioCapture); pAudioCaptureFilter.QueryInterface(&pPropertyBag); pPropertyBag->Load(NULL, NULL); pGraphBuilder->AddFilter(pAudioCaptureFilter, L"Audio Capture Filter");
创建音频捕获筛选器并将其添加到筛选器图形。现在应该初始化视频编码器并将其添加到筛选器图形。您可以借助于DMO Wrapper 筛选器在筛选器图形中使用 DirectX 媒体对象 (DMO) 实例。要使用 DMO Wrapper 筛选器用 WMV 9 DMO 对视频进行编码,可以使用以下代码。
CComPtr<IBaseFilter> pVideoEncoder; CComPtr<IDMOWrapperFilter> pVideoWrapperFilter; pVideoEncoder.CoCreateInstance(CLSID_DMOWrapperFilter); pVideoEncoder.QueryInterface(&pVideoWrapperFilter); pVideoWrapperFilter->Init(CLSID_CWMV9EncMediaObject, DMOCATEGORY_VIDEO_ENCODER); pGraphBuilder->AddFilter(pVideoEncoder, L"WMV9 DMO Encoder");
WMV 9 编码器加载到 DMO Wrapper 筛选器中之后,就该加载 ASF 多路复用器和设置多路复用器的名称了(使用对多路复用器的文件接收接口的引用)。进行此操作的代码如下所示。
CComPtr<IBaseFilter> pAsfWriter; CComPtr<IFileSinkFilter> pFileSink; pAsfWriter.CoCreateInstance(CLSID_ASFWriter); pAsfWriter->QueryInterface(IID_IFileSinkFilter, (void**) &pFileSink); pFileSink->SetFileName(L"//My Documents//test.asf", NULL);
现在已经创建了该图形中需要的所有筛选器。下一步是使用以下代码将这些筛选器的针连接在一起。
pCaptureGraphBuilder->RenderStream(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video, pVideoCapture, pVideoEncoder, pAsfWriter ); pCaptureGraphBuilder->RenderStream(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Audio, pAudioCaptureFilter, NULL, pAsfWriter ); pCaptureGraphBuilder->RenderStream(&PIN_CATEGORY_PREVIEW, &MEDIATYPE_Video, pVideoCapture, NULL, NULL );
视频捕获通过视频编码器连接到多路复用器,然后音频捕获也连接到多路复用器。最后,视频捕获筛选器的预览针连接到视频输出程序。不需要指定视频输出程序(作为最后一个参数),因为它是默认指定的。
既然已经对这些筛选器进行了初始化,将这些筛选器添加到筛选器图形并且连接了所有针,现在就已经准备好使用下面的代码来捕获数据了。
CComPtr<IMediaControl> pMediaControl; CComPtr<IMediaEvent> pMediaEvent; CComPtr<IMediaSeeking> pMediaSeeking; pCaptureGraphBuilder->ControlStream(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video, pVideoCapture, 0, 0 , 0, 0); pCaptureGraphBuilder->ControlStream(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Audio, pAudioCaptureFilter, 0, 0, 0, 0); pGraphBuilder.QueryInterface(&pMediaControl); pMediaControl->Run(); Sleep(1000); LONGLONG dwStart = 0; LONGLONG dwEnd = MAXLONGLONG; OutputDebugString(L"Starting to capture the first file" ); pCaptureGraphBuilder->ControlStream(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video, pVideoCapture, &dwStart, &dwEnd, 0, 0); pCaptureGraphBuilder->ControlStream(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Audio, pAudioCaptureFilter, &dwStart, &dwEnd, 0, 0); Sleep(5000); OutputDebugString(L"Stopping the capture"); pGraphBuilder.QueryInterface(&pMediaSeeking); pMediaSeeking->GetCurrentPosition(&dwEnd); pCaptureGraphBuilder->ControlStream(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video, pVideoCapture, &dwStart, &dwEnd, 1, 2); pCaptureGraphBuilder->ControlStream(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Audio, pAudioCaptureFilter, &dwStart, &dwEnd, 1, 2); OutputDebugString(L"Wating for the control stream events"); pGraphBuilder.QueryInterface(&pMediaEvent); long lEventCode; LONG_PTR lParam1, lParam2; do { pMediaEvent->GetEvent(&lEventCode, &lParam1, &lParam2, INFINITE); pMediaEvent->FreeEventParams(lEventCode, lParam1, lParam2); if(lEventCode == EC_STREAM_CONTROL_STOPPED) { OutputDebugString(L"Received a control stream stop event"); count++; } } while(count < 2); OutputDebugString(L"The file has been captured");
该捕获图形由视频和音频控制流阻塞,然后在实际捕获数据之前允许它运行一秒。该延迟为捕获图形提供了时间,以确保分配它所有的缓冲区以及同步所有进程。一个捕获进行 5 秒,然后停止。视频和音频的控制流用于停止该流,最后,一个循环等待一个标志该流停止的事件。
下面的代码片段显示一种更明确的方式来捕获图形正在运行的时刻(与等待 1 秒相比)。
OAFilterState state = State_Stopped; pMediaControl->Run(); while(state != State_Running) pMediaControl->GetState(100, &state);
GetState 方法的第一个参数是超时(以毫秒为单位),因此该代码每 1/10 秒将尝试一次,以查看该捕获图形是否已经启动且正在运行。
有关视频捕获的更多详细信息,请参阅 Windows Mobile 5.0 Pocket PC SDK 附带的 CameraCapture 示例。
自定义筛选器
正如前面提到的,基本上有三种筛选器类型:源筛选器、转换筛选器和输出程序筛选器。源筛选器提供来自源的原始多媒体数据,如文件、URL 或类似照相机的实时源。源筛选器可以将原始数据传递到分析器或拆分器筛选器,也可以自己进行分析或拆分。输出程序筛选器接受完全处理的数据并在显示器或扬声器上进行呈现,它们包括编写文件的筛选器。源筛选器和输出程序筛选器之间的所有筛选器都是转换筛选器。转换筛选器使用原始数据或部分处理的数据,并在将其传递到下一个筛选器之前进行处理。有许多不同类型的转换筛选器;一些筛选器将字节流解析为示例或帧,而其他筛选器进行压缩或解压缩,甚至进行格式转换。
虽然 DirectShow 包括大量用于播放、转换和捕获许多不同媒体格式的现成筛选器,但是开发人员可以生成自己的自定义筛选器以便处理自定义或标准数据格式。实现自定义筛选器时,它可能是一个转换筛选器。它可能是一个将效果(如淡入或淡出)添加到视频流的筛选器。
DirectShow SDK 包括大量自定义筛选器,SDK 文档提供针对编写自定义筛选器的优秀介绍。以下来自 SDK 文档的摘要提供创建转换筛选器的基本步骤:
• |
确定筛选器是必须复制媒体示例还是适当地处理它们。媒体流中进行的复制越少越好。然而,某些筛选器需要一个复制操作;该要求影响基类的选择。 |
• |
确定使用哪些基类并从基类派生筛选器类(如果需要,也可以派生针类)。在该步骤中,为筛选器创建一个或多个标头。在许多情况中,可以使用转换基类,从正确的转换筛选器类派生类,以及重写几个成员函数。在其他情况中,可以使用更通用的基类。这些类实现了大部分的连接和协商机制;但这些类也提供在重写更多成员函数的开销方面的灵活性。 |
• |
添加实例化筛选器所需的代码。该步骤需要将静态 CreateInstance 成员函数添加到派生的类中,该类还是一个全局数组,包含筛选器名称、CLSID 和指向该成员函数的指针。 |
• |
调用 NonDelegatingQueryInterface 函数在您的筛选器中分布任何唯一的接口。该步骤强调实现接口的 COM 方面,而不是基类中的其他方面。 |
• |
重写基类成员函数。该步骤包括编写对于筛选器而言唯一的转换函数,以及重写连接过程所需的几个成员函数,如设置分配器大小或提供媒体类型。 |
有关更多信息,请参阅 SDK 文档。
小结
Windows Mobile 5.0 软件中针对多媒体的扩展支持可以增强您的托管和本机应用程序,诸如图片选择和照相机捕获对话框这样的高级构造可以轻松集成到应用程序中。对于许多高级媒体播放方案,媒体播放器控件是一个有效的选择。要获取更多的控制和灵活性,DirectShow API 提供低级别功能来捕获、解码、呈现和转换视频和音频流。使用这些资源,您可以实现用户需要的多媒体要求。
发表评论
-
J2ME程序开发全方位基础讲解汇总
2007-08-03 14:19 645一、J2ME中需要的Java基础知识现在有大部分人,都是从零开 ... -
使用J2ME技术开发RPG游戏
2007-08-03 14:24 576RPG(角色扮演游戏)是手机游戏中的一类主要类型,也是相对来说 ... -
搭建J2ME开发环境
2007-08-05 12:48 595由于WTK并没有提供代码编辑的功能,因此本文讲述如何使用Ecl ... -
Experiments in Streaming Content in Java ME(一)
2007-08-13 13:43 603Since my book on Mobile Media A ... -
Experiments in Streaming Content in Java ME(二)----Creating an RTSP Protocol Handler
2007-08-13 13:44 1469Recall that RTSP is the actual ... -
Pocket PC、Pocket PC Phone、Smartphone的区别
2007-08-23 16:59 694首先说明几个概念: 1、什么是Pocket PC?Pocket ... -
Windows mobile Install the Tools
2007-08-23 17:39 603Install Visual Studio 2005 ... -
windows mobile 5.0 模拟器上网的设置
2007-08-28 14:31 668在确保主机已连上互联网的情况下,按以下步骤设置: 1、打 ... -
Windows Mobile的安装与流媒体的安装设置
2007-09-05 17:29 887本文重点描述了在Windows Mobile下使用流媒体服务 ... -
Windows Mobile中的WebService应用
2007-09-07 15:03 674Web Service对于开发者来说已经不再是一个陌生概念了。 ...
相关推荐
- **Windows Mobile 5.0**:在这一版本中,无论是对于Pocket PC还是Smartphone都引入了显著的改进,比如支持SoftKey单手操作、内置文件系统和注册表等。 #### 二、开发工具:Visual Studio 2005 Visual Studio ...
- **图片和视频:** 查看媒体文件。 - **入门:** 教程引导。 - **日历:** 日历应用。 - **电子邮件:** 收发邮件。 - **Marketplace:** 应用商店。 - **Office Mobile 2010:** 移动办公套件。 - **GyPSii:** ...
- 可以通过测距仪和相机设备进行辅助数据采集,例如通过测距仪进行距离和角度测量,通过相机拍摄与地理要素关联的照片。 **2.3 图层管理** - 用户可以自由调整图层的显示顺序,决定哪些图层可见,哪些图层可查询。...
内容概要:本文介绍了基于博途1200PLC的新型彩色广告屏流水灯仿真系统的设计与实现。该系统利用博途1200PLC控制器、彩色广告屏、流水灯及相关传感器,实现了广告内容的自动化播放和流水灯的流水效果。系统通过PLC控制器对广告屏和流水灯进行编程控制,支持环境感知和智能化控制,提高了广告的吸引力和用户体验。此外,系统还采用了节能环保材料,降低了能耗和环境污染。 适合人群:从事工业自动化领域的工程师和技术人员,以及对PLC控制系统感兴趣的读者。 使用场景及目标:适用于商场、酒店、展览馆等需要展示广告的场所,旨在提升广告展示的效果和智能化水平,同时降低能耗和环境污染。 其他说明:该系统展示了PLC技术在现代广告展示中的创新应用,强调了高可靠性、灵活性和智能化的特点。随着科技的发展,该系统有望在更多领域得到广泛应用。
内容概要:本文详细介绍了西门子200PLC全自动定长度裁切机设备程序的设计与应用。该程序由主程序、长度设定程序、切割控制程序和注释程序组成,通过精确控制电机和气动元件的运作,实现了高精度的裁切效果。威纶通触摸屏程序的加入使操作更加简便,同时程序中添加了详细的中文注释,便于新手理解和掌握。此外,该程序不仅适用于特定设备,还可根据需要修改应用于其他类似设备。 适合人群:对工业自动化感兴趣的初学者以及从事相关工作的技术人员。 使用场景及目标:①帮助新手快速掌握西门子200PLC编程技巧;②提高工业生产中的裁切精度和效率;③提供一种简单易用的操作界面,提升工作效率。 其他说明:随着工业自动化技术的发展,该程序有望在未来得到更广泛的应用和改进。
内容概要:本文详细介绍了基于MATLAB平台使用YoloV3算法进行人体目标检测的方法。首先阐述了人体目标检测技术的重要性和应用场景,如公共场所、安防监控和自动驾驶等领域。接着简述了YoloV3算法的基础理论,强调其高效的目标检测能力。然后逐步讲解了实战操作流程,包括数据准备、环境配置、调用YoloV3算法以及结果展示与评估。最后提供了具体的MATLAB代码示例,帮助读者快速上手并实现人体目标检测。 适合人群:对计算机视觉和深度学习感兴趣的科研人员、工程师及学生。 使用场景及目标:适用于需要实现实时人体目标检测的应用场合,旨在提高检测速度和准确性,推动相关领域的技术创新和发展。 其他说明:随着AI技术的进步,未来会有更多先进算法应用于人体目标检测,带来更多可能性。
内容概要:本文探讨了基于自抗扰控制(ADRC)技术的永磁同步电机(PMSM)矢量控制策略。文中介绍了PMSM的特点及其广泛应用背景,指出了传统矢量控制存在的局限性,并详细阐述了ADRC的工作原理,包括跟踪微分器(TD)、扩张状态观测器(ESO)和非线性状态误差反馈控制律(NLSEF)。此外,还展示了如何将ADRC应用于PMSM的速度环和电流环控制中,通过具体代码实例解释了其实现过程。研究表明,ADRC可以显著提高PMSM在面对负载变化和其他扰动情况下的稳定性和响应速度。 适合人群:从事电机控制领域的研究人员和技术人员,尤其是关注永磁同步电机高性能控制方案的专业人士。 使用场景及目标:适用于需要改进现有PMSM控制系统鲁棒性和精度的应用场合,如工业自动化设备、电动汽车等领域。目标是帮助读者理解和掌握ADRC技术,从而优化PMSM的控制效果。 其他说明:尽管ADRC展现了优越的抗干扰能力,但其参数整定仍需深入研究,以确保最佳性能。
内容概要:本文探讨了表贴式永磁同步电机(SPMSM)中滑膜无位置观测器算法的最新进展。传统的一阶模型SMO观测器需要使用低通滤波器来滤除开关函数噪声,导致观测角度出现相位滞后,影响电机性能。新型的SMO无位置观测器通过扩张反电势状态,实现了无需低通滤波器即可精确估计反电动势,消除了相位滞后,提升了电机的稳定性和效率。此外,文中还介绍了滑膜技术的应用,确保电机在突加负载情况下仍能保持高效的运行状态。最后,提供了伪代码示例,展示了SMO无位置观测器的核心实现步骤。 适合人群:从事电机控制系统研究与开发的专业人士,以及对现代控制技术和电机控制感兴趣的科研人员和技术爱好者。 使用场景及目标:适用于需要提高电机控制精度和稳定性的应用场景,如电力传动系统、自动化设备、工业机器人等领域。目标是减少转速波动,提高系统的响应速度和鲁棒性。 其他说明:本文不仅深入剖析了理论背景,还提供了实际的算法实现思路,有助于读者更好地理解和应用这一先进技术。
实训商业源码-微信朋友圈-毕业设计.zip
实训商业源码-苹果CMS V10大气橙色风格影视电影视频网站模板-毕业设计.zip
实训商业源码-风吟导航-毕业设计.zip
数据集介绍:无人机视角多类别交通目标检测数据集 一、基础信息 数据集名称:无人机视角多类别交通目标检测数据集 数据规模: - 训练集:14,066张航拍图片 - 验证集:2,697张航拍图片 - 测试集:1,432张航拍图片 检测类别: - 载具类:汽车(car)、巴士(bus)、摩托车(motor)、船(tekne)、火车(tren) - 人员类:行人(insan)、工人(ismak) - 设施类:起重机(kam)、飞机(uap)、未知设备(uai) 标注格式: YOLO格式标注,包含目标边界框坐标及类别标签,适配主流检测框架 二、适用场景 智慧城市管理系统: 支持无人机航拍影像的实时交通流量分析,适用于城市道路车辆监控、港口船舶调度等场景 无人机应用开发: 为无人机自动巡检系统提供训练数据,支持电力巡检、铁路巡查等场景中的多目标识别 交通规划研究: 提供高空视角下的复杂交通场景样本,适用于交叉路口流量分析、交通设施布局优化等研究 安防监控系统: 包含人员与设备的协同检测能力,适用于工地安全监控、港口作业管理等安防场景 三、数据集优势 多维度覆盖: - 同时包含11类交通要素检测,覆盖陆地/水上/空中三维交通场景 - 包含不同天气条件下的高空视角样本,适应复杂环境检测需求 标注质量保障: - 严格校验的YOLO格式标注,确保坐标系统精准对应航拍视角 - 特殊处理小目标检测样本,优化无人机高空拍摄的像素级标注 任务适配性强: - 支持目标检测任务直接训练,兼容YOLOv5/v7/v8等主流版本 - 包含密集目标分布样本,适用于群体目标检测模型优化 场景多样性: - 涵盖城市道路、港口码头、铁路轨道等多种典型场景 - 包含不同光照条件下的日间作业场景样本
在当今城市化进程加速的背景下,消防安全已成为城市管理和经济发展的基石。面对频繁发生的火灾事故,如厦门BRT公交车纵火、芜湖液化气爆炸等,传统消防管理模式已难以满足现代城市的安全需求。为此,智慧消防信息体系应运而生,它以物联网、大数据、云计算等现代信息技术为支撑,旨在通过智能化手段提升消防工作的效率与精准度,为城市安全保驾护航。 一、智慧消防的需求背景与价值 智慧消防的需求源于多个方面。首先,随着国家治理体系信息化和治理能力现代化的推进,消防工作作为城市管理的重要组成部分,必须紧跟时代步伐,实现数字化转型。其次,传统消防管理模式存在“四轻四重”现象,即重硬件建设轻信息共享、重事后救援轻安全监控、重信息保密轻信息公开、重预案准备轻预警分析,这些问题严重制约了消防工作的有效开展。智慧消防通过构建一体化信息平台,打破信息壁垒,实现信息的实时共享与协同作战,有效解决了上述问题。 智慧消防的价值体现在多个层面。一方面,它利用物联网技术实现全面感知与互联,通过智能传感器、无线通信等手段,对消防设施、重点部位进行实时监测,确保火灾隐患早发现、早处置。另一方面,借助大数据分析技术,智慧消防能够对海量消防数据进行深度挖掘,提供精准的风险评估与预警服务,为消防决策提供科学依据。此外,智慧消防还通过打通基层、消防及各职能部门间的信息共享纽带,实现火警处置过程中的高效协同,提升整体应急响应能力。 二、智慧消防信息体系的核心功能与应用 智慧消防信息体系涵盖了多个核心功能模块,共同构建起一个全方位、多层次的消防安全防护网。其中,消防重点单位物联网监控是重要一环,通过接入既有火灾报警设施、安防视频监控及消防重要设施,实现对重点单位的连续有效监测。一旦发生火情,系统能够自动上报火警信号,并快速对接接处警系统,调派消防力量进行处置。 设施状态监测及上报功能则确保了消防设施的正常运行。系统能够自动监测用户单位消防水、电源、门状态等关键指标,及时发现并上报故障、违规事件,为消防设施的维护与管理提供有力支持。同时,公共消防设施状态监测功能也进一步拓展了消防安全的监测范围,通过监测城市消火栓的碰撞破坏、水压、开关状态等信息,为灭火处置提供便利条件。 社会消防安全网格化管理是智慧消防的另一大亮点。通过划分三级网格,建立权责分明的覆盖中小社会单位的消防管理网络体系,实现了消防安全的群防群治。网格员利用手机App进行日常检查、宣传、采集与报警工作,实现了消防工作的信息化与智能化。此外,安防视频接入与智能分析功能则通过接入平安城市、社会单位等视频资源,利用智能分析技术实现火灾早期探测、人员在岗检测等功能,显著提升了消防工作的主动性与精准性。 三、智慧消防信息体系的展望与未来 展望未来,智慧消防信息体系将继续遵循体系建设规律,运用体系工程方法,按照演化发展思路进行顶层设计、研制建设、集成验证与持续演进。通过不断优化系统架构、提升技术水平、完善标准规范等措施,智慧消防将更好地服务于城市消防安全需求,为构建安全、和谐、宜居的城市环境贡献力量。 同时,智慧消防的发展也将促进相关产业的繁荣与创新。随着物联网、大数据、云计算等技术的不断成熟与应用推广,智慧消防产业链将不断完善与拓展,形成包括设备制造、系统集成、运营服务在内的完整产业生态。这不仅将为消防行业带来新的增长点与发展机遇,也将为城市安全与应急管理领域注入新的活力与动力。 总之,智慧消防信息体系作为现代城市消防安全的重要保障手段之一,正以其独特的优势与潜力引领着消防行业的变革与发展。我们有理由相信,在未来的日子里,智慧消防将不断创造新的奇迹与辉煌,为城市的安全与繁荣贡献更大的力量。
内容概要:本文详细介绍了单直流输入双直流输出Buck转换器的设计原理及其仿真实现。首先简述了Buck转换器作为降压型直流电源转换器的工作机制,接着深入探讨了如何在一个20V输入下,分别输出15V/1A和5V/2A的具体设计方案。文中还展示了通过调节开关管占空比、电感值以及电容值等关键参数来达到预期输出效果的方法,并通过仿真实验验证了这一设计的有效性和稳定性。最后,作者提出了未来可能的研究方向,如提升电路效率和降低功耗。 适合人群:从事电力电子、嵌入式系统设计的专业人士和技术爱好者。 使用场景及目标:适用于希望深入了解Buck转换器工作原理及其应用的人群,特别是那些正在寻找解决多路直流供电问题解决方案的研发人员。 阅读建议:对于想要掌握更多有关Buck转换器的知识并应用于实际项目中的读者来说,本篇文章不仅提供了理论指导,还有具体的实验数据支持,因此非常适合用来做为学习资料或者参考资料。
内容概要:该数据集专注于灭火器检测,包含3255张图片,每张图片均进行了标注。数据集提供了两种格式的标注文件,分别是Pascal VOC格式的xml文件和YOLO格式的txt文件,确保了不同需求下的兼容性。所有图片为jpg格式,标注工具采用labelImg,通过矩形框对单一类别“extinguisher”进行标注,总计标注框数为6185个。数据集旨在支持计算机视觉领域的研究与开发,特别是针对物体检测任务,提供了高质量的标注数据; 适合人群:从事计算机视觉研究或开发的技术人员,尤其是专注于物体检测领域,如安防监控、智能消防系统的研发人员; 使用场景及目标:①作为训练集用于深度学习模型的训练,提升模型对灭火器识别的准确性;②用于测试和验证已有的检测算法性能; 其他说明:数据集不对基于其训练出的模型精度做保证,但承诺提供准确合理的标注。数据集仅含图片及对应的标注文件,不包括预训练模型或权重文件。
实训商业源码-飞飞-毕业设计.zip
实训商业源码-苹果CMS通用电影网站模板[优化版]-毕业设计.zip
3D点云匹配_基于Transformer+旋转不变性实现的点云匹配算法_附项目源码+流程教程_优质项目实战
实训商业源码-电商小程序模板-毕业设计.zip
实训商业源码-人生重开模拟器微信小程序源码-毕业设计.zip