语音传输资料收集

【转贴】wffy的专栏

新一代开源VoIP协议栈--OPAL(OpenH323 v2)

    OPAL是Open Phone Abstraction Library的字母缩写,仍然是Equivalence公司开发的开源VoIP协议栈,从2001年便开始开发了,直到现在还没有正式发布过一个版本,只能从CVS: http://cvs.sourceforge.net/viewcvs.py/openh323/opal下载最新的版本,而且现在的2.05bate版有着非常多的问题,但这些丝毫不能阻止我对OPAL的看好。OPAL仍然采用PWLIB作为开发库,最新的PWLIB实际已经只剩PTLIB,Equivalence公司已经停止了对PWLIB的GUI开发了,GUI全面转向更为强大的wxWindows。OPAL作为Openh323的下一代协议栈最大的特点是加入了对SIP协议的支持,OPAL保留了Openh323大部分代码,并加入了对EndPoint,Connection等抽象描述的基类,使得添加新的协议和设备变得更加简单和方便。

    OPAL中最重要的类是新加的:OpalManager,它跟Openh323中的H323EndPoint地位一样,它定义了OPAL系统中最基本的操作,所以每个应用程序必须从OpalManager派生一个类,然后重载虚函数实现自己的操作。
OpalManager::SetUpCall(partyA,partyB,token); 呼叫,partyA是会话发起人,partyB是会话接受人
OpalManager::OnIncomingConnection(connection); 接听,返回TRUE则接听,返回FLASH则拒绝
OpalManager::AdjustMediaFormats(connection,mediaFormats); 调整媒体格式
OpalManager::ClearCall(token,reason,sync) 清除呼叫,sync如果为NULL则不等待快速返回
OpalEndPoint是描述端点的基类,H323,SIP,PSTN,IVR,LID,声卡的端点都是继承于这个类,在这里值得注意的是连声卡设备也看做一种端点。
OpalPCSSEndPoint则是EndPoing中比较重要的一个类,毕竟打电话还是声卡用的多,OpalPCSSEndPoint的构造函数必须引用派生的OpalManager类的实例。

下面具体例子SimpleOPAL来讲解:
opal = new MyManager;                                                           //新建一个MyManager的实例
if (opal->Initialise(args))                                                          //调用MyManager::Initialise()函数初始化
opal->Main(args);                                                                   //等待用户输入命令
cout << "Exiting " << GetName() << endl;
delete opal;                                                                             //擦pp
部分Initialise()函数代码:
SetVideoInputDevice(video);                                                    //设置输入视频设备
...
SetVideoOutputDevice(video);                                                  //输出视频设备
...
SetAudioJitterDelay(minJitter, maxJitter);                                 //声音抖动缓存的最小和最大值
...
SetMediaFormatMask(args.GetOptionString('D').Lines());          //屏蔽的媒体格式
SetMediaFormatOrder(args.GetOptionString('P').Lines());          //媒体格式的使用顺序
...
SetTranslationAddress(args.GetOptionString("translate"));         //设置外部IP地址
...
Set***Ports(rgs.GetOptionString("tcp-base").AsUnsigned(),
               args.GetOptionString("tcp-max").AsUnsigned());        //设置相关的端口
SetSTUNServer(args.GetOptionString("stun"));                         //使用STUN服务器
...
pcssEP = new MyPCSSEndPoint(*this);                                    //新建一个MyPCSSEndPoint实例
pcssEP->SetSoundDevice(args, "sound", PSoundChannel::Recorder)//设置相关的播放声音设备
...
h323EP = new H323EndPoint(*this);                                         //h323EP的初始化
h323EP->DisableFastStart(args.HasOption('f'));                        //关闭快速拨号模式
h323EP->DisableH245Tunneling(args.HasOption('T'));              //关闭H245通道模式
h323EP->AddAliasName(aliases[i]);                                         //添加昵称
h323EP->SetInitialBandwidth(initialBandwidth);                        //限制带宽
h323EP->StartListeners(listeners)                                             //现在才开始监听,默认是TCP:1720
h323EP->SetGatekeeperPassword(args.GetOptionString('p'));    //注册网守所需的密码
h323EP->UseGatekeeper(gkHost, gkIdentifer, gkInterface)       //注册网守
...
sipEP = new SIPEndPoint(*this);                                               //SIP也来了
sipEP->SetUserAgent(args.GetOptionString("sip-user-agent")); //设置用户代理,UserAgent就相当于最初openh323的endpoint
sipEP->SetProxy(args.GetOptionString("sip-proxy"));              //设置SIP代理,此处proxy相当于h323里的Gatekeeper
sipEP->SetMIMEForm(args.HasOption("use-long-mime"));         //MIME头的格式
sipEP->StartListeners(listeners)                                              //监听开始,默认是TCP:5060和UDP:5060
Main()函数部分代码:
if (pcssEP != NULL && !pcssEP->incomingConnectionToken) {    //小心,有电话来了
      if (cmd == "n")
        pcssEP->ClearCall(pcssEP->incomingConnectionToken,     //打死我都不接
    OpalConnection::EndedByRefusal);
      else if (cmd == "y")      
        pcssEP->AcceptIncomingConnection(pcssEP->                  //喂,小莉吗?
    incomingConnectionToken);
    }
