pwlib是一套跨平台的C++的开发库,使基于pwlib上开发的应用能够很少量的移植就可以跑在windows和unix的平台上. 
Open323是澳洲的一家公司驱动的open source的是
视频会议h323协议族实现, 还不够十分的完整, 但是已经是非常的难得了. 
在windows上和linux下都能编译使用, 我已经试过了. Windows上编译他们比较麻烦, 注意的是一定要用batch building. 在VC7上编译openh323的动态连接库的时候, VS.net会崩溃, 注意避开, 不过也可以试试看看现象, 如果能够解决, 请告诉我一下. 
在linux上编译就没有什么好说的了, 设好两个环境变量(PWLIBDIR, OPENH323DIR), 就可以在展开的目录下编译了, 先编译PWLIB, 再编译OPENH323, 别忘了将相应xx/lib写到/etc/ld.so.conf下. 我这里可能对安装讲的不够详细, openh323讲的非常详细, 大家可以去看. 

以linux平台为例: 
使用pwlib, 在成功编译之后, 到$(PWLIBDIR)/SAMPLES/ 
这里是一些例子, hello_world 是个非常简单的工程, 从这里我们可以看到如何写使用pwlib的Makefile: 
# Simple makefile for the hello world program 
PROG = hello 
SOURCES = hello.cxx 
ifndef PWLIBDIR 
PWLIBDIR=$(HOME)/pwlib 
endif 
include $(PWLIBDIR)/make/ptlib.mak 
关键是包含了一个ptlib.mak 

hello.cxx 
#include 
class Hello : public PProcess 

PCLASSINFO(Hello, PProcess) 
public: 
void Main(); 
}; 

PCREATE_PROCESS(Hello) 
void Hello::Main() 

cout << "Hello world!\n"; 

非常有代表性. Include $(PWLIBDIR)/make/ptlib.mak 这样就可以make all, make debug的之类的进行编译, 需要的头文件库都会替你安排好. 编译的结果就会放在obj_linux_x86_xx, xx 表示你用的是debug编译还是其他, 如果是debug, xx就是d. 

使用pwlib的程序, 必然要有一个PProcess的子类, 作为整个进程, 这是指在console模式下, gui模式的用PApplication这个我没有用过. Pwlib里面的类大多都是P开头, (可能是取其兼容的意思, 跨平台的特性, 我瞎猜的), 在进程中如果想创建新的线程就创建PThread子类的对象, 对于这种关于过程的类,都有Main函数等待子类去实现. 
在使用所有的P类的时候, 注意使用两个宏, 声明类的时候PCLASSINFO(Hello, PProcess); 分号可以加, 也可不加. PProcess的子类的实现的时候要用PCREATE_PROCESS(Hello);, 这个东西把main()之类的系统入口封装了, 由他来调用Main()成员函数. 在使用线程的时候, 如果想让线程从线程的对象一创建就运行, 就应该在PThread子类中的构造函数中调用父类的Resume(). 关于pwlib先说这些, 在使用Openh323的时候到处都会用到pwlib的东西和概念. 

Openh323: 
终于进入正题了, 先粗略的讲点概念(多余了), H323是指协议族了, 包含了很多规范, 它来自ITU, 应会议的需要而产生, 信令相关的东西用H225 H245,类似Q931,用ASN1编码后在tcp之上传输, 数据相关的就是编码解码的东西了(包括音频视频), 音频g711(alaw, ulaw)了等等多了, 视频h261, 好像h263还没实现. 
在H323的系统里进行通讯的角色实体就是Endpoint, 每个Endpoint可以有很多的Connection, 每个Endpoint也可以拥有很多的逻辑角色, 这个不讨论. 
Endpoint 在Openh323中就是类H323Endpoint的实例 
Connection 在Openh323中就是 H323Connection的实例 
当Endpoint接收了一个远程的连接请求, Endpoint就会创建一个H323Connection; 
当Endpoint发出一个连接的请求, Endpoint也会创建一个H323Connection 
Connection 就会进入一个状态机, 在各个状态中, Connetcion会相应的执行相应的方法, 这些方法, 大多都是Onxxxxx(), 是虚函数, 我们可以自己通过继承H323Connection创建其子类, 并且在我们想做事的时机去重载相应的虚函数. 这是使用Openh323的一个基本的思路. 
现在我们可以看看如何写一个自己H323的Endpoint, 让它能够和netmeeting互操作.成功编译Openh323后在它的samples的目录下面有几个例子, mfc是指在windows下如何使用MFC和Openh323一起开发, 还有simple, 这是个简单的H323的Endpoint的实现, 作为理解OpenH323的库如何使用和开发的技巧方法已经足够了. 
程序运行主线: 
PWLIB(PCREATE_PROCESS(SimpleH323Process))--?SimpleH323Process:: SimpleH323Process()--?SimpleH323Process::Main(); 
Main()如果结束, 这个程序就结束了, 可是Main()里面有个死循环, 写过图形程序的朋友们都知道, 这就是在等消息来呀. 在VC中称之为Interface thread. 
程序注解: 
main.h 
这个文件包含了程序用到的所有类的声明, 一般应该至少有三个类: 
来自PProcess的一个主进程的, 或者说作为界面线程的;(只有一个对象) 
来自H323Endpoint的, 标识这个H323端点的;(只有一个对象) 
来自H323Connection的, 标识所有和这个H323端点相关的连接;(可以有多个) 

