h323源码分析

合集下载

使用openh323开发

使用openh323开发

pwlib是一套跨平台的C++的开发库,使基于pwlib上开发的应用能够很少量的移植就可以跑在windows和unix的平台上.Open323是澳洲的一家公司驱动的open source的h323协议族实现, 还不够十分的完整, 但是已经是非常的难得了.在windows上和linux下都能编译使用, 我已经试过了. Windows上编译他们比较麻烦, 注意的是一定要用batch building. 在VC7上编译openh323的动态连接库的时候, 会崩溃, 注意避开, 不过也可以试试看看现象, 如果能够解决, 请告诉我一下.在linux上编译就没有什么好说的了, 设好两个环境变量(PWLIBDIR,OPENH323DIR), 就可以在展开的目录下编译了, 先编译PWLIB, 再编译OPENH323, 别忘了将相应xx/lib写到/etc/ld.so.conf下. 我这里可能对安装讲的不够详细, openh323讲的非常详细, 大家可以去看.以linux平台为例:使用pwlib, 在成功编译之后, 到$(PWLIBDIR)/SAMPLES/这里是一些例子, hello_world 是个非常简单的工程, 从这里我们可以看到如何写使用pwlib的Makefile:# Simple makefile for the hello world programPROG = helloSOURCES = hello.cxxifndef PWLIBDIRPWLIBDIR=$(HOME)/pwlibendifinclude $(PWLIBDIR)/make/ptlib.mak关键是包含了一个ptlib.makhello.cxx#includeclass Hello : public PProcess{PCLASSINFO(Hello, PProcess)public:void Main();};PCREATE_PROCESS(Hello)void Hello::Main(){cout << "Hello world!\n";}非常有代表性. Include $(PWLIBDIR)/make/ptlib.mak 这样就可以make all, make debug的之类的进行编译, 需要的头文件库都会替你安排好. 编译的结果就会放在obj_linux_x86_xx, xx 表示你用的是debug编译还是其他, 如果是debug, xx就是d.使用pwlib的程序, 必然要有一个PProcess的子类, 作为整个进程, 这是指在console模式下, gui模式的用PApplication这个我没有用过. Pwlib里面的类大多都是P开头, (可能是取其兼容的意思, 跨平台的特性, 我瞎猜的), 在进程中如果想创建新的线程就创建PThread子类的对象, 对于这种关于过程的类,都有Main函数等待子类去实现.在使用所有的P类的时候, 注意使用两个宏, 声明类的时候PCLASSINFO(Hello, PProcess); 分号可以加, 也可不加. PProcess的子类的实现的时候要用PCREATE_PROCESS(Hello);,这个东西把main()之类的系统入口封装了, 由他来调用Main()成员函数. 在使用线程的时候, 如果想让线程从线程的对象一创建就运行, 就应该在PThread子类中的构造函数中调用父类的Resume(). 关于pwlib先说这些, 在使用Openh323的时候到处都会用到pwlib的东西和概念.Openh323:终于进入正题了, 先粗略的讲点概念(多余了), H323是指协议族了, 包含了很多规范, 它来自ITU, 应会议的需要而产生, 信令相关的东西用H225 H245,类似Q931,用ASN1编码后在tcp之上传输, 数据相关的就是编码解码的东西了(包括音频视频), 音频g711(alaw, ulaw)了等等多了, 视频h261, 好像h263还没实现.在H323的系统里进行通讯的角色实体就是Endpoint, 每个Endpoint可以有很多的Connection, 每个Endpoint也可以拥有很多的逻辑角色, 这个不讨论.Endpoint 在Openh323中就是类H323Endpoint的实例Connection 在Openh323中就是 H323Connection的实例当Endpoint接收了一个远程的连接请求, Endpoint就会创建一个H323Connection;当Endpoint发出一个连接的请求, Endpoint也会创建一个H323Connection Connection 就会进入一个状态机, 在各个状态中, Connetcion会相应的执行相应的方法,这些方法, 大多都是Onxxxxx(), 是虚函数, 我们可以自己通过继承H323Connection创建其子类, 并且在我们想做事的时机去重载相应的虚函数. 这是使用Openh323的一个基本的思路.现在我们可以看看如何写一个自己H323的Endpoint, 让它能够和netmeeting互操作.成功编译Openh323后在它的samples的目录下面有几个例子, mfc是指在windows下如何使用MFC和Openh323一起开发, 还有simple, 这是个简单的H323的Endpoint的实现, 作为理解OpenH323的库如何使用和开发的技巧方法已经足够了.程序运行主线:PWLIB(PCREATE_PROCESS(SimpleH323Process))--&#61664;SimpleH323Process:: SimpleH323Process()--&#61664;SimpleH323Process::Main();Main()如果结束, 这个程序就结束了, 可是Main()里面有个死循环, 写过图形程序的朋友们都知道, 这就是在等消息来呀. 在VC中称之为Interface thread.程序注解:main.h这个文件包含了程序用到的所有类的声明, 一般应该至少有三个类:来自PProcess的一个主进程的, 或者说作为界面线程的;(只有一个对象)来自H323Endpoint的, 标识这个H323端点的;(只有一个对象)来自H323Connection的, 标识所有和这个H323端点相关的连接;(可以有多个)#ifndef _SimpleH323_MAIN_H#define _SimpleH323_MAIN_H//避免头文件重复包含#includeclass SimpleH323EndPoint : public H323EndPoint{//使用Pwlib的要求, 就像使用MFC, 有n多的宏, 可以看看pwlib的源码,//宏展开都干了什么PCLASSINFO(SimpleH323EndPoint, H323EndPoint);public:SimpleH323EndPoint();~SimpleH323EndPoint();// overrides from H323EndPoint// 重载H323EndPoint的函数// 当收到一个远程的呼入和发出呼出的请求的时候virtual H323Connection * CreateConnection(unsigned callReference);// 有远程的请求来到, 这是在CreateConnection之后的virtual BOOL OnIncomingCall(H323Connection &, const H323SignalPDU &,H323SignalPDU &);//应答远程的呼入virtual H323Connection::AnswerCallResponse OnAnswerCall(H323Connection &, const PString &, const H323SignalPDU &, H323SignalPDU&);//当连接被Forwardvirtual BOOL OnConnectionForwarded(H323Connection &, const PString &, const H323SignalPDU &);//当连接建立virtual void OnConnectionEstablished(H323Connection & connection, const PString & token);//当连接撤销virtual void OnConnectionCleared(H323Connection & connection, const PString &clearedCallToken);//当连接需要打开声音的通道virtual BOOL OpenAudioChannel(H323Connection &, BOOL, unsigned,H323AudioCodec&);// New functions// 自己添加的新函数, 父类中不存在BOOL Initialise(PArgList &);BOOL SetSoundDevice(PArgList &, const char *, PSoundChannel::Directions); // 每个连接会有一个Token来唯一标识PString currentCallToken;protected:BOOL autoAnswer;PString busyForwardParty;};class SimpleH323Connection : public H323Connection{PCLASSINFO(SimpleH323Connection, H323Connection);public://创建连接对象的时候将Endpoint的对象以引用传进来//引用的概念就是将整个对象暴露给你的意思, 不是复制了一份的意思,//对象还是原来的对象, 所以在Connection中修改了EndPoint的某些属性后//就是在操作着传进来的对象, 这是C++的基本概念, OpenH323大量的使用//引用传递对象, 对引用的概念要理解SimpleH323Connection(SimpleH323EndPoint &, unsigned);//重载了两个父类的函数// 当打开逻辑通道的时候(等于没说)virtual BOOL OnStartLogicalChannel(H323Channel &);// 处理用户输入, 这个不是之运行这个程序的用户,而是这个连接上的用户输入// 一般应该是拨号了之类的,virtual void OnUserInputString(const PString &);protected:// 快速连接??BOOL noFastStart;};class SimpleH323Process : public PProcess{//主进程, 类似VC的用户界面线程,//他是整个程序的入口点, 和结束点//创建了EndPoint对象后会有好几个线程启动//这个就是主线程PCLASSINFO(SimpleH323Process, PProcess)public:SimpleH323Process();~SimpleH323Process();//这个函数会被自动调用, 是我们程序的入口了void Main();protected://这个H323端点对象SimpleH323EndPoint * endpoint;};#endif // _SimpleH323_MAIN_H下面是main.cpp 所有的类的实现了#include#ifdef __GNUC__#define H323_STATIC_LIB#endif#include "main.h"#include "../../version.h"#define new PNEW// 这个东西里边可能封装了标准的main函数PCREATE_PROCESS(SimpleH323Process);/////////////////////////////////////////////////////////////////几个宏都在version.h里面定义SimpleH323Process::SimpleH323Process(): PProcess("OpenH323 Project", "SimpleH323",MAJOR_VERSION, MINOR_VERSION, BUILD_TYPE, BUILD_NUMBER){endpoint = NULL;}SimpleH323Process::~SimpleH323Process(){delete endpoint;}void SimpleH323Process::Main(){cout << GetName()<< " Version " << GetVersion(TRUE)<< " by " << GetManufacturer()<< " on " << GetOSClass() << << GetOSName()<< " (" << GetOSVersion() << - << GetOSHardware() << ")\n\n";// Get and parse all of the command line arguments.// 分析命令行参数, 略去数行PArgList & args = GetArguments();args.Parse("a-auto-answer.""b-bandwidth:""B-forward-busy:""D-disable:” FALSE);if (args.HasOption(h) || (!args.HasOption(l) && args.GetCount() == 0)){//如果没有参数或者参数是h, 就输出如何使用, 此处略去数行}//这个东西暂时不管#if PTRACING#endif// Create the H.323 endpoint and initialise it// H323 EndPoint 创建了, 并且把命令参数传过去初始化, 初始化的时候做了一些事endpoint = new SimpleH323EndPoint;if (!endpoint->Initialise(args))return;//看看命令行里是不是想直接呼叫另一个H323的endpoint.有没有l(listen)的option//如果是就MakeCall,// See if making a call or just listening.if (args.HasOption(l))cout << "Waiting for incoming calls for \"" <<endpoint->GetLocalUserName() <<"\"\n";else {cout << "Initiating call to \"" << args[0] << "\"\n";endpoint->MakeCall(args[0], endpoint->currentCallToken);}cout << "Press X to exit." << endl;// Simplest possible user interface// 简单的用户界面, 会有一个提示>// 取pid是我加的for (;;) {pid_t thispid;char prom[20];thispid = getpid();sprintf(prom, "H323 %d >", thispid);cout << prom << flush;PCaselessString cmd;cin >> cmd;if (cmd == "X")break;if (cmd.FindOneOf("HYN") != P_MAX_INDEX) {H323Connection*connection;//使用lock就是怕别的线程把它给删了//因为这里正用着呢connection=endpoint->FindConnectionWithLock(endpoint->currentCallToke n);if (connection != NULL) {if (cmd == "H")connection->ClearCall();else if (cmd == "Y")connection->AnsweringCall(H323Connection::AnswerCallNow);else if (cmd == "N")connection->AnsweringCall(H323Connection::AnswerCallDenied); connection->Unlock();}}}cout << "Exiting " << GetName() << endl;}// Main 函数结束// 自己的Init函数BOOL SimpleH323EndPoint::Initialise(PArgList & args){// Get local username, multiple uses of -u indicates additional aliases if (args.HasOption(u)) {PStringArray aliases = args.GetOptionString(u).Lines();// 设定改Endpoint的usernameSetLocalUserName(aliases[0]);// 设定Aliases 就是每个Endpoint可以有好多名字的意思for (PINDEX i = 1; i < aliases.GetSize(); i++)AddAliasName(aliases[i]);}// Set the various options//设置静音检测否SetSilenceDetectionMode(args.HasOption(e) ?H323AudioCodec::NoSilenceDetection: H323AudioCodec::AdaptiveSilenceDetection);//快速连接?DisableFastStart(args.HasOption(f));//H245通道DisableH245Tunneling(args.HasOption(T));autoAnswer = args.HasOption(a);busyForwardParty = args.GetOptionString(B);if (args.HasOption()) {initialBandwidth = args.GetOptionString().AsUnsigned()*100;if (initialBandwidth == 0) {cerr << "Illegal bandwidth specified." << endl;return FALSE;}}if (args.HasOption(j)) {unsigned jitter = args.GetOptionString(j).AsUnsigned();//设定音频抖动的, 应该影响到接收的缓存if (jitter >= 20 && jitter <= 10000)SetMaxAudioDelayJitter(jitter);else {cerr << "Jitter should be between 20 milliseconds and 10 seconds." << endl;return FALSE;}}//设定声音设备//也可以不用声音设备, 比如Openh323工程的子项目 OpenAM和OpenMCU//都使演示了如何不使用声音物理设备的方法, 我想那里边的东西会对某些朋友们//的需求比较合适if (!SetSoundDevice(args, "sound", PSoundChannel::Recorder))return FALSE;if (!SetSoundDevice(args, "sound", PSoundChannel::Player))return FALSE;if (!SetSoundDevice(args, "sound-in", PSoundChannel::Recorder))return FALSE;if (!SetSoundDevice(args, "sound-out", PSoundChannel::Player))return FALSE;// 设定decode encode的能力// H323 EndPoint在真正进行数据通讯之前要进行能力的交换, 说明自己能够接收和发送什么标准的数据, g.711是必须支持的.// Set the default codecs available on sound cards.AddAllCapabilities(0, 0, "GSM*{sw}");AddAllCapabilities(0, 0, "G.711*{sw}");AddAllCapabilities(0, 0, "LPC*{sw}");AddAllUserInputCapabilities(0, 1);RemoveCapabilities(args.GetOptionString(D).Lines()); ReorderCapabilities(args.GetOptionString(P).Lines());cout << "Local username: " << GetLocalUserName() << "\n"<< "Silence compression is " << (GetSilenceDetectionMode() ==H323AudioCodec::NoSilenceDetection ? "Dis" : "En") << "abled\n"<< "Auto answer is " << autoAnswer << "\n"<< "FastConnect is " << (IsFastStartDisabled() ? "Dis" : "En") << "abled\n"<< "H245Tunnelling is " << (IsH245TunnelingDisabled() ? "Dis" : "En") << "abled\n"<< "Jitter buffer: " << GetMaxAudioDelayJitter() << " ms\n"<< "Sound output device: \"" << GetSoundChannelPlayDevice() << "\"\n" "Sound input device: \"" << GetSoundChannelRecordDevice() << "\"\n" << "Codecs (in preference order):\n" << setprecision(2) << GetCapabilities() << endl;//启动一个来电的监听//可以使用配置的端口, 也可以使用default的端口// Start the listener thread for incoming calls.H323ListenerTCP * listener;if (args.GetOptionString(i).IsEmpty())listener = new H323ListenerTCP(*this);else {PIPSocket::Address interfaceAddress(args.GetOptionString(i));listener = new H323ListenerTCP(*this, interfaceAddress);}if (!StartListener(listener)) {cerr << "Could not open H.323 listener port on "<< listener->GetListenerPort() << endl;delete listener;return FALSE;}//这是连接GateKeeper相关的东西, 先不讨论了// Initialise the security infoif (args.HasOption(p)) {SetGatekeeperPassword(args.GetOptionString(p));cout << "Enabling H.235 security access to gatekeeper." << endl;}// Establish link with gatekeeper if required.if (args.HasOption(g) || !args.HasOption( )) {H323TransportUDP * rasChannel;if (args.GetOptionString(i).IsEmpty())rasChannel = new H323TransportUDP(*this);else {PIPSocket::Address interfaceAddress(args.GetOptionString(i)); rasChannel = new H323TransportUDP(*this, interfaceAddress);}if (args.HasOption(g)) {PString gkName = args.GetOptionString(g);if (SetGatekeeper(gkName, rasChannel))cout << "Gatekeeper set: " << *gatekeeper << endl;else {cerr << "Error registering with gatekeeper at \"" << gkName << \" << endl; return FALSE;}}else {cout << "Searching for gatekeeper..." << flush;if (DiscoverGatekeeper(rasChannel))cout << "\nGatekeeper found: " << *gatekeeper << endl;else {cerr << "\nNo gatekeeper found." << endl;if (args.HasOption( ))return FALSE;}}}return TRUE;}//设定音频设备, 没什么可讲的BOOL SimpleH323EndPoint::SetSoundDevice(PArgList & args,const char * optionName,PSoundChannel::Directions dir){if (!args.HasOption(optionName))return TRUE;PString dev = args.GetOptionString(optionName);if (dir == PSoundChannel::Player) {if (SetSoundChannelPlayDevice(dev))return TRUE;}else {if (SetSoundChannelRecordDevice(dev))return TRUE;}cerr << "Device for " << optionName << " (\"" << dev << "\") must be one of:\n";PStringArray names = PSoundChannel::GetDeviceNames(dir);for (PINDEX i = 0; i < names.GetSize(); i++)cerr << " \"" << names[i] << "\"\n";return FALSE;}//这个函数很简单但是非常关键, 是从EndPoint中重载过来的.//本来是return new H323Connection()的, 现在改成Simplexxx//自己实现的一个Connection, 这样当Endpoint里面调用//Connection的一些东西的时候, 实际上运行的是Simplexxx//的实现, 看到C++的好处了吧, C里用函数指针也可以实现, 没有//C++这么native.H323Connection * SimpleH323EndPoint::CreateConnection(unsigned callReference){return new SimpleH323Connection(*this, callReference);}//没什么东西, 关键是看看这个东西的调用的时机BOOL SimpleH323EndPoint::OnIncomingCall(H323Connection & connection, const H323SignalPDU &,H323SignalPDU &){if (currentCallToken.IsEmpty())return TRUE;if (busyForwardParty.IsEmpty()) {cout << "Incoming call from \"" << connection.GetRemotePartyName() << "\" rejected, line busy!" << endl;return FALSE;}cout << "Forwarding call to \"" << busyForwardParty << "\"." << endl; return !connection.ForwardCall(busyForwardParty);}//这个东西, 很有用, H323Connection的类里也有这个虚函数//返回的值决定告诉远程的连接者是否接收这份连接请求H323Connection::AnswerCallResponseSimpleH323EndPoint::OnAnswerCall(H323Connection & connection,const PString & caller,const H323SignalPDU &,H323SignalPDU &){currentCallToken = connection.GetCallToken();if (autoAnswer) {cout << "Automatically accepting call." << endl;return H323Connection::AnswerCallNow;}cout << "Incoming call from \""<< caller<< "\", answer call (Y/n)? "<< flush;return H323Connection::AnswerCallPending;}BOOL SimpleH323EndPoint::OnConnectionForwarded(H323Connection &/*connection*/,const PString & forwardParty,const H323SignalPDU & /*pdu*/){if (MakeCall(forwardParty, currentCallToken)) {cout << "Call is being forwarded to host " << forwardParty << endl; return TRUE;}cout << "Error forwarding call to \"" << forwardParty << \" << endl; return FALSE;}//连接建立时候void SimpleH323EndPoint::OnConnectionEstablished(H323Connection & connection,const PString & token){currentCallToken = token;cout << "In call with " << connection.GetRemotePartyName() << endl; }//连接断开时候void SimpleH323EndPoint::OnConnectionCleared(H323Connection & connection,const PString & clearedCallToken){if (currentCallToken == clearedCallToken)currentCallToken = PString();PString remoteName = \" + connection.GetRemotePartyName() + \"; switch (connection.GetCallEndReason()) {case H323Connection::EndedByRemoteUser :cout << remoteName << " has cleared the call";break;case H323Connection::EndedByCallerAbort :cout << remoteName << " has stopped calling";break;case H323Connection::EndedByRefusal :cout << remoteName << " did not accept your call";break;case H323Connection::EndedByNoAnswer :cout << remoteName << " did not answer your call";break;case H323Connection::EndedByTransportFail :cout << "Call with " << remoteName << " ended abnormally";break;case H323Connection::EndedByCapabilityExchange :cout << "Could not find common codec with " << remoteName;break;case H323Connection::EndedByNoAccept :cout << "Did not accept incoming call from " << remoteName;break;case H323Connection::EndedByAnswerDenied :cout << "Refused incoming call from " << remoteName;break;case H323Connection::EndedByNoUser :cout << "Gatekeeper could find user " << remoteName;break;case H323Connection::EndedByNoBandwidth :cout << "Call to " << remoteName << " aborted, insufficient bandwidth."; break;case H323Connection::EndedByUnreachable :cout << remoteName << " could not be reached.";break;case H323Connection::EndedByHostOffline :cout << remoteName << " is not online.";break;case H323Connection::EndedByNoEndPoint :cout << "No phone running for " << remoteName;break;case H323Connection::EndedByConnectFail :cout << "Transport error calling " << remoteName;break;default :cout << "Call with " << remoteName << " completed";}cout << ", duration "<< setprecision(0) << setw(5)<< (PTime() - connection.GetConnectionStartTime())<< endl;}//打开声音设备时候//isEncoding 表示编码吗//编码表示向外发送数据, 从声音设备读//解码表示从网络读出数据, 写到声音设备上//不同的方向的codec是不同的, 所以在这里有好多文章可以做//可以给codec attach上不同的channel根据isEncoding的值BOOL SimpleH323EndPoint::OpenAudioChannel(H323Connection & connection, BOOL isEncoding,unsigned bufferSize,H323AudioCodec & codec){if (H323EndPoint::OpenAudioChannel(connection, isEncoding, bufferSize, codec))return TRUE;cerr << "Could not open sound device ";if (isEncoding)cerr << GetSoundChannelRecordDevice();elsecerr << GetSoundChannelPlayDevice();cerr << " - Check permissions or full duplex capability." << endl;return FALSE;}//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////EndPoint的实现分析完毕.H323Connection的实现, 这个Connection的实现太简单了.可能不足以说明问题我也没什么好说的了///////////////////////////////////////////////////////////////SimpleH323Connection::SimpleH323Connection(SimpleH323EndPoint & ep, unsigned ref): H323Connection(ep, ref){}BOOL SimpleH323Connection::OnStartLogicalChannel(H323Channel & channel) {if (!H323Connection::OnStartLogicalChannel(channel))return FALSE;cout << "Started logical channel: ";switch (channel.GetDirection()) {case H323Channel::IsTransmitter :cout << "sending ";break;case H323Channel::IsReceiver :cout << "receiving ";break;default :break;}cout << channel.GetCapability() << endl;return TRUE;}void SimpleH323Connection::OnUserInputString(const PString & value) {cout << "User input received: \"" << value << \" << endl;}// End of File///////////////////////////////////////////////////////////////总结一下基本的过程就是创建一个H323Endpoint的对象endpoint, 创建对象后这个程序就有好多个小的线程被创建了.然后EndPoint开始监听来电, 之后判断是否直接呼叫另一个h323的Endpoint. 然后就是一个for循环, 判断标准的输入, 并通过当前的token来lock一个Connection, 每个连接会有唯一的一个token, lock的意思是说, 在被lock的期间是不能被释放的. 根据输入的字符决定对得到的连接做什么.OpenAM:是个answer machine, 自动应答机, 或者是留言机. 实现的很简单, 里面对OpenH323使用的思路很有价值../openam –n –-g711message sample_message.wav这样运行, 用netmeeting 连接一下这个IP, netmeeting就会放一段简单的英语, 测测你的英语听力, 他在讲什么?这个程序是一个支持多连接和并发连接的Endpoint, 但是他没有使用真正的声音设备, 放出的音从一个已有的wav文件中读出来, 远程用户的留言被录到一个文件里, 文件的名字表示了是什么时间录制的.主要的思路是给在连接打开声音通道的时候, 根据isEncoding的值区别是录音还是放音,如果是录音, 将读文件的Channel附加在codec上, 相反写文件的Channel附件在codec上,注意这是两个codec.这个东西给了我们一个方法, 如何使用文件IO来代替声音设备的IO来使用OpenH323.这是main.h#ifndef _Voxilla_MAIN_H#define _Voxilla_MAIN_H#include#include#include#include#include#include#include//主进程class OpenAm : public PProcess{PCLASSINFO(OpenAm, PProcess)public:OpenAm();~OpenAm();void Main();void RecordFile(PArgList & args);void PlayFile(PArgList & args);protected:long GetCodec(const PString & codecname);OpalLineInterfaceDevice * GetDevice(const PString & device);};//H323 端点class MyH323EndPoint : public H323EndPoint{PCLASSINFO(MyH323EndPoint, H323EndPoint);public:MyH323EndPoint(unsigned callLimit,const PString & runCmd,const PDirectory & dir,int flags);// overrides from H323EndPointvirtual H323Connection * CreateConnection(unsigned callReference); BOOL OnIncomingCall(H323Connection &, const H323SignalPDU &,H323SignalPDU &);// new functionsBOOL Initialise(PConfigArgs & args);PString GetGSMOGM() const { return gsmOgm; }void SetGSMOGM(const PString & s) { gsmOgm = s; }PString GetG711OGM() const { return g711Ogm; }void SetG711OGM(const PString & s) { g711Ogm = s; }PString GetLPC10OGM() const { return lpc10Ogm; }void SetLPC10OGM(const PString & s) { lpc10Ogm = s; }#ifdef SPEEX_CODECPString GetSPEEXOGM() const { return speexOgm; }void SetSPEEXOGM(const PString & s) { speexOgm = s; }#endifPString GetG7231OGM() const { return g7231Ogm; }void SetG7231OGM(const PString & s) { g7231Ogm = s; }unsigned GetCallLimit() const { return callLimit; }PString GetRunCmd() const { return runCmd; }PDirectory GetDirectory() const { return dir; }void SetRecordWav(const BOOL rec){ recordWav = rec; }BOOL GetRecordWav() const { return recordWav; }enum {DeleteAfterRecord = 0x01,NoRecordG7231 = 0x02,HangupAfterPlay = 0x04};BOOL GetDeleteAfterRecord() const { return flags & DeleteAfterRecord; } BOOL GetNoRecordG7231() const { return flags & NoRecordG7231; } BOOL GetHangupAfterPlay() const { return flags & HangupAfterPlay; }protected:unsigned callLimit;PString pcmOgm, g711Ogm, gsmOgm, lpc10Ogm, g7231Ogm, runCmd;#ifdef SPEEX_CODECPString speexOgm;#endifPDirectory dir;int flags;BOOL recordWav;};class PCM_RecordFile;class MyH323Connection;PQUEUE(PStringQueue, PString);// Out Going Channel OGM//就是发送语音的通道//即是读文件的通道class PCM_OGMChannel : public PIndirectChannel{PCLASSINFO(PCM_OGMChannel, PIndirectChannel);public:PCM_OGMChannel(MyH323Connection & conn);BOOL Read(void * buffer, PINDEX amount);void PlayFile(PFile * chan);BOOL Close();void QueueFile(const PString & cmd);void FlushQueue();void SetRecordTrigger();void SetHangupTrigger();void SetPlayOnce() { playOnce = TRUE; }protected:virtual BOOL ReadFrame(PINDEX amount);virtual void CreateSilenceFrame(PINDEX amount);virtual void Synchronise(PINDEX amount);virtual BOOL IsWAVFileValid(PWAVFile *chan);BOOL AdjustFrame(void * buffer, PINDEX amount);PStringQueue playQueue;MyH323Connection & conn;PMutex chanMutex;int silentCount;int totalData;BOOL recordTrigger, hangupTrigger;BOOL closed;BOOL playOnce;PAdaptiveDelay ogm_delay;PBYTEArray frameBuffer;PINDEX frameLen, frameOffs;};//这个是之读的文件是个g723编码的文件, 暂时不研究这个类相关的一切class G7231_OGMChannel : public PCM_OGMChannel{PCLASSINFO(G7231_OGMChannel, PCM_OGMChannel);public:G7231_OGMChannel(MyH323Connection & conn);protected:BOOL ReadFrame(PINDEX amount);void CreateSilenceFrame(PINDEX amount);void Synchronise(PINDEX amount);BOOL IsWAVFileValid(PWAVFile *chan);};//连接,都是从这个类实例出来的class MyH323Connection : public H323Connection{PCLASSINFO(MyH323Connection, H323Connection);public:MyH323Connection(MyH323EndPoint &, unsigned);~MyH323Connection();// overrides from H323ConnectionBOOL OpenAudioChannel(BOOL, unsigned, H323AudioCodec & codec); AnswerCallResponse OnAnswerCall(const PString &, const H323SignalPDU &, H323SignalPDU &);BOOL OnStartLogicalChannel(H323Channel & channel);void OnUserInputString(const PString & value);// new functionsvoid StartRecording();void Hangup();void SetE164Number(const PString & _num){ e164Number = _num; }PString GetE164Number() const{ return e164Number; }protected:void OnUserInputChar(char ch);BOOL StartMenu(int menuNumber);BOOL ProcessMenuCmd(const PString & cmdStr);const MyH323EndPoint & ep;PString product;PTime callStartTime;PTime recordStartTime;PString basename;PFilePath recordFn;PString transmitCodecName, receiveCodecName;BOOL recordTrigger;PMutex connMutex;PCM_RecordFile * recordFile;PCM_OGMChannel * ogmChannel;PString digits, lastDigits;int currentMenu;PStringList menuNames;PString securityToken, e164Number;};//是录音class PCM_RecordFile : public PIndirectChannelPCLASSINFO(PCM_RecordFile, PIndirectChannel)public:PCM_RecordFile(MyH323Connection & conn, const PFilePath & fn, unsigned callLimit);~PCM_RecordFile();BOOL Write(const void * buf, PINDEX len);BOOL Close();void StartRecording();virtual void DelayFrame(PINDEX len);virtual BOOL WriteFrame(const void * buf, PINDEX len);BOOL WasRecordStarted() const { return recordStarted; }protected:MyH323Connection & conn;PTime finishTime;PFilePath fn;unsigned callLimit;BOOL recordStarted;BOOL timeLimitExceeded;BOOL closed;BOOL isPCM;BOOL dataWritten;PAdaptiveDelay delay;PMutex pcmrecordMutex;PFile *fileclass; // will point to a PWAVFile or PFile class};//录的结果是个g723文件, 我们暂时不考虑这个类相关的一切class G7231_RecordFile : public PCM_RecordFile{PCLASSINFO(G7231_RecordFile, PCM_RecordFile);public:G7231_RecordFile(MyH323Connection & conn, const PFilePath & fn, unsigned callLimit);void DelayFrame(PINDEX len);BOOL WriteFrame(const void * buf, PINDEX len);};。

