蛙蛙推荐:c#编写网络电话

蛙蛙推荐:c#编写网络电话
摘要:语音通话已经是IM的基本功能了,qq,MSN甚至连刚出来的百度HI都自带语音聊天的功能,大家可能觉得很炫,其实大家都是用的windows平台上的API,懂了原理之后自己也可以做,再说了微软也提供了DirectSound的托管互操作程序集,使.net开发人员也很容易的介入到这个领域,甚至你还可以写一个能跑在window mobile上的语音电话,现在好多手机都支持wifi,这样一个简单的wifi电话就由你的手里诞生了。本帖来和大家一起看看如何来做网络电话。

思路:要想做一个网络电话,基本遵循以下步骤
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 class DirectSoundManager
{
    
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;
    }


    
属性
}


用于播放流式声音
   

 

public class SoundPlayer : IDisposable
{
    
私有成员

    
构造函数

    
公开属性

    
IDisposable Members

    
私有方法

    
公开方法
}


 

用户录制声音

 

public class CaptureSound
{

    
私有成员

    
构造函数

    
公开属性

    
公开事件

    
私有方法

    
公开方法
}


 

程序下载地址如下(自己反射看源码,因为程序只是用于演示,所以考虑很不周全,不足以效仿)
http://files.cnblogs.com/onlytiancai/wawaim.zip
PPT下载地址如下
http://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


 

posted @ 2008-08-02 20:14 蛙蛙王子 Views(7419) Comments(58) Edit 收藏

 回复 引用 查看   
#1楼[楼主]2008-08-02 20:21 | 蛙蛙池塘      
又找到一些新的资料,
说skype等用的是iBlc音频压缩协议,是一种商业协议,特点是实时性容错性好。还有一个标准是,g.729,也是商业协议,链接如下
http://baike.baidu.com/view/1389750.htm
下面是一点简单摘要
G.729协议使用的算法是共轭结构的算术码本激励线性预测(CS-ACELP),它基于CELP编码模型。由于G.729编解码器具有很高的语音质量和很低的延时,被广泛地应用在数据通信的各个领域,如VoIP和H.323网上多媒体通信系统等。

 回复 引用 查看   
#2楼2008-08-02 20:24 | 侯垒      
支持.
 回复 引用 查看   
#3楼2008-08-02 20:35 | Lordz      
在博客园可以看到很多好文章,真好
 回复 引用 查看   
#4楼2008-08-02 20:51 | 肥仔鱼      
不错,非常好
但是好像没有代码

 回复 引用 查看   
#5楼2008-08-02 21:11 | 浪子      
羡慕,蛙蛙好像有很多时间哦,做了很多东西:)
 回复 引用 查看   
#6楼2008-08-02 21:14 | jingo      
不错,支持
 回复 引用 查看   
#7楼2008-08-02 21:19 | Angel Lucifer      
蛙蛙兄有没有考虑下 Global IP Sound 等商业解决方案?
 回复 引用 查看   
#8楼2008-08-02 21:19 | 怪怪      
LZ越来越猛啦~
 回复 引用 查看   
#9楼[楼主]2008-08-02 21:47 | 蛙蛙池塘      
@侯垒
@Lordz
@jingo
多谢捧场
@肥仔鱼
源码自己反编译吧,要是不会单独问我要,.NET根本就不用给源码我觉得。
@浪子
花费一个周末,两个晚上做的,关键看兴趣了,别人什么都懂,我只懂代码。
不懂体育,不懂历史,不玩游戏,不爱运动,不抽烟,不爱喝酒,不搞对象,不爱逛街,不看电视,不看报纸,不懂时事,不懂政治,不泡论坛,不看新闻,不会干农活儿,不认路,不记事,不懂英语,不会数学,只会敲代码,这就是俺,简简单单,简单就是美,哈哈。

@Angel Lucifer
是说http://zh.wikipedia.org/wiki/Global_IP_Sound吗
我可做不了人家那么专业,我刚才和山东的朋友在网上试了下我的程序(我在北京),声音有一些杂音,并且有3秒左右的延迟,我现在的程序是原始WAV格式,并且是通过服务器中转的,要想做专业,还需要好多改进的地方。况且运营商肯定会打压这种产品的,呵呵。不过大家都应该换个思路,你运营商不去做,迟早有人去做,不如早做技术上的准备。
@怪怪
不敢当,有愧有愧。

 回复 引用 查看   
#10楼2008-08-02 21:55 | XeonWell      
蛙贴留名
 回复 引用 查看   
#11楼2008-08-02 22:07 | lexus      
你的涉猎也太过广泛了吧,^_^
 回复 引用 查看   
