前期回顾:
Sync Music Player 项目 (1) ——功能简述与技术设想
Sync Music Player 项目 (2) ——技术测试
四、技术测试续
在上回的技术测试中,我们完成了客户端下载文件的功能。现在的问题是如何解决播放的问题。
显然,单纯的播放功能是很容易实现的,我最终使用的是winmm.dll这个动态链接库。这是windows自己提供的一个库。下面简要介绍一下它的使用吧。
我把跟播放有关的内容都写到了一个Player类中,这个类是一个静态类。我们要做的第一步是添加对这个动态链接库的引用,由于这个库不能直接通过"Add Reference”添加(不是一个有效的.Net程序集),所以我们要使用一种通用的方式。通过使用DllImport属性,我们可以把一个用C、C++等语言写的DLL加载入项目中。用法如下:(需要导入System.Runtime.InteropServices命名空间);
[DllImport("winmm.dll")]
private static extern long mciSendString(string strCommand, StringBuilder strReturn, int iReturnLength, int bla);
使用这种方式导入dll要把需要使用的方法的函数原型写出来。mciSendStirng这个方法很强大,能完成打开、播放、暂停、停止、调整音量等各种操作。各个操作直接看代码就知道了,很简单的。
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
Code
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
namespace integrated
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
{
public class Player
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
[DllImport("winmm.dll")]
private static extern long mciSendString(string strCommand, StringBuilder strReturn, int iReturnLength, int bla);
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
static string Pcommand = String.Empty;
static bool isOpen = new bool();
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
public static void Open(string filename)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
Pcommand = "open \"" + filename + "\" type mpegvideo alias MediaFile";
mciSendString(Pcommand, null, 0, 0);
isOpen = true;
}
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
public static void Pause()
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
Pcommand = "pause MediaFile";
mciSendString(Pcommand, null, 0, 0);
}
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
public static void Play(bool loop)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
if (isOpen)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
Pcommand = "play MediaFile";
if (loop)
Pcommand += " REPEAT";
mciSendString(Pcommand, null, 0, 0);
}
}
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
public static void Stop()
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
Pcommand = "close MediaFile";
mciSendString(Pcommand, null, 0, 0);
isOpen = false;
}
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//// <summary>
/// Returns the current status player: playing,paused,stopped etc.
/// </summary>
public string Status()
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
int i = 128;
System.Text.StringBuilder stringBuilder = new System.Text.StringBuilder(i);
mciSendString("status MediaFile mode", stringBuilder, i, 0);
return stringBuilder.ToString();
}
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//// <summary>
/// Get/Set Lelf Volume Factor
/// </summary>
public int LeftVolume
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
get
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
return 0; //Guess could be used to return Volume level: I don't need it
}
set
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
mciSendString(string.Concat("setaudio MediaFile left volume to ", value), null, 0, 0);
}
}
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//// <summary>
/// Get/Set Right Volume Factor
/// </summary>
public int RightVolume
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
get
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
return 0; //Guess could be used to return Volume level: I don't need it
}
set
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
mciSendString(string.Concat("setaudio MediaFile right volume to ", value), null, 0, 0);
}
}
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//// <summary>
/// Get/Set Main Volume Factor
/// </summary>
public int MasterVolume
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
get
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
return 0; //Guess could be used to return Volume level: I don't need it
}
set
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
mciSendString(string.Concat("setaudio MediaFile volume to ", value), null, 0, 0);
}
}
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//// <summary>
/// Get/Set Bass Volume Factor
/// </summary>
public int Bass
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
get
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
return 0;
}
set
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
mciSendString(string.Concat("setaudio MediaFile bass to ", value), null, 0, 0);
}
}
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//// <summary>
/// Get/Set Treble Volume Factor
/// </summary>
public int Treble
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
get
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
return 0;
}
set
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
mciSendString(string.Concat("setaudio MediaFile treble to ", value), null, 0, 0);
}
}
}
}
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
播放的问题解决了,只要使用刚才那个player类就行了。现在是同步播放的问题。原来的想法是客户端发送指令询问服务端播放时间,后来考虑到各个客户端的系统时间不同,所以这个方式不好。想到应该使用UDP协议进行广播,这样就可以通过服务端发送一个播放指令,所有客户端都可以接收到这个指令。收到指令后各台客户端开始播放。恩,这个思路我觉得可以,试试效果吧!服务端使用UDP协议发送播放指令的代码如下:
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
Code
private void Broadcast()
{
Socket sock = new Socket(AddressFamily.InterNetwork, SocketType.Dgram,ProtocolType.Udp);
IPEndPoint iep1 = new IPEndPoint(IPAddress.Broadcast, 9050);//255.255.255.255
string command = "play";
byte[] data = Encoding.ASCII.GetBytes(command);
sock.SetSocketOption(SocketOptionLevel.Socket,
SocketOptionName.Broadcast, 1);
sock.SendTo(data, iep1);
sock.Close();
}
客户端接收指令并播放的代码为:
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
Code
private void UDP()
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
{
Socket sock = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
IPEndPoint iep = new IPEndPoint(IPAddress.Any, 9050);
sock.Bind(iep);
EndPoint ep = (EndPoint)iep;
byte[] data = new byte[1024];
int recv = sock.ReceiveFrom(data, ref ep);
string stringData = Encoding.ASCII.GetString(data, 0, recv);
sock.Close();
if (stringData == "play")
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
Player.Open("1.mp3");
Player.Play(false);
}
}
经过测试,发现播放的时候没法同步,会有一段时间的延迟。UDP的发送和接收所花费的时间是很少的,所以,时间差别主要在于Player.Open()方法,由于各台电脑的性能不同,载入的时间是不同的,所以最好在正式播放之前给一个载入(load)的指令,客户端收到该指令后载入音频,播放,接着迅速暂停,然后服务端等待一定时间后发送play指令,客户端接着播放,实测结果是效果相当好,4台机子测试发现基本听不出差别。具体代码就不给了,因为最终还是没有使用这种方式,不过采用了这个思想。
技术测试到此结束。