OpenH323简介

OpenH323简介

OPENH323介绍目录前言(一)、综述1.H.323协议简介2.OPENH323协议栈总体框架3、OPENH323协议栈主要类之间的关系图(二)、重要协议过程和体系结构分析1、RAS2、H.245控制信令3、H.225呼叫信令4、Q.9315、控制协议的实现:6、编解码器H323Codec7、逻辑信道:(三)、运行期分析1、侦听2、发现网守并向网守注册:3、呼叫:4、逻辑信道的打开及实时数据传输:5、挂断:前言H.323协议群标准描述了在分组网上由端点EP和其它网元(网守GK、网关GW和多点控制单元MCU)组成的多媒体网络的系统架构,它是指一组多媒体的协议群,包括负责呼叫控制信令H.225和负责媒体控制信令H.245。

媒体控制信令是负责能力交换,媒体通道建立/释放和会议控制协议。

H.323系统架构还包括数据会议T.120协议和传真T.38协议,以及实时媒体流协议RTP和其控制协议RTCP。

H.323支持语音编解码G.711、G.723、G.729和视频编解码H.261、H.263等。

H.323不仅系统地规范了分组语音会议标准而且也包括了完整的数据和视频会议标准。

尽管H.323-v1原本是为分组局域网LAN上设计的多媒体会议系统,但人们从一开始就将H.323用于广域互联网,结果H.323取得了成功并马上受到广泛重视。