#ifndef _SimpleH323_MAIN_H 
#define _SimpleH323_MAIN_H 
//避免头文件重复包含 

#include 

class SimpleH323EndPoint : public H323EndPoint 

//使用Pwlib的要求, 就像使用MFC, 有n多的宏, 可以看看pwlib的源码, 
//宏展开都干了什么 
PCLASSINFO(SimpleH323EndPoint, H323EndPoint); 

public: 
SimpleH323EndPoint(); 
~SimpleH323EndPoint(); 

// overrides from H323EndPoint 
// 重载H323EndPoint的函数 

// 当收到一个远程的呼入和发出呼出的请求的时候 
virtual H323Connection * CreateConnection(unsigned callReference); 
// 有远程的请求来到, 这是在CreateConnection之后的 
virtual BOOL OnIncomingCall(H323Connection &, const H323SignalPDU &, H323SignalPDU &); 
//应答远程的呼入 
virtual H323Connection::AnswerCallResponse OnAnswerCall(H323Connection &, const PString &, const H323SignalPDU &, H323SignalPDU 
&); 
//当连接被Forward 
virtual BOOL OnConnectionForwarded(H323Connection &, const PString &, const H323SignalPDU &); 
//当连接建立 
virtual void OnConnectionEstablished(H323Connection & connection, const PString & token); 
//当连接撤销 
virtual void OnConnectionCleared(H323Connection & connection, const PString & clearedCallToken); 
//当连接需要打开声音的通道 
virtual BOOL OpenAudioChannel(H323Connection &, BOOL, unsigned, H323AudioCodec &); 

// New functions 
// 自己添加的新函数, 父类中不存在 
BOOL Initialise(PArgList &); 
BOOL SetSoundDevice(PArgList &, const char *, PSoundChannel::Directions); 
// 每个连接会有一个Token来唯一标识 
PString currentCallToken; 

protected: 
BOOL autoAnswer; 
PString busyForwardParty; 
}; 

class SimpleH323Connection : public H323Connection 

PCLASSINFO(SimpleH323Connection, H323Connection); 

public: 
//创建连接对象的时候将Endpoint的对象以引用传进来 
//引用的概念就是将整个对象暴露给你的意思, 不是复制了一份的意思, 
//对象还是原来的对象, 所以在Connection中修改了EndPoint的某些属性后 
//就是在操作着传进来的对象, 这是C++的基本概念, OpenH323大量的使用 
//引用传递对象, 对引用的概念要理解 
SimpleH323Connection(SimpleH323EndPoint &, unsigned); 

//重载了两个父类的函数 

// 当打开逻辑通道的时候(等于没说) 
virtual BOOL OnStartLogicalChannel(H323Channel &); 
// 处理用户输入, 这个不是之运行这个程序的用户,而是这个连接上的用户输入 
// 一般应该是拨号了之类的, 
virtual void OnUserInputString(const PString &); 

protected: 
// 快速连接?? 
BOOL noFastStart; 
}; 
class SimpleH323Process : public PProcess 

//主进程, 类似VC的用户界面线程, 
//他是整个程序的入口点, 和结束点 
//创建了EndPoint对象后会有好几个线程启动 
//这个就是主线程 
PCLASSINFO(SimpleH323Process, PProcess) 

public: 
SimpleH323Process(); 
~SimpleH323Process(); 
//这个函数会被自动调用, 是我们程序的入口了 
void Main(); 
protected: 

//这个H323端点对象 
SimpleH323EndPoint * endpoint; 
}; 

#endif // _SimpleH323_MAIN_H 

下面是main.cpp 所有的类的实现了 

#include 

#ifdef __GNUC__ 
#define H323_STATIC_LIB 
#endif 

#include "main.h" 
#include "../../version.h" 


#define new PNEW 

