OPENH323源码分析

OPENH323源码分析-http://www.within.net/tech
1
OPENH323源码分析
LI Chun-lin
URL: http://www.withlin.net/tech
2004/12
Version: 0.1
PDF created with pdfFactory Pro trial version www.pdffactory.com
摘 要
本文主要从系统架构和运行过程两个方面对OPENH323源码进行了分析,
可以作为OPENH323应用开发人员的一点辅助资料,亦可为网络协议开发人员
提供参考.文中大部分内容根据作者开发过程中的笔记整理而成.
PDF created with pdfFactory Pro trial version www.pdffactory.com
OPENH323源码分析-http://www.within.net/tech
0
目录
1. 体系结构分析..............................................................................................1
1.1 传输层............................................................................................1
1.1.1 概述.........................................................................................1
1.1.2 传输对象的创建和打开..........................................................2
1.1.3 接口.........................................................................................3
1.1.4 应用.........................................................................................4
1.2 H.225.0 RAS协议..........................................................................6
1.2.1 协议数据单元抽象..................................................................6
1.2.2 协议处理机实现......................................................................8
1.3 H.225.0呼叫信令协议.................................................................16
1.3.1 协议数据单元抽象................................................................16
1.3.2 呼叫信令协议实现和呼叫管理.............................................18
1.3.3 H.225.0传输信道..................................................................25
1.3.4 呼叫信令协议线程................................................................26
1.4 H.245传输控制协议....................................................................27
1.4.1 协议数据单元抽象................................................................27
1.4.2 控制协议的实现....................................................................28
1.4.3 协议状态机的简单描述........................................................32
1.4.4 协议数据传输信道................................................................34
1.4.5 H.245控制协议线程.............................................................35
1.5 实时传输协议..............................................................................35
1.5.1 数据报抽象............................................................................35
1.5.2 RTP信道...............................................................................35
1.5.3 发送和接收过程....................................................................36
1.5.4 RTP会话的控制....................................................................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
PDF created with pdfFactory Pro trial version www.pdffactory.com
OPENH323源码分析-http://www.within.net/tech
1
1. 体系结构分析
1.1 传输层
1.1.1 概述
传输层主要负责上层协议数据单元的发送和接收,在OPENH323中,它由
H323Transport抽象类来描述,其实现分别依赖于子类H323TransportTCP和
H323TransportUDP.其中,H323TransportTCP利用TCP协议实现可靠有序
的数据传输,为H.225.0信令协议和H.245控制协议提供协议数据单元发送和接
收服务;H323TransportUDP利用UDP协议实现不可靠的数据传输,为H.225.0
RAS协议提供协议数据单元发送和接收服务.
+Read()
+Write()
PIndirectChannel
+Read()
+Write()
PChannel
+ReadPDU()
+WritePDU()
H323Transport
H323TransportIP
+ReadPDU()
+WritePDU()
H323TransportTCP
+ReadPDU()
+WritePDU()
H323TransportUDP
图1-1 H323Transport类结构示意
H323Transport类派生于PIndirectChannel类,因而,H323Transport
提供的接口包括两部分:一部分是自身定义的接口,一部分属于继承自
PDF created with pdfFactory Pro trial version www.pdffactory.com
OPENH323源码分析-http://www.within.net/tech
2
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
PDF created with pdfFactory Pro trial version www.pdffactory.com
OPENH323源码分析-http://www.within.net/tech
3
对象,利用它接受远端连接之后将其设定为自己的基本读写通道.只有具备侦听
功能的传输对象可以使用这种方式.
其三,利用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$isdn.iscas.ac.cn: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中有不同的实
PDF created with pdfFactory Pro trial version www.pdffactory.com
OPENH323源码分析-http://www.within.net/tech
4
现,实现过程需要调用父类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对象.这个方法只在H323TransportUDP中
有实现,实现过程中,原来的基本读写通道会被关闭,然后新建一个
PUDPSocket对象作为新的基本读写通道,完成RAS消息的发送和接收.
HandleFirstSignallingChannelPDU方法从本地的呼叫信令端口读取
Connect消息,并因之建立连接对象,同时启动呼叫信令的协商过程.这个方法
只在H323TransportTCP有实现.
CreateControlChannel方法仅在H323TransportTCP具体类中有实现,该
方法可以创建一个具备侦听功能的H323Transport对象.
AcceptControlChannel方法仅在H323TransportTCP具体类中有实现,对
于已经处于打开状态的对象,该方法立刻返回;对于尚未处于打开状态的对象,
要求其具备侦听功能,从而可以通过接受远端连接的方式设置其基本读写通道,
使其处于打开状态.
StartControlChannel方法启动H.245媒体控制协议的交互线程.
1.1.4 应用
H323Transport类的基本作用是收发数据报,然而,上层类不会直接调用
它的两个方法ReadPDU和WritePDU,而是通过H323TransactionPDU,
H323SignalPDU, H323TransactionPDU 的Read和Write方法间接调用它
们.
PDF created with pdfFactory Pro trial version www.pdffactory.com
OPENH323源码分析-http://www.within.net/tech
5
+Read()
+Write()
H323TransactionPDU
+ReadPDU(out pdu : PBYTEArray)
+WritePDU(in pdu : PBYTEArray)
H323Transport
+Read()
+Write()
H323SignalPDU
+Read()
+Write()
H323ControlPDU
图1-2 H323Tranport类的应用
在OPENH323中,主要有两个上层的类依赖H323Transport.
一个是H323Transactor类(包括其派生类H225_RAS,
H323Gatekeeper,H323GatekeeperListener),它的保护成员transport指向
一个H323Transport对象,主要负责完成RAS数据报的收发以及某些集成化的
操作(如发现网守).
二个是H323Connection类,它有两个作为保护成员的H323Transport
指针signallingChannel和controlChannel,分别负责H.225.0呼叫信令消息和H.245
控制消息的收发.
PDF created with pdfFactory Pro trial version www.pdffactory.com
OPENH323源码分析-http://www.within.net/tech
6
1.2 H.225.0 RAS协议
1.2.1 协议数据单元抽象
+SetTag(in tag : unsigned char, in tagClass : TagClass)
+Encode(out bytes : PASN_Stream)
+Decode(in bytes : PASN_Stream)
PASN_Object
+SetTag(in tag : unsigned char, in tagClass : TagClass)
+Encode(out bytes : PASN_Stream)
+Decode(in bytes : PASN_Stream)
+CreateObject()
#choice : PASN_Object
PASN_Choice
+CreateObject()
+operator H225_GatekeeperRequest &()
+ ...()
H225_RASMessage
+GetPDU() : PASN_Object
+Read(in t : H323Transport)
+Write(in t : H323Transport)
H323TransactionPDU
+GetPDU()
+BuildGatekeeperRequest(in num : signed int)
+ ...()
H323RasPDU
+Encode(out bytes : PASN_Stream)
+Decode(in bytes : PASN_Stream)
H225_GatekeeperRequest
+Encode(out bytes : PASN_Stream)
+Decode(in bytes : PASN_Stream)
PASN_Sequence

图1-3 H.225.0 RAS协议数据单元抽象
OPENH323采用图1-3所示的类系列对H.225.0 RAS协议数据单元进行了抽
象.其中,PASN_Object,PASN_Choice和PASN_Sequence来自PWLIB
库;H225_RASMessage由ASN.1解析程序自动生成;H323TransactionPDU
和H323RasPDU在OPENH323库中定义.
PASN_Object用于描述ASN.1数据结构,它是一个抽象类,SetTag方法设
定数据类型标签,依赖于另一个类PASN_Stream的纯虚函数Encode和Decode
提供面向比特流的编解码接口.
PASN_Choice派生于PASN_Object,用于描述CHOICE类型的ASN.1数
据结构,它也是一个抽象类.choice是它的一个成员,指向一个PASN_Object
的对象.CreateObject是一个纯虚函数,提供建立对象的接口.PASN_Choice
重载SetTag方法,不仅为PASN_Choice对象设定标签,而且还调用
CreateObject方法初始化choice指针,并为choice指向的对象设定标签.
PASN_Choice还实现了Encode和Decode方法,前者通过调用choice->Encode
实现已经设定类型标签的CHOICE数据类型的编码,后者根据比特流设定标签
PDF created with pdfFactory Pro trial version www.pdffactory.com
OPENH323源码分析-http://www.within.net/tech
7
值并利用CreateObject方法初始化choice指针,然后调用choice->Decode实现
CHOICE数据类型的解码.
H225_RASMessage是一个派生自PASN_Choice的具体类,它由ASN.1
语法解释器自动生成,用于描述使用ASN.1定义的H.225.0 RasMessage消息结
构.这个类定义了一个的枚举类型,对应RasMessage的各种备选消息结构;实
现了CreateObject方法,可以依据数据类型标签值来创建相应的具体对象;还
定义了一系列类型强制转换函数,能够使一个H225_RASMessage对象转换为
与RasMessage各备选消息结构对应的对象.这些备选消息包括
GatekeeperRequest,GatekeeperConfirm ,GatekeeperReject等,由ASN.1语法解
析器自动生成的H225_GatekeeperRequest,H225_GatekeeperConfirm,
H225GatekeeperReject等类描述.这些类派生于PASN_Sequence,它们根
据ASN.1的描述定义相应的成员,并重载Encode,Decode方法.
H323TransactionPDU是一个抽象类,它定义了Read和Write方法,可以
从传输层读取数据,并解码到纯虚函数GetPDU以引用形式返回的
PASN_Object对象中,也可以将GetPDU返回的PASN_Object对象编码并发
送到传输层.GetPDU方法则在具体类H323RasPDU中实现.
H323RasPDU派生自H225_RASMessage和H323TransactionPDU,这
个类定义了BuildGatekeeperRequest等成员函数,这些函数首先调用
PChoice::SetTag,然后初始化内建的H225_GatekeeperRequest等对象并将其
以引用的方式返回.
下面两段代码来自OPENH323,给出了采用H323RasPDU对象实现H.225.0
协议数据单元相关行为的过程:
一段代码摘自RAS消息接收线程的入口函数
H323Transactor::HandleTransactions.在这段代码中,首先由
H323Transactor::CreateTransactionPDU方法创建一个H323TranactionPDU对
象,该对象使用H323TransactionPDU定义的接口Read从传输层读取数据,
解码并初始化对象,之后使用H323TransactionPDU定义的GetPDU接口获取
对象,将对象交由H323Transactor::HandleTransaction方法处理,
lastRequest->responseHandled.Signal()通知发送线程,所发送请求的应答已经收
到.
H323TransactionPDU * response = CreateTransactionPDU();
if (response->Read(*transport)) {
consecutiveErrors = 0;
lastRequest = NULL;
if (HandleTransaction(response->GetPDU()))
lastRequest->responseHandled.Signal();
if (lastRequest != NULL)
lastRequest->responseMutex.Signal();
}
二段代码摘自H323Gatekeeper::AdmissionRequest函数,该函数负责发
送H.225.0 AdmissionRequest消息.这段代码中,首先定义一个H323RasPDU
的局部对象pdu,然后调用H323RasPDU::BuildAdmissionRequest方法创建
H225_AdmissionRequest对象,并将该对象以引用的形式返回给另一个局部
变量arq.通过引用arq可以进行一系列AdmissionRequest消息的参数设置,设
PDF created with pdfFactory Pro trial version www.pdffactory.com
OPENH323源码分析-http://www.within.net/tech
8
置完成后,定义一个H323Transactor::Request对象,利用pdu将其初始化,
最后调用H323Gatekeeper::MakeRequest方法发送消息.
H323RasPDU pdu;
H225_AdmissionRequest & arq =
pdu.BuildAdmissionRequest(GetNextSequenceNumber());
arq.m_callType.SetTag(H225_CallType::e_pointToPoint);
arq.m_endpointIdentifier = endpointIdentifier;
……………
Request request(arq.m_requestSeqNum, pdu);
……………
if (!MakeRequest(request)) {
……………
}
1.2.2 协议处理机实现
框架实现
H.225.0 RAS协议是H.323网守与H.323端点之间的请求-应答协议,即端点
向网守发送请求,网守针对某个端点的某个特定请求返回一个相应的响应,整个
过程在不可靠的传输信道上进行.为了保证请求和响应的对应,需要为每个请求
数据报赋一个请求序列号,相应的响应数据报中必须包含该请求序列号,以确认
它与该请求是对应的.
Gatekeeper
Endpoint1
Request
Endpoint2EndpointN
RequestRequestResponse
ResponseResponse
图1-4 H.225.0 RAS交互方式示意
OPENH323定义了一系列类来实现这一过程,它们之间的关系如图1-5所示.
抽象类H323Transactor定义了协议处理机的基本框架,其子类H225_RAS根
据H.225.0 RAS协议将该框架进行了初步的具体化.由它派生而来的
PDF created with pdfFactory Pro trial version www.pdffactory.com
OPENH323源码分析-http://www.within.net/tech
9
H323Gatekeeper类和H323GatekeeperListener类分别实现端点方(客户端)
与网守方(服务器端)的RAS交互功能.
+CreateTransactionPDU()
+HandleTransaction()
+OnSendingPDU()
+WritePDU()
+WriteTo()
+StartChannel()
+StopChannel()
#MakeRequest()
#CheckForResponse()
#HandleTransactions()
#endpoint
#transport
#requests
#responses
#lastRequest
H323Transactor
+CreateTransactionPDU()
+HandleTransaction()
+OnSendingPDU()
+OnReceiveAdmissionConform()
+...()
+OnSendAdmissionConfirm()
+...()
H225_RAS
...
H323GatekeeperH323GatekeeperListener
...
图1-5 H.225.0 RAS协议处理机
以下代码片段是H323Transactor类的定义:
class H323Transactor : public PObject
{
PCLASSINFO(H323Transactor, PObject);
public:
H323Transactor(
H323EndPoint & endpoint,
H323Transport * transport,
WORD localPort,
WORD remotePort
);
……
PDF created with pdfFactory Pro trial version www.pdffactory.com
OPENH323源码分析-http://www.within.net/tech
10
~H323Transactor();
virtual BOOL StartChannel();
virtual void StopChannel();
virtual H323TransactionPDU * CreateTransactionPDU() const = 0;
virtual BOOL HandleTransaction(const PASN_Object & rawPDU) = 0;
virtual void OnSendingPDU(PASN_Object & rawPDU) = 0;
virtual BOOL WritePDU(H323TransactionPDU & pdu );
virtual BOOL WriteTo(
H323TransactionPDU & pdu,
const H323TransportAddressArray & addresses,
BOOL callback = TRUE
);
……
protected:
PDECLARE_NOTIFIER(PThread, H323Transactor, HandleTransactions);
class Request : public PObject { ………… };
class Response : public PString { ………… };
virtual BOOL MakeRequest(Request & request);
BOOL SendCachedResponse(const H323TransactionPDU & pdu );
BOOL CheckForResponse(unsigned, unsigned, const PASN_Choice * = NULL);
……
H323EndPoint & endpoint;
WORD defaultLocalPort;
WORD defaultRemotePort;
H323Transport * transport;
BOOL checkResponseCryptoTokens;
unsigned nextSequenceNumber;
PMutex nextSequenceNumberMutex;
PDictionary requests;
PMutex requestsMutex;
Request * lastRequest;
PMutex pduWriteMutex;
PSortedList responses;
};
在其构造函数中,需要指定所属的H323Endpoint对象,传输层所采用的
H323Transport对象,本地端口号和远端端口号,其中后两个参数用于未设定
H323Transport对象的情况下新建H323Transport对象.
在端点一方,发送请求和接受应答在两个线程中进行,StartChannel方法创
建一个匿名的响应接收线程并将成员transport联结到该线程.StopChannel结束
响应接受线程并删除transport.
保护的成员函数HandleTransactions是响应接收线程的入口函数,这个函数
不断新建H323TransactionPDU对象,利用它从网络读取数据报并交由
PDF created with pdfFactory Pro trial version www.pdffactory.com
OPENH323源码分析-http://www.within.net/tech
11
HandleTransaction处理后再将对象删除.
CreateTransactionPDU和HandleTransaction是两个纯虚函数,前者规定了
一个创建H323TransactionPDU对象的接口,后者是维持协议运行的处理机的
入口,它们的实现均在派生类H225_RAS类中给出.
保护成员CheckForResponse方法由响应接收线程调用,负责确认响应.该
函数从requests表中找出对应当前响应的H323Transactor::Request对象,并
调用H323Transactor::Request::CheckResponse方法填写该请求的响应信息,包
括设置responseResult,rejectReason等成员变量.
保护成员MakeRequest方法负责发起请求.在实现过程中,该方法首先调
用回调函数OnSendingPDU,然后将的request对象置入requests表中,接着调
用Request::Poll方法发起请求并等待响应,最后将request从requests表中删除.
保护成员SendCachedResponse方法负责发送缓存的响应,如果要发送的响
应在responses表中能找到,则调用Response::SendCachedResponse发送响应,
如果找不到,则建一个新的Response对象插入responses表.
OnSendingPDU是一个纯虚函数,在派生类中实现,主要完成数据发送前的
一些预处理.
WriteTo方法完成协议数据单元的发送,它有三个输入参数:一个
H323TransactionPDU的引用,一个H323TransportAddressArray的引用,
以及一个回调标志.如果回调标志设为FALSE,该方法直接调用
H323TransactionPDU::Write发送数据;否则调用WritePDU.WritePDU方法
会首先调用OnSendingPDU回调函数,然后检查responses表并设置其中的
Response对象,最后才调用H323TransactionPDU::Write.一般,端点方发送请
求时设回调标志为FALSE,网守方发送响应时设回调标志为TRUE.
H323Transactor内部定义了两个类:Request和Response.
Request类提供两个重要方法:Poll完成请求的发送并等待响应;
CheckResponse检查响应情况并设定成员变量responseResult和rejectReason.
Response也提供了两个重要方法:SetPDU设置成员replyPDU,
SendCachedResponse将replyPDU交给传输层发送.
H323Transactor类的成员requests是一个表,存放正在等待响应的
Request对象,responses也是一个表,存放缓存的Response对象.
以下代码片段是H225_RAS类的定义:
class H225_RAS : public H323Transactor
{
PCLASSINFO(H225_RAS, H323Transactor);
public:
H225_RAS(H323EndPoint & endpoint,H323Transport * transport);
~H225_RAS();
virtual H323TransactionPDU * CreateTransactionPDU() const;
virtual BOOL HandleTransaction(const PASN_Object & rawPDU);
virtual void OnSendingPDU(PASN_Object & rawPDU);
virtual void OnSendGatekeeperRequest(H323RasPDU&, H225_GatekeeperRequest&);
PDF created with pdfFactory Pro trial version www.pdffactory.com
OPENH323源码分析-http://www.within.net/tech
12
……
virtual BOOL OnReceiveGatekeeperRequest(
const H323RasPDU &,
const H225_GatekeeperRequest &
);
………….
virtual BOOL OnReceiveUnknown(const H323RasPDU & pdu );
………….
protected:
PString gatekeeperIdentifier;
};
H225_RAS类重载父类中定义的纯虚函数CreateTransactionPDU,创建一
个H323RasPDU的对象;重载父类中定义的纯虚函数HandleTransaction,实
现了协议处理机的核心框架;重载了父类中定义的纯虚函数OnSendingPDU,分
请求与响应发送前的处理过程.
以下列出HandleTransaction方法的部分代码:
BOOL H225_RAS::HandleTransaction(const PASN_Object & rawPDU)
{
const H323RasPDU & pdu = (const H323RasPDU &)rawPDU;
switch (pdu.GetTag()) {
case H225_RasMessage::e_gatekeeperRequest :
if (SendCachedResponse(pdu))
return FALSE;
OnReceiveGatekeeperRequest(pdu, pdu);
break;
case H225_RasMessage::e_gatekeeperConfirm :
return OnReceiveGatekeeperConfirm(pdu, pdu);
case H225_RasMessage::e_gatekeeperReject :
return OnReceiveGatekeeperReject(pdu, pdu);
…….
default :
OnReceiveUnknown(pdu);
}
return FALSE;
}
以下列出OnSendingPDU方法的部分代码:
void H225_RAS::OnSendingPDU(PASN_Object & rawPDU)
PDF created with pdfFactory Pro trial version www.pdffactory.com
OPENH323源码分析-http://www.within.net/tech
13
{
H323RasPDU & pdu = (H323RasPDU &)rawPDU;
switch (pdu.GetTag()) {
case H225_RasMessage::e_gatekeeperRequest :
OnSendGatekeeperRequest(pdu, pdu);
break;
case H225_RasMessage::e_gatekeeperConfirm :
OnSendGatekeeperConfirm(pdu, pdu);
break;
……
default :
break;
}
}
此外,H225_RAS类还定义了诸如OnSendGatekeeperRequest等发送回调
函数和诸如OnRecieveGatekeeperRequest等事件处理函数,这些都是虚函数,
在派生类中可以将其重载.下面给出OPENH323中给出的
OnSendGatekeeperRequest函数和OnReceiveGatekeeperRequest函数的缺省实
现:
void H225_RAS::OnSendGatekeeperRequest(H323RasPDU &, H225_GatekeeperRequest
& grq)
{
if (!gatekeeperIdentifier) {
grq.IncludeOptionalField(H225_GatekeeperRequest::e_gatekeeperIdentifier);
grq.m_gatekeeperIdentifier = gatekeeperIdentifier;
}
OnSendGatekeeperRequest(grq);
}
void H225_RAS::OnSendGatekeeperRequest(H225_GatekeeperRequest &)
{
}
BOOL H225_RAS::OnReceiveGatekeeperRequest(const H323RasPDU &, const
H225_GatekeeperRequest & grq)
{
return OnReceiveGatekeeperRequest(grq);
}
BOOL H225_RAS::OnReceiveGatekeeperRequest(const H225_GatekeeperRequest &)
{
return TRUE;
PDF created with pdfFactory Pro trial version www.pdffactory.com
OPENH323源码分析-http://www.within.net/tech
14
}
从上面的代码可以看出,H225_RAS给出了部分发送回调函数和事件处理
函数的缺省实现,派生类可以重载这些缺省实现,也可以重用缺省实现的代码.
通过函数名的过载,每个发送回调函数和事件处理函数的函数名对应两个参数个
数不同的函数,参数个数为2的函数为协议处理机直接调用,包含缺省实现的代
码;参数个数为1的函数被前者调用,它只有一个空函数体,需要在派生类中被
重载.
端点方RAS功能实现(客户)
端点方RAS功能由H323Gatekeeper类实现,它主要完成了以下几个方面
的工作:
一,重载MakeRequest方法,定义MakeRequestWithReregister方法.
后者在因端点没有向网守注册导致发送请求失败的情况下自动通知监控线程重
新注册.成员monitor描述监控线程,其入口函数为
H323Gatekeeper::MonitorMain.
二,定义DiscoverAny,DiscoverByName,DiscoverByAddress,
DiscoverByNameAndAddress等具体的方法,完成网守的发现过程,它们都通过
保护成员函数StartDiscovery实现.此外还定义了保护成员函数
SetGatekeeperRequest,对设置GRQ数据报的过程进行了封装.
三,定义一系列具体的方法:RegistrationRequest,AdmissionRequest,
UnRegistrationRequest,BandWidthRequest,LocationRequest,
DisengageRequest,InfoRequestResponse.这些方法实现RRQ,ARQ,URQ,
BRQ,LRQ,DRQ,IRR消息的设置,发送以及响应结果分析.
四,重载与端点方相关的部分发送回调函数和事件处理函数.
描述传输对象的transport成员在搜寻网守成功后会调用Connect方法与将
网守地址绑为自己的远端地址,之后,H323Gatekeeper启动数据报接收线程,
RAS通道正式建立.因此,除了网守发现过程之外,其它RAS过程中消息的发
送和接收都是在两个不同的线程中进行的:发送线程发送请求并等待响应,响应
到达后分析响应结果;接收线程接收响应,填写响应结果并通知发送线程.
网守方RAS功能的实现(服务器)
网守方RAS功能由H323GatekeeperListener类实现,它主要完成了以下
几个方面的工作:
一,定义DisengageRequest,InfoRequest和UnregistrationRequest三个
方法,完成DRQ,IRQ和URQ数据报的设置和发送.
二,重载部分相关的事件处理函数.
三,定义成员函数OnDiscovery,OnRegistration,OnAdmission,
OnDisengage,OnUnregistration,OnLocation,OnBandWidth和
OnInfoResponse,分别对接收到的GRQ,RRQ,ARQ,DRQ,URQ,LRQ,BRQ
和IRR消息进行甄别并返回一个H323GatekeeperRequest::Response类型的结果.
H323GatekeeperListener事件处理函数的实现依赖于另外几个类,它们之间
PDF created with pdfFactory Pro trial version www.pdffactory.com
OPENH323源码分析-http://www.within.net/tech
15
的相互关系如图1-6所示.
+HandlePDU()
#OnHandlePDU()
+WritePDU()
#request
#reject
#conform
#transactor
H323Transaction
#rasChannel
H323GatekeeperRequest
#OnHandlePDU()
#grq
#gcf
#grj
H323GatekeeperGRQ
#OnHandlePDU()
#rrq
#rcf
#rrj
H323GatekeeperRRQ
#OnHandlePDU()
#urq
#ucf
#urj
H323GatekeeperURQ
H323TransactionServer
H323GatekeeperServer
H323GatekeeperListener
1
0..*
图1-6 实现RAS服务器事件处理的实体
PDF created with pdfFactory Pro trial version www.pdffactory.com
OPENH323源码分析-http://www.within.net/tech
16
1.3 H.225.0呼叫信令协议
1.3.1 协议数据单元抽象
+SetTag(in tag : unsigned int, in tagClass : TagClass)
+Encode(out bytes : PASN_Stream)
+Decode(in bytes : PASN_Stream)
PASN_Object
+Encode(out bytes : PASN_Stream)
+Decode(in bytes : PASN_Stream)
PASN_Sequence
+Encode(out bytes : PASN_Stream)
+Decode(in bytes : PASN_Stream)
+m_protocal_discriminator
+m_h323_uu_pdu
H225_H323_UserInformation
+Read(in trans : H323Transport)
+Write(in trans : H323Transport)
+GetQ931()
+BuildQ931(in conn : H323Connection)
+SetQ931Fields()
+BuildSetup(in conn : H323Connection)
+BuildAlerting(in conn : H323Connection)
+BuildCallProceeding(in conn : H323Connection)
+BuildConnect(in conn : H323Connection)
+BuildFacility(in conn : H323Connection)
+BuildInformation(in conn : H323Connection)
+BuildNotify(in conn : H323Connection)
+BuildProgress(in conn : H323Connection)
+BuildStatus(in conn : H323Connection)
+BuildReleaseComplete(in conn : H323Connection)
+BuildStatusInquiry(in conn : H323Connection)
+BuildSetupAcknowledge(in conn : H323Connection)
#q931pdu
H323SignalPDU
+Encode(out bytes : PASN_Stream)
+Decode(in bytes : PASN_Stream)
+m_h323_message_body
+m_nonStandardData
+m_h4501SupplementaryService
+m_h245Tunneling
+m_h245Control
+m_callLinkage
-m_tunnelledSignallingMessage
-m_provisionalRespToH245Tunneling
-m_stimulusControl
-m_genericData
H225_H323_UU_PDU
+CreateObject()
+operator H225_Setup_UUIE ()
+ ()
H225_H323_UU_PDU_h323_message_body1
1
11
+SetTag(in tag : unsigned int, in tagClass : TagClass)
+Encode(out bytes : PASN_Stream)
+Decode(in bytes : PASN_Stream)
+CreateObject()
#choice
PASN_Choice