...
OpalCall * call = FindCallWithLock(currentCallToken);             //让我看看是那一个通话 
if (call != NULL) {
  cout << "Clearing call " << *call << endl;
  call->Clear();                                                                      //废话那么多,打IP电话也要钱的,挂拉
  call->Unlock();
...
if (potsEP != NULL)                                                                //是打电脑还是打座机呢?
  SetUpCall("pots:*", params[1], currentCallToken);                 //打座机吧(PSTN)
else
  SetUpCall("pc:*", params[1], currentCallToken);                    //打电脑不要钱,嘿嘿

    OPAL中的例子MFC根本无法使用,Openphone的GUI使用了wxWindows,所以必须安装了wxWindows才能正常编译,opalgw这玩意我试过好多次根本无法让别人注册,看来OPAL要走的路还很长,不过Equivalence公司开发已经渐渐的从Openh323转向了OPAL,相信不久一个稳定的OPAL将会被发布.


     语音编码分成波形编码、参数编码和混合编码.波形编码就是语音信号形成的波形进行处理和加工,参数编码是以语音信号产生的数字模型为基础,求出其模型参数,然后按照这些参数还原数字模型,合成语音。混合编码是综合了波形编码和参数编码的长处,在4-16kbps的速率上进行高质量的语音合成。例如多脉冲激励线性预测编码(MPLPC),码本激励线性预测编码(CELP)都是混合编码。

    人耳的听觉范围在20-20KHZ,根本抽样定律要保证声音不失真,就必须用44KHZ左右采样频率,但是人说话的范围在300-3400HZ,所以就用8KHZ主要对人声采样。

   波形编码前,首先对模拟语音信号进行采样,然后对采样结果进行幅度量化,再进行二进制编码。
   参数编码则是对数字模型的参数进行求值,目前常用的是无损声管离散时间模型,该模型综合了人声的最重要三个因素:声门,激励和辐射。这三个因素都可以用数学函数表达。再加上在语音信号的非均匀性和相关性,非均匀性表现在小幅度信号出现的概率较大,相关性表现在相邻的两个样本点相关和相隔基音周期之间的两个样本点也相关。线性预测就时利用了相关性从原来的语音信号计算出现在的信号。而且人在正常的交谈中有50%的时间是静音,只在听对方讲话,语音检测VAD就是用来判断是否是静音,而柔和噪声再生CNG就是用来产生一个“好听”的静音传给对方,

   G.729与G.723是ITU制定的适用于IP电话语音编码,因为其高质量,低码率而得到广泛应用,下面分别做介绍:

   G.729是ITU制定通过的8kbps的语音编码协议,换算成字节才1k,它采用共轭结构的算术码激励线性预测(CS-ACELP)。
   G.723也是ITU制定的,不过它是双速率语音编码,它可以工作在5.3kbps和6.3kbps两个方式上,相应分别采用代数码激励线性预测(ACELP)和多脉冲最大似能量化(MP-MLQ)。

    在ITU上注册一个帐号便可以下载现实的C源代码和描述文档,在VC 6下编译并运行,却发现编码效率太低,编码时间基本上是优化后5-6倍。ITU的源代码中采用的是最基本的运算,而且没有做过任何优化,所以效率极度低下。在优化之前先推荐一款调试软件DevPartner Studio Professional,它能进行错误分析和性能分析,功能强大,操作简单。

    通过DevPartner对源代码进行分析发现无论是G.729还是G.723大部分运算都集中在basic_op.c的函数中,basic_op.c里面都是些基本运算,L_mac(),L_mult(),L_add()和sature()又占用basic_op.c中的绝大部分运算。所以优化也应该集中在这几个函数中。在basic_op.c有个溢出标志位overflow,很多基本函数多对其判断浪费了很多时间,其实可以直接去掉,使用时直接判断结果时候为最大值即可知道是否溢出,对basic_op.c的函数采用MMX指令优化,使用其饱和特性可以减掉很多判断溢出的操作,
下面给出一个加法的例子
Word16 add(Word16 var1,Word16 var2)
{
 _asm{
  movd mm0,var1;
  movd mm1,var2;
  paddsw mm0,mm1;
  movd eax,mm0;
  emms;
 }
}
   源代码中太多的函数调用,比如L_mac就调用了L_mult和L_add函数,如果直接把L_mult和L_add代码填入L_mac中则可以省去很多函数调用浪费的时间,ITU的代码中为了详细的描述算法有很多类似循环没有合并,将其合并也可以提高效率,并且有些在循环体内的运算也可以提到循环体外,对于用For()控制的循环次数最好使用参数,这样编译器可以将循环解开。
    因为本人能力和精力有限,仅仅是在源代码的层面上对G.729和G.723进行优化,不过也取得了相当可观的效果,优化后的效率比优化前提高了一倍,如果再做更深层次的算法优化相信会有更高的提升。

当点击下载的时候出现登陆对话框,让你输入密码,点击右边的Electronic Bookshop,然后选择I wish to REGISTER in order to download up to three (3) ITU Recommendations free of charge注册一个帐号,注册后再选择I have already registered and wish to DOWNLOAD ITU Recommendations free of charge登陆就可以下载3次了,多注册几个就可以反复下载了。


    说来惭愧,VoIP技术博大精深,偶只菜鸟一只却在这里班门弄斧,只能接象一些还没有接触过VoIP的朋友们概括性的介绍一下。
    虽然我是学通信的,可是本科水平好像根本接触不到任何实质性的VoIP技术内容,全部只能自己钻研,而且我走的基本是偏软的路线,也就是做这方面的软件设计。
VoIP=Voice over IP,也就是声音信息通过IP协议传输,从而实现分组交换。为什么要通过IP协议呢?因为便宜嘛,通过现有的互联网传输又不钱。为什么要分组交换呢?可以提高线路利用率撒。下面通俗点来说明,传统电话对话时双方的线路在通话时被完全占用,也就是一条线路只能装一部电话,而分组交换是把声音信息做个缓冲,200毫秒发送一次,这样只是在发送的时候占用了网络,所以可以容纳多部电话。而且VOIP技术还对声音进行了压缩,据调查通话时,有一半的时候是静音,所以声音压缩比特别高,采用G.723算法可以压缩到6.4kbits/s,换成字节连1k 都不到,所以多路传输占用的带宽非常小,VOIP服务提供商的成本是传统电话服务器商成本的几分之一,所以大家现在用的IP电话卡自然是很便宜啦。
    VOIP有几种实现协议,最主要的有两种:H.323和SIP,H.323协议十分强大和复杂,并且应用已经非常普及,SIP协议简单使用,在H.323基础上做了很多改进和删减,未来前途光明,其实SIP协议就是软交换的核心内容。这两种协议栈网络上都有开源的实现,它们分别在http://www.openh323.org/http://www.vovida.org/可以找到,我建议大家学习SIP,H.323协议栈又臭又长,一共有几十万行源代码,看都看死了,反正我看了是有点晕,再说从现在行情看,SIP更有前途。H.323分为3大部分,音频压缩,视频压缩和传输控制。音频压缩包括G.711、G.722、G.723.1、G.728、G.729等协议,它们使用不同算法对声音进行压缩从而满足各种不同的带宽和质量,视频压缩包括H.261和H.263协议,传输控制是H.323最复杂的部分,它包括实时传输协议RTP与RTCP,用来建立两端连接的H.225呼叫信令,会话过程中的会话控制协议H.245和用于数据传输的T.120协议。还有控制信息都用的是ASN.1编码然后传输的,上面每种协议都可以写出一本来讲,所以我们也只能了解一个大概,而且好东西我也是正在学习中,敲出一篇菜鸟文章来抛砖引玉,希望能找到有兴趣的朋友一起学习和进步

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值