// 这个东西里边可能封装了标准的main函数 
PCREATE_PROCESS(SimpleH323Process); 


/////////////////////////////////////////////////////////////// 

//几个宏都在version.h里面定义 
SimpleH323Process::SimpleH323Process() 
: PProcess("OpenH323 Project", "SimpleH323", 
MAJOR_VERSION, MINOR_VERSION, BUILD_TYPE, BUILD_NUMBER) 

endpoint = NULL; 


SimpleH323Process::~SimpleH323Process() 

delete endpoint; 

void SimpleH323Process::Main() 

cout << GetName() 
<< " Version " << GetVersion(TRUE) 
<< " by " << GetManufacturer() 
<< " on " << GetOSClass() << << GetOSName() 
<< " (" << GetOSVersion() << - << GetOSHardware() << ")\n\n"; 

// Get and parse all of the command line arguments. 
// 分析命令行参数, 略去数行 
PArgList & args = GetArguments(); 
args.Parse( 
"a-auto-answer." 
"b-bandwidth:" 
"B-forward-busy:" 
"D-disable:” FALSE); 
if (args.HasOption(h) || (!args.HasOption(l) && args.GetCount() == 0)) { 
//如果没有参数或者参数是h, 就输出如何使用, 此处略去数行 

//这个东西暂时不管 
#if PTRACING 
#endif 

// Create the H.323 endpoint and initialise it 
// H323 EndPoint 创建了, 并且把命令参数传过去初始化, 初始化的时候做了一些事 
endpoint = new SimpleH323EndPoint; 
if (!endpoint->Initialise(args)) 
return; 
//看看命令行里是不是想直接呼叫另一个H323的endpoint.有没有l(listen)的option 
//如果是就MakeCall, 
// See if making a call or just listening. 
if (args.HasOption(l)) 
cout << "Waiting for incoming calls for \"" << endpoint->GetLocalUserName() << "\"\n"; 
else { 
cout << "Initiating call to \"" << args[0] << "\"\n"; 
endpoint->MakeCall(args[0], endpoint->currentCallToken); 

cout << "Press X to exit." << endl; 

// Simplest possible user interface 
// 简单的用户界面, 会有一个提示> 
// 取pid是我加的 
for (;;) { 
pid_t thispid; 
char prom[20]; 

thispid = getpid(); 
sprintf(prom, "H323 %d >", thispid); 

cout << prom << flush; 
PCaselessString cmd; 
cin >> cmd; 
if (cmd == "X") 
break; 

if (cmd.FindOneOf("HYN") != P_MAX_INDEX) { 
H323Connection*connection; 
//使用lock就是怕别的线程把它给删了 
//因为这里正用着呢 
connection=endpoint->FindConnectionWithLock(endpoint->currentCallToken); 
if (connection != NULL) { 
if (cmd == "H") 
connection->ClearCall(); 
else if (cmd == "Y") 
connection->AnsweringCall(H323Connection::AnswerCallNow); 
else if (cmd == "N") 
connection->AnsweringCall(H323Connection::AnswerCallDenied); 
connection->Unlock(); 




cout << "Exiting " << GetName() << endl; 

// Main 函数结束 

// 自己的Init函数 
BOOL SimpleH323EndPoint::Initialise(PArgList & args) 

// Get local username, multiple uses of -u indicates additional aliases 
if (args.HasOption(u)) { 
PStringArray aliases = args.GetOptionString(u).Lines(); 
// 设定改Endpoint的username 
SetLocalUserName(aliases[0]); 
// 设定Aliases 就是每个Endpoint可以有好多名字的意思 
for (PINDEX i = 1; i < aliases.GetSize(); i++) 
AddAliasName(aliases[i]); 


// Set the various options 
//设置静音检测否 

SetSilenceDetectionMode(args.HasOption(e) ? H323AudioCodec::NoSilenceDetection 
: H323AudioCodec::AdaptiveSilenceDetection); 
//快速连接? 
DisableFastStart(args.HasOption(f)); 
//H245通道 
DisableH245Tunneling(args.HasOption(T)); 

autoAnswer = args.HasOption(a); 
busyForwardParty = args.GetOptionString(B); 

if (args.HasOption()) { 
initialBandwidth = args.GetOptionString().AsUnsigned()*100; 
if (initialBandwidth == 0) { 
cerr << "Illegal bandwidth specified." << endl; 
return FALSE; 



if (args.HasOption(j)) { 
unsigned jitter = args.GetOptionString(j).AsUnsigned(); 
//设定音频抖动的, 应该影响到接收的缓存 
if (jitter >= 20 && jitter <= 10000) 
SetMaxAudioDelayJitter(jitter); 
else { 
cerr << "Jitter should be between 20 milliseconds and 10 seconds." << endl; 
return FALSE; 



//设定声音设备 
//也可以不用声音设备, 比如Openh323工程的子项目 OpenAM和OpenMCU 
//都使演示了如何不使用声音物理设备的方法, 我想那里边的东西会对某些朋友们 
//的需求比较合适 
if (!SetSoundDevice(args, "sound", PSoundChannel::Recorder)) 
return FALSE; 
if (!SetSoundDevice(args, "sound", PSoundChannel::Player)) 
return FALSE; 
if (!SetSoundDevice(args, "sound-in", PSoundChannel::Recorder)) 
return FALSE; 
if (!SetSoundDevice(args, "sound-out", PSoundChannel::Player)) 
return FALSE; 

// 设定decode encode的能力 
// H323 EndPoint在真正进行数据通讯之前要进行能力的交换, 说明自己能够接收和发送什么标准的数据, g.711是必须支持的. 
// Set the default codecs available on sound cards. 
AddAllCapabilities(0, 0, "GSM*{sw}"); 
AddAllCapabilities(0, 0, "G.711*{sw}"); 
AddAllCapabilities(0, 0, "LPC*{sw}"); 
AddAllUserInputCapabilities(0, 1); 

RemoveCapabilities(args.GetOptionString(D).Lines()); 
ReorderCapabilities(args.GetOptionString(P).Lines()); 

cout << "Local username: " << GetLocalUserName() << "\n" 
<< "Silence compression is " << (GetSilenceDetectionMode() == H323AudioCodec::NoSilenceDetection ? "Dis" : "En") << "abled\n" 
<< "Auto answer is " << autoAnswer << "\n" 
<< "FastConnect is " << (IsFastStartDisabled() ? "Dis" : "En") << "abled\n" 
<< "H245Tunnelling is " << (IsH245TunnelingDisabled() ? "Dis" : "En") << "abled\n" 
<< "Jitter buffer: " << GetMaxAudioDelayJitter() << " ms\n" 
<< "Sound output device: \"" << GetSoundChannelPlayDevice() << "\"\n" 
"Sound input device: \"" << GetSoundChannelRecordDevice() << "\"\n" 
<< "Codecs (in preference order):\n" << setprecision(2) << GetCapabilities() << endl; 

//启动一个来电的监听 
//可以使用配置的端口, 也可以使用default的端口 
// Start the listener thread for incoming calls. 
H323ListenerTCP * listener; 
if (args.GetOptionString(i).IsEmpty()) 
listener = new H323ListenerTCP(*this); 
else { 
PIPSocket::Address interfaceAddress(args.GetOptionString(i)); 
listener = new H323ListenerTCP(*this, interfaceAddress); 

if (!StartListener(listener)) { 
cerr << "Could not open H.323 listener port on " 
<< listener->GetListenerPort() << endl; 
delete listener; 
return FALSE; 


//这是连接GateKeeper相关的东西, 先不讨论了 
// Initialise the security info 
if (args.HasOption(p)) { 
SetGatekeeperPassword(args.GetOptionString(p)); 
cout << "Enabling H.235 security access to gatekeeper." << endl; 


// Establish link with gatekeeper if required. 
if (args.HasOption(g) || !args.HasOption( )) { 
H323TransportUDP * rasChannel; 
if (args.GetOptionString(i).IsEmpty()) 
rasChannel = new H323TransportUDP(*this); 
else { 
PIPSocket::Address interfaceAddress(args.GetOptionString(i)); 
rasChannel = new H323TransportUDP(*this, interfaceAddress); 


if (args.HasOption(g)) { 
PString gkName = args.GetOptionString(g); 
if (SetGatekeeper(gkName, rasChannel)) 
cout << "Gatekeeper set: " << *gatekeeper << endl; 
else { 
cerr << "Error registering with gatekeeper at \"" << gkName << \" << endl; 
return FALSE; 


else { 
cout << "Searching for gatekeeper..." << flush; 
if (DiscoverGatekeeper(rasChannel)) 
cout << "\nGatekeeper found: " << *gatekeeper << endl; 
else { 
cerr << "\nNo gatekeeper found." << endl; 
if (args.HasOption( )) 
return FALSE; 




return TRUE; 


//设定音频设备, 没什么可讲的 
BOOL SimpleH323EndPoint::SetSoundDevice(PArgList & args, 
const char * optionName, 
PSoundChannel::Directions dir) 

if (!args.HasOption(optionName)) 
return TRUE; 

PString dev = args.GetOptionString(optionName); 

if (dir == PSoundChannel::Player) { 
if (SetSoundChannelPlayDevice(dev)) 
return TRUE; 

else { 
if (SetSoundChannelRecordDevice(dev)) 
return TRUE; 


cerr << "Device for " << optionName << " (\"" << dev << "\") must be one of:\n"; 

PStringArray names = PSoundChannel::GetDeviceNames(dir); 
for (PINDEX i = 0; i < names.GetSize(); i++) 
cerr << " \"" << names[i] << "\"\n"; 

return FALSE; 


//这个函数很简单但是非常关键, 是从EndPoint中重载过来的. 
//本来是return new H323Connection()的, 现在改成Simplexxx 
//自己实现的一个Connection, 这样当Endpoint里面调用 
//Connection的一些东西的时候, 实际上运行的是Simplexxx 
//的实现, 看到C++的好处了吧, C里用函数指针也可以实现, 没有 
//C++这么native. 
H323Connection * SimpleH323EndPoint::CreateConnection(unsigned callReference) 

return new SimpleH323Connection(*this, callReference); 

//没什么东西, 关键是看看这个东西的调用的时机 
BOOL SimpleH323EndPoint::OnIncomingCall(H323Connection & connection, 
const H323SignalPDU &, 
H323SignalPDU &) 

if (currentCallToken.IsEmpty()) 
return TRUE; 

if (busyForwardParty.IsEmpty()) { 
cout << "Incoming call from \"" << connection.GetRemotePartyName() << "\" rejected, line busy!" << endl; 
return FALSE; 


cout << "Forwarding call to \"" << busyForwardParty << "\"." << endl; 
return !connection.ForwardCall(busyForwardParty); 



//这个东西, 很有用, H323Connection的类里也有这个虚函数 
//返回的值决定告诉远程的连接者是否接收这份连接请求 
H323Connection::AnswerCallResponse 
SimpleH323EndPoint::OnAnswerCall(H323Connection & connection, 
const PString & caller, 
const H323SignalPDU &, 
H323SignalPDU &) 

currentCallToken = connection.GetCallToken(); 

if (autoAnswer) { 
cout << "Automatically accepting call." << endl; 
return H323Connection::AnswerCallNow; 


cout << "Incoming call from \"" 
<< caller 
<< "\", answer call (Y/n)? " 
<< flush; 

return H323Connection::AnswerCallPending; 


BOOL SimpleH323EndPoint::OnConnectionForwarded(H323Connection & /*connection*/, 
const PString & forwardParty, 
const H323SignalPDU & /*pdu*/) 

if (MakeCall(forwardParty, currentCallToken)) { 
cout << "Call is being forwarded to host " << forwardParty << endl; 
return TRUE; 


cout << "Error forwarding call to \"" << forwardParty << \" << endl; 
return FALSE; 



//连接建立时候 
void SimpleH323EndPoint::OnConnectionEstablished(H323Connection & connection, 
const PString & token) 

currentCallToken = token; 
cout << "In call with " << connection.GetRemotePartyName() << endl; 


//连接断开时候 
void SimpleH323EndPoint::OnConnectionCleared(H323Connection & connection, 
const PString & clearedCallToken) 

if (currentCallToken == clearedCallToken) 
currentCallToken = PString(); 

PString remoteName = \" + connection.GetRemotePartyName() + \"; 
switch (connection.GetCallEndReason()) { 
case H323Connection::EndedByRemoteUser : 
cout << remoteName << " has cleared the call"; 
break; 
case H323Connection::EndedByCallerAbort : 
cout << remoteName << " has stopped calling"; 
break; 
case H323Connection::EndedByRefusal : 
cout << remoteName << " did not accept your call"; 
break; 
case H323Connection::EndedByNoAnswer : 
cout << remoteName << " did not answer your call"; 
break; 
case H323Connection::EndedByTransportFail : 
cout << "Call with " << remoteName << " ended abnormally"; 
break; 
case H323Connection::EndedByCapabilityExchange : 
cout << "Could not find common codec with " << remoteName; 
break; 
case H323Connection::EndedByNoAccept : 
cout << "Did not accept incoming call from " << remoteName; 
break; 
case H323Connection::EndedByAnswerDenied : 
cout << "Refused incoming call from " << remoteName; 
break; 
case H323Connection::EndedByNoUser : 
cout << "Gatekeeper could find user " << remoteName; 
break; 
case H323Connection::EndedByNoBandwidth : 
cout << "Call to " << remoteName << " aborted, insufficient bandwidth."; 
break; 
case H323Connection::EndedByUnreachable : 
cout << remoteName << " could not be reached."; 
break; 
case H323Connection::EndedByHostOffline : 
cout << remoteName << " is not online."; 
break; 
case H323Connection::EndedByNoEndPoint : 
cout << "No phone running for " << remoteName; 
break; 
case H323Connection::EndedByConnectFail : 
cout << "Transport error calling " << remoteName; 
break; 
default : 
cout << "Call with " << remoteName << " completed"; 

cout << ", duration " 
<< setprecision(0) << setw(5) 
<< (PTime() - connection.GetConnectionStartTime()) 
<< endl; 


//打开声音设备时候 
//isEncoding 表示编码吗 
//编码表示向外发送数据, 从声音设备读 
//解码表示从网络读出数据, 写到声音设备上 
//不同的方向的codec是不同的, 所以在这里有好多文章可以做 
//可以给codec attach上不同的channel根据isEncoding的值 
BOOL SimpleH323EndPoint::OpenAudioChannel(H323Connection & connection, 
BOOL isEncoding, 
unsigned bufferSize, 
H323AudioCodec & codec) 

if (H323EndPoint::OpenAudioChannel(connection, isEncoding, bufferSize, codec)) 
return TRUE; 

cerr << "Could not open sound device "; 
if (isEncoding) 
cerr << GetSoundChannelRecordDevice(); 
else 
cerr << GetSoundChannelPlayDevice(); 
cerr << " - Check permissions or full duplex capability." << endl; 

return FALSE; 

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 
EndPoint的实现分析完毕. 


H323Connection的实现, 这个Connection的实现太简单了.可能不足以说明问题 
我也没什么好说的了 
/////////////////////////////////////////////////////////////// 

SimpleH323Connection::SimpleH323Connection(SimpleH323EndPoint & ep, unsigned ref) 
: H323Connection(ep, ref) 




BOOL SimpleH323Connection::OnStartLogicalChannel(H323Channel & channel) 

if (!H323Connection::OnStartLogicalChannel(channel)) 
return FALSE; 

cout << "Started logical channel: "; 

switch (channel.GetDirection()) { 
case H323Channel::IsTransmitter : 
cout << "sending "; 
break; 

case H323Channel::IsReceiver : 
cout << "receiving "; 
break; 

default : 
break; 


cout << channel.GetCapability() << endl; 

return TRUE; 




void SimpleH323Connection::OnUserInputString(const PString & value) 

cout << "User input received: \"" << value << \" << endl; 

// End of File /////////////////////////////////////////////////////////////// 

总结一下基本的过程就是创建一个H323Endpoint的对象endpoint, 创建对象后这个程序就有好多个小的线程被创建了.然后EndPoint开始监听来电, 之后判断是否直接呼叫另一个h323的Endpoint. 然后就是一个for循环, 判断标准的输入, 并通过当前的token来lock一个Connection, 每个连接会有唯一的一个token, lock的意思是说, 在被lock的期间是不能被释放的. 根据输入的字符决定对得到的连接做什么. 

OpenAM: 
是个answer machine, 自动应答机, 或者是留言机. 实现的很简单, 里面对OpenH323使用的思路很有价值. 
./openam –n –-g711message sample_message.wav 
这样运行, 用netmeeting 连接一下这个IP, netmeeting就会放一段简单的英语, 测测你的英语听力, 他在讲什么? 
这个程序是一个支持多连接和并发连接的Endpoint, 但是他没有使用真正的声音设备, 放出的音从一个已有的wav文件中读出来, 远程用户的留言被录到一个文件里, 文件的名字表示了是什么时间录制的. 
主要的思路是给在连接打开声音通道的时候, 根据isEncoding的值区别是录音还是放音,如果是录音, 将读文件的Channel附加在codec上, 相反写文件的Channel附件在codec上,注意这是两个codec. 
这个东西给了我们一个方法, 如何使用文件IO来代替声音设备的IO来使用OpenH323. 


这是main.h 

#ifndef _Voxilla_MAIN_H 
#define _Voxilla_MAIN_H 

#include 
#include 
#include 
#include 
#include 

#include 
#include 

//主进程 
class OpenAm : public PProcess 

PCLASSINFO(OpenAm, PProcess) 

public: 
OpenAm(); 
~OpenAm(); 

void Main(); 
void RecordFile(PArgList & args); 
void PlayFile(PArgList & args); 

protected: 
long GetCodec(const PString & codecname); 
OpalLineInterfaceDevice * GetDevice(const PString & device); 
}; 

//H323 端点 
class MyH323EndPoint : public H323EndPoint 

PCLASSINFO(MyH323EndPoint, H323EndPoint); 

public: 
MyH323EndPoint(unsigned callLimit, 
const PString & runCmd, 
const PDirectory & dir, 
int flags); 

// overrides from H323EndPoint 
virtual H323Connection * CreateConnection(unsigned callReference); 
BOOL OnIncomingCall(H323Connection &, const H323SignalPDU &, H323SignalPDU &); 

// new functions 
BOOL Initialise(PConfigArgs & args); 

PString GetGSMOGM() const { return gsmOgm; } 
void SetGSMOGM(const PString & s) { gsmOgm = s; } 

PString GetG711OGM() const { return g711Ogm; } 
void SetG711OGM(const PString & s) { g711Ogm = s; } 

PString GetLPC10OGM() const { return lpc10Ogm; } 
void SetLPC10OGM(const PString & s) { lpc10Ogm = s; } 

#ifdef SPEEX_CODEC 
PString GetSPEEXOGM() const { return speexOgm; } 
void SetSPEEXOGM(const PString & s) { speexOgm = s; } 
#endif 

PString GetG7231OGM() const { return g7231Ogm; } 
void SetG7231OGM(const PString & s) { g7231Ogm = s; } 

unsigned GetCallLimit() const { return callLimit; } 
PString GetRunCmd() const { return runCmd; } 
PDirectory GetDirectory() const { return dir; } 

void SetRecordWav(const BOOL rec){ recordWav = rec; } 
BOOL GetRecordWav() const { return recordWav; } 

enum { 
DeleteAfterRecord = 0x01, 
NoRecordG7231 = 0x02, 
HangupAfterPlay = 0x04 
}; 

BOOL GetDeleteAfterRecord() const { return flags & DeleteAfterRecord; } 
BOOL GetNoRecordG7231() const { return flags & NoRecordG7231; } 
BOOL GetHangupAfterPlay() const { return flags & HangupAfterPlay; } 

protected: 
unsigned callLimit; 
PString pcmOgm, g711Ogm, gsmOgm, lpc10Ogm, g7231Ogm, runCmd; 
#ifdef SPEEX_CODEC 
PString speexOgm; 
#endif 
PDirectory dir; 
int flags; 
BOOL recordWav; 
}; 

class PCM_RecordFile; 
class MyH323Connection; 
PQUEUE(PStringQueue, PString); 
// Out Going Channel OGM 
//就是发送语音的通道 
//即是读文件的通道 
class PCM_OGMChannel : public PIndirectChannel 

PCLASSINFO(PCM_OGMChannel, PIndirectChannel); 

public: 
PCM_OGMChannel(MyH323Connection & conn); 

BOOL Read(void * buffer, PINDEX amount); 
void PlayFile(PFile * chan); 

BOOL Close(); 

void QueueFile(const PString & cmd); 
void FlushQueue(); 

void SetRecordTrigger(); 
void SetHangupTrigger(); 

void SetPlayOnce() { playOnce = TRUE; } 

protected: 
virtual BOOL ReadFrame(PINDEX amount); 
virtual void CreateSilenceFrame(PINDEX amount); 
virtual void Synchronise(PINDEX amount); 
virtual BOOL IsWAVFileValid(PWAVFile *chan); 

BOOL AdjustFrame(void * buffer, PINDEX amount); 

PStringQueue playQueue; 

MyH323Connection & conn; 
PMutex chanMutex; 
int silentCount; 
int totalData; 
BOOL recordTrigger, hangupTrigger; 
BOOL closed; 
BOOL playOnce; 

PAdaptiveDelay ogm_delay; 

PBYTEArray frameBuffer; 
PINDEX frameLen, frameOffs; 
}; 
//这个是之读的文件是个g723编码的文件, 暂时不研究这个类相关的一切 
class G7231_OGMChannel : public PCM_OGMChannel 

PCLASSINFO(G7231_OGMChannel, PCM_OGMChannel); 
public: 
G7231_OGMChannel(MyH323Connection & conn); 

protected: 
BOOL ReadFrame(PINDEX amount); 
void CreateSilenceFrame(PINDEX amount); 
void Synchronise(PINDEX amount); 
BOOL IsWAVFileValid(PWAVFile *chan); 
}; 

//连接,都是从这个类实例出来的 
class MyH323Connection : public H323Connection 

PCLASSINFO(MyH323Connection, H323Connection); 

public: 
MyH323Connection(MyH323EndPoint &, unsigned); 
~MyH323Connection(); 

// overrides from H323Connection 
BOOL OpenAudioChannel(BOOL, unsigned, H323AudioCodec & codec); 
AnswerCallResponse OnAnswerCall(const PString &, const H323SignalPDU &, H323SignalPDU &); 
BOOL OnStartLogicalChannel(H323Channel & channel); 
void OnUserInputString(const PString & value); 

// new functions 
void StartRecording(); 
void Hangup(); 

void SetE164Number(const PString & _num) 
{ e164Number = _num; } 

PString GetE164Number() const 
{ return e164Number; } 

protected: 
void OnUserInputChar(char ch); 
BOOL StartMenu(int menuNumber); 
BOOL ProcessMenuCmd(const PString & cmdStr); 

const MyH323EndPoint & ep; 
PString product; 
PTime callStartTime; 
PTime recordStartTime; 
PString basename; 
PFilePath recordFn; 
PString transmitCodecName, receiveCodecName; 
BOOL recordTrigger; 
PMutex connMutex; 

PCM_RecordFile * recordFile; 
PCM_OGMChannel * ogmChannel; 

PString digits, lastDigits; 
int currentMenu; 
PStringList menuNames; 

PString securityToken, e164Number; 
}; 

//是录音 
class PCM_RecordFile : public PIndirectChannel 

PCLASSINFO(PCM_RecordFile, PIndirectChannel) 

public: 
PCM_RecordFile(MyH323Connection & conn, const PFilePath & fn, unsigned callLimit); 
~PCM_RecordFile(); 

BOOL Write(const void * buf, PINDEX len); 
BOOL Close(); 
void StartRecording(); 

virtual void DelayFrame(PINDEX len); 
virtual BOOL WriteFrame(const void * buf, PINDEX len); 

BOOL WasRecordStarted() const { return recordStarted; } 

protected: 
MyH323Connection & conn; 
PTime finishTime; 
PFilePath fn; 
unsigned callLimit; 
BOOL recordStarted; 
BOOL timeLimitExceeded; 
BOOL closed; 
BOOL isPCM; 
BOOL dataWritten; 
PAdaptiveDelay delay; 
PMutex pcmrecordMutex; 
PFile *fileclass; // will point to a PWAVFile or PFile class 
}; 
//录的结果是个g723文件, 我们暂时不考虑这个类相关的一切 
class G7231_RecordFile : public PCM_RecordFile 

PCLASSINFO(G7231_RecordFile, PCM_RecordFile); 

public: 
G7231_RecordFile(MyH323Connection & conn, const PFilePath & fn, unsigned callLimit); 
void DelayFrame(PINDEX len); 
BOOL WriteFrame(const void * buf, PINDEX len); 
}; 


#endif // _Voxilla_MAIN_H 


// End of File /////////////////////////////////////////////////////////////// 


这是main.cxx 
#include 
#include 

#include "version.h" 
#include "lpc10codec.h" 

#ifdef SPEEX_CODEC 
#include "speexcodec.h" 
#endif 

#include "mscodecs.h" 
#include "opalvxml.h" 
#include "main.h" 

PCREATE_PROCESS(OpenAm); 

#define new PNEW 

//default 录音时间 
#define DEFAULT_MSG_LIMIT 30 
#define DEFAULT_CALL_LOG "call_log.txt" 

#define G7231_SAMPLES_PER_BLOCK 240 

#define CHECK_PCM 1 
#define CHECK_G7231 2 

#define MENU_PREFIX "UserMenu-" 

static PMutex logMutex; 
static PTextFile logFile; 
static PFilePath logFilename = DEFAULT_CALL_LOG; 

PString G7231Ext = ".g723"; 
PString WAVExt = ".wav"; 
PString PCMExt = ".sw"; 

//关于log的一切先不用看 
static void LogMessage(const PString & str) 

PTime now; 
PString msg = now.AsString("hh:mm:ss dd/MM/yyyy") & str; 
logMutex.Wait(); 

if (!logFile.IsOpen()) { 
logFile.Open(logFilename, PFile::ReadWrite); 
logFile.SetPosition(0, PFile::End); 


logFile.WriteLine(msg); 

logFile.Close(); 

logMutex.Signal(); 


static void LogCall(const PFilePath & fn, 
const PString & from, 
const PString & user, 
unsigned len, 
const PString & codec, 
const PString & product) 

PString addr = from; 
LogMessage(addr & "\"" + user + "\"" & PString(PString::Unsigned, len) & codec & "\"" + product + "\"" & "\"" + fn + "\""); 



/////////////////////////////////////////////////////////////// 

OpenAm::OpenAm() 
: PProcess("OpenH323 Project", "OpenAM", 
MAJOR_


推荐文章:H.323系统组成      OpenH323的实现流程     视频会议两大阵营标准H323和SIP       
视频会议开发中协议栈的选择

Powered by PageAdmin CMS