#12楼[楼主]2008-08-02 22:33 | 蛙蛙池塘      
@lexus
确实有点儿,应该适可而止。

 回复 引用   
#13楼2008-08-02 23:13 | liujun888[未注册用户]
楼主能把这个程序的源码发给我一份吗?谢谢!反编译后客户端到是没有问题了,但是服务器端编译通过后却不能正常运行。下载你的程序是可以的。谢谢!pangguigao#163.com
 回复 引用   
#14楼2008-08-02 23:19 | liujun888[未注册用户]
“WawaIMServer.vshost.exe”(托管): 已加载“C:\Documents and Settings\pgg\桌面\wawaim\WawaImServer\WawaIMServer_Source\bin\Debug\WawaIMServer.exe”,符号已加载。
“WawaIMServer.vshost.exe”(托管): 已加载“C:\Documents and Settings\pgg\桌面\wawaim\WawaImServer\WawaIMServer_Source\bin\Debug\SoundChat.dll”,符号已加载。
开始监听:0.0.0.0:54321
start ok
关闭监听:0.0.0.0:54321
“WawaIMServer.vshost.exe”(托管): 已加载“C:\WINDOWS\assembly\GAC_MSIL\System.Xml\2.0.0.0__b77a5c561934e089\System.Xml.dll”,已跳过符号加载。已对模块进行了优化并启用了调试器选项“仅我的代码”。
“WawaIMServer.vshost.exe”(托管): 已加载“C:\WINDOWS\assembly\GAC_MSIL\mscorlib.resources\2.0.0.0_zh-CHS_b77a5c561934e089\mscorlib.resources.dll”
在 System.Net.Sockets.SocketException 中第一次偶然出现的“System.dll”类型的异常
线程 0x11a8 已退出,返回值为 0 (0x0)。
线程 0x1408 已退出,返回值为 0 (0x0)。
线程 0x7b8 已退出,返回值为 0 (0x0)。
程序“[1404] WawaIMServer.vshost.exe: 托管”已退出,返回值为 0 (0x0)。
ServerThreadHandler:System.Net.Sockets.SocketException: 一个封锁操作被对 WSACancelBlockingCall 的调用中断。
在 System.Net.Sockets.Socket.Accept()
在 System.Net.Sockets.TcpListener.AcceptSocket()
在 WawaSoft.Net.WaTcpServer.ServerThreadHandler() 位置 E:\huhao\workfloder\src\SoundTest\SoundChat\Net\WaTcpServer.cs:行号 126

 回复 引用 查看   
#15楼2008-08-02 23:28 | 游利卡      
移植到手机平台呢?

蛙蛙想过期间摇出现的问题吗?

 回复 引用 查看   
#16楼[楼主]2008-08-02 23:44 | 蛙蛙池塘      
@liujun888
这个错应该没问题吧,如果有错,说明我服务端写的有问题,服务端就那么两行代码,一个类呀,而且我看到服务端已经启动成功了,你停止服务端的时候出了个错而已,应该是停止的时候又有新的连接进来了好像。你google一下“一个封锁操作被对 WSACancelBlockingCall 的调用中断。 ”吧。
@游利卡
啥问题,代码有可能不能直接移植到手机上,原理应该差不多,到时候再找手机方面的开发资料呗。要出现问题应该就是运营商封杀吧

 回复 引用   
#17楼2008-08-02 23:50 | liujun888[未注册用户]
反编译成功,程序正常运行
 回复 引用 查看   
#18楼[楼主]2008-08-02 23:51 | 蛙蛙池塘      
@liujun888
答案找到了,如下
========
监听线程的处理

监听线程会在下列语句处阻塞,直到有连接请求进入。
'接收连接请求
m_sckAccept = m_sckListen.AcceptTcpClient

在阻塞的状况下,简单的使用Abort,仅仅是将线程设置为AbortRequest状态,而没有真正的解除线程的阻塞,甚至使用 Application.Exit(),也无法真正的终止线程并释放线程所占有的资源。这样在下次在同一个端口调用监听时就会有错误发生。并且在应用程序退出后,监听线程依然像孤魂野鬼一般在内存中阻塞着。

如果在此时直接调用m_sckListen.Stop来终止监听,则会发生以下描述信息的错误:

一个封锁操作被对 WSACancelBlockingCall 的调用中断。

但是这样似乎并不会影响以后对此端口监听的调用,并且能够结束线程。

彻底解决问题的一种方法是用一个终止标志作为监听循环的条件,需要终止监听时,先设置终止标志为退出监听循环,然后向自己的监听器发送一个连接请求解除监听线程的阻塞,然后就可以安全的退出监听循环,关闭监听,并结束监听线程。这样在程序结束以后也不会有线程滞留的现象。
http://coffecoco.bokee.com/806282.html
=============
我的服务端是粗暴的把监听线程abort了,解决方案如上,不过有些繁琐,呵呵,还得自己发起个连接,让阻塞住的accept函数往下走,执行下一个循环,从而退出,还不如直接abort呢,然后捕获socketexception然后分拣socketerror.code,如果是这个错就忽略。

 回复 引用   
