思路:要想做一个网络电话,基本遵循以下步骤
1、一方实时的录音,把模拟信号转换成数字信号;
2、把声音实时压缩;
3、通过网络协议把压缩后的数据传输给接收方;
4、接收方解压缩接受到的音频数据;
5、实时的把接收到的数字信号转换成模拟信号并播放出来。
下面我们来看看每一步面临的挑战及其解决方案。
1、第一步,实时录音,DirectxSound有录音方面的API,托管的类分别是Microsoft.DirectX.DirectSound.CaptureDevicesCollection,Microsoft.DirectX.DirectSound.Capture和Microsoft.DirectX.DirectSound.CaptureBuffer,CaptureDevicesCollection用来枚举本机的可用的录音设备,Capture则表示一个录音设备,CaptureBuffer是用来存放录音数据的缓冲区,我们开始录音后,音频数据会不断的写入到环形的流式缓冲区,然后我们定期从缓冲区中把录音数据取出来返回给上层应用层就可以了。关于环形的流式缓冲区,可以看参考链接部分。
2、声音的压缩是一个很难抉择的步骤,默认的DirectSound只能播放和录制PCM格式(WAV)的音频数据,但这种声音格式特别大。常用的声音压缩格式有h.7231,gsm,amr,h.711等等,各种压缩算法都有自己的码率和适用范围。因为我们做的是互联网的语音电话,不考虑慢速网络和无线连接下的情况,也不用考虑终端设备的CPU能不能支持我们选用的压缩算法,我们做的语音电话双方都是PC机,应该什么解压缩算法都不会引起什么性能上的问题,所以只要网络快一些,选择哪个压缩算法都无所谓了,网上有h.711的压缩算法,我打算就采用这个,他的码率是64Kbps,比PCM的1.544Mbps和2.048Mbps要小的多。然后我们进行了音频数据压缩后,还可以对字节流进行GZIP或者7ZIP压缩,前者用SharpZip,后者7zip的官方有c#的使用代码,大家可以测试一下这两个算法的性能后做出适合自己的决定。关于各种压缩格式的特性可以参考我做的PPT及提供的参考链接。
3、网络电话注重实时性,而把声音从网络上传输就要走IP网络,而IP网络不是一个等时系统,所以我们就要尽量的去模拟实时的语音传输,提到实时,肯定UDP比TCP要实时,因为TCP要保证传输的可靠性,有序性等,而专门用于实时传输有一个应用层协议是RTP协议,这个协议一般就是建立在UDP基础上的,它在每个包头提供了一些序列号、时间戳等信息,但UDP本身并不会使用这些信息,这时候就有一个RTCP协议来用这些信息进行流量控制和拥塞控制,比如说RTCP检测到网络拥挤,会告诉发送方变换一种低码率的语音压缩算法来传输数据。这些大多都需要自己去实现,本文的源码没有去实现这些,关于RTP和RTCP可以参考相关资料或者我做的PPT。
4、每个压缩算法都有相应的解压缩算法,呵呵。
5、播放声音肯定也需要用到DS,也需要用到StreamBuffer,大致流程如下
1)创建一个声音设备Microsoft.DirectX.DirectSound.Device dev = new Microsoft.DirectX.DirectSound.Device();
2)设置协调级别dev.SetCooperativeLevel(this, Microsoft.DirectX.DirectSound.CooperativeLevel.Normal);
3)创建声音格式、缓冲区描述、及辅助缓冲区;
4)给辅助缓冲区设定通知;
5)用声音数据填满缓冲区;
6)播放缓冲区的声音数据,播放到一定的通知点,通知填充线程,填充新的声音数据;
7)循环第6步,直到没有新的声音数据填充到缓冲区。
具体的过程参考PPT或者具体代码。
版权声明:
附件源代码里的CaptureSound,SoundPlayer和CircularBuffer类反编译自随意桌面的代码(注释是我加的),版权归作者所有。
PPT里的图片和一些文字选自一个叫做ch11-DxSound&Input2.ppt的文件,源链接已丢失,还有一些选择一个叫做“SIP之 穿越NAT.ppt”的文件,网上可以搜索到,版权均归原作者所有,源作者要是再引用别人的东西,我就不知道了。
下面看一些具体的代码
用户创建声音格式
{
public static WaveFormat CreateWaveFormat(int hz, short bits, short channels)
{
WaveFormat format = new WaveFormat();
//声音的格式,通常使用WAVE_FORMAT_PCM来设定,
//因为PCM是比较常用的声音格式。
format.FormatTag = WaveFormatTag.Pcm;
//采样率(单位:赫兹)典型值:11025、22050、44100Hz
format.SamplesPerSecond = hz;
//每个采样点数;8-bit或16-bit;
format.BitsPerSample = bits;
//声道的设置,当其值为1时是单声道,为2时是双声道;
format.Channels = channels;
//每个采样点字节数
format.BlockAlign = (short)(format.Channels * (format.BitsPerSample / 8));
//平均传输率,每秒的数据流量
format.AverageBytesPerSecond = format.BlockAlign * format.SamplesPerSecond;
return format;
}
#region 属性
// Properties
public static WaveFormat DefaultFormat
{
get
{
return WaveFormat_8000_8_1;
}
}
public static WaveFormat WaveFormat_11025_8_1
{
get
{
return CreateWaveFormat(0x2b11, 8, 1);
}
}
public static WaveFormat WaveFormat_22050_16_2
{
get
{
return CreateWaveFormat(0x5622, 0x10, 2);
}
}
public static WaveFormat WaveFormat_44100_16_2
{
get
{
return CreateWaveFormat(0xac44, 0x10, 2);
}
}
public static WaveFormat WaveFormat_8000_8_1
{
get
{
return CreateWaveFormat(0x1f40, 8, 1);
}
}
#endregion
}
用于播放流式声音
{
私有成员
构造函数
公开属性
IDisposable Members
私有方法
公开方法
}
用户录制声音
{
私有成员
构造函数
公开属性
公开事件
私有方法
公开方法
}
程序下载地址如下(自己反射看源码,因为程序只是用于演示,所以考虑很不周全,不足以效仿)
https://files.cnblogs.com/onlytiancai/wawaim.zip
PPT下载地址如下
https://files.cnblogs.com/onlytiancai/p2p语音.zip
参考链接如下:
ch11-DxSound&Input2.ppt:建立DirectSound 声音的播放与控制 使用3D音效
SIP之 穿越NAT.ppt
DirectX技术实现视频会议中的音频通信
http://www.ctiforum.com/forum/2008/03/forum08_0357.htm
C#中使用DirectSound录音
http://blog.donews.com/uplook/archive/2005/12/14/657145.aspx
在C#下利用DirectSound实现声音播放
http://www.cnblogs.com/yangbeibei/archive/2006/08/30/490270.html
隨意桌面,數位溝通
http://cuteofdragon.blogspot.com/2007/05/blog-post_9694.html
用DirectX Audio和DirectShow播放声音和音乐
http://www.cppblog.com/lovedday/archive/2007/09/24/32815.html