H.323标准从一开始就重视与传统公共电话PSTN网完整互联互通,其中H.225中呼叫控制部分和H.245是借用了已有的ITU的窄带视频H.320、H.324协议群H.221和H.242。

但H.225中的注册Registration、准许Admission、状态Status和地址解析机制(RAS)则不得不完全从头开始设计。

基本呼叫程序由RAS信令(端点注册、准许控制和地址解析)和呼叫信令(连接建立、能力交换、打开逻辑通道)组成。

H.323会议是通过多点控制单元MCU来进行的。

H.323的实时媒体流协议和其分别采用了已有的IETF标准RTP和RTCP。

H323协议讲解p24精品

H323协议讲解p24精品

3
H.323系统组成
内部公开▲
MCU
GK GW
PSTN
Soft Phone
Terminal
Terminal
Terminal:终端。在分组网络上遵循H.323标准进行实时通信的端点设备 GK:Gatekeeper,网守。处于高层,提供对端点(终端、网关、多点控制单元统称为 端点)和呼叫的管理功能,是H323电话网络系统中的重要管理实体 GW:Gateway,网关。负责不同网络间信令和控制信息转换以及媒体信息变换和复用 MCU:Multipoint Conrtrol Unit,多点控制单元。用在做多媒体视讯会议(Video Conference)时所用到的设备 ,主要功能是协调及控制多个终端间的视讯传输
4
H.323协议的常用术语
内部公开▲
终端:基于IP的网络上是一个客户端点。它需要支持下面3项功能:支持信令和 控制;支持实时通信;支持编码,即传前压缩,收后进行解压缩
网关:提供在包交换网络和电路交换网络(SCN,Switch Circuit Network)之间 的一个连接。
网守:完成地址翻译、接纳控制、带宽控制、域管理4个必须功能。网守还支持 呼叫控制信令、呼叫鉴权、带宽管理和呼叫管理,以及用户管理等可选的功能。
RRQ/RCF/RRJ:Registration Request/Confirm/Reject 注册
URQ/UCF/URJ:UnRegistration Request/Confirm/Reject 注销
呼叫类消息(呼叫、路由、管理):
ARQ/ACF/ARJ:Admission Request/Confirm/Reject
TCP UDP
实体 。