#19楼2008-08-03 00:21 | liujun888[未注册用户]
问以下Microsoft.DirectX.DirectSound.dll和Microsoft.DirectX.dll是微软提供的吧?
 回复 引用   
#20楼2008-08-03 00:31 | 匿名[未注册用户]
引用lz的:
----------------------------------------------------------------------------
不懂体育,不懂历史,不玩游戏,不爱运动,不抽烟,不爱喝酒,不搞对象,不爱逛街,不看电视,不看报纸,不懂时事,不懂政治,不泡论坛,不看新闻,不会干农活儿,不认路,不记事,不懂英语,不会数学,只会敲代码,这就是俺,简简单单,简单就是美,哈哈。
----------------------------------------------------------------------------

青春献给了代码, 太专注了!

 回复 引用 查看   
#21楼2008-08-03 00:48 | clefoo      
哎,只能做来自己玩玩,真要去投入应用,还是很困难啊
 回复 引用 查看   
#22楼2008-08-03 02:12 | Microshaoft      
你写的东西非常
深入浅出、代码简洁、易于收藏
我喜欢


不懂体育,不玩游戏,不爱运动,不爱逛街,不看报纸,不懂时事,不懂政治,不看新闻,不会干农活儿,不认路,不记事
这几条跟我差不多

但是不鼓励

 回复 引用 查看   
#23楼[楼主]2008-08-03 08:10 | 蛙蛙池塘      
@liujun888
是的,装了Directx SDK就有了,或者直接引用这两个程序集也可以,DirectX的API是COM的,只要用.NET包装成托管程序集就可以了。

@clefoo
没错,要想做到QQ那么NB,还有很长的距离,QQ的语音实时性好,没有杂音,而且录音这边还有三种模式可选,我觉得这就需要靠大家努力了,博客园这么多开发者,齐心协力,什么做不出来呀,关键看如何去组织人力了。

@匿名
@Microshaoft
呵呵,我也不鼓励,和个人喜好有关吧,常识还是应该知道一点点的。

 回复 引用 查看   
#24楼2008-08-03 10:10 | 笑疯^_^      
支持一下楼主
 回复 引用 查看   
#25楼2008-08-03 12:18 | Desmend      
关注一下
 回复 引用 查看   
#26楼2008-08-03 16:24 | 迷梦江南      
支持蛙蛙!
 回复 引用   
#27楼2008-08-03 18:08 | Alexander-lee[未注册用户]
好像离网络电话还又很远的距离的样子
 回复 引用 查看   
#28楼[楼主]2008-08-03 20:40 | 蛙蛙池塘      
@Alexander-lee
恩,我在研究研究。

 回复 引用 查看   
#29楼2008-08-31 10:51 | Jimmy Zhang      
挺有意思的,有空了试着玩一下 ^O^
 回复 引用 查看   
#30楼2008-09-01 09:00 | yellowyu      
我们也有在做,能打VOIP,甚至混音做网络会议都实现了,

但语音质量一直很难上得去。。。。

这跟运营商也有很大关系。。。。

 回复 引用 查看   
#31楼2008-09-01 09:05 | yellowyu      
而且也没那个钱能买到SKYPE那个语音编码,

上次调研过,实在没这份财力能购买呀

SKYPE是做得最好的

 回复 引用 查看   
#32楼[楼主]2008-09-01 09:29 | 蛙蛙池塘      
@yellowyu
skype用的也是商业的解码器,属于高带宽编码,语音质量确实很好。混音网络会议也不难做,关键还是动态编码而且还要保证低带宽和高语音质量就不好做了,得好好研究语音学和UDP的实时传输。

 回复 引用 查看   
#33楼2008-09-15 13:05 | 张旋      
再mark个。回去看
 回复 引用   
#34楼2008-10-04 16:57 | is5wellljj[未注册用户]
有个问题啊,怎么知道采集到的声音是不是回声啊???我照你这样做后测试的时候发觉有回声啊,都挺严重的,,怎么解决啊???
 回复 引用   
#35楼2008-10-06 12:50 | wawa[未注册用户]
你用两台机器测,别再同一个屋子里
 回复 引用   
#36楼2008-10-08 22:37 | klgsyy[未注册用户]
能不能给全一点的代码,我的邮箱klgsyy@126.com,谢谢了.
 回复 引用   
