`
touchinsert
  • 浏览: 1287347 次
  • 性别: Icon_minigender_1
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

[转]c#编写网络电话

 
阅读更多

本文转自:http://www.cnblogs.com/dfsxh/archive/2008/12/30/1355886.html

原文如下:

摘要:语音通话已经是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”的文件,网上可以搜索到,版权均归原作者所有,源作者要是再引用别人的东西,我就不知道了。

下面看一些具体的代码

用户创建声音格式

<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->publicclassDirectSoundManager
{
publicstaticWaveFormatCreateWaveFormat(inthz,shortbits,shortchannels)
{
WaveFormatformat
=newWaveFormat();
//声音的格式,通常使用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;
returnformat;
}


属性#region属性
//Properties
publicstaticWaveFormatDefaultFormat
{
get
{
returnWaveFormat_8000_8_1;
}

}


publicstaticWaveFormatWaveFormat_11025_8_1
{
get
{
returnCreateWaveFormat(0x2b11,8,1);
}

}


publicstaticWaveFormatWaveFormat_22050_16_2
{
get
{
returnCreateWaveFormat(0x5622,0x10,2);
}

}


publicstaticWaveFormatWaveFormat_44100_16_2
{
get
{
returnCreateWaveFormat(0xac44,0x10,2);
}

}


publicstaticWaveFormatWaveFormat_8000_8_1
{
get
{
returnCreateWaveFormat(0x1f40,8,1);
}

}

#endregion

}


用于播放流式声音

<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->publicclassSoundPlayer:IDisposable
{
私有成员#region私有成员
privateconstintMaxLatencyMs=300;
privateconstintNumberRecordNotifications=4;
privatereadonlyCircularBuffercircularBuffer;
privatereadonlyintm_BufferBytes;
privatereadonlyboolm_OwnsDevice;
privatereadonlyintnotifySize;
privatereadonlyBufferPositionNotify[]positionNotify;
privateboolisRunning;
privateSecondaryBufferm_Buffer;
privateDevicem_Device;
privateintnextWriteOffset;
privateAutoResetEventnotificationEvent;
privateNotifynotify;
privateThreadnotifyThread;
#endregion


构造函数#region构造函数
publicSoundPlayer(Controlowner,WaveFormatformat)
:
this(owner,null,format)
{
}


publicSoundPlayer(Controlowner,Devicedevice,WaveFormatformat)
{
positionNotify
=newBufferPositionNotify[5];
notificationEvent
=null;
notify
=null;
notifyThread
=null;
notifySize
=0;
m_Device
=device;
if(m_Device==null)
{
m_Device
=newDevice();
m_Device.SetCooperativeLevel(owner,CooperativeLevel.Normal);
m_OwnsDevice
=true;
}

//设定通知的大小,大小为播放一秒钟声音所需要的字节。这里为什么除以8,我不清楚
notifySize=(1024>(format.AverageBytesPerSecond/8))?(1024):((format.AverageBytesPerSecond/8));
notifySize
=(notifySize-(notifySize%format.BlockAlign));
m_BufferBytes
=(notifySize*4);//整体缓冲区的大小
BufferDescriptiondesc=newBufferDescription(format);
//缓冲区具有控制音量的能力;
desc.ControlVolume=true;
//缓冲区具有控制位置的能力。
desc.ControlPositionNotify=true;
//设置缓冲区能取到当前的播放位置
desc.CanGetCurrentPosition=true;
//缓冲区不具有控制3D音效的能力;
desc.Control3D=false;
//Specifieswhetherthebuffersupportseffectsprocessing.
desc.ControlEffects=false;
//缓冲区具有控制频率的能力;
desc.ControlFrequency=true;
//缓冲区具有控制左右声道的能力;
desc.ControlPan=true;
//设置是否使用全局缓存
desc.GlobalFocus=true;
//设置缓冲区大小为整个缓冲区的大小
desc.BufferBytes=m_BufferBytes;

//创建辅助缓冲区
m_Buffer=newSecondaryBuffer(desc,m_Device);
//创建环形缓冲区
circularBuffer=newCircularBuffer((m_BufferBytes*10));
InitNotifications();
m_Buffer.Play(
0,BufferPlayFlags.Looping);
}


publicSoundPlayer(Controlowner,intsr,shortbps,shortch)
:
this(owner,null,DirectSoundManager.CreateWaveFormat(sr,bps,ch))
{
}


publicSoundPlayer(Controlowner,Devicedevice,intsr,shortbps,shortch)
:
this(owner,device,DirectSoundManager.CreateWaveFormat(sr,bps,ch))
{
}

#endregion


公开属性#region公开属性
publicintBitsPerSample
{
get{returnm_Buffer.Format.BitsPerSample;}
}


publicintChannels
{
get{returnm_Buffer.Format.Channels;}
}


publicDeviceDevice
{
get{returnm_Device;}
}


publicintSamplingRate
{
get{returnm_Buffer.Format.SamplesPerSecond;}
}

#endregion


IDisposableMembers#regionIDisposableMembers

publicvoidDispose()
{
Stop();
if(m_Buffer!=null)
{
m_Buffer.Dispose();
m_Buffer
=null;
}

if(m_OwnsDevice&&<span
分享到:
评论

相关推荐

    用C#编写网络电话(含方法和源代码)

    用C#语言编写网络电话,pdf格式文档,上面有详细的方法和源代码

    网络电话的编写c#实现

    网路电话用c#实现,实现细节详细,详细描述了实现每个市县细节。

    C#编写的企业电话客服系统

    C#编写的企业电话客服系统,系统运用现代化的技术,为中小型企业提供现代化的管理手段,提高企业产品信息的收集、处理能力,联动及反映能力,为各级领导和管理人员提供准确、及时的分析数据,提高管理的科学性和工作...

    C++编写基于socket的网络电话

    网络电话,可参考,socket编程,简单实用

    C# 编写的短信猫发送短信dll源码和一个简单的winform测试

    c#编写的短信猫(WAVECOM)发送短信程序dll,没有任何功能限制,在你的程序中引用SIMSMS.dll就可以了。帮助文档就不写了,在程序中有注释,在测试的winform中也有简单应用的示例。看看就明白了! 如果感觉功能不够,...

    C#微软培训资料

    第六章 类 型 转 换 .48 6.1 隐式类型转换 .48 6.2 显式类型转换 .53 6.3 小 结 .56 第七章 表 达 式 .58 7.1 操 作 符 .58 7.2 算术操作符和算术表达式.59 7.3 赋值操作符和赋值表达式.64 7.4 关系...

    c#学习笔记.txt

    51099在线学习网发布 文章来源:网络收集 发布时间:2006-05-25 字体: [大 中 小] 51099在线学习网 http://www.51099.com 1, 结构(struct) 与 类(class) [attributes] [modifiers] struct identifier [:...

    C#开发经验技巧宝典

    0995 如何编写带图片的报表 582 0996 如何使图片成为整个报表的背景 583 0997 如何设置水晶报表中节的背景图片 583 0998 如何设置水晶报表中节的背景色 584 0999 如何设置水晶报表的页面 584 1000 如何在...

    C#实现语音卡电话呼叫系统

    C#源码下载,visual C#编写实现语音卡电话呼叫系统,本实例使用了语音卡中的newsig.dll和tc08a32.dll组件,运行前需将其拷贝到Debug文件夹中。本语音卡程序展示了一个完整的电话来电的处理过程,比如来电接听、响铃...

    C#源码大集合 03(共3卷)

    │ ├─实例60 如何启动电话拨号程序 │ │ ├─实例61 如何启动屏幕保护程序 │ │ ├─实例62 如何启动系统控制面板程序 │ │ ├─实例63 如何编写多线程程序 │ │ ├─实例64 如何编写DLL服务端...

    Visual C# .NET精彩编程实例集锦

    实例60 如何启动电话拨号程序 实例61 如何启动屏幕保护程序 实例62 如何启动系统控制面板程序 实例63 如何编写多线程程序 实例64 如何编写DLL服务端程序 实例65 如何编写DLL客户端程序 实例66 如何编写用户控件程序 ...

    C#源码大集合 02(共3卷)

    │ │ ├─第28讲 玩转三维空间 │ │ ├─第30讲 绘制液晶显示的数字 │ │ └─第31讲 拖拉练习 │ ├─第05部分 ASP.NET │ │ ├─第36讲 在线投票 │ │ ├─第37讲 论坛 │ │ └─第三十三讲 绕过ASP.NET │ ...

    C#源码大集合 01(共3卷)

    │ ├─实例60 如何启动电话拨号程序 │ │ ├─实例61 如何启动屏幕保护程序 │ │ ├─实例62 如何启动系统控制面板程序 │ │ ├─实例63 如何编写多线程程序 │ │ ├─实例64 如何编写DLL服务端...

    C#编程经验技巧宝典

    58 &lt;br&gt;0081 文本中首字母改为大写 59 &lt;br&gt;0082 C#随机数的产生 59 &lt;br&gt;0083 身份证从15位升至18位算法 60 &lt;br&gt;0084 十进制数转二进制数的算法 60 &lt;br&gt;0085 十进制数转八进制数的算法 61...

    C#.net_经典编程例子400个

    273 实例190 获取窗口文本 273 实例191 判断文件是否正在被使用 274 实例192 在程序中调用.HLP文件 275 实例193 C#中实现文件拖放 276 实例194 文件比较 276 第7章 操作系统与Windows...

    C#技术大全带源代码

    第06部分 移动电话上网 第07部分 多线程 第08部分XML 第09部分 文件 第10部分 安全性 第11部分 其它高级论题 如何编写多线程程序 Asp.net(c#)实现多线程断点续传 多线程互斥 解压到当前文件夹就有30多兆,再解压一...

    《Visual C# .NET精彩编程实例集锦》配套光盘文件【全】

    《Visual C# .NET精彩编程实例集锦》配套光盘文件【全】 目录回到顶部↑ 前言 第1章 控件操作 实例1 如何使用错误提醒控件 实例2 如何使用信息提示控件 实例3 如何使用菜单控件 实例4 如何使用工具栏控件 实例...

    基于DirectX组件的IP电话程序设计

    此程序是用 C#编写的 基于DirectX组件的 IP电话程序设计。该程序用于网络语音聊天,用户可请求聊天,断开聊天,接受聊天,修改自己的名称,标识在线用户,修改自己的状态(隐身)等。

    C#课程作业-药品进销存管理系统源码+项目说明+sln解决方案.zip

    目前掌握了mssql的操作方法,熟悉php语言,对网络技术和计算机组成原理有一定了解,具备独立编写数据库系统的能力。 2.1.2**经济可行性** 目标系统开发需求较低,开发周期较短,比较简单,开发难度不大。该系统的...

Global site tag (gtag.js) - Google Analytics