Pocket Streamer






3.30/5 (15投票s)
使用 Pocket Streamer 将音频流式传输到您的 Pocket PC。
引言
此应用程序允许您从 Pocket PC 浏览台式 PC 上的音乐库,选择艺术家、专辑、曲目或广播电台,通过网络连接对其进行流式传输,并在 Pocket PC 上收听。使用此应用程序,您可以随身携带整个音乐收藏,而无需将选定的曲目下载到扩展卡。您还可以收听许多 Internet 广播电台,而无需连接到 PC。只要您有台式机的网络连接,就可以无论身在何处收听音乐库,理论上还可以通过 WiFi 热点从世界另一端收听。
系统要求
桌面
掌上电脑
- Pocket PC 2000、2002 或 2003。
- .NET Compact Framework。您可以在此处下载。
安装
Pocket Streamer 包括一个在 Pocket PC 上运行的客户端应用程序和一个在台式 PC 上运行的服务器应用程序。
Setup.exe
提供了一个 Setup.exe,用于安装客户端和服务器应用程序。在运行 Setup.exe 之前,请确保台式 PC 上已安装 Windows Media Player 和 Windows Media Encoder。
源代码
您还可以下载一个 Visual Studio .NET 2003 解决方案,其中包含客户端和服务器应用程序的源代码。在编译和运行服务器应用程序之前,您需要安装 Windows Media Player SDK 和 Windows Media Encoder SDK。这些 SDK 提供了允许您自动执行 Windows Media Player 和 Windows Media Encoder 的接口。
您可以在此处下载 Windows Media Player SDK。您可以在此处下载 Windows Media Encoder SDK。
下载并安装这些 SDK 后,您必须注册“Primary Interop Assemblies”,它们提供 COM 互操作层。这些程序集随 Windows Media Player SDK 一起提供。请按照以下说明注册程序集
- 使用命令行工具 regasm 注册 Windows Media Player 互操作程序集。
regasm C:\WMSDK\WMPSDK9\redist\wmppia.dll
- 使用命令行工具 gacutil 将 Windows Media Player 互操作程序集安装到全局程序集缓存中。
Gacutil /i C:\WMSDK\WMPSDK9\redist\wmppia.dll
注意:Setup.exe 会为您执行这些注册步骤。
安装完 SDK 后,您应该能够打开解决方案并生成项目。
注意:如果您需要更新客户端应用程序中的 Web 引用,请务必编辑 URL 指向您的台式 PC。在更新引用时,您还必须运行服务器应用程序。
运行应用程序
运行应用程序
- 在台式 PC 上启动服务器。
- 以您通常的方式将 Pocket PC 连接到网络(您必须在 Pocket PC 和台式 PC 之间具有 IP 连接)。
- 启动客户端应用程序,然后单击“工具”-“设置”菜单项。输入台式 PC 的 IP 地址,然后关闭表单。
- 单击“库”菜单项,然后单击箭头工具栏按钮。这将把您的媒体库下载到您的 Pocket PC。
- 通过单击并按住来从树中选择一个媒体项。在弹出菜单中,选择“播放”。这应该开始将选定的项流式传输到您的 Pocket PC。
背景
Pocket Streamer 使用 Windows Media 技术套件,使您能够浏览您的媒体库并在 Pocket PC 上收听选定的曲目。Pocket Streamer 使用三个独立的 Windows Media 应用程序来支持。
- Windows Media Player 9 用于访问存储在台式 PC 上的媒体库并播放选定的媒体项。Windows Media Player 不仅播放 MP3 等媒体项,还可以让您通过将它们组织到播放列表和专辑中来管理这些媒体项。Pocket Streamer 从 Media Player 检索播放列表等详细信息,并在您的 Pocket PC 上显示它们。一旦您选择了一个要播放的媒体项,Pocket Streamer 就会指示 Windows Media Player 通过台式 PC 的声卡播放该项。
- Windows Media Encoder 提供编码音频或视频流的基本功能。Media Encoder 提供了各种选项,其中之一是流式传输 PC 连接声卡的输出。客户端可以使用 Windows Media Player 连接并收听流。Pocket Streamer 会自动执行 Media Encoder,并指示它通过 HTTP 流式传输声卡输出。
- Windows Media Player Pocket PC Edition 用于连接 Media Encoder 提供的流。Pocket Streamer 客户端本身不播放选定的媒体项,而是启动 Pocket Windows Media Player,并带有一个包含要连接的 URL 的命令行参数。然后,Windows Media Player 会播放所选 URL 的流。
Pocket Streamer 使整个过程对用户透明,用户只需选择他们的曲目并收听。
使用代码
服务器
Pocket Streamer 服务器运行在系统托盘中,负责:
- 启动和停止编码会话。由
Encoder
类提供。 - 查询 Windows Media 库。由
Library
类提供。 - 播放媒体项。由
Player
类提供。 - 提供一个供 Pocket PC 客户端调用的接口。
Encoder 类
Encoder
类提供了两个方法来自动执行 Windows Media Encoder 应用程序:Start()
和 Stop()
。
/// <SUMMARY>
/// Starts the media encoder.
/// </SUMMARY>
public void Start()
{
//Check whether the encoder is currently running
if (WMP.Encoder.RunState == WMENC_ENCODER_STATE.WMENC_ENCODER_STOPPED)
{
try
{
// Specify the source for the input stream.
IWMEncSourceGroupCollection SrcGrpColl =
WMP.Encoder.SourceGroupCollection;
IWMEncSourceGroup SrcGrp = SrcGrpColl.Add("SG_1");
IWMEncSource SrcAud = SrcGrp.AddSource(WMENC_SOURCE_TYPE.WMENC_AUDIO);
SrcAud.SetInput("Default_Audio_Device", "Device", "");
SrcGrp.set_Profile(System.Windows.Forms.Application.StartupPath
+ @"\pocketprofile.prx");
// Create a broadcast.
IWMEncBroadcast BrdCst = WMP.Encoder.Broadcast;
BrdCst.set_PortNumber (WMENC_BROADCAST_PROTOCOL.WMENC_PROTOCOL_HTTP, 8080);
//Start the encoder
WMP.Encoder.Start();
}
catch (Exception ex)
{
throw new ApplicationException("Failed to start the encoder.", ex);
}
}
}
/// <SUMMARY>
/// Stops the media encoder.
/// </SUMMARY>
public void Stop()
{
try
{
WMP.Encoder.Stop();
}
catch (Exception ex)
{
throw new ApplicationException("Failed to stop the encoder.", ex);
}
}
这段代码几乎是直接从 SDK 的示例中提取的。需要注意的一点是 pocketprofile.prx 文件的使用。这是一个 Windows Media Encoder 文件,其中包含编码会话的所有属性和值。Windows Media Encoder 带有一个工具,允许您创建和编辑这些文件。这是创建允许我流式传输到 Pocket PC 的 Media Encoder 配置文件的最快方法。我尝试在代码中设置编码器会话但失败了,所以这是最简单的选择。这种方法的一个问题是应用程序受限于文件中指定的比特率。如果您希望能够根据不同情况更改比特率,则需要从文件中设置编码会话参数,而不是在代码中设置。
Library 类
Library
类有各种方法用于检索有关 Windows Media Player 库中媒体项的详细信息。提供的方法用于检索库中所有音频项、所有播放列表和所有广播电台。这些方法中的每一个都将库数据作为 XML 字符串返回。Windows Media Player SDK 提供了各种查询库的方法。以下代码片段演示了如何获取广播电台列表
IWMPPlaylist stations =
WMP.Player.mediaCollection.getByAttribute("MediaType", "RADIO");
Player 类
Player
类提供了标准的 Media Player 控件,如开始、停止、下一首和上一首曲目以及暂停。它还提供了播放专辑、单曲、某艺术家所有曲目、播放列表和广播电台的方法。以下代码片段演示了如何设置 Media Player 来播放某艺术家所有曲目
IWMPPlaylist playlist =
WMP.Player.mediaCollection.getByAuthor("artist_name");
WMP.Player.currentPlaylist = playlist;
WMP.Player.Ctlcontrols.play();
Windows Media Encoder 不需要通过 Windows Media Player 播放选定的媒体项。Windows Media Encoder 可以流式传输文件以及音频设备的输出。但是,我选择使用 Media Player,因为更容易让 Media Player 播放整个专辑,而不必担心需要哪些音频文件、它们的位置、播放顺序、何时加载下一首曲目、当前播放列表是什么或当前曲目是什么。通过让 Media Player 处理所有这些,播放专辑变得很简单。这种方法的缺点是您会通过桌面扬声器和 Pocket PC 同时听到声音。
主窗体
应用程序运行时,主窗体是隐藏的。提供了一个系统托盘图标,其中包含一个“退出”菜单项。窗体的主要职责是设置 .NET Remoting 配置。Remoting 用于提供一个可在网络上调用的接口。上面提到的三个类都继承自 MarshalByRefObject
,因此可以跨进程边界调用。主窗体将 .NET Remoting 配置为使用 HTTP 通道的 SOAP 格式化程序将这些类公开为 SingleCall
类。选择 HTTP 作为通道和 SOAP 作为格式化程序至关重要,因为它允许 Pocket PC 客户端将远程对象视为 Web 服务。.NET Compact Framework 不支持 Remoting。但是,它支持 Web 服务,因此这是向 .NET Compact Framework 客户端公开远程对象的简便方法。
最后,主窗体选择端口 9000 作为 HTTP 端口。
客户端
客户端应用程序负责:
- 以可选树状结构显示媒体库。
- 提供开始播放、停止播放、暂停以及导航到上一首/下一首曲目的控件。
- 显示当前播放列表和当前专辑封面。
- 启动 Pocket Media Player,然后连接到流。
在最初构思此应用程序时,我非常想将 Windows Media Player 托管在我的应用程序中并直接控制播放器。不幸的是,我找不到适当的方法。Pocket Media Player 不提供可以在 Pocket PC 应用程序中嵌入的 ActiveX 风格控件,而且无论如何,.NET Compact Framework 不支持 COM 互操作。我曾花时间研究 Pocket Internet Explorer 的 Windows Media Player 控件。我的想法是将该控件嵌入到我将在应用程序中托管的 HTML 页面中。但是,我遇到了类似的问题,找不到合适的 HTML 查看器控件。我看了一下 OpenNetCF.org 的 HTMLViewer
控件,它显示出希望,但我随后发现 PIE 的 Media Player 控件在 Windows Mobile 2003 上无法正常工作,并且实际上已被 Microsoft 网站删除。我所剩下的就是通过调用 CreateProcess
并传入要流式传输的 URL 作为参数来启动 Pocket Media Player 的一种粗糙方法。
这种方法有效,但有一些令人讨厌的副作用。首先,当我启动 Windows Media Player 时,它会显示在最前面,覆盖我的应用程序。我可以将我的应用程序重新调到最前面,但这种效果很烦人。其次,也是更重要的,是 Windows Media Player 缓冲传入流的方式。这种缓冲会导致在暂停、停止和导航到上一首或下一首曲目时出现延迟。例如,如果我在应用程序中按下“暂停”,台式 Media Player 会立即暂停,但是,由于 Pocket Media Player 会播放完它的缓冲区,因此音乐实际上需要几秒钟才能真正暂停。我找不到解决这个问题的办法,除非每次有人暂停等操作时都杀死 Pocket Player 进程。这样做是可行的,但很粗暴。
客户端应用程序由三个窗体组成:主窗体、库窗体和设置窗体。
主窗体
主窗体提供了媒体播放器中预期的标准控件,如播放、暂停等。这些按钮只需调用服务器提供的相应远程方法。除了控制音频播放外,主窗体还显示当前播放列表以及当前专辑的专辑封面(如果可用)。
客户端应用程序基本上是单线程的。这并不理想,但我没有时间使应用程序完全多线程。在检索当前播放列表和专辑封面时会使用一个线程。检索专辑封面可能需要几秒钟,因此将其运行在其自己的线程上下文中至关重要。线程通过使用标准的 ThreadStart
委托启动。由于 .NET Compact Framework 不支持将参数传递给 Contol.Invoke
方法,因此使用了框架快速入门中描述的 ControlInvoker
类。有关更多详细信息,请参阅此快速入门教程。
库
库窗体提供了一个选项卡式对话框,其中有三个选项卡:音频、播放列表和广播。每个选项卡都包含一个树控件,用于显示从库中检索到的媒体项。提供了一个工具栏按钮,用于将媒体库从服务器下载为 XML 字符串。然后解析 XML 并填充树。完成此操作后,XML 文档将被保存到本地文件。每次加载库窗体时,它都会检查本地文件是否存在,如果存在,则从该文件而不是从服务器填充树。
填充树的代码相当简单。XML 文档一次检查一个节点。服务器为每个节点命名,可以是 artist、album、track、playlist 或 radiostation。artist、album 和 track 节点添加到音频树中,而 playlist 和 radiostation 节点添加到 playlist 和 radiostation 树中。
private void AddNode(XmlTextReader reader)
{
switch (reader.Name)
{
case "artist":
artistNode = audioTreeView.Nodes.Add(reader.GetAttribute("name"));
if (artistNode.Text.Length == 0)
artistNode.Text = "Unknown";
artistNode.Tag = "artist";
break;
case "album":
albumNode = artistNode.Nodes.Add(reader.GetAttribute("title"));
if (albumNode.Text.Length == 0)
albumNode.Text = "Unknown";
albumNode.Tag = "album";
break;
case "track":
TreeNode track = albumNode.Nodes.Add(reader.GetAttribute("title"));
if (track.Text.Length == 0)
track.Text = "Unknown";
track.Tag = reader.GetAttribute("ID");
break;
case "playlist":
playlistNode = playlistsTreeView.Nodes.Add(reader.GetAttribute("name"));
if (playlistNode.Text.Length == 0)
playlistNode.Text = "Unknown";
playlistNode.Tag = "playlist";
break;
case "radiostation":
radioStationNode = radioTreeView.Nodes.Add(reader.GetAttribute("name"));
if (radioStationNode.Text.Length == 0)
radioStationNode.Text = "Unknown";
radioStationNode.Tag = "radiostation";
break;
}
}
设置
设置窗体允许用户输入服务器的 IP 地址。IP 地址使用 CodeProject 上 Page Brooks 的文章中的示例保存到本地配置文件中,该文章描述了标准 .NET 应用程序设置框架的实现,而该框架不属于 .NET Compact Framework。有关更多信息,请参阅“Compact Framework 的AppSettings 实现”。
改进
我相信还有很多可以改进的地方,但这里列出了一些主要的改进。
- “排队”选项。这将允许您选择一个媒体项并将其添加到当前播放列表的末尾,而不是用新选择替换当前播放列表。
- 更具响应性的 UI,更好地利用线程。
- 流式传输视频。这应该相当简单,应用程序的主要变化将是从文件而不是设备进行流式传输。Pocket Streamer 目前通过流式传输声卡输出来工作。要流式传输视频,您可以流式传输显示器的输出,但是,您将获得整个桌面流式传输到 Pocket PC,而不是仅仅视频。