第12讲-H323协议分析

第12讲-H323协议分析

H.323的协议框架
H.323的协议族
H.225.0 RAS(Registration, Admission and Status):在endpoints与gatekeeper之间的通信协 议,用于注册,寻址以及终端状态的查询和维护。 H.225.0 Call Signaling:主要用于在endpoint之 间建立与撤销连接,和传统电信的信令过程类似 H.245:用于在终端之间交换多媒体能力信息, 建立多媒体连接。 RTP:终端之间用于收发媒体数据的协议标准 RTCP:Real Time Control Protocol, 用于控制与 监督RTP连接的情况。
H323协议中的一些不足
ASN1编码,不能明文分析 消息过于复杂,从呼叫到通信要经过太多的消息交换,容 易出问题 从H225 Connect到开始交换RTP,需要经过多次消息交 换,让用户产生摘机后很久才能听到声音的感觉。 H225, H245要占用两路TCP, 对系统资源影响比较大. 整个的消息体系,和协议体系中,很多都是为video应用 的,在voice中无法使用,又增加了很多复杂性
– – – – – – – – Grq,gcf, rrq, rcf 成功注册 Arq,acf, setup 主叫发起呼叫 CallProceeding, arq, acf, alerting 被叫相应呼叫 Connect h225通道连接 CapSet, Msd, CapSetAck, MsdAck 能力集协商 Olc, OlcAck, 打开媒体通道 Rtp 语音流传送 EndSession, Release complete, Drq 挂断话机
H225的消息分类
Setup 主叫建立呼叫 CallProceeding 被叫处理呼叫 Alerting 被叫振铃指示 Connect H225连接成功 ReleaseComplete H225释放连接 Facility Status/StatusInquiry状态查询