#37楼2008-10-08 23:18 | v730825[未注册用户]
楼主能把这个程序的源码发给我一份吗?
v730825@gmail.com
谢谢

 回复 引用 查看   
#38楼[楼主]2008-10-09 10:02 | 蛙蛙池塘      
@v730825
@klgsyy
看不懂,慢慢看,完整的程序我也不知道弄哪儿了,你下载下来反编译不就行啦,汗。

 回复 引用 查看   
#39楼2008-10-17 10:14 | stg609      
正要研究这方面的资料,谢谢分享
 回复 引用   
#40楼2008-10-21 15:12 | winston5188[未注册用户]
请问怎么用c#修改一个wav文件的sample rate,急用,请跟我联系。
msn:mawj040103@hotmail.com
skype:winston1023

 回复 引用 查看   
#41楼[楼主]2008-10-21 15:27 | 蛙蛙池塘      
@winston5188
你找个wav格式的说明,c#修改二进制就用system.io下的类.

 回复 引用   
#42楼2008-10-21 15:31 | winston5188[未注册用户]
@蛙蛙池塘 :
可是如果我把sample rate改大的话需要加入好多字节数据,这些数据怎么算出来啊,你有算法吗

 回复 引用   
#43楼2008-10-21 15:33 | winston5188[未注册用户]
@蛙蛙池塘 :
能加我msn或skype吗?这个功能有急用

 回复 引用 查看   
#44楼[楼主]2008-10-21 15:35 | 蛙蛙池塘      
@winston5188
大哥,急也没用呀,我也没弄过呀,我就是那阵子学习了学习语音方面的基础知识而已.

 回复 引用 查看   
#45楼[楼主]2008-10-21 15:35 | 蛙蛙池塘      
我MSN是onlytiancai@msn.com
 回复 引用 查看   
#46楼2008-10-25 16:48 | stg609      
楼主,有没有实现语音传输方面的功能呀?上面好像只有语音采集呀?
有这方面资料吗?

 回复 引用 查看   
#47楼[楼主]2008-10-29 09:46 | 蛙蛙池塘      
@stg609
你下载下代码,里面有TCP传输的示例,UDP实施传输没弄好。

 回复 引用 查看   
#48楼2008-10-30 20:00 | 温景良(Jason)      
谢谢,学习了
 回复 引用   
#49楼2009-04-04 09:25 | 陈默[未注册用户]
你好,我很想看看一看你的源码,我是大三学生,在做一个创新项目,就是想做一个局域网里的聊天软件,我用的是udp方式连接,基本的我都已经实现了,就是我想再添加参考一下你的程序在接受到用户连接请求的,如何弹出消息的,我想知道你具体怎么做的,不知可否给我发一份源码,或者部分的也可以,万分感谢,我只是用于学习,并非商用。。。
还有,我想在压缩音频时用7zip来做,正在努力,不知你有什么好的思路没~

 回复 引用 查看   
#50楼[楼主]2009-04-04 09:30 | 蛙蛙池塘      
大哥,我的代码不是提供了下载了呀,弹出消息那就是wnform的事了,你让它弹它就弹呗。UDP默认发包不保证先后顺序,而且不一定能发到,7ZIP压缩太耗时了吧,不是专用的音频压缩协议。
 回复 引用   
#51楼2009-05-11 23:39 | 春天的颜色[未注册用户]
顶一下,正在学习中。。。
 回复 引用   
#52楼2009-07-04 23:56 | liukai_dream[未注册用户]
蛙哥,麻烦你传份源代码~~~跪谢啊~~~~~最近做这方面的项目,奈何音质奇差~~~谢谢啦

103029827@qq.com
谢谢

 回复 引用   
#53楼2009-09-18 17:32 | gj7722064@163.com[未注册用户]
蛙哥,这个程序可以在网络上使用么??如果可以改怎么弄呢??
 回复 引用 查看   
#54楼2010-01-29 13:14 | laohou      
学习学习,不错呀。
 回复 引用 查看   
#55楼2010-05-14 16:48 | kaigege      
能不能把源代码给我发一下啊,我正在学习这东西,用了感觉很好,想好好研究一下,我的邮箱cuikailei2006@163.com
 回复 引用 查看   
#56楼2011-03-04 11:02 | 学习呀      
楼主,能否把源码发我一份,感觉你写的代码思路清晰,容易读
 回复 引用 查看   
#57楼[楼主]2011-03-07 09:58 | 蛙蛙王子      
不好意思,代码找不到了
 回复 引用 查看   
#58楼2011-09-28 10:54 | Jos      
楼主,你的网络电话可以与手机或固话通话吗,怎么实现这个功能。
(一下网络电话可以与手机或固话通话的,大致是怎么实现的啊?)