+HasIE(in ie : InformationElementCodes)
+SetIE(in ie : InformationElementCodes, in data : PBYTEArray)
+GetIE(in ie : InformationElementCodes)
+Encode(out bytes : PBYTEArray)
+Decode(in bytes : PBYTEArray)
+BuildSetup(in callRef : int)
+BuildAlerting(in callRef : int)
+BuildCallProceeding(in callRef : int)
+BuildConnect(in callRef : int)
+BuildFacility(in callRef : int, in fromDest : bool)
+BuildInformation(in callRef : int, in fromDest : bool)
+BuildNotify(in callRef : int, in fromDest : bool)
+BuildProgress(in callRef : int, in fromDest : bool, in discription : int)
+BuildStatus(in callRef : int, in fromDest : bool)
+BuildReleaseComplete(in callRef : int, in fromDest : bool)
+BuildStatusEnquiry(in callRef : int, in fromDest : bool)
+BuildSetupAcknowledge(in callRef : int, in fromDest : bool)
+SetBearerCapabilities()
+GetBearerCapabilities()
+ ()
#callReference
#fromDestination
#messageType
#protocolDiscriminator
#infomationElements
Q931
11

图1-7 H.225.0 呼叫信令协议数据单元抽象(1)
PDF created with pdfFactory Pro trial version www.pdffactory.com
OPENH323源码分析-http://www.within.net/tech
17
+Encode()
+Decode()
PASN_Sequence
H225_CallProceeding_UUIE
H225_Connect_UUIE
H225_Alerting_UUIE
H225_Information_UUIE
H225_ReleaseComplete_UUIE
H225_Facility_UUIE
H225_Progress_UUIE
H225_Status_UUIE
H225_Notify_UUIE
H225_Setup_UUIE
图1-8 H.225.0 呼叫信令协议数据单元抽象(2)
PDF created with pdfFactory Pro trial version www.pdffactory.com
OPENH323源码分析-http://www.within.net/tech
18
OPENH323中采用图1-7与图1-8所示的类描述在H.225.0呼叫信令信道中
传输的协议数据单元.
H.225.0呼叫信令信道中传输的是Q.931消息,由Q931类描述.这种消息
于ISDN中的用户-网络间信令,它包括固定的头部信息和若干依消息类型不
同而不同的信息要素(IE). 与H.323协议相关的专门信息包含于Q.931消息的
UserUser信息要素中,这是一个由ASN.1描述的数据结构H323-UserInformation,
对应图1-7中的类H225_H323_UserInformation,这个类以及相关的
H225_H323_UU_PDU,H225_H323_UU_PDU_H323Messagebody等均由
ASN.1解析器根据ASN.1文件自动生成.Q931提供若干成员函数对信息要素进
行操作:HasIE判断某信息要素是否存在;GetIE将指定信息要素的内容拷贝到
一个字节数组对象中;SetIE使用一个字节数组对象的内容设置指定信息要素;
高层的方法则通过调用以上函数以更直观的方式读取和设置信息要素,如
GetCallingPartyNumber方法和SetCallingPartyNumber方法分别可以获取和设
置CallingPartyNumber的内容.Q931还有两个成员函数Encode和Decode,前
者执行Q.931消息的编码,将消息的头信息和各信息要素拷贝到一个字节数组对
象中,后者执行Q.931消息的解码,从一个字节数组对象中读取头信息和各信息
要素.此外,Q931类的成员函数BuildAlerting,BuildSetup等主要用于以缺省
的信息初始化各种消息.
然而,程序中真正使用的协议数据单元接口却是由H323SignalPDU类来定
义的, Q931的实例q931pdu只是它的一个保护成员.H323SignalPDU类定义
了Read和Write方法,通过传输对象实现了协议数据与传输网络的交互.Read
方法使用传输对象读取数据将信息解码到q931pdu中,如果存在UserUser信息
要素,还会调用父类的Decode方法解码其中的ASN.1数据;Write方法则先判
断q931pdu是否含有 UserUser信息要素,如有则调用父类的Encode方法将
ASN.1数据编码并添加到q931pdu中(这一过程已由BuildQ931函数封装),然
后把q931pdu编码并利用传输对象送到网络.GetQ931和SetQ931Fields这两个
方法提供访问保护成员q931pdu的接口.H323SignalPDU类也有BuildAlerting,
BuildSetup等成员函数用于实现协议数据单元的初始化,它们首先调用Q931的
同名成员函数初始化q931pdu,然后为成员
m_h323_uu_pdu.m_h323_message_body设置标签,创建相应
H225_Alerting_UUIE或H225_Setup_UUIE等对象,并返回新建对象的引用.
1.3.2 呼叫信令协议实现和呼叫管理
H.225.0呼叫信令协议实现及呼叫管理由H323Connection类完成.
其中,协议的核心部分主要由以下成员函数实现:
发送消息
BOOL WriteSignalPDU(H323SignalPDU & pdu);
virtual CallEndReason SendSignalSetup(
const PString & alias,
const H323TransportAddress & address /// Address of destination
);
PDF created with pdfFactory Pro trial version www.pdffactory.com
OPENH323源码分析-http://www.within.net/tech
19
WriteSignalPDU将呼叫信令消息发送到网络.
SendSignalSetup对主叫发送Setup消息的具体过程进行了封装(如图),参
数alias指定被叫的名称,参数address指定被叫的呼叫信令信道传输地址.这是
一个虚函数,用户可以根据需要将其重载,虽然这种情况并不多见.
Start
Set remote information: name, address etc.
Build Setup message
Have a gatekeeper
Access token needed
Send ARQ and waiting for ACF
Add access token to Setup message
Set signalling channel and connect
Check faststart
Check h245 in setup
Send Setup message
return
图1-9 Setup消息的发送流程
PDF created with pdfFactory Pro trial version www.pdffactory.com
OPENH323源码分析-http://www.within.net/tech
20
消息泵
virtual void HandleSignallingChannel();
这个成员函数实现从呼叫信令信道中读取消息并送交协议处理机处理的循
环.对于主叫,该函数在发送Setup消息之后被调用;对于被叫,该函数在接受
远端的连接并读取第一个呼叫信令消息之后被调用.这也是一个虚函数,可以在
派生类中被重载.
协议处理机
virtual BOOL HandleSignalPDU(H323SignalPDU & pdu);
virtual BOOL OnReceivedSignalSetup(const H323SignalPDU & pdu);
virtual BOOL OnReceivedSignalSetupAck(const H323SignalPDU & pdu);
virtual BOOL OnReceivedSignalInformation(const H323SignalPDU & pdu);
virtual BOOL OnReceivedCallProceeding(const H323SignalPDU & pdu);
virtual BOOL OnReceivedProgress(const H323SignalPDU & pdu);
virtual BOOL OnReceivedAlerting(const H323SignalPDU & pdu);
virtual BOOL OnReceivedSignalConnect(const H323SignalPDU & pdu);
virtual BOOL OnReceivedFacility(const H323SignalPDU & pdu);
virtual BOOL OnReceivedSignalNotify(const H323SignalPDU & pdu);
virtual BOOL OnReceivedSignalStatus(const H323SignalPDU & pdu);
virtual BOOL OnReceivedStatusEnquiry(const H323SignalPDU & pdu);
virtual BOOL OnReceivedReleaseComplete(const H323SignalPDU & pdu);
virtual BOOL OnUnknownSignalPDU(const H323SignalPDU & pdu);
以上一系列函数给出了协议处理机核心部分的缺省实现.其中,
HandleSignalPDU分析输入参数pdu,并根据消息的类型递交相应的消息处理函
数进行具体处理.
OnReceivedSignalSetup实现对Setup消息的缺省响应,该函数通过构造Call
Proceeding,Alerting,Connect等消息,发起ARQ请求以及调用相应的回调函数
对建立连接的握手过程进行控制(如图1-10).
PDF created with pdfFactory Pro trial version www.pdffactory.com
OPENH323源码分析-http://www.within.net/tech
21
Start
Get call information such as Call ID, CID and remote
information such as version ,name and address.
Build Call proceeding message
Send Call proceeding message
(OnSendCallProceeding called)
Build Alerting message
OnIncomingCall called
Has a gatekeeper
Send ARQ and waiting for ACF
Check faststart
Build Connect message
Answer call
(OnAnswerCall called)
return
图1-10 被叫对Setup消息的响应流程
PDF created with pdfFactory Pro trial version www.pdffactory.com
OPENH323源码分析-http://www.within.net/tech
22
OnReceivedCallProceeding,OnReceivedAlerting及OnReceivedProgress
分别实现对Call Proceeding,Alerting和progress消息的缺省响应,这几个函数
首先根据消息内容更新远端信息,然后检查消息中的faststart项和H.245相关信
息,从而决定是否处理以及如何处理faststart项,是否开始以及如何开始H.245
协商.
OnReceivedSignalConnect实现对Connect消息的缺省响应,该函数的流程
如图1-11所示.
Start
Refresh remote information
according to received message
OnOutgoingCall called
Check H4502 state
Check faststart
Check H245 address
Faststart acknowleged
Control channel OK
Stop faststart
return
Start H245 negotiations
Start control channel
Build facility message with
h.245 address information
图1-11 对Connect消息的响应流程
PDF created with pdfFactory Pro trial version www.pdffactory.com
OPENH323源码分析-http://www.within.net/tech
23
OnReceivedReleaseComplete实现对Release Complete消息的缺省响应:结
束呼叫.
OnReceivedFacility实现对Facility消息的缺省响应,包括更新远端信息,
检查faststart,检查H.245地址以及检查call forward信息.
OnRecievedUnknownPDU完成对无法识别的消息的响应,缺省实现为空.
除此之外,对其它消息的响应函数如OnReceivedSignalInformation,
OnReceivedSignalNotify等都没有定义具体的缺省实现.
控制协议的回调函数
virtual BOOL OnIncomingCall(
const H323SignalPDU & setupPDU,
H323SignalPDU & alertingPDU
);
virtual AnswerCallResponse OnAnswerCall(
const PString & callerName,
const H323SignalPDU & setupPDU,
H323SignalPDU & connectPDU
);
virtual BOOL OnAlerting(
const H323SignalPDU & alertingPDU,
const PString & user
);
virtual BOOL OnOutgoingCall(const H323SignalPDU & connectPDU);
virtual BOOL OnSendCallProceeding(H323SignalPDU & callProceedingPDU);
virtual BOOL OnSendReleaseComplete(H323SignalPDU & releaseCompletePDU);
virtual BOOL OnSendSignalSetup(H323SignalPDU & setupPDU);
这些回调函数提供了若干控制协议运行过程的接口.
OnIncomingCall函数在收到Setup消息后被调用,参数setupPDU引用的对
象存储收到的Setup消息数据,参数alertingPDU引用的对象存储待发送的
Alerting消息数据.该函数提供的缺省实现是调用端点对象的OnIncomingCall
方法;
OnAnswerCall函数在响应Setup消息时被调用,其返回值决定响应的方式,
该函数提供的缺省实现为调用端点对象的OnAnswerCall方法;
OnAlerting函数在接收到Alerting消息后被调用,该函数的缺省实现为调用
端点对象的OnAlerting方法;
OnOutgoingCall函数在接收到Connect消息后被调用,该函数的缺省实现
为调用端点对象的OnOutgoingCall方法.
OnSendCallProceeding,OnSendReleaseComplete和OnSendSignalSetup
三个回调函数分别在发送Call Proceeding,ReleaseComplete和Setup消息前被调
用,它们没有具体的缺省实现.
PDF created with pdfFactory Pro trial version www.pdffactory.com
OPENH323源码分析-http://www.within.net/tech
24
CallerCalled
Setup
Call Proceeding
Progress/Facility/Alerting/
Connect
...
OnIncomingCall
OnAnswerCall
OnOutgoingCall/
OnAlerting
OnSendCallproceeding
OnSendSignalSetup
图1-12 回调函数的调用时机
呼叫管理
void AnsweringCall(AnswerCallResponse response );
virtual BOOL ClearCall(CallEndReason reason = EndedByLocalUser );
virtual BOOL ClearCallSynchronous(
PSyncPoint * sync,
CallEndReason reason = EndedByLocalUser /// Reason for call clearing
);
virtual BOOL ForwardCall(const PString & forwardParty);
AnsweringCall方法用于回应接收到的Setup消息,它在发送Call Proceeding
后被调用.根据输入参数的不同有以下几种响应方式:
1) AnswerCallPending:发送Alerting消息.
2) AnswerCallAlertWithMedia:启动H.245控制信道并发送含有H.245控
制信道地址信息的Alerting消息.
3) AnswerCallDeferred:不发送任何消息.
4) AnswerCallDeferredWithMedia:启动H.245控制信道并发送一个含有
H.245控制信道地址信息的Progress消息.
5) AnswerCallDenied:调用ClearCall,结束呼叫.
6) AnswerCallNow:启动H.245控制信道并发送含有H.245控制信道地址
信息的Connect消息.
如果选择采用H.245隧道方式的话,第2)中情况下不发送消息.
PDF created with pdfFactory Pro trial version www.pdffactory.com
OPENH323源码分析-http://www.within.net/tech
25
ClearCall和ClearCallSynchronous用于清除呼叫,它们都是虚函数,缺省
的操作为调用端点对象的同名函数.另一个虚函数ForwardCall实现呼叫转移,
缺省操作为发送一个含有呼叫转移信息的Facility消息.
呼叫监视
void MonitorCallStatus();
virtual void InternalEstablishedConnectionCheck();
BOOL InternalEndSessionCheck(PPER_Stream & strm);
保护成员函数MonitorCallStatus负责监视连接的状态,控制信道没有建立
之前,它在HandleSignallingChannel的消息泵循环中被周期性的调用,控制信
道建立之后,它在HandleControlChannle的消息泵循环中被周期性的调用.该
函数会定期启动H.245环路延迟检测过程检查对端是否仍处于活动状态,同时检
查各媒体信道的空闲时间是否超过指定时间.
保护成员函数InternalEstablishedConnectionCheck在每次处理一个呼叫信
令消息或控制消息后被调用,也会在AnswerCall中被调用,它负责检查H.245
主从确定过程和能力集交换过程是否结束,是否该开启逻辑信道并完成连接的建
立.
保护成员函数InternalEndSessionCheck负责检查H.245控制信息中是否含
有EndSession命令.
1.3.3 H.225.0传输信道
每个H.323呼叫具备一个不可靠的RAS传输信道,一个可靠的呼叫信令传
输信道,一个可靠的H.245控制信息传输信道以及若干逻辑信道.其中,只有呼
叫信令传输信道是必须的,是否建立以及如何建立H.245控制信息传输信道将在
呼叫信令协议协商过程中决定,而逻辑信道的建立又将由H.245控制协议协商来
实现.
在OPENH323中,呼叫信令传输信道由一个H323TransportTCP对象实现,
H323Connection的成员signallingChannel指向该对象.事实上,每个呼叫信令
传输信道就是一个具备可靠,有序,双向传输能力的TCP连接.
PDF created with pdfFactory Pro trial version www.pdffactory.com
OPENH323源码分析-http://www.within.net/tech
26
EndpointEndpoint
EndpointEndpoint
Network Address
Well-known TSAPDynamical TSAPs
Call Signalling Channel(arrow to called)
图1-13 H.323呼叫的呼叫信令传输信道示意
图1-13给出多个端点(Endpoint)之间的呼叫信令传输信道的示意,其中,
每个呼叫信令传输信道对应一个H.323呼叫.从图中可以看出,理论上每个端点
可以有多个呼叫并存,但在通常的终端应用中,端点会对呼叫的数目进行控制,
如已经有一个呼叫存在的情况下不允许发出其它呼叫,而对此时接入的远端呼叫
要进行呼叫转移等.
1.3.4 呼叫信令协议线程
对于主叫方,呼叫信令协议线程由一个H225CallThread对象管理,这个线
程首先向对方发送Setup消息,之后启动消息泵进入循环.
对于被叫方,呼叫信令协议线程由一个H225TransportThread对象管理,
这个线程首先从传输层读取第一个消息,因之建立H323Connection对象,之
后启动消息泵进入循环.
PDF created with pdfFactory Pro trial version www.pdffactory.com
OPENH323源码分析-http://www.within.net/tech
27
1.4 H.245传输控制协议
1.4.1 协议数据单元抽象
+SetTag(in tag : unsigned int, in tagClass : TagClass)
+Encode(out bytes : PASN_Stream)
+Decode(in bytes : PASN_Stream)
PASN_Object
+SetTag(in tag : unsigned int, in tagClass : TagClass)
+Encode(out bytes : PASN_Stream)
+Decode(in bytes : PASN_Stream)
+CreateObject()
#choice : PASN_Object
PASN_Choice
+CreateObject()
+operator H245_TerminalCapabilitySet ()
+ ()
H245_MultimediaSystemControlMessage
...
+BuildTerminalCapabilitySet()
+ ()
H323ControlPDU
...
+Encode(out bytes : PASN_Stream)
+Decode(in bytes : PASN_Stream)
PASN_Sequence
+Encode(out bytes : PASN_Stream)
+Decode(in bytes : PASN_Stream)
H245_TerminalCapabilitySet
图1-14 H.245控制协议数据单元抽象
OPENH323采用图所示的类系列对H.245控制协议数据单元进行了抽象.其
中,类H245_MultimediaSystemControlMessage对应ASN.1语言描述的
MultimediaSystemControlMessage结构,H245_TerminalCapabilitySet等类对应
ASN.1语言描述的TerminalCapabilitySet等结构,它们都由解析器自动生成.
与H323RasPDU,H323SignalPDU类似,H323ControlPDU也定义了若
干以Build起首命名的成员函数,它们负责设置标签,建立并初始化如
H245_TerminalCapabilitySet等类的实例并且将其以引用的方式返回.与
H323RasPDU,H323SignalPDU不同的是,H323ControlPDU没有定义Read,
Write方法,即它没有实现利用传输对象接收和发送数据单元的功能.这是由于
H.245消息的传输有两种方式:采用独立的控制信道和隧道传输,后者是将H.245
消息封装到H.225.0呼叫消息的一个数据域中的.
PDF created with pdfFactory Pro trial version www.pdffactory.com
OPENH323源码分析-http://www.within.net/tech
28
H323Connection::WriteControlPDU实现消息的发送,这个函数根据设置选择采
用独立信道方式还是隧道的方式.
1.4.2 控制协议的实现
H.245控制协议的基本框架在H323Connection类中实现,主要包括消息
的接收和分发,但协议的状态机由H245Negotiator的几个派生类进行了封装,
它们是H245NegLogicalChannels, H245NegMasterSlaveDetermination ,
H245NegRequestMode, H245NegRoundTripDelay 以及
H245NegTerminalCapabilitySet,分别描述逻辑信道管理,主从关系确定,模
式优先请求,环路延迟检测以及能力集交换过程,反过来,H323Connection
类又通过定义若干可重载的回调函数供这些子类调用以实现对协议的控制.
PDF created with pdfFactory Pro trial version www.pdffactory.com
OPENH323源码分析-http://www.within.net/tech
29
+HandleTimeout()
#endpoint
#connection
#replyTimer
#mutex
H245Negotiator
+HandleOpen()
+HandleOpenAck()
+HandleClose()
+HandleCloseAck()
+HandleOpenConfirm()
+HandleReject()
+HandleRequestClose()
+HandleRequestCloseAck()
+HandleRequestCloseRelease()
+HandleRequestCloseReject()
H245NegLogicalChannels
+HandleIncoming()
+HandleReject()
+HandleAck()
+HandleRelease()
H245NegMasterSlaveDetermination
+HandleRequest()
+HandleReject()
+HandleAck()
+HandleRelease()
H245NegRequestMode
+HandleRequest()
+HandleResponse()
H245NegRoundTripDelay
+HandleIncoming()
+HandleReject()
+HandleAck()
+HandleRelease()
H245NegTerminalCapabilitySet
图1-15 H245Negotiator类系
消息泵
消息泵在H323Connection类中实现:
virtual void HandleControlChannel();
virtual void HandleTunnelPDU(H323SignalPDU * txPDU);
virtual BOOL HandleControlData(PPER_Stream & strm);
virtual BOOL HandleControlPDU(const H323ControlPDU & pdu);
PDF created with pdfFactory Pro trial version www.pdffactory.com
OPENH323源码分析-http://www.within.net/tech
30
virtual BOOL OnH245Request(const H323ControlPDU & pdu);
virtual BOOL OnH245Response(const H323ControlPDU & pdu);
virtual BOOL OnH245Command(const H323ControlPDU & pdu);
virtual BOOL OnH245Indication(const H323ControlPDU & pdu);
virtual BOOL OnH245_SendTerminalCapabilitySet(
const H245_SendTerminalCapabilitySet & pdu
);
virtual BOOL OnH245_FlowControlCommand(
const H245_FlowControlCommand & pdu
);
virtual BOOL OnH245_MiscellaneousCommand(
const H245_MiscellaneousCommand & pdu
);
virtual BOOL OnH245_MiscellaneousIndication(
const H245_MiscellaneousIndication & pdu
);
virtual BOOL OnH245_JitterIndication(
const H245_JitterIndication & pdu
);
virtual BOOL OnControlProtocolError(
ControlProtocolErrors errorSource,
const void * errorData = NULL
);
HandleControlChannel方法启动主从确定过程和能力集交换过程并开始从
控制信道中读取消息的循环.读到的消息由HandleControlData处理,该函数首
先对消息进行解码,然后调用HandleControlPDU根据消息的类型把解码后的协
议数据单元分发给OnH245Request,OnH245Response,OnH245command或
OnH245Indication.
OnH245Request和OnH245Response处理请求及响应,具体的处理过程通
过H245Negotiator的各派生类完成.
OnH245command处理控制命令,它会继续把消息分发给
OnH245_SendTerminalCapabilitySet ,OnH245_FlowControlCommand 以及
OnH245_MiscellaneousCommand.其中,第一个函数处理
SendTerminalCapabilitySet命令,给出的缺省实现是启动能力集交换过程;第二
个函数处理流控命令,给出的缺省实现是调用OnLogicalChannelFlowControl
方法;第三个函数处理其它各种端对端命令,给出的缺省实现是将命令递交给指
定的逻辑信道.
OnH245Indication处理提示消息,它把消息分发给OnH245_JitterIndication
和OnH245_MiscellaneousIndication.前者处理抖动提示,调用
OnLogicalChannelJitter方法;后者处理其它端对端提示,直接把提示信息递交
给指定的逻辑信道.
OnControlProtocolError处理错误的协议消息,没有给出缺省实现.
隧道方式下,H.245控制消息作为Q.931 UUIE中的一个数据项在H.225.0
呼叫信令信道中传输,每次处理过一个呼叫消息后,HandleTunnelPDU会被调
PDF created with pdfFactory Pro trial version www.pdffactory.com
OPENH323源码分析-http://www.within.net/tech
31
用,该方法取出控制消息数据交给HandleControlData方法,完成上述控制消息
的处理过程.
协议的控制
virtual void OnSendCapabilitySet(
H245_TerminalCapabilitySet & pdu
);
virtual BOOL OnReceivedCapabilitySet(
const H323Capabilities & remoteCaps,
const H245_MultiplexCapability * muxCap,
H245_TerminalCapabilitySetReject & reject
);
以上两个回调函数控制能力集交换过程.其中,OnSendCapabilitySet在发
送TerminalCapabilitySet消息之前被调用,对于主叫,缺省操作为设置RFC2833
参数,对于被叫,该函数不进行任何操作;OnReceivedCapabilitySet在收到
TerminalCapabilitySet后被调用,该函数根据收到的数据获取对端的复用和编解
码能力.
virtual BOOL OnOpenLogicalChannel(
const H245_OpenLogicalChannel & openPDU,
H245_OpenLogicalChannelAck & ackPDU,
unsigned & errorCode
);
virtual BOOL OnConflictingLogicalChannel(H323Channel & channel);
以上回调函数控制逻辑信道的打开/关闭过程.OnOpenLogicalChannel在接
收到OpenLogicalChannel消息后被调用,给出的缺省实现是禁止快速连接.
OnConflictingLogicalChannel在发生逻辑信道冲突时被调用,通常是本地启动
一个逻辑信道没有成功或者启动逻辑信道的请求遭到具备master身份的对方拒
绝.
virtual BOOL OnRequestModeChange(
const H245_RequestMode & pdu,
H245_RequestModeAck & ack,
H245_RequestModeReject & reject,
PINDEX & selectedMode
);
virtual void OnModeChanged(const H245_ModeDescription & newMode);
virtual void OnAcceptModeChange(const H245_RequestModeAck & pdu);
virtual void OnRefusedModeChange(const H245_RequestModeReject * pdu);
以上回调函数控制模式优先请求过程.OnRequestModeChange在接收到
RequestMode消息后被调用;OnModeChanged在发送RequestModeAck后被调
用;OnAcceptModeChange在接收到RequestModeAck后被调用;
OnRefusedModeChange在接收到RequestModeRefuse后被调用.
PDF created with pdfFactory Pro trial version www.pdffactory.com
OPENH323源码分析-http://www.within.net/tech
32
virtual void OnLogicalChannelFlowControl(
H323Channel * channel,
long bitRateRestriction
);
virtual void OnLogicalChannelJitter(
H323Channel * channel,
DWORD jitter,
int skippedFrameCount,
int additionalBuffer
);
以上两个回调函数分别处理流控命令和抖动提示,它们的缺省实现都是把命
令或提示递交给指定的逻辑信道对象处理.
1.4.3 协议状态机的简单描述
以下对协议状态机的实现作以简单的描述.
能力集交换
+Start()
+Stop()
+HandleIncoming()
+HandleAck()
+HandleReject()
+HandleRelease()
#state
#receivedCapabilities
H245NegTerminalCapabilitySet
+OnSendCapablitySet()
+OnReceivedCapablitySet()
H323Connection
图1-16 能力集交换过程状态机实现
能力集交换过程的状态机由类H245NegTerminalCapabilitySet描述,它
的成员变量state记录本地能力集的发送状态,共有e_Idle,e_InProgress和e_Sent
三个值可取;布尔变量receivedCapabilities指示是否接受远端的能力集.
控制信道建立之后,能力集交换过程在连接的两端同时开始.
Start方法启动交换过程,建立描述本地能力集的TerminalCapabilitySet消息
并调用H323Connection::OnSendCapabilitySet方法,消息发送后本地协商过程
进入e_InProgress状态.
远端接收到TerminalCapabilitySet请求消息后交给HandleIncoming方法,
该方法调用H323Connection::OnReceivedCapabilitySet,并根据其返回值决定发
送TerminalCapabilitySetAck还是TerminalCapabilitySetReject.
本地收到TerminalCapabilitySetAck后进入e_Sent状态,若收到的是
TerminalCapabilitySetReject则进入e_Idle 状态.
如果本地状态为e_Sent且receivedCapabilities为真值,则认为整个能力集交
PDF created with pdfFactory Pro trial version www.pdffactory.com
OPENH323源码分析-http://www.within.net/tech
33
换过程成功完成.
逻辑信道管理
+Open()
+Close()
+HandleOpen()
+HandleOpenAck()
+HandleReject()
+HandleClose()
+HandleCloseAck()
#state
H245NegLogicalChannel
+OnSendingPDU()
+CreateChannel()
H323Capability
+OnSendingPDU()
+OnSendOpenAck()
+OnReceivedAckPDU()
+Open()
+Start()
+CleanUpOnTermination()
H323Channel
+OnOpenLogicalChannel()
+CreateLogicalChannel()
H323Connection
图1-17 逻辑信道管理过程状态机实现
逻辑信道管理过程由类H245NegLogicalChannel描述,成员state描述协
商的状态,可以有e_AwaitingEstablishment,e_Established,e_AwaitingRelease,
e_Released,e_AwaitingConfirmation及e_AwaitingResponse六个取值.
逻辑信道包括单向和双向两种,这里主要介绍单向逻辑信道开启过程.如果
不采用快速连接方式,主从关系确定且能力集交换完成之后连接的两端同时向对
方发出逻辑信道开启请求,通过打开两个逻辑信道实现双向的通信.
Open方法启动逻辑信道开启过程,首先调用H323Capability::OnSendPDU
填写PDU中与能力相关的数据域,接着调用H323Capability::CreateChannel
建立相应的H323Channel对象,再调用H323Channel::OnSendPDU填写PDU
中与信道参数相关的数据域,然后调用H323Channel::Open打开信道,最后利
用H323Connection::WriteControlPDU发送数据报.此时,本地发送方向的逻
辑信道状态为e_AwaitingEstablishment.
远端收到OpenLogicalChannel请求消息后递交给HandleOpen方法,该方法
首先调用H323Connection::OnOpenLogicalChannel,如果该函数返回TRUE,
则利用H323Connection::CreateLogicalChannel建立H323Channel对象并调用
H323Channel::Start启动信道,远端的接收信道状态由e_AwaitingEstablishment
转为e_Established;如果H323Connection::OnOpenLogicalChannel返回FALSE,
则中止协商,发送OpenLogicalChannelReject消息.
本地收到OpenLogicalChannelAck响应消息后递交给HandleOpenAck方法,
该方法调用H323Channel::Start启动信道,本地发送方向的信道状态转为
PDF created with pdfFactory Pro trial version www.pdffactory.com
OPENH323源码分析-http://www.within.net/tech
34
e_Established.如果收到的是OpenLogicalChannelReject消息,则依据当时的状
态调用H323Connection::OnConflictingLogicalChannel,或者直接中止协商.
环路延迟检测
+StartRequest()
+HandleRequest()
+HandleResponse()
+GetRoundTripDelay()
#awaitingResponse
H245NegRoundTripDelay
图1-18 环路延迟检测过程的状态机实现
保护成员awaitingResponse记录状态,它是个布尔变量,表示是否仍在等待
环路延迟的响应.
本地调用StartRequest方法启动环路延迟检测过程,发送一个
RoundTripDelay消息,并将awaitingResponse置为TRUE.
对端收到RoundTripDelay消息后会调用HandleRequest方法,返回一个
RoundTripDelayAck消息.
本地收到RoundTripDelayAck后激活HandleResponse,该方法计算环路延
迟时间,并将awaitingResponse重新设为FALSE,一次环路延迟检测至此结束.
使用GetRoundTripDelay方法可以获取环路延迟的时间.
1.4.4 协议数据传输信道
H.245消息的传输有两种方式:一是采用独立信道,二是采用隧道方式.
如果采用独立信道的传输方式,需要调用
H323Connection::StartControlChannel来建立信道,H323Connection的成员
controlChannel指向控制信道传输对象.
virtual BOOL StartControlChannel();
virtual BOOL StartControlChannel(
const H225_TransportAddress & h245Address H245 address
);
StartControlChannel函数名是过载的,它对应了两个函数.第一个函数没
有参数,它建立一个具备侦听功能的H323TransportTCP对象,并启动控制协
议线程;第二个参数带有一个地址参数,它建立一个普通H323TransportTCP
对象,完成与远端的连接,使传输实体处于打开状态,并启动控制协议线程.
如果采用隧道方式的话,不会建立独立的控制信道,H.245控制消息附加到
H.225.0信令消息的特定数据域中传输.当H.225.0呼叫信令线程处理Q.931消
息后会调用HandleTunnelPDU方法检查是否有隧道方式的H.245信息,如果有
的话会将该信息读出并交由控制协议处理机函数HandleControlData处理,
H323Connection有两个成员H245TunnelRxPDU和H245TunnelTxPDU,分
别指向隧道方式下已经收到和准备发送的含有H.245信息的H323SignelPDU对
PDF created with pdfFactory Pro trial version www.pdffactory.com
OPENH323源码分析-http://www.within.net/tech
35
象.在以隧道方式发送H.245控制消息的时候,需要检查H245TunnelTxPDU是
否为空,如果为空的话则新建一个H323SignalPDU对象,设置为Facility消息,
由该消息承载H.245信息.
1.4.5 H.245控制协议线程
只有在不采用隧道方式的情况下,H.245控制协议才运行于独立的线程.该
程由H245TransportThread描述,其入口函数首先调用
H323TransportTCP::Accept方法,检查传输对象是侦听状态还是打开状态,如
果是侦听状态等待则等待接受远端的连接,如果是打开状态则直接返回.最后调
用H323Connection::HandleControlChannel开始能力集协商和主从协商过程,
并启动从控制信令信道中读取消息并送交协议处理机处理的循环.
1.5 实时传输协议
1.5.1 数据报抽象
RTP协议的数据报分为数据帧和控制帧两种,这两种帧分别由类
RTP_DataFrame和RTP_ControlFrame来描述,它们均派生于PByteArray,
形式上可以看作为字节流.两个类都提供若干方法从字节流中读取特定域的值,
或对字节流中特定域的值进行设定,具体的帧格式在RFC1889中有详细规定.
1.5.2 RTP信道
RTP信道由H323_RTPChannel来描述的,这个类的实例在
H323Connection::CreateRealTimeLogicalChannel方法中建立,建立时要指定其
所属连接,使用的能力,传输方向以及所属RTP会话ID.
H323Channel * H323Connection::CreateRealTimeLogicalChannel(
const H323Capability & capability,
H323Channel::Directions dir,
unsigned sessionID,
const H245_H2250LogicalChannelParameters * param)
{
RTP_Session * session;
if (param != NULL)
session = UseSession(param->m_sessionID, param->m_mediaControlChannel);
else {
H245_TransportAddress addr;
GetControlChannel().SetUpTransportPDU(addr,
H323Transport::UseLocalTSAP);
session = UseSession(sessionID, addr);
}
PDF created with pdfFactory Pro trial version www.pdffactory.com
OPENH323源码分析-http://www.within.net/tech
36
if (session == NULL)
return NULL;
return new H323_RTPChannel(*this, capability, dir, *session);
}
以上给出了H323Connection::CreateRealTimeLogicalChannel实现代码,
在建立H323_RTPChannel对象前首先要调用UseSession获得一个
RTP_Session对象的指针,这个对象可能是新建的,也可能是已经存在的.
UseSession方法是H323Connection的成员,它的输入参数sessionID确定会话
的ID.
1.5.3 发送和接收过程
H323_RTPChannel重载Transmit和Receive方法,实现了RTP信道上数
据发送和接收.RTP信道启动的具体过程为:首先由H323Channel::Start方法
建立H323LogicalChannelThread对象,从而启动逻辑信道发送或接收线程,
H323Channel::Transmit方法或H323Channel::Receive方法在线程的入口函数
中被调用.
PDF created with pdfFactory Pro trial version www.pdffactory.com
OPENH323源码分析-http://www.within.net/tech
37
Start
Get media information from codec such as frame size, frame
rate, frame number per packet, audio or video, payload type
etc.
Read one frame from codec
Audio
Handle silence
Read enough
frames
Write data via
RTP_Session object
Terminating
End
图1-19 RTP发送过程示意
PDF created with pdfFactory Pro trial version www.pdffactory.com
OPENH323源码分析-http://www.within.net/tech
38
Start
Get media information from codec
such as jitter buffer, frame rate etc.
Read data via
RTP_Session object
Write to codec
Terminating
End
图1-20 RTP接收过程示意
1.5.4 RTP会话的控制
RTP会话RTP_Session类抽象,其主要功能包括实现数据帧和控制帧的发
送,接收以及会话过程相关数据的统计,这是个抽象类.在RFC1889中,规定
一对目的传输地址(在IP网络中体现为一个网络地址加两个端口号)标识一个
RTP会话过程(RTP Session),但在OPENH323中,一个RTP_Session可以同
时管理一个发送者和和一个接受者.RTP_Session有两个成员syncSourceIn和
syncSourceOut,它们分别标识接收数据和发送数据的同步源,对于单纯的发送者,
syncSourceIn为0.
PDF created with pdfFactory Pro trial version www.pdffactory.com
OPENH323源码分析-http://www.within.net/tech
39
数据帧和控制帧的发送
virtual BOOL WriteData(RTP_DataFrame & frame) = 0;
virtual BOOL WriteControl(RTP_ControlFrame & frame) = 0;
virtual BOOL SendReport();
virtual SendReceiveStatus OnSendData(RTP_DataFrame & frame);
纯虚函数WriteData提供向网络发送数据帧的接口,具体实现由子类
RTP_UDP提供.
OnSendData在发送数据报文之前被调用,它设置RTP数据帧的序列号和
SSRC,更新RTP会话状态并统计发送数据,如果定时器到时,还要发送控制报
文.
纯虚函数WriteControl提供向网络发送控制帧的接口,具体实现也由子类
RTP_UDP提供.但是一般不直接调用WriteControl来发送RTCP报文,另一
个成员函数SendReport对该过程进行了进一步的封装,这个函数会首先检查定
时器,判断发送SR还是RR,设置SR/RR以及SDES各域,最后调用WriteControl
发送报文.SendReport的执行受定时器控制,无论是发送方还是接收方,都要
求每隔一段时间发送一次控制报文.OPENH323代码中有三处调用SendReport,
分别为OnSendData方法(发送数据之前),OnReceiveData方法(接收到数据
之后)以及被RTP_UDP重载的ReadData方法(定时器到时但没有接收到数据).
数据帧和控制帧的接收
virtual BOOL ReadData(RTP_DataFrame & frame);
BOOL ReadBufferedData(DWORD timestamp, RTP_DataFrame & frame);
virtual SendReceiveStatus OnReceiveData(const RTP_DataFrame & frame);
virtual SendReceiveStatus OnReceiveControl(RTP_ControlFrame & frame);
纯虚函数ReadData提供向网络发送数据帧的接口,具体实现由子类
RTP_UDP提供,该函数只有在接收到数据帧时才返回TRUE,如果接收到控制
帧或什么数据都没有收到,则返回FALSE.
RTP会话接收到数据帧后会调用OnRecieveData,该函数根据接收到的数据
帧更新会话的状态;接收到控制帧后会调用OnReceiveControl,该函数根据接
收到的控制帧生成控制报告.
值得一提的是,在OPENH323中,有两种读取报文的方式,一种是直接调
用ReadData方法,另一种则是通过ReadBufferedData方法.后者仅针对音频
数据,使用了抑制抖动的算法.这种情况下,RTP_Session的jitter成员不是空
值,该成员会启动一个线程将数据帧首先读到缓冲区中,ReadBufferedData方
法从该缓冲区而非传输层读取数据.
会话过程监控
virtual void OnRxSenderReport(const SenderReport & sender,
const ReceiverReportArray & reports);
virtual void OnRxReceiverReport(DWORD src,
PDF created with pdfFactory Pro trial version www.pdffactory.com
OPENH323源码分析-http://www.within.net/tech
40
const ReceiverReportArray & reports);
RTU_UDP重载的ReadData方法接收到控制报文后调用
OnReceiveControl,该方法分析控制报文的内容,依情况填写一份发送情况报告
或一组接受情况报告,并递交给OnRxSenderReport或OnRxRecieverReport函
数,因而,以上两个函数能够获得详尽的RTP会话过程监控信息,如果需要的
话,它们可以将这些信息递交给应用,但在OPENH323中,这两个函数其实没
有进行任何操作.
OPENH323利用RTP_UserData系列类实现应用与RTP会话过程的接口.
+OnTxStatistics(in RTP_Session)
+OnRxStatistics(in RTP_Session)
RTP_UserData
+OnTxStatistics(in RTP_Session)
+OnRxStatistics(in RTP_Session)
H323_RTP_Session
图1-21 RTP会话与应用的接口
RTP_UserData::OnRxStatistics在RTP_Session::OnRecieveData方法中被
调用,RTP_UserData::OnTxStatistics在RTP_Session::OnSendData方法被调用,
它们的缺省实现为调用H323Connection类的OnRTPStatistics方法,由子类
H323_RTP_Session给出,从而使H323Connection能有机会能够获悉RTP
会话的状态.
1.6 应用程序接口
1.6.1 能力及能力集
虚基类H323Capablity规定了描述端点编解码能力的接口,其实现由派生
的具体类完成.需要注意的是,这个类仅仅是编解码能力的描述,并不是编解码
器本身的实现,每个派生自H323Capability的具体类在端点的能力集表中通常
只有一个实例,而相应编解码器的实例却可能不止一个.
以下对H323Capablity类的成员函数做一个简单的分析:
一类函数用于读取和设定能力的基本特征:
virtual MainTypes GetMainType() const = 0;
virtual unsigned GetSubType() const = 0;
virtual PString GetFormatName() const = 0;
virtual unsigned GetDefaultSessionID() const;
virtual unsigned GetTxFramesInPacket() const;
virtual unsigned GetRxFramesInPacket() const;
CapabilityDirection GetCapabilityDirection() const;
PDF created with pdfFactory Pro trial version www.pdffactory.com
OPENH323源码分析-http://www.within.net/tech
41
unsigned GetCapabilityNumber() const;
RTP_DataFrame::PayloadTypes GetPayloadType();
void SetCapabilityNumber(unsigned num);
void SetCapabilityDirection(CapabilityDirection dir);
virtual void SetTxFramesInPacket(unsigned frames);
这些特征包括:
1. 类型:共有音频,视频,数据和用户输入四种,而每种类型又有若干子
类型,这些类型和子类型的划分都是与H.245 规定的各种Capablity结
构相对应的.如音频能力包括g711Alaw64k,g711Alaw56k,
g711Ulaw64k,g711Ulaw56k,g7231等.
2. 能力编号:与H.245 控制协议中规定的CapabilityTableEntryNumber对
应,初始值为0.能力编号在添加能力到能力集或根据收到的
TerminalCapabilitySet消息初始化能力集时设定.
3. 发送或接收的数据包中包含的帧数目.
4. 媒体数据格式:OPENH323中标识媒体格式的一个字符串.
5. 负载类型:在RTP中规定,初始值为非法类型.
6. 传输方向:初始值为未知,根据接收到的TerminalCapabilitySet确定.
7. 缺省会话类型标识:音频,视频和数据分别对应数字1,2,3.
二类为几个回调函数:
virtual BOOL OnSendingPDU(H245_Capability & pdu) const = 0;
virtual BOOL OnSendingPDU(H245_DataType & pdu) const = 0;
virtual BOOL OnReceivedPDU(const H245_Capability & pdu );
virtual BOOL OnReceivedPDU(const H245_DataType & pdu, BOOL receiver
) = 0;
一个函数在发送 TerminalCapabilitySet消息前被调用,用以设定对应
Capablility结构中的各个字段;第二个函数在发送H.245 OpenLogicalChannel消
息前被调用,用以设定DataType字段;第三个函数在收到TerminalCapabilitySet
消息后被调用,根据消息中对应的Capability结构确定自己的参数;第四个函数
在收到OpenLogicalChannel消息后被调用,负责根据消息中的DataType字段确
定自己的参数.
还有两个纯虚函数函数分别用于创建于该能力集对应的H323Channel对象
和H323Codec对象,它们令H323Capability有些抽象工厂的味道.
virtual H323Channel * CreateChannel(
H323Connection & connection,
H323Channel::Directions dir,
unsigned sessionID,
const H245_H2250LogicalChannelParameters * param
) const = 0;
virtual H323Codec * CreateCodec(
H323Codec::Direction direction ) const = 0;
图给出H323Capablility与其派生类之间的层次关系,这一层次基本上和
H.245 规定的Capability结构相对应.具体的编解码能力类可以直接派生自
H323Capability,也可以派生自图中的其它节点(如图中的H323_G711Capability).
派生类需要根据H.245协议中的规定增加对应的参数并实现父类的纯虚函数.
PDF created with pdfFactory Pro trial version www.pdffactory.com
OPENH323源码分析-http://www.within.net/tech
42
H323Capablity
H323RealTimeCapabilityH323DataCapabilityH323UserInputCapability
H323AudioCapabilityH323VideoCapability
H323_G711Capability
图1-22 H323Capability层次结构
类H323Capabilities描述能力集表.这个类中有两个成员table和set,前
者以线性表的形式存储可用的H323Capability实例指针;后者以三维表的形式
表述各种可用能力的关系,其结构与H.245协议中的TerminalCapabilitySet消息
结构相对应:一个存储H323Capability实例指针的线性表对应一个可选能力集
结构(AlternativeCapabilitySet),一个若干上述线性表的组成的数组对应一个同
时能力集结构(simultaneousCapabilities),一个若干上述数组的集合便可以完全
描述一个端点的编解码能力.H323Capabilities的构造函数可以有一个
H323Connection对象和一个描述TerminalCapabilitySet消息的
H245_TerminalCapabilitySet对象作为参数,从而能够根据
TerminalCapabilitySet消息的内容以及连接的本地能力集实现能力集表的初始
化.
PDF created with pdfFactory Pro trial version www.pdffactory.com
OPENH323源码分析-http://www.within.net/tech
43
set
a simultaneous capabilities structurea alternative capbility set
............
............
............
............
cap1
cap2
capn
...
cap1
cap2
capm
...table
simultaneousNum
descriptorNum
图1-23 H323Capablities类示例
H323Capabilities提供成员函数Add在table中添加可用能力;提供成员函
数SetCapability在table中添加可用能力并设置其在set中的位置;提供成员函
数AddAllCapabilities在table中添加指定名字的可用能力并设置它们在set中的
位置;提供成员函数Remove从table和set中删除指定能力;提供若干同名成员
函数FindCapability从table中查找符合条件的H323Capability对象并返回其
指针;提供成员函数Merge合并两个能力集表;提供若干同名函数IsAllowed
判断一个指定能力是否可用或两个指定能力是否同时可用;提供成员函数
BuildPDU构建H.245 TerminalCapabilitySet消息结构.
1.6.2 编解码器
编解码器由抽象类H323Codec描述,该类及其子类的层次关系如图1-24
所示.H323Codec有几个重要的保护成员:logicalChannel为联到编解码器上
的H323Channel指针,指向使用编解码器的逻辑信道对象;rawDataChannel
为联到编解码器上的PChannel指针,指向采集或播放原始媒体数据的设备;
direction为编解码器的方向,取Encoder和Decoder两个值;mediaFormat描述
媒体格式,是一个OpalMediaFormat对象,规定了RTP负载类型,采样率,帧
PDF created with pdfFactory Pro trial version www.pdffactory.com
OPENH323源码分析-http://www.within.net/tech
44
长度,会话类型,帧率等参数.H323Codec的相当一部分成员函数都与这几个
成员函数密切相关:
BOOL AttachLogicalChannel(H323Channel *channel);
virtual BOOL AttachChannel(PChannel * channel,BOOL autoDelete = TRUE);
virtual PChannel* SwapChannel(PChannel* newChannel,BOOL autoDelete = TRUE);
virtual BOOL CloseRawDataChannel();
BOOL ReadRaw(void * data, PINDEX size, PINDEX & length);
BOOL WriteRaw(void * data, PINDEX length);
PChannel *GetRawDataChannel();
Direction GetDirection();
const OpalMediaFormat & GetMediaFormat();
virtual unsigned GetFrameRate() const;
如上,AttachLogicalChannel和AttachChannel分别设置逻辑信道和采集/
播放设备;SwapChannel设定新的采集/播放设备并返回以前的PChannel对象
指针;CloseRawDataChannel关闭采集/播放设备;ReadRaw从采集设备中读取
原始媒体数据;WriteRaw往播放设备中写原始媒体播放数据;
GetRawDataChannel获取描述采集/播放设备的PChannel指针;GetDirection,
GetMediaFormat,GetFrameRate分别获取编解码器方向,媒体格式和帧率等
信息.
#logicalChannel
#rawDataChannel
#direction
#mediaFormat
H323Codec
H323AudioCodeH323VideoCodec
H323FramedAudioCodecH323_H261CodecH323_H263Codec
H323StreamAudioCodec
H323_ALawCodecH323_muLawCodec
图1-24 H323Codec层次示意
PDF created with pdfFactory Pro trial version www.pdffactory.com
OPENH323源码分析-http://www.within.net/tech
45
H323Codec类规定了以下接口描述编解码行为,Open方法打开编解码器,
依赖于参数connection引用的H323Connection对象;Close方法关闭编解码器;
Read方法编码一帧数据,编码后的帧长度在参数length中返回,它不能超过
mediaFormat中设定的长度;Write方法解码一帧数据,参数length给出待解码
数据缓冲的长度,它也不能超过mediaFormat中设定的长度.这四个方法在基类
中都没有实现,需要具体的派生类对其进行重载.
virtual BOOL Open(H323Connection & connection);
virtual void Close() = 0;
virtual BOOL Read(
BYTE * buffer,
unsigned & length,
RTP_DataFrame & rtpFrame
) = 0;
virtual BOOL Write(
const BYTE * buffer,
unsigned length,
const RTP_DataFrame & frame,
unsigned & written
) = 0;
H323Codec类提供了一个AddFilter方法,为编解码器添加回调函数,这
些回调函数在从采集设备读取数据后及往播放设备写数据前被调用.
void AddFilter(const PNotifier & notifier);
H323Codec还有三个成员函数用于处理来自控制信道的命令及提示,没有
给出特定得缺省行为.
virtual void OnFlowControl(ong bitRateRestriction);
virtual void OnMiscellaneousCommand(
const H245_MiscellaneousCommand_type & type
);
virtual void OnMiscellaneousIndication(
const H245_MiscellaneousIndication_type & type
);
1.6.3 逻辑信道
逻辑信道由抽象类H323Channel描述,该类及其子类的层次关系由图1-25
给出.
PDF created with pdfFactory Pro trial version www.pdffactory.com
OPENH323源码分析-http://www.within.net/tech
46
H323Channel
H323UnidirectionalChannelH323BidirectionalChannel
H323RealTimeChannelH323DataChannel
H323_RTPChannel
图1-25 H323Channel层次示意
逻辑信道的属性主要包括:逻辑信道编号,会话类型标识,传输方向,所属
的H.323端点,所属的H.323连接,使用的能力以及使用的编解码器,其中,所
属连接和使用的能力在逻辑信道对象建立时指定.
H323Channel定义了以下与这些属性相关的成员函数:
const H323ChannelNumber & GetNumber() const;
void SetNumber();
virtual unsigned GetSessionID() const;
virtual Directions GetDirection() const = 0;
const H323Capability & GetCapability() const;
H323Codec * GetCodec() const;
H323Channel还有一些成员函数与逻辑信道的状态有关,如IsRunning,
IsPaused,SetPause, Open等.其中,IsRunning判断信道的接收或发送线程
是否启动;IsPaused判断信道是否处于暂停态;SetPause暂停或重启信道;Open
打开逻辑信道.CleanUpOnTermination结束逻辑信道.
Open方法的缺省实现仅调用了H323Connection::OnStartLogicalChannel.
H323_RTP_Channel重载该方法,首先建立H323Codec对象并进行相关设置,
之后才调用H323Connection::OnStartLogicalChannel.
CleanUpOnTermination方法关闭编解码器,结束接收/发送线程,并调用
H323Connection:: OnClosedLogicalChannel.
逻辑信道的主要行为则由H323Channel的三个纯虚成员函数描述,它们都
需要在派生类中实现:
virtual BOOL Start() = 0;
PDF created with pdfFactory Pro trial version www.pdffactory.com
OPENH323源码分析-http://www.within.net/tech
47
virtual void Receive() = 0;
virtual void Transmit() = 0;
其中,Start启动逻辑信道,H323UnidirectionalChannel给出的实现是首
先调用Open方法打开信道,然后启动接收或发送线程;Receive是接收线程的
入口函数,实现数据的接收;Transmit是发送线程的入口函数,实现数据的发
送.
最后,需要说明几个回调函数,它们分为两类,第一类在发送或接收 H.245
逻辑信道请求和应答消息时被调用,主要负责设置消息的各数据域或读取消息中
的信息;第二类在收到H.245命令和提示时被调用,除OnJitterIndication外,
缺省实现都是调用编解码器相应的成员函数.
virtual BOOL OnSendingPDU(H245_OpenLogicalChannel & openPDU) const = 0;
virtual void OnSendOpenAck(
const H245_OpenLogicalChannel & open,
H245_OpenLogicalChannelAck & ack
) const;
virtual BOOL OnReceivedPDU(
const H245_OpenLogicalChannel & pdu,
unsigned & errorCode
);
virtual BOOL OnReceivedAckPDU(const H245_OpenLogicalChannelAck & pdu);
virtual void OnFlowControl(long bitRateRestriction);
virtual void OnMiscellaneousCommand(
const H245_MiscellaneousCommand_type & type
);
virtual void OnMiscellaneousIndication(
const H245_MiscellaneousIndication_type & type
);
virtual void OnJitterIndication(
DWORD jitter,
int skippedFrameCount,
int additionalBuffer
);
1.6.4 连接
H323Connection类描述一个H.323连接,其主要功能包括连接参数的设
置;呼叫信令信道,控制信道以及各种逻辑信道的管理和控制;呼叫信令协议,
控制协议以及各种媒体传输协议的管理和控制.这个类是OPENH323的核心,
作为传输实体和各协议实体的控制中枢,其实例在整个运行过程中会被主线程,
呼叫线程,媒体控制线程,媒体传输线程等多个线程引用.
PDF created with pdfFactory Pro trial version www.pdffactory.com
OPENH323源码分析-http://www.within.net/tech
48
连接参数的设置
A. 相关成员变量定义
PString localPartyName;
PStringList localAliasNames;
PString remoteApplication;
PString remotePartyAddress;
PString remotePartyNumber;
PString remotePartyName;
PString callToken;
unsigned callReference;
OpalGloballyUniqueID callIdentifier;
OpalGloballyUniqueID conferenceIdentifier;
unsigned h225version;
unsigned h245version;
BOOL h245versionSet;
BOOL callAnswered;
int remoteCallWaiting;
B. 相关成员函数定义
H323EndPoint & GetEndPoint();
BOOL HadAnsweredCall();
const PString & GetCallToken();
unsigned GetCallReference();
const OpalGloballyUniqueID & GetCallIdentifier();
const OpalGloballyUniqueID & GetConferenceIdentifier();
const PString & GetLocalPartyName();
void SetLocalPartyName(const PString & name);
const PStringList & GetLocalAliasNames();
const PString & GetRemotePartyName();
const PString & GetRemotePartyNumber();
const PString & GetRemotePartyAddress();
void SetRemotePartyInfo(const H323SignalPDU & pdu);
const PString & GetRemoteApplication();
void SetRemoteApplication(const H225_EndpointType & pdu);
unsigned GetSignallingVersion();
unsigned GetControlVersion();
void SetRemotCallWaiting(const unsigned value);
const int GetRemoteCallWaiting();
localPartyName是一个标识本地端名称的字符串,如果它符合E164规范,
将作为Q.931消息中Calling Party Number 或Called Party Number IE的内容;否
则将作为Q.931消息中 Display IE的内容.其初始值由所属端点指定,利用成员
PDF created with pdfFactory Pro trial version www.pdffactory.com
OPENH323源码分析-http://www.within.net/tech
49
函数SetLocalPartyName可以设定其值,利用成员函数GetLocalPartyName可
以获取其值.
localAliaseNames是一个字符串表,存储若干本地端的别名.它们将作为
Setup-UUIE中sourceAddress域的dialedDigits项或h323_ID项.如果
localPartyName为空或者是符合E164的号码,localAliaseNames中的某个字符串
还会作为Q.931消息中Display IE的内容.其初始值由所属端点指定,利用成员
函数SetLocalPartyName可以设定该表中的字符串,利用成员函数
GetLocalAliasNames可以获取该表.
remoteApplication是一个标识远端应用程序信息的字符串, 其值从接收到的
呼叫消息中获取.相关的成员函数包括GetRemoteApplication 和
SetRemoteApplication.
remotePartyName是一个标识对端名称的字符串,它是一个传输地址或一个
别名.主叫方在调用方法SendSignalSetup时设定该成员,如果它是一个别名,
其值对应Setup-UUIE结构中的destinationAddress域;对于被叫方,该成员的值
从接收到的呼叫消息中获取.相关的成员函数有GetRemotePartyName 和
SetRemotePartyInfo.
remotePartyAddress也是一个字符串,它是一个传输地址或一个别名加一个
传输地址.主叫在调用方法SendSignalSetup时设定该成员,其中传输地址对应
Setup-UUIE结构中的destCallSignalAddress域;对于被叫,该成员的值从接收到
的呼叫消息中获取.相关的成员函数有GetRemotePartyAddress.
remotePartNumber是一个符合 E164规范的对端号码,它对应Q.931消息中
Calling Party Number 或Called Party Number IE的内容.对于主叫方,如果
remotePartyName符合E164规范,remotePartNumber与remotePartyName是一样
的;对于被叫方,该成员的值从接收到的呼叫消息中获取.相关的成员函数有
GetRemotePartyNumber 和SetRemotePartyInfo.
callReference成员对应Q.931消息中的呼叫参考值(CRV),唯一标识一个
Q.931呼叫,由主叫生成,利用成员函数GetCallReference可以获取该值.
callIdentifier成员对应H.225.0协议中的呼叫标志(Call ID),唯一标识一个
H.225.0呼叫,由主叫生成.属于同一个H.225.0呼叫中的所有呼叫信令和RAS
消息都采用同一个呼叫标志.
conferenceIdentifier成员对应H.225.0协议中的会议标志(CID),唯一标识
一个H.323会议,由主叫生成.属于同一个会议的所有呼叫信令和RAS消息都
采用同一个会议标志.
callToken成员是OPENH323标识连接的一个字符串,它包括一个呼叫信令
信道的地址和一个呼叫参考值(CRV).对于主叫,callToken为"ip$localhost/yyyy"
的形式,对于被叫,callToken为"ip$x.x.x.x:pppp/yyyy"的形式,其中x.x.x.x:pppp
为传输地址,yyyy为CRV.callToken在调用AttatchSigalChannel方法时指定.
h225version,h245version标识协议的版本.对于主叫方,这两个值分别为4
和7,对于被叫,这两个值从呼叫消息中获取.使用成员函数GetSignallingVersion
和GetControlVersion可以获得这两个值.
callAnswered标记本地是否是被叫端,初值为FALSE,设定呼叫信令信道时
需要指定其值.利用成员函数HadAnsweredCall可以获取该标记.
PDF created with pdfFactory Pro trial version www.pdffactory.com
OPENH323源码分析-http://www.within.net/tech
50
呼叫信令协议控制
A. 相关成员变量定义
H323Transport * signallingChannel;
H323SignalPDU * alertingPDU;
H323SignalPDU * connectPDU;
enum ConnectionStates {
NoConnectionActive,
AwaitingGatekeeperAdmission,
AwaitingTransportConnect,
AwaitingSignalConnect,
AwaitingLocalAnswer,
HasExecutedSignalConnect,
EstablishedConnection,
ShuttingDownConnection,
NumConnectionStates
} connectionState;
CallEndReason callEndReason;
PTime setupTime;
PTime alertingTime;
PTime connectedTime;
BOOL h245Tunneling;
H323SignalPDU * h245TunnelRxPDU;
H323SignalPDU * h245TunnelTxPDU;
BOOL doH245inSETUP;
BOOL lastPDUWasH245inSETUP;
signallingChannel是一个传输对象的指针,呼叫信令数据报的发送和接收由
该对象完成,初始值为空值.
alertingPDU和connectPDU是两个呼叫信令数据报对象的指针,分别指向
待发送的alerting消息对象和 connect消息对象,初始值均为空值.
connectionState记录连接的状态,初始值为NoConnectionActive.对于主叫
端,发起呼叫时首先将connectionState置为AwaitingGatekeeperAdmission,呼叫
信令信道的传输对象向远端发起连接之前将connectionState置为
AwaitingTransportConnect,连接成功后转为AwaitingSignalConnect,收到Connect
消息后转为HasExecutedSignalConnect,执行逻辑信道打开过程后转为
EstablishedConnection.对于被叫端,如果isConsultationTransfer成员变量为
FALSE,收到Setup消息后会将connectionState置为AwaitingLocalAnswer,发送
Connect消息之后将connectionState置为HasExecutedSignalConnect,执行逻辑信
道打开过程后转为EstablishedConnection.
callEndReason记录呼叫结束的原因.
setupTime,alertTime以及connectedTime是三个时间标记.
B. 相关成员函数定义
PDF created with pdfFactory Pro trial version www.pdffactory.com
OPENH323源码分析-http://www.within.net/tech
51
const H323Transport * GetSignallingChannel();
void AttachSignalChannel(
const PString & token,
H323Transport * channel,
BOOL answeringCall
);
BOOL WriteSignalPDU(H323SignalPDU & pdu);
以上成员函数负责呼叫信令信道的管理.其中AttachSignalChannel设置呼
叫信令信道:参数token设定连接的OPENH323标识,参数channel指定呼叫信
令信道使用的传输对象,参数answeringCall提示呼叫的方向(被叫还是主叫);
GetSignallingChannel获取呼叫信令信道传输对象的指针;WriteSignalPDU利
用传输对象发送数据报.
virtual void HandleSignallingChannel();
virtual BOOL HandleSignalPDU(H323SignalPDU & pdu);
virtual BOOL OnReceivedSignalSetup(const H323SignalPDU & pdu);
virtual BOOL OnReceivedSignalSetupAck(const H323SignalPDU & pdu);
virtual BOOL OnReceivedSignalInformation(const H323SignalPDU & pdu);
virtual BOOL OnReceivedCallProceeding(const H323SignalPDU & pdu);
virtual BOOL OnReceivedProgress(const H323SignalPDU & pdu);
virtual BOOL OnReceivedAlerting(const H323SignalPDU & pdu);
virtual BOOL OnReceivedSignalConnect(const H323SignalPDU & pdu);
virtual BOOL OnReceivedFacility(const H323SignalPDU & pdu);
virtual BOOL OnReceivedSignalNotify();
virtual BOOL OnReceivedSignalStatus(const H323SignalPDU & pdu);
virtual BOOL OnReceivedStatusEnquiry(const H323SignalPDU & pdu);
virtual void OnReceivedReleaseComplete(const H323SignalPDU & pdu);
virtual BOOL OnUnknownSignalPDU(const H323SignalPDU & pdu);
以上成员函数实现H.225.0呼叫信令协议的消息泵和处理机,具体分析参考
1.3.2节.
virtual BOOL OnIncomingCall(
const H323SignalPDU & setupPDU,
H323SignalPDU & alertingPDU
);
virtual AnswerCallResponse OnAnswerCall(
const PString & callerName,
const H323SignalPDU & setupPDU,
H323SignalPDU & connectPDU
);
virtual BOOL OnAlerting(
const H323SignalPDU & alertingPDU,
const PString & user
PDF created with pdfFactory Pro trial version www.pdffactory.com
OPENH323源码分析-http://www.within.net/tech
52
);
virtual BOOL OnOutgoingCall(const H323SignalPDU & connectPDU);
virtual BOOL OnSendSignalSetup(H323SignalPDU & setupPDU);
virtual BOOL OnSendCallProceeding(H323SignalPDU & callProceedingPDU);
virtual BOOL OnSendReleaseComplete(H323SignalPDU & releaseCompletePDU);
virtual BOOL ClearCall(CallEndReason reason = EndedByLocalUser);
virtual BOOL ClearCallSynchronous(
PSyncPoint * sync,
CallEndReason reason = EndedByLocalUser
);
virtual void OnEstablished();
virtual void OnCleared();
virtual BOOL OnSendReleaseComplete(H323SignalPDU & releaseCompletePDU);
以上是控制呼叫信令协议的几个回调函数,根据需要可以重载其缺省实现.
其中第一组由被叫端在呼叫信令协议处理过程中被调用,第二组由主叫端调用,
最后一组主叫端和被叫端都会调用,更多分析可以参考1.3.2节.
virtual CallEndReason SendSignalSetup(
const PString & alias,
const H323TransportAddress & address
);
void AnsweringCall(AnswerCallResponse response);
virtual void CleanUpOnCallEnd();
以上呼叫信令协议的几个控制函数,SendSignalSetup发起呼叫;
AnsweringCall接收呼叫;ClearCall结束呼叫.更多分析参考1.3.2节.
BOOL IsConnected();
BOOL IsEstablished();
PTime GetSetupUpTime();
PTime GetAlertingTime();
PTime GetConnectionStartTime();
PTime GetConnectionEndTime();
unsigned GetQ931Cause() const;
CallEndReason GetCallEndReason();
virtual void SetCallEndReason(CallEndReason reason, PSyncPoint * sync = NULL);
以上成员函数获取当前连接状态,相关时间,呼叫结束原因等信息.
virtual void HandleTunnelPDU(H323SignalPDU * txPDU);
virtual BOOL SendFastStartAcknowledge(
H225_ArrayOf_PASN_OctetString & array
);
virtual BOOL HandleFastStartAcknowledge(
const H225_ArrayOf_PASN_OctetString & array
);
PDF created with pdfFactory Pro trial version www.pdffactory.com
OPENH323源码分析-http://www.within.net/tech
53
以上三个函数实现H.245隧道选项和快速连接选项.HandleTunnelPDU读
取控制协议数据并递交控制协议处理机;被叫端调用SendFastStartAcknowledge
设定faststart域;主叫端调用HandleFastStartAcknowledge处理faststart域.
媒体控制协议的控制
A. 相关成员变量定义
H323Transport * controlChannel;
BOOL transmitterSidePaused;
BOOL earlyStart;
H245NegMasterSlaveDetermination * masterSlaveDeterminationProcedure;
H245NegTerminalCapabilitySet * capabilityExchangeProcedure;
H245NegLogicalChannels * logicalChannels;
H245NegRequestMode * requestModeProcedure;
H245NegRoundTripDelay * roundTripDelayProcedure;
OpalRFC2833 * rfc2833handler;
OpalT120Protocol * t120handler;
OpalT38Protocol * t38handler;
H323Capabilities localCapabilities;
H323Capabilities remoteCapabilities;
SendUserInputModes sendUserInputMode;
BOOL startT120;
PString t38ModeChangeCapabilities;
RTP_SessionManager rtpSessions;
PTimer roundTripDelayTimer;
BOOL endSessionNeeded;
BOOL endSessionSent;
PSyncPoint endSessionReceived;
controlChannel是一个指针,指向媒体控制信道使用的传输对象,初值为空.
roundTripDelayTimer是一个定时器,控制回路延迟估计过程的运行间隔,由
端点设定.
earlyStart指示被叫方是否在发送Connect消息之前启动媒体控制协议,初值
为FALSE.
masterSlaveDeterminationProcedure控制主从确定过程.
capabilityExchangeProcedure控制能力集交换过程.
logicalChannels控制逻辑信道开闭过程.
requestModeProcedure控制模式请求过程.
roundTripDelayProcedure控制回路延迟估计过程.
rfc2833handler, t120handler, t38handler实现附属的传输协议.
此外,几个与能力集交换相关的成员变量:
localCapabilities是本地的能力集,连接初始化时从端点对象获取;
remoteCapabilities是对端能力集,初始化在收到TerminalCapabilitySet请求
后结合本地能力集完成;
PDF created with pdfFactory Pro trial version www.pdffactory.com
OPENH323源码分析-http://www.within.net/tech
54
sendUserInputMode指定UserInputcapability的实现方式,由端点指定;
startT120指示是否启动T120数据传输,缺省初值为TRUE;
t38ModeChangeCapabilities指示T38数据传输的模式;
transmitterSidePaused指示连接一端是否因接收到空的能力集而处于暂停状
态,不能够发送媒体数据.
几个与RTP相关的成员变量:
rtpSessions管理RTP会话对象;
几个与H.245EndSession命令相关的成员变量:
endSessionNeeded指示是否需要发送EndSession命令,启动H.245主从关系
确定过程和能力集交换过程之后该值设为TRUE;
endSessionSent指示EndSession命令是否已经发出;
endSessionReceived是一个同步点,收到EndSession命令后通知其它被阻塞
的线程.
B. 相关成员函数定义
virtual BOOL StartControlChannel();
virtual BOOL StartControlChannel(
const H225_TransportAddress & h245Address
);
const H323Transport & GetControlChannel() const;
BOOL WriteControlPDU(const H323ControlPDU & pdu);
StartControlChannel设置控制信道的传输对象并启动控制信道线程;
GetControlChannel获取控制信道使用的传输对象;WriteControlPDU发送
H.245消息.具体的分析参考1.4.4节.
virtual void HandleControlChannel();
virtual BOOL HandleControlData(PPER_Stream & strm);
virtual BOOL HandleControlPDU(const H323ControlPDU & pdu);
virtual BOOL OnUnknownControlPDU(const H323ControlPDU & pdu);
virtual BOOL OnH245Request(const H323ControlPDU & pdu);
virtual BOOL OnH245Response(const H323ControlPDU & pdu);
virtual BOOL OnH245Command(const H323ControlPDU & pdu);
virtual BOOL OnH245Indication(const H323ControlPDU & pdu);
virtual BOOL OnH245_SendTerminalCapabilitySet(
const H245_SendTerminalCapabilitySet & pdu
);
virtual BOOL OnH245_FlowControlCommand(
const H245_FlowControlCommand & pdu
);
virtual BOOL OnH245_MiscellaneousCommand(
const H245_MiscellaneousCommand & pdu
);
virtual BOOL OnH245_MiscellaneousIndication(
const H245_MiscellaneousIndication & pdu
PDF created with pdfFactory Pro trial version www.pdffactory.com
OPENH323源码分析-http://www.within.net/tech
55
);
virtual BOOL OnH245_JitterIndication(
const H245_JitterIndication & pdu
);
virtual BOOL OnControlProtocolError(
ControlProtocolErrors errorSource,
const void * errorData = NULL
);
以上函数实现H.245控制协议的框架,具体分析参考1.4.2节.
virtual void OnSendCapabilitySet(
H245_TerminalCapabilitySet & pdu
);
virtual BOOL OnReceivedCapabilitySet(
const H323Capabilities & remoteCaps,
const H245_MultiplexCapability * muxCap,
H245_TerminalCapabilitySetReject & reject
);
virtual BOOL OnOpenLogicalChannel(
const H245_OpenLogicalChannel & openPDU,
H245_OpenLogicalChannelAck & ackPDU,
unsigned & errorCode
);
virtual BOOL OnConflictingLogicalChannel(H323Channel & channel);
virtual BOOL OnRequestModeChange(
const H245_RequestMode & pdu,
H245_RequestModeAck & ack,
H245_RequestModeReject & reject,
PINDEX & selectedMode
);
virtual void OnModeChanged(const H245_ModeDescription & newMode);
virtual void OnAcceptModeChange(const H245_RequestModeAck & pdu);
virtual void OnRefusedModeChange(const H245_RequestModeReject * pdu);
以上是控制H.245协议的回调函数,具体分析参考1.4.2节.
const H323Capabilities & GetLocalCapabilities();
const H323Capabilities & GetRemoteCapabilities();
virtual void OnSetLocalCapabilities();
virtual void SendCapabilitySet(BOOL empty);
BOOL StartControlNegotiations();
BOOL IsH245Master() const;
void StartRoundTripDelay();
PTimeInterval GetRoundTripDelay() const;
virtual BOOL RequestModeChange(const PString & newModes);
PDF created with pdfFactory Pro trial version www.pdffactory.com
OPENH323源码分析-http://www.within.net/tech
56
virtual BOOL RequestModeChange(
const H245_ArrayOf_ModeDescription & newModes
);
void SendLogicalChannelMiscCommand(H323Channel &channel,unsigned command);
以上成员函数负责启动各种H.245协商过程以及获取相关的协商信息.
GetLocalCapabilities和GetRemoteCapabilities分别用以获取本地能力集和
对端能力集.
OnSetLocalCapabilities在发送Setup消息前或收到Setup消息后被调用,提
供一个核实,修正本地能力集的机会,没有给出缺省实现.
SendCapabilitySet重新启动能力集协商过程.
StartControlNegotiations方法启动主从关系确定和能力集交换过程,要求控
制协议信道已经建立或者采用了H.245隧道技术;
IsH245Master判断连接的本地端是否被确定为master;
StartRoundTripDelay启动环路延迟检测过程;
GetRoundTripDelay获取环路检测延迟的结果;
RequestModeChange发送改变模式的请求.
SendLogicalChannelMiscCommand向逻辑信道的数据发送端发送H.245控
制命令.
快速连接的实现
快速连接过程受两个成员变量的控制:
FastStartStates fastStartState;
H323LogicalChannelList fastStartChannels;
其中,fastStartState标识快速连接的状态,有FastStartDisabled,
FastStartInitiate,FastStartResponse和FastStartAcknowledged四个取值,分别表示
快速连接没有启用,快速连接发起中,快速连接响应中和快速连接已确认四个状
态.fastStartChannels是一个列表,存放快速连接过程使用的逻辑信道对象.
如果启用了快速连接选项,fastStartState置为FastStartInitiate.主叫方调用
OnSelectLogicalChannels根据本地能力集选择建立H323Channel对象并添加
到fastStartChannels列表中,然后依据这个表填充Setup消息中的faststart项;被
叫收到含有faststart项的Setup消息后,调用CreateLogicalChannel根据本地能
力集判断是否支持对方要求的逻辑信道,如果支持则复制该能力到远端能力集,
建立对应的逻辑信道对象并添加到fastStartChannels列表中, fastStartState转为
FastStartResponse,接着从fastStartChannels列表中选择启动信道并填充faststart
响应项,fastStartState转为FastStartAcknowledged;对于主叫,收到含有faststart
响应的消息后,从fastStartChannels列表中选择启动相应信道,fastStartState转
为FastStartAcknowledged.
相关的成员函数包括:
virtual BOOL SendFastStartAcknowledge(
H225_ArrayOf_PASN_OctetString & array
);
virtual BOOL HandleFastStartAcknowledge(
const H225_ArrayOf_PASN_OctetString & array
PDF created with pdfFactory Pro trial version www.pdffactory.com
OPENH323源码分析-http://www.within.net/tech
57
);
virtual void SelectFastStartChannels(
unsigned sessionID,
BOOL transmitter,
BOOL receiver
);
static void StartFastStartChannel(
H323LogicalChannelList & fastStartChannels,
unsigned sessionID,
H323Channel::Directions direction
)
SendFastStartAcknowledge方法选择建立快速连接使用的逻辑信道并填充
faststart应答项,若fastStartState为FastStartResponse, OnSelectLogicalChannels
会在该函数中被调用,用以从fastStartChannels选择启动逻辑信道,如果
fastStartChannels列表为空,函数将返回FALSE.
HandleFastStartAcknowledge方法在接收到的含有faststart项的H.225.0呼
叫消息时被调用,它根据faststart的内容从fastStartChannels中选择启动相应的
逻辑信道.如果不成功,这个函数返回FALSE,连接会被释放.
SelectFastStartChannels在OnSelectLogicalChannels中被调用,该函数根
据本地能力集选择逻辑信道,并调用OpenLogicalChannel方法.参数sessionID
标识会话类型,transmitter和receiver指定要打开的信道的方向(打开发送信道
或接收信道或两者都打开).
StartFastStartChannel是全局静态函数,在OnSelectLogicalChannels中被
调用,它从fastStartChannels参数指定的列表中选择启动会话类型,方向分别与
参数sessionID,direction相符的信道.
H.245隧道控制
与H.245隧道有关的成员变量包括:
BOOL h245Tunneling;
H323SignalPDU * h245TunnelRxPDU;
H323SignalPDU * h245TunnelTxPDU;
BOOL doH245inSETUP;
BOOL lastPDUWasH245inSETUP;
h245Tunneling指示是否采用隧道方式传输H.245控制消息,初始值由端点
指定.
h245TunnelRxPDU和 h245TunnelTxPDU是两个指向装载H.245控制消息内
容的呼叫信令消息对象的指针.
doH245inSETUP指示是否在Setup消息中装载H.245控制消息内容,初始值
由端点指定.
lastPDUWasH245inSETUP指示上次发送的呼叫信令消息是不是含有H.245
控制消息内容的Setup消息.
PDF created with pdfFactory Pro trial version www.pdffactory.com
OPENH323源码分析-http://www.within.net/tech
58
逻辑信道的控制
相关成员函数定义:
virtual BOOL OpenLogicalChannel(
const H323Capability & capability,
unsigned sessionID,
H323Channel::Directions dir
);
virtual void SelectDefaultLogicalChannel(unsigned sessionID);
virtual void OnSelectLogicalChannels();
virtual BOOL OnOpenLogicalChannel(
const H245_OpenLogicalChannel & openPDU,
H245_OpenLogicalChannelAck & ackPDU,
unsigned & errorCode
);
virtual H323Channel * CreateLogicalChannel(
const H245_OpenLogicalChannel & open,
BOOL startingFast,
unsigned & errorCode
);
virtual H323Channel * CreateRealTimeLogicalChannel(
const H323Capability & capability,
H323Channel::Directions dir,
unsigned sessionID,
const H245_H2250LogicalChannelParameters * param
);
virtual BOOL OnCreateLogicalChannel(
const H323Capability & capability,
H323Channel::Directions dir,
unsigned & errorCode
);
virtual BOOL OnStartLogicalChannel(H323Channel & channel);
virtual void CloseLogicalChannel(unsigned number, BOOL fromRemote);
virtual void CloseLogicalChannelNumber(const H323ChannelNumber & number);
virtual void CloseAllLogicalChannels(BOOL fromRemote);
virtual void OnClosedLogicalChannel(const H323Channel & channel);
virtual BOOL OnClosingLogicalChannel(H323Channel & channel);
H323Channel * GetLogicalChannel(unsigned number, BOOL fromRemote) const;
H323Channel * FindChannel(unsigned sessionId, BOOL fromRemote) const;
OpenLogicalChannel方法打开由参数capability指定能力,参数sessionID
规定会话类型,dir规定方向的逻辑信道.该函数进行的操作会因fastStartState
变量的不同而不同:如果fastStartState为FastStartDisabled,函数启动常规的H.245
逻辑信道开启过程,发送打开单向发送信道的请求;如果fastStartState为
FastStartInitiate,该函数利用capability建立H323Channel对象,并将其添加到
PDF created with pdfFactory Pro trial version www.pdffactory.com
OPENH323源码分析-http://www.within.net/tech
59
fastStartChannels列表中.如果fastStartState为FastStartResponse,该函数返回
FALSE.
SelectDefaultLogicalChannel选择指定会话类型的逻辑信道,该函数从本地
能力集和对端能力集的交集中选择能力,并调用OpenLogicalChannel方法.
OnSelectLogicalChannels完成整个选择打开逻辑信道的过程.该函数的操
作会因fastStartState变量的不同而不同.如果fastStartState为FastStartDisabled,
函数调用SelectDefaultLogicalChannel按照常规方式为各会话类型选择逻辑信
道;如果fastStartState为FastStartInitiate,该函数调用SelectFastStartChannels
按照快速连接模式为各会话类型选择逻辑信道;如果fastStartState为
FastStartResponse,该函数调用StartFastStartChannel从fastStartChannels表中选
择启动逻辑信道.
以上三个函数实现连接的一端主动打开逻辑信道的行为,其中
OpenLogicalChannel最底层,最直观,直接指定了信道的能力,会话类型和方
向;SelectDefaultLogicalChannel做了一些的封装,为指定的会话类型选择信道
(同样功能的函数还有SelectFastStartChannels).OnSelectLogicalChannels抽
象了整个选择打开逻辑信道的过程,位于最顶层.
OnSelectLogicalChannels的调用有四处,SendSignalSetup中一处,
InternalEstablishedConnectionCheck中两处,SendFastStartAcknowledge中有
一处
CreateLogicalChannel方法根据收到的H.245逻辑信道开启请求建立相应
H323Channel对象并返回其指针,如果对方要求的信道本地不支持,函数返回
一个空值.该函数在收到逻辑信道开启请求后被调用.
OnCreateLogicalChannel在CreateLogicalChannel中被调用,检查使用的
能力.
CloseLogicalChannel和CloseLogicalChannelNumber关闭指定逻辑信道.
CloseAllLogicalChannels关闭所有逻辑信道.它们都会启动H.245逻辑信道关闭
过程.
GetLogicalChannel从已经打开的逻辑信道中获取指定信道号的
H323Channel对象.
FindChannel从已经打开的逻辑信道中获取指定会话类型的H323Channel
对象.
还有几个重要的回调函数.
CreateRealTimeLogicalChannel在H323RealTimeCapability的成员函数
CreateChannel中被调用.给出的缺省实现是利用UseSession方法获取一个
RTP_Session指针并依据该指针指向的RTP_Session对象建立一个
H323_RTPChannel对象.
OnStartLogicalChannel在H323Channel::Open中被调用,给出的缺省实现
是为编解码器增加处理DTMF信号的回调函数.
OnClosedLogicalChannel在H323Channel::CleanUpOnTermination中被调
用,给出的缺省实现是调用所属端点的对应方法.
OnClosingLogicalChannel在收到逻辑信道关闭请求后被调用,没有给出缺
省实现.
PDF created with pdfFactory Pro trial version www.pdffactory.com
OPENH323源码分析-http://www.within.net/tech
60
用户输入数据发送模式的设定和实现
相关成员函数定义:
void SetSendUserInputMode(SendUserInputModes mode);
SendUserInputModes GetSendUserInputMode();
SendUserInputModes GetRealSendUserInputMode() const;
virtual void SendUserInput(const PString & value);
virtual void SendUserInputTone(
char tone,
unsigned duration = 0,
unsigned logicalChannel = 0,
unsigned rtpTimestamp = 0
);
virtual void SendUserInputIndicationQ931(const PString & value);
virtual void SendUserInputIndicationString(const PString & value);
virtual void SendUserInputIndicationTone(
char tone,
unsigned duration = 0,
unsigned logicalChannel = 0,
unsigned rtpTimestamp = 0
);
virtual void SendUserInputIndication(const H245_UserInputIndication & pdu);
void SendUserInputHookFlash(int duration = 500);
virtual void OnUserInputString(const PString & value);
virtual void OnUserInputTone(
char tone,
unsigned duration,
unsigned logicalChannel,
unsigned rtpTimestamp
);
OPENH323支持四种UserInput信息的传送方式:在Q.931消息的KeyPad IE
中传输,在H.245 UserInputIndication消息中以字符形式传输,在H.245
UserInputIndication消息中以音调形式传输,利用RTP信道(RFC2833).
SetSendUserInputMode设定UserInput信息传输方式,GetSendUserInputMode
获取UserInput信息传输方式,GetRealSendUserInputMode获取实际使用的
UserInput信息传输方式.
SendUserInput发送字符串描述的UserInput信息,参数value为要发送的字
符串.SendUserInputTone发送DTMF描述的UserInput信息,参数tone,duration
描述要发送的DTMF信息.这两个函数都会调用SendUserInputIndicationQ931
方法,SendUserInputIndicationString方法,SendUserInputIndicationTone方法
或利用rfc2833handler实现不同发生方式下的发送.
此外还有一个成员函数SendUserInputIndication可直接发送参数pdu指定
的H.245 UserInputIndication消息.
SendUserInputHookFlash发送hookflash信息,该函数调用
PDF created with pdfFactory Pro trial version www.pdffactory.com
OPENH323源码分析-http://www.within.net/tech
61
SendUserInputTone完成实际操作.
回调函数OnUserInputString 和OnUserInputTone在接收到UserInput信息
后被调用,它们的缺省实现是将收到的信息交由连接所属端点处理.
连接对象的管理
相关的成员函数有四个:
BOOL Lock();
int TryLock();
void Unlock();
virtual void CleanUpOnCallEnd();
Lock方法和Unlock方法分别用于为H323Connection对象加锁和解锁.为了
避免多个线程同时访问一个H323Connection对象时造成冲突,每次访问开始
时必须调用Lock方法为对象加锁,访问结束后给对象解锁.比如,H.225呼叫
信令协议线程在调用H323Connection::HandleSignalPDU方法处理呼叫消息之
前首先需要给连接对象加锁,处理结束需要给连接对象解锁;H.245控制协议线
程在调用H323Connection::HandleControlData方法处理控制消息之前也需要给
连接对象加锁,处理之后给连接对象解锁.(AnsweringCall中调用Lock不会造
成死锁 )
CleanUpOnCallEnd在呼叫结束时被调用,负责清理工作,包括结束各逻辑
信道,等待EndSession消息,向网守发送DRQ等.
RTP会话的控制
相关的成员函数有:
void SetAudioJitterDelay(unsigned minDelay, unsigned maxDelay);
unsigned GetMaxAudioJitterDelay();
unsigned GetMinAudioJitterDelay();
unsigned GetRemoteMaxAudioDelayJitter();
virtual RTP_Session * UseSession(
unsigned sessionID,
const H245_TransportAddress & pdu
);
virtual RTP_Session * GetSession(unsigned sessionID) const;
virtual H323_RTP_Session * GetSessionCallbacks(unsigned sessionID) const;
virtual PString GetSessionCodecNames(unsigned sessionID) const;
virtual void OnRTPStatistics(const RTP_Session & session) const;
SetAudioJitterDelay设置音频抖动延迟值,利用GetMaxAudioJitterDelay
和GetMinAudioJitterDelay可以获取这两个值,它们是设定RTP抖动缓冲长度
的依据.
GetRemoteMaxAudioDelayJitter可以获取对端设置的音频抖动延迟最大
值.
PDF created with pdfFactory Pro trial version www.pdffactory.com
OPENH323源码分析-http://www.within.net/tech
62
UseSession方法根据参数sessionID指定的会话类型返回对应RTP_Session
对象的指针.该方法检查指定会话类型的RTP_Session对象是否存在,如果不
存在则新建一个RTP_UDP对象,并设置其userData成员.
GetSession方法同样返回由参数sessionID指定会话类型的RTP_Session
对象指针,但如果对象不存在即返回空值.
GetSessionCallbacks方法返回由参数sessionID指定会话类型的
RTP_Session对象的userdata成员.
GetSessionCodecNames方法返回参数sessionID指定会话类型的RTP会话
使用的编解码器名称.
OnRTPStatistics是一个回调函数,被H323_RTP_Session::OnTxStatistics
和H323_RTP_Session::OnRxStatistics调用,参数session为RTP会话对象.给
的该函数的缺省实现为调用所属端点对象的同名函数.
与网守通信的控制
H323Connection定义了若干与网守控制相关的成员变量.
unsigned uuiesRequested;
PString gkAccessTokenOID;
PBYTEArray gkAccessTokenData;
BOOL addAccessTokenToSetup;
BOOL mustSendDRQ;
BOOL gatekeeperRouted;
UuiesRequested指示需要将哪些H.225.0 UUIE发送给网守,它的值在收到
ACF消息后根据ACF的uuiesRequested域确定.成员函数SetUUIEsRequested
设定该变量,成员函数GetUUIEsRequested获取该变量.
gkAccessTokenOID是接入令牌号,初值由所属端点对象决定.
SetGkAccessTokenOID和GetGkAccessTokenOID方法分别用于设定和获取该
值.gkAccessTokenData是令牌内容,从网守发送来的ACF消息中获取.
GetGkAccessTokenData方法可以获取该值.如果addAccessTokenToSetup变量
为TRUE,发送的Setup消息中将加入gkAccessTokenData变量的内容.这是一
种代替别名和传输地址控制访问权限的方法.
gatekeeperRouted指示传输信令是否经由网守发送,由网守在ACF消息中指
定,IsGatekeeperRouted方法可以查询该参数.
GetAdmissionRequestAuthentication方法提供了一个在发送ARQ消息时临
时改变鉴别机制的接口,没有给出缺省实现.参数arq为带发送的ARQ消息,
参数authenticators为新指定的鉴别机制对象.
void SetUUIEsRequested(unsigned mask);
unsigned GetUUIEsRequested();
void SetGkAccessTokenOID(const PString & oid);
const PBYTEArray & GetGkAccessTokenData();
const PString & GetGkAccessTokenOID();
virtual BOOL GetAdmissionRequestAuthentication(
const H225_AdmissionRequest & arq,
H235Authenticators & authenticators
PDF created with pdfFactory Pro trial version www.pdffactory.com
OPENH323源码分析-http://www.within.net/tech
63
);
BOOL IsGatekeeperRouted();
其它
virtual OpalT120Protocol * CreateT120ProtocolHandler();
virtual OpalT38Protocol * CreateT38ProtocolHandler();
virtual BOOL OpenAudioChannel(
BOOL isEncoding,
unsigned bufferSize,
H323AudioCodec & codec
);
virtual BOOL OpenVideoChannel(
BOOL isEncoding, /// Direction of data flow
H323VideoCodec & codec /// codec doing the opening
);
void SetEnforcedDurationLimit(unsigned seconds);
unsigned GetDistinctiveRing() const { return distinctiveRing; }
void SetDistinctiveRing(unsigned pattern) { distinctiveRing = pattern&7; }
void ConsultationTransfer(const PString & primaryCallToken);
virtual void HandleTransferCall(
const PString & token,
const PString & identity
);
void TransferCall(
const PString & token,
const PString & callIdentity = PString::Empty()
);
virtual BOOL ForwardCall(const PString & forwardParty);
成员函数CreateT120ProtocolHandler和 CreateT38ProtocolHandler是两
个工厂方法,分别创建用于实现T120协议和T38协议的OpalT120Protocol和
OpalT38Protocol对象.
成员函数OpenAudioChannel和OpenVideoChannel用于打开编解码器对象
使用的音视频设备,分别为H323AudioCodec和H323VideoCodec的Open
方法调用.这两个函数的缺省操作都是调用所属端点的同名成员函数.
成员函数SetEnforcedDurationLimit设定强制通话时间,通话时间超出所设
值后呼叫自动挂断.
GetDistinctiveRing返回成员变量distinctiveRing的值,利用该方法可以获取
收到的Q.931消息中携带的振铃信息.
SetDistinctiveRing设置成员变量distinctiveRing的值,在OnSendSignalSetup
回调函数中调用该方法可以设定要发送的Q.931消息中携带的振铃信息.
ConsultationTransfer方法实现协商呼叫转移,即通过呼叫转移是第一个呼
叫的对端与第二个呼叫的被叫连接,参数primaryCallToken标识第一个呼叫,该
PDF created with pdfFactory Pro trial version www.pdffactory.com
OPENH323源码分析-http://www.within.net/tech
64
函数借助h4502handler成员实现这一功能.
HandleTransferCall
TransferCall
ForwardCall由被叫调用实现呼叫前转功能,即向主叫发送一个呼叫前转的
Facility消息,希望将呼叫转到由参数forwardPart指定的目的地.该函数一般在
被叫收到Setup消息但不愿意或无法与主叫建立连接时被调用.
1.6.5 端点
端点由H323Endpoint类描述,这个类实现侦听线程的启动,RAS过程管
理,本地能力的设定以及连接的建立和控制等诸多H.323端点行为.
成员变量
定义:
PStringList localAliasNames;
PString soundChannelPlayDevice;
PString soundChannelRecordDevice;
PString videoChannelPlayDevice;
PString videoChannelRecordDevice;
BOOL autoStartReceiveVideo;
BOOL autoStartTransmitVideo;
BOOL autoStartReceiveFax;
BOOL autoStartTransmitFax;
BOOL autoCallForward;
BOOL disableFastStart;
BOOL disableH245Tunneling;
BOOL disableH245inSetup;
BOOL disableDetectInBandDTMF;
BOOL canDisplayAmountString;
BOOL canEnforceDurationLimit;
unsigned callIntrusionProtectionLevel;
H323AudioCodec::SilenceDetectionMode defaultSilenceDetection;
H323Connection::SendUserInputModes defaultSendUserInputMode;
PString ilsServer;
unsigned soundChannelBuffers;
BYTE rtpIpTypeofService;
PTimeInterval signallingChannelCallTimeout;
PTimeInterval controlChannelStartTimeout;
PTimeInterval endSessionTimeout;
PTimeInterval masterSlaveDeterminationTimeout;
unsigned masterSlaveDeterminationRetries;
PTimeInterval capabilityExchangeTimeout;
PTimeInterval logicalChannelTimeout;
PDF created with pdfFactory Pro trial version www.pdffactory.com
OPENH323源码分析-http://www.within.net/tech
65
PTimeInterval requestModeTimeout;
PTimeInterval roundTripDelayTimeout;
PTimeInterval roundTripDelayRate;
PTimeInterval noMediaTimeout;
PTimeInterval gatekeeperRequestTimeout;
unsigned gatekeeperRequestRetries;
PTimeInterval rasRequestTimeout;
unsigned rasRequestRetries;
PTimeInterval registrationTimeToLive;
PString gkAccessTokenOID;
unsigned minAudioJitterDelay;
unsigned maxAudioJitterDelay;
unsigned initialBandwidth; // in 100s of bits/sev
BOOL clearCallOnRoundTripFail;
struct PortInfo {
void Set(unsigned base, unsigned max, unsigned range, unsigned dflt);
WORD GetNext(unsigned increment);
PMutex mutex;
WORD base;
WORD max;
WORD current;
} tcpPorts, udpPorts, rtpIpPorts;
PSTUNClient * stun;
BYTE t35CountryCode;
BYTE t35Extension;
WORD manufacturerCode;
TerminalTypes terminalType;
PTimeInterval callTransferT1;
PTimeInterval callTransferT2;
PTimeInterval callTransferT3;
PTimeInterval callTransferT4;
PTimeInterval callIntrusionT1;
PTimeInterval callIntrusionT2;
PTimeInterval callIntrusionT3;
PTimeInterval callIntrusionT4;
PTimeInterval callIntrusionT5;
PTimeInterval callIntrusionT6;
PINDEX cleanerThreadStackSize;
PINDEX listenerThreadStackSize;
PINDEX signallingThreadStackSize;
PINDEX controlThreadStackSize;
PINDEX logicalThreadStackSize;
PINDEX rasThreadStackSize;
PINDEX jitterThreadStackSize;
PDF created with pdfFactory Pro trial version www.pdffactory.com
OPENH323源码分析-http://www.within.net/tech
66
H323ListenerList listeners;
H323Capabilities capabilities;
H323Gatekeeper * gatekeeper;
PString gatekeeperPassword;
H323ConnectionDict connectionsActive;
H323CallIdentityDict secondaryConenctionsActive;
PMutex connectionsMutex;
PMutex noMediaMutex;
PStringSet connectionsToBeCleaned;
H323ConnectionsCleaner * connectionsCleaner;
PSyncPoint connectionsAreCleaned;
一部分(用户常用的设置变量):
localAliasNames是一个字符串列表,存储用户别名,对象初始化时指定为进
程使用者的用户名或进程名.
soundChannelPlayDevice和soundChannelRecordDevice分别存储声音播放设
备和采样设备的名称,对象初始化时从系统获取.
videoChannelPlayDevice和videoChannelRecordDevic分别存储视频播放设备
和采样设备的名称,对象初始化时没有设定这两个变量.
autoStartReceiveVideo和autoStartTransmitVideo是两个视频传输设置变量,
决定连接建立后是否自动开启视频传输信道,对象初始化时均设为TRUE.
autoStartReceiveFax 和autoStartTransmitFax是两个数据传输设置变量,决
定连接建立后是否自动开启数据传输信道,对象初始化时均设为FALSE.
autoCallForward是自动呼叫前转控制,对象初始化时设为TRUE.
disableFastStart控制是否禁止快速连接,对象初始化时设为FALSE.
disableH245Tunneling控制是否禁止H.245隧道,对象初始化时设为FALSE.
disableH245inSetup控制是否禁止在Setup消息中装载H.245控制消息,对
象初始化时设为FALSE.
disableDetectInBandDTMF控制是否禁止在RTP信道中检测DTMF信号,对
象初始化时设为FALSE.
canDisplayAmountString控制是否显示话费,对象初始化时设为FALSE.
canEnforceDurationLimit控制是否限制通话时间,对象初始化时设为TRUE.
callIntrusionProtectionLevel控制呼叫插入保护级别,对象初始化时设为3.
defaultSilenceDetection控制静音检测模式,对象初始化时设为自适应检测.
defaultSendUserInputMode控制用户输入发送模式,对象初始化时设为以字
符串形式在H.245提示消息中传输.
ilsServer是一个字符串,存储ILS服务器名称.
二部分(用户不常用的设置变量,一般保留缺省值):
soundChannelBuffers为声音通道缓冲的个数.
rtpIpTypeofService为RTP数据所在IP报的TOS.
signallingChannelCallTimeout为呼叫信令信道的超时值,缺省为1分钟.
controlChannelStartTimeout为媒体控制信道的超时值,缺省为2分钟.
endSessionTimeout为EndSession的超时值,缺省为10秒.
masterSlaveDeterminationTimeout为主从关系确定过程的超时值,缺省为30
秒.
PDF created with pdfFactory Pro trial version www.pdffactory.com
OPENH323源码分析-http://www.within.net/tech
67
masterSlaveDeterminationRetries为主从确定过程的尝试次数,缺省为10.
capabilityExchangeTimeout为能力集交换过程的超时值,缺省为30秒.
logicalChannelTimeout为逻辑信道打开和结束过程的超时值,缺省为30秒.
requestModeTimeout为模式请求过程的超时值,缺省为30秒.
roundTripDelayTimeout为回路延迟测量过程的超时值,缺省为10秒.
roundTripDelayRate为回路延迟测量过程的间隔,缺省为1分钟.
noMediaTimeout为逻辑信道空闲的最大值,当逻辑信道中无媒体传输的时间
超过该值时结束连接,该值在对象初始化时设为5分钟.
gatekeeperRequestTimeout和gatekeeperRequestRetries 分别为GRQ的超时值
和尝试次数,缺省为5秒钟和两次.
rasRequestTimeout和rasRequestRetries分别为其他RAS消息的超时值和尝
试次数,缺省为3秒钟和两次.
registrationTimeToLive,用户在网守上的注册有效实现,缺省为0.
gkAccessTokenOID为发送给网守的接入令牌号,缺省为空.
minAudioJitterDelay 和maxAudioJitterDelay分别为音频抖动延迟的最小值
和最大值,对象初始化时设为50毫秒和250毫秒.
initialBandwidth为初始带宽,缺省为100000.
clearCallOnRoundTripFail控制是否在H.245回路延迟检测过程失败后结束
连接,对象初始化时设为FALSE.
t35CountryCode ,t35Extension 和manufacturerCode对应非标准信息标识中
的几个参数,分别表示国家号码,扩展号码和制造商号码.
terminalType描述终端类型,如纯终端,纯网关,终端兼MC,网关兼MC
等.
tcpPorts,udpPorts和rtpIpPorts为程序使用的TCP,UDP和RTP的端口信
息.
callTransferT1,callTransferT2,callTransferT3和callTransferT4是四个控制
呼叫转移的定时器.
callIntrusionT1,callIntrusionT2,callIntrusionT3,callIntrusionT4,
callIntrusionT5和callIntrusionT6是六个控制呼叫插入的定时器.
cleanerThreadStackSize ,listenerThreadStackSize, signallingThreadStackSize,
controlThreadStackSize, logicalThreadStackSize, rasThreadStackSize 和
jitterThreadStackSize分别为连接清除线程,侦听线程,呼叫信令协议线程,控制
协议线程,逻辑信道线程,RAS接受线程和抖动缓冲线程的栈长度.
三部分(成员对象容器或指针):
列表listeners是一个侦听对象的容器,存放使用的侦听对象.
capabilities是能力对象的容器,描述端点的能力集.
gatekeeper指向端点使用的H323Gatekeeper对象,gatekeeperPassword为
网守密码..
字典connectionsActive是一个连接对象的容器,存放活动的连接对象.
connectionsCleaner指向连接清除线程对象.
API
virtual void SetEndpointTypeInfo(H225_EndpointType & info) const;
PDF created with pdfFactory Pro trial version www.pdffactory.com
OPENH323源码分析-http://www.within.net/tech
68
virtual void SetVendorIdentifierInfo(H225_VendorIdentifier & info) const;
virtual void SetH221NonStandardInfo(H225_H221NonStandard & info) const;
SetEndpointTypeInfo方法设定info参数引用的H225_EndpointType对象
的内容,这个对象对应H.225.0规定的EndpointType结构,主要包含端点类型及
制造商信息.
SetVendorIdentifierInfo方法设定info参数引用的H225_VendorIdentifier
对象的内容,这个对象对应H.225.0规定的VenderIdentifier结构,是EndpointType
结构中的一个域,主要包括制造商,产品及版本等信息.
SetH221NonStandardInfo方法设定info参数引用的
H225_H221NonStandard对象的内容,这个对象对应H.225.0规定的
H221NonStandard结构,是VenderIdentifier结构的一个域,主要包括国家号码,
扩展号码和制造商号码.
void AddCapability(H323Capability * capability);
PINDEX SetCapability(
PINDEX descriptorNum,
PINDEX simultaneous,
H323Capability * cap
);
PINDEX AddAllCapabilities(
PINDEX descriptorNum,
PINDEX simultaneous,
const PString & name
);
void AddAllUserInputCapabilities(PINDEX descriptorNum, PINDEX simultaneous);
void RemoveCapabilities(const PStringArray & codecNames);
void ReorderCapabilities(const PStringArray & preferenceOrder);
H323Capability * FindCapability(const H245_Capability & cap) const;
H323Capability * FindCapability(const H245_DataType & dataType) const;
H323Capability * FindCapability(
H323Capability::MainTypes mainType,
unsigned subtype
) const;
AddCapability方法将capability指定的能力添加到端点能力集的表中并分
配一个独一无二的能力号.
SetCapability调用AddCapability方法将cap指定的能力添加到端点能力集
的表中并设定其在能力集的位置,descriptorNum指定所属同时能力集的编号,
simultaneous设定同时能力集内所属可选能力集的编号.
AddAllCapabilities方法根据字符串name创建对应的H323Capability对象
并调用SetCapablility方法将其添加到能力集中.如果name为"*",该方法会
添加所有知名的能力.
AddAllUserInputCapabilities方法调用SetCapability添加所支持的所有用
户输入能力,descriptorNum指定所属同时能力集的编号,simultaneous设定同时
能力集内所属可选能力集的编号.
RemoveCapabilities从端点能力集中删除所有与codecNames指定的字符串
PDF created with pdfFactory Pro trial version www.pdffactory.com
OPENH323源码分析-http://www.within.net/tech
69
表中匹配的能力.
ReorderCapabilities按照preferenceOrder指定的顺序对端点能力集中的能
力排序.
三个同名的FindCapability方法分别在端点能力集中按照不同的匹配方式
查找对应的能力.
BOOL SetGatekeeper(const PString & address, H323Transport * transport = NULL );
BOOL SetGatekeeperZone(
const PString & address,
const PString & identifier,
H323Transport * transport = NULL
);
BOOL LocateGatekeeper(
const PString & identifier,
H323Transport * transport = NULL
);
BOOL DiscoverGatekeeper(H323Transport * transport = NULL);
BOOL UseGatekeeper(
const PString & address = PString::Empty(),
const PString & identifier = PString::Empty(),
const PString & localAddress = PString::Empty()
);
virtual H323Gatekeeper * CreateGatekeeper(H323Transport * transport);
H323Gatekeeper * GetGatekeeper();
BOOL IsRegisteredWithGatekeeper() const;
BOOL RemoveGatekeeper(int reason = -1);
virtual void SetGatekeeperPassword(const PString & password);
virtual const PString & GetGatekeeperPassword() const;
virtual H235Authenticators CreateAuthenticators();
SetGatekeeper方法为端点创建H323Gatekeeper对象并由它寻找参数
address指定地址的网守,然后完成注册.transport为H323Gatekeeper对象使
用的传输实体,缺省为空,此时函数创建新的传输实体.
SetGatekeeperZone方法为端点创建H323Gatekeeper对象并由它寻找参
数address给定地址,identifier指定网守标识的网守,然后完成注册.transport
为H323Gatekeeper对象使用的传输实体,缺省为空,此时函数创建新的传输
实体.
LocateGatekeeper方法为端点创建H323Gatekeeper对象并由它寻找标识
为identifier的网守,然后完成注册.transport参数指定H323Gatekeeper对象
使用的传输实体,缺省为空,函数会创建新的传输实体.
DiscoverGatekeeper方法为端点创建H323Gatekeeper对象并利用它发现
一个网守,然后完成注册.transport参数指定H323Gatekeeper对象使用的传
输实体,缺省为空,函数会创建新的传输实体.
UseGatekeeper创建H323Gatekeeper对象,利用它寻找网守并完成注册.
这个函数的接口更简单统一, address,identifier,localAddress分别指定网守的
地址,网守的标识和与网守通信的传输实体的本地地址,函数依据输入参数的不
PDF created with pdfFactory Pro trial version www.pdffactory.com
OPENH323源码分析-http://www.within.net/tech
70
同分别调用SetGatekeeper,SetGatekeeperZone,LocateGatekeeper或
DiscoverGatekeeper完成具体的操作.
CreateGatekeeper是一个工厂方法,给出缺省是建立一个H323Gatekeeper
对象,用户可以根据需要对其重载以创建H323Gatekeeper派生类的对象.参
数transport指定H323Gatekeeper对象使用的传输实体.
GetGatekeeper返回端点使用H323Gatekeeper对象.
IsRegisteredWithGatekeeper判断是否注册到网守.
RemoveGatekeeper方法向网守去注册并删除使用的H323Gatekeeper对
象,参数reason指定去注册的原因.
SetGatekeeperPassword设定网守认证密码.
GetGatekeeperPassword获取网守认证密码.
CreateAuthenticators是一个工厂方法,给出的缺省实现是建立
H235AuthProcedure1, H235AuthSimpleMD5和 H235AuthCAT认证对象.
可以对其重载以使用新的认证方法.
BOOL StartListener(H323Listener * listener);
BOOL StartListener(const H323TransportAddress & iface);
BOOL StartListeners(const H323TransportAddressArray & ifaces);
BOOL RemoveListener(H323Listener * listener);
H323TransportAddressArray GetInterfaceAddresses(
BOOL excludeLocalHost = TRUE,
H323Transport * associatedTransport = NULL
);
StartListener方法为端点添加侦听对象并启动侦听线程,如果指定的侦听对
象已经存在,该方法无操作但返回TRUE.StartListener有两个过载的同名函数,
分别以指针的形式和传输地址的形式设定侦听对象,对于前者,对象在方法外被
创建,对于后者,对象由方法本身创建.
StartListeners方法为端点添加若干个侦听对象并启动多个侦听线程.
RemoveListener删除端点的侦听对象,如果输入参数为空,端点所有的侦
听对象都会被删除.
GetInterfaceAddress返回端点所有侦听对象的绑定地址.
H323Connection * MakeCall(
const PString & remoteParty,
PString & token,
void * userData = NULL
);
H323Connection * MakeCall(
const PString & remoteParty,
H323Transport * transport,
PString & token,
void * userData = NULL
);
H323Connection * MakeCallLocked(
const PString & remoteParty,
PString & token,
PDF created with pdfFactory Pro trial version www.pdffactory.com
OPENH323源码分析-http://www.within.net/tech
71
void * userData = NULL,
H323Transport * transport = NULL
);
BOOL ParsePartyName(
const PString & party,
PString & alias,
H323TransportAddress & address
);
MakeCall方法负责发起一个呼叫,它对应有两个同名函数:第一个函数由
参数remoteParty指定被叫,token返回呼叫标记(一般由静态函数
BuildConnectionToken创建);第二个函数多出一个transport参数,用于设定呼
叫线程引用的传输实体.
参数remoteParty是一个字符串,用于设定被叫,通过ParsePartyName方
法它会被解析为一个标识被叫别名的字符串和一个标识被叫传输地址的
H323TransportAddress对象,这两个参数决定连接对象的两个成员
remotePartyName和remotePartyAddress的取值.在不使用网守的模式下,
remoteParty的格式为host:[port],其中host是被叫的IP地址或域名,port是端口
号,缺省为1720;在使用网守的情况下,remoteParty的格式为alias [@host:[port]],
其中alias是被叫在网守上注册的用户名,host和port可以不设.
MakeCall方法返回之前会自动将创建的连接对象解锁,因而返回过程中连
接对象可能会被其它线程删除,另一个MakeCallLocked方法避免了这种情况,
该方法不会自动解锁连接对象,这一操作需要由调用者完成.
void ConsultationTransfer(
const PString & primaryCallToken,
const PString & secondaryCallToken
);
virtual H323Connection * SetupTransfer(
const PString & token,
const PString & callIdentity,
const PString & remoteParty,
PString & newToken,
void * userData = NULL
);
void TransferCall(
const PString & token,
const PString & remoteParty,
const PString & callIdentity = PString::Empty()
);
ConsultationTransfer方法请求将第一个呼叫的对端与第二个呼叫的被叫连
接,这一过程中转移源会向转移目的发送一个呼叫转移初始化请求.参数
primaryCallToken为第一个呼叫的标识,secondaryCallToken为第二个呼叫的标
识.函数通过调用第二个呼叫对应的连接对象的同名成员函数完成操作.
SetupTransfer方法转移一个存在的呼叫,这个过程中转移者会向转移目的
PDF created with pdfFactory Pro trial version www.pdffactory.com
OPENH323源码分析-http://www.within.net/tech
72
发送一个Setup消息.参数token标识将被转移的呼叫,callIdentity是呼叫的Call
ID,remoteParty是一个描述转移目的的字符串,新的呼叫标识返回到newToken
中.
TransferCall请求转移一个呼叫,这个过程中转移源会向转移者发送一个转
移初始化请求消息.参数token标识希望被转移的呼叫,remoteParty是一个描述
转移目的的字符串,callIdentity是呼叫的Call ID.
void HoldCall(const PString & token, BOOL localHold);
HoldCall实现呼叫保持,参数token为呼叫标识.
H323Connection * IntrudeCall(
const PString & remoteParty,
PString & token,
unsigned capabilityLevel,
void * userData = NULL
);
H323Connection * IntrudeCall(
const PString & remoteParty,
H323Transport * transport,
PString & token,
unsigned capabilityLevel,
void * userData = NULL
);
IntrudeCall实现呼叫侵入,它对应有两个同名函数:第一个函数由参数
remoteParty指定被叫,token返回呼叫标记,capabilityLevel指定级别;第二个函
数多出一个transport参数,用于设定呼叫线程引用的传输实体.
virtual BOOL ForwardConnection(
H323Connection & connection,
const PString & forwardParty,
const H323SignalPDU & pdu
);
ForwardConnection方法实现呼叫前转,connection为连接对象,
posted on 2007-03-27 17:01  yerlu  阅读(2544)  评论(0)    收藏  举报