基于OpenH323视频会议音频处理技术研究

基于OpenH323视频会议音频处理技术研究
n t o k vd oc n ee c y t msa e te o g n li tn in o o kn o e t r g c n e in e u o n y t mstuy ew r i e o f rn e s se r h r i a e t f r i gf r h m o b n o v n e c ,b t w ma y s se r l i n o w t i h b n er o k n o v n e c 9 W i sl a o ,o e o emo t mp ra t r b e st a st ea d o po e s g tc n lg , i r g t t i r i g c n e i n e t i ¥ n n f h s ot n o l m i p s u i rc s i h oo y oh w ht  ̄ t i p o h n e
21 02年第 1 期
文 章 编 号 :0627 (0 2 0 -120 10 -4 5 2 1 ) 1 3 -3 0
计 算 机 与 现 代 化 JS A J Y I N AHU IU N I U X A D I A
总第 17期 9
基 于 O eH 2 pn 3 3视 频会 议 音频 处理 技 术研 究
Re e r h o di o e sn c no o y i s a c n Au o Pr c s i g Te h l g n Ope H32 d o Co e e c n 3 Vi e nf r n e
C HE n — o g N Mig d n
( e amet f o p t cec , b ece o ee Wecun6 30 , h a D pr n o m ue Si e A a ah r C l g , nha 2 02 C i ) t C r n T s l n

防火墙H323协议处理流程及H32ALG应用

防火墙H323协议处理流程及H32ALG应用

一.H.323协议简介H.323协议簇是ITU的一个标准协议栈,它是一个有机的整体,根据功能可以将它分为4类协议,也就是说该协议从系统的总体框架(H.323)、视频编解码(H.263)、音频编解码(G.723.1)、系统控制(H.245)、数据流的复用(H.225)等各方面作了比较详细的规定。

H323系统中的信息流是视频、音频和控制消息的组合。

系统控制的协议包括H.323、H245和H225.0,而Q.931和RTP/RTCP是H225.0的主要组成部分。

整个系统控制由H.245控制信道、H225.0呼叫信令信道和RAS(注册、许可、状态)信道提供。

H.225它主要处理传输路径问题,描述了如何操作网络包上的视频、音频、数据和控制信息使其提供H.323 装备会话服务。

H.225 主要有两个部分:呼叫信令和RAS (注册、接入允许和状态)。

H.225 详细定义了Q.931 信令信息的使用和支持。

在IP 网络的TCP端口1720需要创建一个可靠的TCP 呼叫控制信道,该端口完成Q.931 呼叫控制信息的初始化,从而实现连接、维持和呼叫分离功能。

H.245 是H.323 多媒体通信体系中的控制信令协议,其主要用于处于通信中的H.323终点或终端间的端到端H.245 信息交换。

H.245制定了一个控制信道分段和重新装配的协议层(CCSRL,Control Channel Segmentation and Reassembly Layer),它可以在易出错环境下保证应用的可靠性。

H.245提供了一种功能交换的功能,它支持两端设备通过协商确定一组通用的功能集。

二.防火墙H.323 ALG功能简介当内部网络的H.323终端穿越防火墙与公网上的H.323终端进行通信时,由于NAT功能只能将传输层的IP及端口进行转换,无法对H.323协议应用层携带的内部数据进行转换,应用层中内部数据直接被转发至公网,后续协议信息处理时会出现问题;而H323 ALG则可以实现应用层数据转换,协议数据发至Internet 时,将其应用层内部信息转换成公网信息,实现完全隐藏内部终端达到通信正常的目的。

基于H.323的VOIP呼叫过程分析

基于H.323的VOIP呼叫过程分析

VOIP实验报告四(基于H.323的VOIP呼叫过程分析)
班级:网络131 姓名:学号:
一.实验目的
1.掌握GnuGk的安装使用方法,掌握H.323软终端的使用方法,
理解软终端与GK的交互。

2.学习使用WireShark协议分析软件分析VOIP注册过程,呼叫过
程以及语音通话过程,深入理解H.323协议栈组成以及工作原理。

二.实验环境
硬件:PC机
软件:Polycom RealPresence,GnuGk,Wireshark
三.实验内容
1.H.323呼叫
2.Wireshark分析H.323完整呼叫过程。

四.实验步骤及截图
1.H.323呼叫过程
(1)在PC机上安装GnuGk软件并运行
(2)分别在PC机和手机上安装Ploycom RealPresence软件,并注册
到GK
(3)PC机和手机互相呼叫,完成H.323通话过程
2. Wireshark分析H.323完整呼叫过程。

(1)下载并安装Wireshark
(2)正常呼叫
(3)分析GnuGk日志文件,截取其中呼叫过程中H.323协议相关的
消息交互内容。

防火墙H323协议处理流程及H323-ALG应用

防火墙H323协议处理流程及H323-ALG应用

H.323 协议簇是 ITU 的一个标准协议栈,它是一个有机的整体,根据功能可以将它分为 4 类协议 ,也就是说该协议从系统的总体框架〔 H.323〕、视频编解码〔 H.263〕、音频编解码〔G.723.1〕、系统控制〔H.245〕、数据流的复用〔H.225〕等各方面作了比较详细的规定.H323 系统中的信息流是视频、音频和控制消息的组合 .系统控制的协议包括 H.323、H245 和 H225.0,而 Q.931 和 RTP/RTCP 是 H225.0 的主要组成部份.整个系统控制由 H.245 控制信道、 H225.0 呼叫信令信道和 RAS 〔注册、许可、状态〕信道提供.H.225 它主要处理传输路径问题,描述了如何操作网络包上的视频、音频、数据和控制信息使其提供 H.323 装备会话服务.H.225 主要有两个部份:呼叫信令和 RAS 〔注册、接入允许和状态〕 .H.225 详细定义了 Q.931 信令信息的使用和支持 .在 IP 网络的 TCP 端口1720 需要创建一个可靠的 TCP 呼叫控制信道,该端口完成 Q.931 呼叫控制信息的初始化, 从而实现连接、维持和呼叫分离功能.H.245 是 H.323 多媒体通信体系中的控制信令协议 ,其主要用于处于通信中的 H.323 终点或者终端间的端到端 H.245 信息交换.H.245 制定了一个控制信道分段和重新装配的协议层〔CCSRL,Control Channel Segmentation and Reassembly Layer〕,它可以在易出错环境下保证应用的可靠性 .H.245 提供了一种功能交换的功能 ,它支持两端设备通过商议确定一组通用的功能集.当内部网络的 H.323 终端穿越防火墙与公网上的 H.323 终端进行通信时,由于 NAT 功能只能将传输层的 IP 与端口进行转换,无法对 H.323 协议应用层携带的内部数据进行转换,应用层中内部数据直接被转发至公网,后续协议信息处理时会浮现问题;而 H323 ALG 则可以实现应用层数据转换,协议数据发至 Internet 时,将其应用层内部信息转换成公网信息,实现彻底隐藏内部终端达到通信正常的目的.此外,应用防火墙普通只开放特定端口的数据进入内部网络 ,H.323 协议属于多通道协议, 控制连接使用端口 1720,数据交换使用端口为暂时商议,无法事先预知,若无 ALG 功能,商议出数据交换通道所用端口后 ,外部网络终端尝试对内部终端数据交换的端口进行连接时 ,防火墙会对其进行阻断,从而数据传输通道无法建立;开启 H.323 ALG 功能后,会在对应用层转换的IP 地址与端口进行转换的同时 ,将其信息进行记录,使其在外部网络终端尝试对内部终端数据交换的端口进行连接时 ,防火墙进行协议识别,对后续相关协议报文执行放通策略 ,从而成功建立传输通道.1. 客户端与服务器建立 TCP 三次握手连接2.建立 TCP 连接之后,主叫终端通过 H.225 协议发送 setup 消息至被叫终端,表示主叫方希翼建立通话〔FW 开启了 H323 ALG 功能〕1〕内网主叫终端抓包报文2〕外网被叫终端抓包报文由上面 2 个报文可以明显看出 ALG 对协议应用层的数据进行了处理.3.被叫终端返回 CallProceeding 给主叫终端,表示被叫终端正在处理.4.被叫终端返回 Alerting报文给主叫终端,表示被叫用户已被振铃.1〕内网主叫终端抓包报文2〕外网被叫终端抓包报文5.被叫终端返回 Connect 报文给主叫终端,表示被叫用户已摘机并告知被叫终端已开放特定端口来进行下一阶段的协议商议过程.1〕内网主叫终端抓包报文2〕外网被叫终端抓包报文6.主叫方收到 Connect 报文后,进入 H.245 商议阶段,H.245 整个商议阶段包括能力交换、主从确定、打开逻辑通道〔通道打开之后传输数据〕、关闭逻辑通道、断开 H.245TCP 连接. 1〕内网主叫终端抓包报文〔TCP 三次握手阶段〕2 〕内网主叫终端抓包报文〔打开逻辑通道阶段〔能力交换、主从确定阶段省略〕〕3〕外网被叫终端抓包报文由以上报文可以看出后续数据传输被叫方将使用 1503 端口来建立连接.7.通道建立之后,进行数据传输〔主叫方将使用多个端口与被叫方的 1503 端口进行连接来进行视频、音频数据的传输〕8.数据传输完成后〔通讯结束〕后,由主叫方发起 EndSessionCommand 与 ReleaseComplete 消息来释放连接1 ) 内网主叫方抓包报文2 ) 外网被叫终端抓包报文完成上述报文交互之后,断开 TCP 连接,至此已完成整个 H323 呼叫流程.注:防火墙会话如下〔与上述抓包无关联,仅作参考〕1.内部终端向外网终端发起会话,防火墙做 SNAT;由于 h323 通话 setup 消息中被叫方只关注应用层中 destCallSignalAdress 字段信息〔检查目的 IP 是否为自己,确认其是想要和自己通信〕与传输层的源 IP 〔主叫方 IP〕,符合以上条件后才会进行后续协议商议;当发起方为内部终端时, 目的 IP 即为被叫终端的 IP,不需 ALG 转换;Netmeeting 软件数据传输通道都是由主叫方发起,不存在 Untrust 到 Trust的阻断问题;基于以上两点,在此种场景下,是否开启 ALG 都对其通讯无影响;但是没有开启 ALG 功能时, 不会对 setup 消息中 sourceCallSignalAdress 字段的私网 IP 进行转换而将其地址暴露在公网中;2.外网终端向内部终端发起会话,防火墙做 DNAT;当主叫方在外部网络时 , 若没有开启 ALG 功能 ,H323 的 setup 消息中字段信息destCallSignalAdress 仍为防火墙目的 NAT 前的 IP 地址〔没有转换为私网地址〕 ,被叫方在收到该消息后发现其不是想和自己进行通讯,会直接返回 releaseComplete 消息来结束通讯请求;故在此种应用环境下,必须开启 ALG 功能才干正常通讯.说了这么多的呼叫流程,大家是不是有些头晕眼花,没有关系,看了下面的小故事,相信大家对于 H.323 一次呼叫过程就有了比较全面的了解.请看:在 H.323 的王国里有许多成员〔各种 H.323 节点〕 ,为了确保这个王国的正常运转,颂布了许多法令〔H.323 协议簇,其中主要有 RAS、Q.931、H.245、TCP/IP、RTP/RTCP、UDP〕, 无论是国王、还是臣民,大家都严格遵守这些法规.在这里将介绍 H.323 王国最重要的两个角色国王〔GK〕、臣民〔GW〕是如何遵照法规〔RAS、Q.931、H.245〕通信的.其中国王与臣民之间的通信遵守 RAS 协议,臣民与臣民间的通信遵守 Q.931、 H.245 协议.首先,臣民〔GW〕应向国王注册.一个臣民〔GW〕诞生后,会使用 RAS 协议去寻觅自己的国王〔GK〕,他高声问到: "谁是我的国王请回答我!〞,这时可能会有一个或者多个国王来响应: "你是我的臣民〔GW〕,到我这里来注册吧,这是我的地址.〞,固然国王也可以拒绝臣民〔GW〕的请求: "你不是我的臣民〔GW〕,别来烦我.〞如果臣民〔GW〕幸运地得到了多个国王的青睐,他可以选择一个国王并向他注册.注册成功后,臣民〔GW〕就可以享受国王提供的各种服务〔如接入控制、带宽管理、地址翻译等功能〕 .这时,当臣民〔GW〕与另一臣民〔GW〕通信时,不需要知道对方的地址,只需告诉国王想要和谁通信,国王会把对方的地址找来给他.对于那些没有找到国王的臣民〔GW〕来说就有点惨了,因为没有国王的匡助,他只能与自己相当熟悉的臣民〔GW〕通信〔即知道对方的地址〕 .臣民〔GW〕向国王注册可以有一个生命期 ,过了这个有效期,臣民〔GW〕还要再向国王注册.下面看看 H.323 的国王与臣民是如何匡助 PSTN 王国的臣民通过 IP 网相互通信的〔即IP 是如何实现的〕 .一个 PSTN 王国的臣民 C 想通过 IP 网送给他远方的朋友 D 一份特殊的礼物,他跑去找与自己相熟的 H.323 王国的臣民 A〔GW〕,并把朋友的告诉他,请他匡助通过 IP 网找这个朋友〔即一个 PSTN 用户拨打 IP ,呼入 GW〕 .臣民 A〔GW〕看不懂这个 ,他应该怎么做才干找到那位朋友呢?向国王〔GK〕寻求匡助,解析 .由于在 H.323 王国里是使用 IP 协议通信的,所以臣民 A〔GW〕拿到对方的是没有办法与对方联系的,他惟独去寻觅与对方相知的臣民 B 〔目的 GW〕的地址.于是臣民 A〔GW〕将发送给他注册的国王〔GK〕,让国王匡助寻觅臣民 B〔目的 GW〕的地址.首先国王会对臣民 A〔GW〕的请求进行认证,认证通过后,国王才会去寻觅臣民 B 〔目的 GW〕的地址.如果国王不知道臣民 B 〔目的 GW〕的地址〔即这个 GW 未在该 GK 上注册〕 , 他会向其它的国王〔GK〕问询有谁知道臣民 B 〔目的 GW〕的地址.当国王得到臣民 B 〔目的 GW〕的地址后,就将该地址〔呼叫信令传输地址 = 目的 GW 的IP 地址+端口号〕发回给臣民 A〔GW〕 .这样,就可以在这两个臣民〔GW〕间建立联系〔建立呼叫信令信道,开始 Q.931 协议流程〕 .臣民 A〔GW〕告诉臣民 B〔目的 GW〕:"我的朋友 C 有礼物要送给你的朋友 D,他的是##X,他在家吗?〔即被叫用户 C 是否空闭〕〞 ,臣民 B 〔目的 GW〕赶紧告诉 D,别走开,有人要送礼物给你〔即目的 GW 提醒被叫用户,并将该用户空闭态置为忙〕 .然后臣民 B〔目的 GW〕通知臣民 A〔GW〕"一切搞掂〞〔即 GWB 向 GWA 发送 CONNECTION 消息后〕 ,双方开始讨论采用什么方式将朋友 C 的礼物送给朋友 D 〔即开始 H.245 协议流程,进行能力的商议〕 .臣民 A〔GW〕说: "朋友 C 的礼物是:播放一首凯利金的《GOING HOME》萨克斯曲给他听,我可以将这首曲子编辑为 CD、VCD 两种格式,你可以解读吗?〞臣民 B〔目的 GW〕:"我这里的设备还没有升级呢,不好意思目前我只能解读 CD 格式的曲子〞〔这就是 H.245 中的所谓能力商议,通过商议,获得双方都可以接受的语音编解码类型〕 .臣民 B 〔目的 GW〕通知臣民 A〔GW〕传送曲子所使用的地址〔即 H.245 中的打开 RTP/RCTP 通道,用于传送媒体流〕 ,这样,这份礼物在 IP 网的传送通道打开了.这时,朋友 C 开始通过线向远方的朋友 D 播放乐曲,优美的乐曲经过线传送到臣民A〔GW〕处,臣民 A〔GW〕将曲子压缩成 CD 的格式〔进行语音编码压缩,并打成 IP 包发送到 IP 网上〕 ,通过 IP 网传送给臣民 B〔GW〕,臣民 B〔GW〕再将编码解读还原成曲子通过线传送给朋友 D.〔IP 就是这样拨通了〕。

H323视频会议原理讲解

H323视频会议原理讲解

H323视频会议原理讲解一、组网原理二、主要名词解释会议由一组呼叫组成,由全局唯一会议标识符关联;呼叫由两个H.323节点参与,由全局唯一呼叫标识符区分.地址翻译:将一个地址的别名翻译成传输地址。

H.323终端可能有电话号码(或其余名称)、别名、传输地址等多种名称,管理、更新和翻译地址表是非常重要的。

访问控制:设定访问者的权限,提供允许或拒绝访问等管理。

带宽控制:根据网络带宽,GateKeeper控制访问的人数以确保通道顺畅。

区域管理:GateKeeper提供区域内的终端、MCU和网关的注册、更新、管理等功能。

呼叫控制信令――GK可以选择由节点完成呼叫信令,也可以选择由自己处理呼叫信令(Q.931路由)。

呼叫鉴权――通过使用H.225.0信令,GK可以因鉴权失败拒绝来自终端的呼叫。

拒绝的原因至少包括:接入特定的终端或网络受限,在某个时间限制接入。

带宽管理――控制同时允许访问网络的终端数目。

通过使用H.225.0信令,GK可以因带宽受限拒绝来自终端的呼叫。

呼叫管理――例如,GK可以维护正在进行的呼叫列表。

这个信息对于指示被叫是否忙是有用的,以及给带宽管理功能提供信息。

提供传输格式,呼叫建立信令和流程,连接控制信令和流程的转换功能在SCN 侧,网关可以有SCN 终端或MCU 的功能,从H323网络侧看,网关可以表现为一个或多个终端,或者是H323MCU来自SCN 的GW 无法处理的呼叫信令,要转发给H323终端三、呼叫过程MC 决定会议的SCM ,会议模式的选择发生在使用H245信令连接到MC 后。

当能力交换和主从决定后,MC 首先分配一个终端号给新的节点(terminalNumberAssign ),MC 应该通知其他节点关于新的节点加入会议的信息。

新节点可要求一个关于其他节点的列表(terminalListRequest )。

MC 要建立一种公共的通信模式,MC 可以通过发送给节点一个接收能力集列表的方式,强制节点到一个公共模式中。

《H323协议讲解》课件

《H323协议讲解》课件
《H323协议讲解》PPT 课件
H323协议是一种用于实时多媒体通信的协议,广泛应用于视频会议和网络电 话等领域。本课件将详细介绍H323协议的原理和应用。
什么是H323协议
H323协议是一种用于实时通信的通信协议,包括音频、视频和数据传输。
H323协议架构
H323协议采用分层架构,包括应用层、呼叫控制层、媒体层和网络层。
H323协议安全性
了解H323协议的安全性介绍和安全性实现方式,如H.235加密。
H323协议的应用
了解H323协议在视频会议和网络电话等领域的应用,以及其优缺点。
总结
总结H323协议的优缺点、发展历程以及未来发展趋势。
H323协议基本概念
了解H323协议中的一些重要概念和术语,如终端设备和门户。
H323协议分层详解
深入了解H323协议各层的功能和相互关系,包括应用层、呼叫控制层、媒体层和网络层。
H323协议建立连接
详细介绍H323协议建立连接的流程和断开连接的流程。
H323协议中的音频和视频ห้องสมุดไป่ตู้
探索H323协议中媒体传输的音频格式和视频格式,如G.711和H.264。

了解和分析3H323的呼叫过程与音视频编码原理

了解和分析3H323的呼叫过程与音视频编码原理

终端2能力集 建立Audio通道 建立Video通道 建立Other通道
建立Audio通道 建立Video通道 建立Other通道
终端2
11
小结
端点(EndPointer)到端点产生呼叫 GK (Gatekeeper)的作用 呼叫带宽、与能力集的作用
12
(二) H323中MCU设备的作用
6
专网GK呼叫时序及通道
1 ARQ 2 ACF/ARJ 3 Setup 4 Setup 5 ARQ 6 ACF/ARJ 7 Connect 8 Connect 9 H.245 Channel
12 3 8 Endpoint 1
H.245 Control Channel Messages Call Signalling Channel Messages RAS Channel Messages
MCU只做端点信源的转换 呼叫时,MCU不会根据终端能力集,改变呼叫带宽!
TR (Transcoding)
MCU在做端点信源的转换之前,可以实现协议适配、 速率适配(即:可根据终端能力,建立连接)
CP (Continue Presence)
包括TR的所有功能,并可做硬件的图像分屏。
17
终端1
终端2
能力集举例:
终端1能力集
g711Alaw64k - 60
终端2能力集
g722_64k - 60 g728 - 60
建立Audio通道
h261 - 1920000
建立Video通道 建立Audio通道
Bps CIF at 30 fps QCIF at 30 fps
建立Video通道
h263 - 1920000
编码速率为5.3kbits/s和6.3kbit/s的双码率编码方案

基于OpenH323的编解码器插件的研究

基于OpenH323的编解码器插件的研究
3 3是完全符合 H.2 协议 的, 以遵 33 所
要是免费面向所有想从事 V l oP和网络视频传输软 件的开发商使用 , 这个协议库是完全符合 H.2 协 33
议的, 几乎能和任何符合该协议 的软件进行语音和 视频 的通 讯 , 如 Mio f 例 c s t的 N t e n ro e Met g等。 i O eH 2 协议栈 的源码是用 C pn 33 ++ 来完成的, 适用 于 Wi o s 台和 Ln x n w平 d i 平台, u 该协议栈描述的是
码 器 插 件 开发 方 法 的 可 用性 。
关键词 : 3 3 O eH 2 ; H.2 ; pn 3 3 编解码 器; 插件 中图分类号 : I 9 8 T 1 . 文献标识码 : A 文章编号 :0 7—36 {0 00 —0 4 —0 10 2 42 1 ) 1 04 5
引言
收稿 日期 :0 9 6 5 2 0 —0 —0
作者简介 : 王
佩(94 , , 18 一)女 陕西宝鸡人 , 西安邮 电学院通信技术研究所硕士研究生 ;
朱志祥(99 , , 15 一)男 天津人 , 邮电学 院通信技术研究所教授 , 西安 硕士生导师 。
第1 期
王 佩 , : 于 O H3 3的编解码器插件 的研 究 等 基  ̄m 2
符合 H.2 框架协议的终端的结构如图 1 33 所示。 O eH 2 项 目是澳大利亚 的 E u a ne t pn 3 3 q i l c Py ve
Ld 司组 织 开 发 的 , 个 项 目于 19 t公 这 98年 开始 , 到
了 20 年底基本的 H.2 00 33协议框架 已经实现 , 主
近年来 , 随着 网络的不断建设 , 于 I 基 P网络的

H323中系统组件解释

H323中系统组件解释

H323中系统组件解释ACF Admissions ConfirmARJ Admissions RejectARQ Admissions RequestBCF Bandwidth ConfirmBRJ Bandwidth RejectBRQ Bandwidth Requestcall 在H.323节点间的点对点多媒体通讯。

呼叫由呼叫建立流程开始,由呼叫终止流程结束。

call signalling channle 在两个H.323实体间传递呼叫建立和拆除消息的可信信道。

data 与音频,视频,控制不同的信息流,有逻辑数据通道承载。

DCF Disengage ConfirmDRJ Disengage RejectDRQ Disengage Requestendpoint 节点指H.323终端,网关,或MCU。

它能发起呼叫或被呼叫。

由它产生/终止信息流。

gatekeeper GK是网络上的H.323实体,它为H.323终端,网关和MCU 提供地址翻译,控制网络访问。

也可以为终端,网关,和MCU提供其它服务:如带宽管理,定位网关等。

gateway H.323网关是指在PBN上的H.323终端与SCN上的其它终端之间,或另一个H.323网关之间,提供实时双向业务的节点。

GCF Gatekeeper ConfirmGK GatekeeperGRJ Gatekeeper RejectGRQ Gatekeeper RequestGW GatewayH.245 control channel 在两个H.323 节点之间承载H.245控制信息消息的可信信道。

H.245 seeeion 呼叫的一部分,开始于H.245控制信道的建立,终止于H.245 EndSessionCommand的接收,或终止于失败。

不要与呼叫混淆,呼叫由H.225.0的Setup和Release Complete消息描绘。

H.323 Entity H.323实体是H.323组件,包括终端,网关,GK,MC,MP,MCU等。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

OPENH323源码分析-/techOPENH323源码分析LI Chun-lin<li_chunlin@> URL: /tech2004/12V ersion: 0.1摘要本文主要从系统架构和运行过程两个方面对OPENH323源码进行了分析,可以作为OPENH323应用开发人员的一点辅助资料,亦可为网络协议开发人员提供参考。

文中大部分内容根据作者开发过程中的笔记整理而成。

目录1.体系结构分析 (1)1.1传输层 (1)1.1.1概述 (1)1.1.2传输对象的创建和打开 (2)1.1.3接口 (3)1.1.4应用 (4)1.2H.225.0 RAS协议 (6)1.2.1协议数据单元抽象 (6)1.2.2协议处理机实现 (8)1.3H.225.0呼叫信令协议 (16)1.3.1协议数据单元抽象 (16)1.3.2呼叫信令协议实现和呼叫管理 (18)1.3.3H.225.0传输信道 (25)1.3.4呼叫信令协议线程 (26)1.4H.245传输控制协议 (27)1.4.1协议数据单元抽象 (27)1.4.2控制协议的实现 (28)1.4.3协议状态机的简单描述 (32)1.4.4协议数据传输信道 (34)1.4.5H.245控制协议线程 (35)1.5实时传输协议 (35)1.5.1数据报抽象 (35)1.5.2RTP信道 (35)1.5.3发送和接收过程 (36)1.5.4RTP会话的控制 (38)1.6应用程序接口 (40)1.6.1能力及能力集 (40)1.6.2编解码器 (43)1.6.3逻辑信道 (45)1.6.4连接 (47)1.6.5端点 (64)1.6.6类之间的相互关系 (80)2运行期分析 (81)2.1侦听 (81)2.2发现网守并向网守注册 (82)2.3呼叫 (83)2.4逻辑信道的打开及实时数据传输 (85)2.5挂断 (88)附录A 专门词定义 (90)附录B 参考资料 (91)附录C H.323网络结构 (92)1. 体系结构分析1.1 传输层1.1.1 概述传输层主要负责上层协议数据单元的发送和接收,在OPENH323中,它由H323Transport抽象类来描述,其实现分别依赖于子类H323TransportTCP和H323TransportUDP。

其中,H323TransportTCP利用TCP协议实现可靠有序的数据传输,为H.225.0信令协议和H.245控制协议提供协议数据单元发送和接收服务;H323TransportUDP利用UDP协议实现不可靠的数据传输,为H.225.0 RAS协议提供协议数据单元发送和接收服务。

图1-1 H323Transport类结构示意H323Transport类派生于PIndirectChannel类,因而,H323Transport 提供的接口包括两部分:一部分是自身定义的接口,一部分属于继承自PIndirectChannel类的接口,对于H323Transport类的客户而言,可以认为后者是一种低级的接口,尽量不去使用。

1.1.2 传输对象的创建和打开以下给出H323Tranport类及其派生类的构造函数:H323Transport(H323EndPoint & endpoint);H323TransportIP(H323EndPoint & endpoint,PIPSocket::Address binding,WORD remPort);H323TransportUDP(H323EndPoint & endpoint,PIPSocket::Address binding = INADDR_ANY,WORD localPort = 0,WORD remotePort = 0);H323TransportTCP(H323EndPoint & endpoint,PIPSocket::Address binding = INADDR_ANY,BOOL listen = FALSE);所有由H323Transport类派生的具体类在初始化时必须指定它所属的H323EndPoint对象。

此外,对于H323TransportUDP,其实例在初始化时还可以指定本地IP地址、本地端口以及远端端口,它们的缺省值分别为INADDR_ANY、0、1719;对于H323TransportTCP,其实例在初始化时可以设定本地IP并指定是否具备侦听功能,缺省时本地IP采用INADDR_ANY,不具备侦听功能。

H323Transport的父类是PIndirectChannel,这个类利用其它PChannel 对象间接实现读写功能,它的打开依赖于一个硬编码的Open方法,该方法设定实现读写功能的PChannel对象;PIndirectChannel还有了一个可重写的OnOpen保护成员,提供了在Open方法中完成自定义操作的接口,如果设定的PChannel对象是打开的,Open方法会自动调用OnOpen。

H323TransportUDP在对象构造过程中会自动建立一个PUDPSocket对象,并调用PIndirectChannel::Open方法将其作为基本读、写通道,使传输对象可用。

H323TransportTCP在对象构造过程中不会建立PTCPSocket对象,其基本读、写通道为空,传输对象不可用。

要想使一个H323TransportTCP对象可用,必须采用以下三种方式:其一,调用Connect方法,该方法会建立一个PTCPSocket对象,调用PIndirectChannel::Open将其作为基本读写通道,并利用这个PTCPSocket对象向远端发起连接。

其二,调用AcceptControlChannel方法,该方法会建立一个PTCPSocket对象,利用它接受远端连接之后将其设定为自己的基本读写通道。

只有具备侦听功能的传输对象可以使用这种方式。

其三,利用H323Listener的Accept方法获得一个可用的H323TransportTCP对象,该对象有一个已经连到远端的PTCPSocket对象作为基本读写通道。

H323TransportTCP重载了PIndirectChannel的OnOpen方法,用以设定父类H323TransportIP定义的localAddress,localPort,remoteAddress,remotePort成员。

利用以上三种方式打开传输对象的过程中都会调用OnOpen。

1.1.3 接口地址操作virtual H323TransportAddress GetLocalAddress() = 0;virtual H323TransportAddress GetRemoteAddress() = 0;virtual BOOL SetRemoteAddress(const H323TransportAddress & address) = 0;以上三个方法都是纯虚函数,其实现在派生类中完成,它们仅仅提供一个接口。

一般来说,本地传输地址在创建对象时设定,远端传输地址由SetRemoteAddress方法设定。

传输层地址由H323TransportAddress描述,它派生于PString,一个H323TransportAddress对象实质上是一个字符串,但它可以利用GetIpAddress、GetIpAndPort等方法将自己解析为一个PIPSocket::Address 对象和两字节的端口号。

H323TransportAddress的格式为:ip$[ip/hostname]:[port],如“ip$:1720”、“ip$159.226.5.65:1720”、“ip$*:1720”、“ip$159.226.5.65:*”等(对于后二者分别认为IP地址为0和端口为0)。

采用”*”可以初始化对象为ip$0:0。

连接操作virtual BOOL Connect() = 0;BOOL ConnectTo(const H323TransportAddress & address);Connect方法也是一个纯虚函数,它描述了一个接口,实现与远端建立传输层上的连接。

调用Connect方法之前需要首先调用SetRemoteAddress方法设定远端的传输地址。

ConnectTo方法则将SetRemoteAddress方法和Connect方法封装到一起。

这两个接口在H323TransportTCP和H323TransportUDP中有不同的实现。

收发操作virtual BOOL ReadPDU(PBYTEArray & pdu) = 0;virtual BOOL WritePDU(const PBYTEArray & pdu) = 0;纯虚函数ReadPDU提供接口从网络中接收数据报并拷贝到pdu,纯虚函数WritePDU则提供接口将pdu中的数据发送到网络。

这两个接口在H323TransportTCP和H323TransportUDP中有不同的实现,实现过程需要调用父类PIndirectChannel提供的Read、Write方法。

其它通用操作H323EndPoint & GetEndPoint();void AttachThread(PThread * thread);GetEndPoint方法获取所属H323EndPoint对象的引用;AttachThread方法设定使用传输对象的线程。

专用操作以下描述专用操作的5个虚函数:virtual BOOL DiscoverGatekeeper(H323Gatekeeper & gk,H323RasPDU & pdu,const H323TransportAddress & address);BOOL HandleFirstSignallingChannelPDU();virtual H323Transport * CreateControlChannel(H323Connection & connection);virtual BOOL AcceptControlChannel(H323Connection & connection);virtual void StartControlChannel(H323Connection & connection);DiscoverGatekeeper方法对向网守发送GRQ并接收GCF的过程进行了封装,它有3个参数,第一个参数是调用该方法的H323Gatekeeper对象,第二个参数是描述GRQ消息的H323RasPDU对象,最后一个参数是指示网守传输地址的H323TransportAddress对象。

相关文档
最新文档