当前位置:Linux教程 - Linux - 使用OpenH323开发入门

使用OpenH323开发入门



         使用OpenH323开发
    bricks 原创
    [email protected]
    必备软件包可以从http://www.openh323.org下载.
    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_VERSION, MINOR_VERSION, BUILD_TYPE, BUILD_NUMBER)
    {
    }


    OpenAm::~OpenAm()
    {
    }


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

    // 分析命令行了
    PConfigArgs args(GetArguments());

    args.Parse(
    "D-disable:"
    "d-directory:"
    "g-gatekeeper:" "n-no-gatekeeper."
    "-g711-ulaw." "-no-g711-ulaw."
    "-g711-alaw." "-no-g711-alaw."
    "-g711message:" "-no-g711message."
    "-g7231." "-no-g7231."
    "-g7231message:" "-no-g7231message."
    "-gsm." "-no-gsm."
    "-gsmmessage:" "-no-gsmmessage."
    "h-help."
    "H-hangup." "-no-hangup."
    "i-interface:" "-no-interface."
    "k-kill." "-no-kill."
    "l-limit:" "-no-limit."
    "-listenport:" "-no-listenport."
    "-lpc10message:" "-no-lpc10message."
    "-speexmessage:" "-no-speexmessage."
    "m-message:" "-no-message."
    "-no-recordg7231."
    #if PTRACING
    "o-output:"
    #endif
    "P-prefer:"
    "-pcm." "-no-pcm."
    "-pcmmessage:" "-no-pcmmessage."
    "-port:"
    "q-quicknet:" "-no-quicknet:"
    "r-run:" "-no-run."
    "-recordraw."
    "-require-gatekeeper." "-no-require-gatekeeper."
    "-save."
    #if PMEMORY_CHECK
    "-setallocationbreakpoint:"
    #endif
    #if PTRACING
    "t-trace."
    #endif
    "u-username:" "-no-username."
    , FALSE);

    #if PMEMORY_CHECK
    if (args.HasOption("setallocationbreakpoint"))
    PMemoryHeap::SetAllocationBreakpoint(args.GetOptionString("setallocationbreakpoint").AsInteger());
    #endif

    #if PTRACING
    PTrace::Initialise(args.GetOptionCount( ),
    args.HasOption(o) ? (const char *)args.GetOptionString(o) : NULL);
    #endif

    if (args.HasOption(h)) {
    cout << "Usage : " << GetName() << " [options]\n"
    "Options:\n"
    " -d --directory dir : Put recorded mesages into dir\n"
    " -l --limit secs : Limit recorded messages to secs duration (default " << DEFAULT_MSG_LIMIT << ")\n"
    " -m --pcmmessage fn : Set outgoing message for PCM derived codecs (G.711/GSM) to fn\n"
    " --g7231message fn : Set outgoing message for G723.1 codec to fn\n"
    " --g711message fn : Set outgoing message for G711 codec to fn\n"
    " --gsmmessage fn : Set outgoing message for GSM codec to fn\n"
    " --lpc10message fn : Set outgoing message for LPC10 codec to fn\n"
    #ifdef SPEEX_CODEC
    " --speexmessage fn : Set outgoing message for Speex codec to fn\n"
    #endif

    " --recordraw : Record PCM audo in raw files (.sw) instead of .wav\n"
    " -r --run cmd : Run this command after each recorded message\n"
    " -k --kill : Kill recorded files after user command\n"
    " -H --hangup : hangup after playing message\n"
    " -u --username str : Set the local endpoint name to str\n"
    " -i --interface ip : Bind to a specific interface\n"
    " --listenport port : Listen on a specific port\n"
    " -g --gatekeeper host: Specify gatekeeper host.\n"
    " -n --no-gatekeeper : Disable gatekeeper discovery.\n"
    " --require-gatekeeper: Exit if gatekeeper discovery fails.\n"
    " -D --disable codec : Disable the specified codec (may be used multiple times)\n"
    " -P --prefer codec : Prefer the specified codec (may be used multiple times)\n"
    #if PTRACING
    " -t --trace : Enable trace, use multiple times for more detail\n"
    " -o --output : File for trace output, default is stderr\n"
    #endif
    " --save : Save arguments in configuration file\n"
    " -h --help : Display this help message\n";
    return;
    }

    args.Save("save");

    //是指有IXJ卡吗? 我想你肯定没有, 那是电话卡 不用管他
    #if HAS_IXJ
    if (args.GetCount() > 0) {
    if (args[0] *= "record")
    RecordFile(args);
    else if (args[0] *= "play")
    PlayFile(args);
    else
    cerr << "unknown command \"" << args[0] << "\"" << endl;
    return;
    }
    #endif

    unsigned callLimit = DEFAULT_MSG_LIMIT;
    if (args.HasOption(l)) {
    callLimit = args.GetOptionString(l).AsInteger();
    if (callLimit > 3600) {
    cout << "warning: maximum call length " << callLimit << " is out of range. Using " << DEFAULT_MSG_LIMIT << " instead\n";
    callLimit = DEFAULT_MSG_LIMIT;
    } else if (callLimit == 0)
    cout << "warning: recorded message call limit disabled\n";
    }
    cout << "Recorded messages limited to " << callLimit << " seconds\n";

    PString runCmd;
    if (args.HasOption( )) {
    runCmd = args.GetOptionString( );
    cout << "Executing \"" << runCmd << "\" after each message" << endl;
    }

    PDirectory dir;
    if (args.HasOption(d))
    dir = args.GetOptionString(d);

    int flags = 0;

    if (args.HasOption("no-recordg7231")) {
    cout << "Supressing recording of G723.1 messages" << endl;
    flags |= MyH323EndPoint::NoRecordG7231;
    }
    if (args.HasOption(k)) {
    cout << "Deleting recorded files after processing" << endl;
    if (runCmd.IsEmpty())
    cout << "WARNING: recorded files will be deleted even though no run command is present" << endl;
    flags |= MyH323EndPoint::DeleteAfterRecord;
    }

    if (args.HasOption(H))
    flags |= MyH323EndPoint::HangupAfterPlay;

    //创建H323 EndPoint
    MyH323EndPoint endpoint(callLimit, runCmd, dir, flags);

    PString userName = "OpenH323 Answering Machine v" + GetVersion();
    if (args.HasOption(u))
    userName = args.GetOptionString(u);
    endpoint.SetLocalUserName(userName);

    if (!endpoint.Initialise(args))
    return;

    // start the H.323 listener
    // 开始监听 H323有个默认的端口应该是1572 之类的 DefaultSignalPort, 我忘了
    H323ListenerTCP * listener;
    PIPSocket::Address interfaceAddress(INADDR_ANY);
    WORD listenPort = H323ListenerTCP::DefaultSignalPort;

    if (args.HasOption("listenport"))
    listenPort = (WORD)args.GetOptionString("listenport").AsInteger();

    if (args.HasOption(i))
    interfaceAddress = PIPSocket::Address(args.GetOptionString(i));

    listener = new H323ListenerTCP(endpoint, interfaceAddress, listenPort);

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

    //gatekeeper 相关的东西我们也不必考虑 用-n这个参数这个端点就不会
    //去找gatekeeper

    if (args.HasOption(g)) {
    PString gkName = args.GetOptionString(g);
    if (endpoint.SetGatekeeper(gkName, new H323TransportUDP(endpoint)))
    cout << "Gatekeeper set: " << *endpoint.GetGatekeeper() << endl;
    else {
    cout << "Error registering with gatekeeper at \"" << gkName << \" << endl;
    return;
    }
    }
    else if (!args.HasOption( )) {
    cout << "Searching for gatekeeper..." << flush;
    if (endpoint.DiscoverGatekeeper(new H323TransportUDP(endpoint)))
    cout << "\nGatekeeper found: " << *endpoint.GetGatekeeper() << endl;
    else {
    cout << "\nNo gatekeeper found." << endl;
    if (args.HasOption("require-gatekeeper"))
    return;
    }
    }

    cout << "Waiting for incoming calls for \"" << endpoint.GetLocalUserName() << \" << endl;

    //瞧瞧, 这就是主进程干的这点事, Sleep的是毫秒
    for (;;)
    PThread::Current()->Sleep(5000);
    }


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


    MyH323EndPoint::MyH323EndPoint(unsigned _callLimit,
    const PString & _runCmd,
    const PDirectory & _dir,
    int _flags)
    : callLimit(_callLimit), runCmd(_runCmd), dir(_dir), flags(_flags)
    {
    }

    BOOL MyH323EndPoint::OnIncomingCall(H323Connection & _conn,
    const H323SignalPDU & setupPDU,
    H323SignalPDU &)
    {
    //传过来的是引用, 引用这个东西必须马上赋值
    MyH323Connection & conn = (MyH323Connection &)_conn;

    // see if incoming call is to a getway address
    PString number;
    if (setupPDU.GetDestinationE164(number))
    conn.SetE164Number(number);

    return TRUE;
    }
    //这个没什么异常, 都这样做
    H323Connection * MyH323EndPoint::CreateConnection(unsigned callReference)
    {
    return new MyH323Connection(*this, callReference);
    }

    //分析命令, 看看应该使用什么样的能力去交换,
    // 我们在使用的时候指定—g711message
    //就是说只是用g711 的alaw或者mulaw
    //很多废话就不用看了
    //除了和g711相关的其他都没有用处了

    BOOL MyH323EndPoint::Initialise(PConfigArgs & args)
    {
    // format for record files, raw or wav
    if (args.HasOption("recordraw"))
    SetRecordWav(FALSE);
    else
    SetRecordWav(TRUE);

    // get G723.1 OGM
    if (args.HasOption("g7231message"))
    g7231Ogm = args.GetOptionString("g7231message");
    else if (args.HasOption(m)) {
    if (PFile::Exists(args.GetOptionString(m) + "_g7231" + WAVExt)) {
    g7231Ogm = args.GetOptionString(m) + "_g7231" + WAVExt;
    }
    else if (PFile::Exists(args.GetOptionString(m) + PCMExt)) {
    g7231Ogm = args.GetOptionString(m) + G7231Ext;
    }
    }

    if (!g7231Ogm.IsEmpty()) {
    if ((g7231Ogm.Find("%s") == P_MAX_INDEX) && !PFile::Exists(g7231Ogm)) {
    cout << "warning: cannot open G723.1 OGM file \"" << g7231Ogm << "\"" << endl;
    g7231Ogm = "";
    }
    }

    if (g7231Ogm.IsEmpty())
    cout << "No G.723.1 outgoing message set\n";
    else {
    cout << "Using \"" << g7231Ogm << "\" as G.723.1 outgoing message\n";
    }


    // Get the OGM message for the PCM codecs
    // Check if the file specified exists. If it does, use it.
    // If it does not exist, try with .wav and .sw extensions.
    if (args.HasOption("pcmmessage")) {
    pcmOgm = args.GetOptionString("pcmmessage");
    }
    else if (args.HasOption(m)) {
    if (g7231Ogm.Find("%s") == P_MAX_INDEX) {
    pcmOgm = args.GetOptionString(m);
    } else {
    if (PFile::Exists(args.GetOptionString(m))) {
    pcmOgm = args.GetOptionString(m);
    }
    else if (PFile::Exists(args.GetOptionString(m) + WAVExt)) {
    pcmOgm = args.GetOptionString(m) + WAVExt;
    }
    else if (PFile::Exists(args.GetOptionString(m) + PCMExt)) {
    pcmOgm = args.GetOptionString(m) + PCMExt;
    }
    }
    }


    // By default, use the pcmOgm for all the PCM codecs, but allow the user
    // to override them.
    gsmOgm = pcmOgm;
    g711Ogm = pcmOgm;
    lpc10Ogm = pcmOgm;
    #ifdef SPEEX_CODEC
    speexOgm = pcmOgm;
    #endif


    // We can set the filename for specific codecs.
    if (args.HasOption("gsmmessage"))
    gsmOgm = args.GetOptionString("gsmmessage");


    //这句话用的着
    if (args.HasOption("g711message"))
    g711Ogm = args.GetOptionString("g711message");

    if (args.HasOption("lpc10message"))
    lpc10Ogm = args.GetOptionString("lpc10message");
    //这是一个codec设备, 你没有!
    #ifdef SPEEX_CODEC
    if (args.HasOption("speexmessage"))
    speexOgm = args.GetOptionString("speexmessage");
    #endif

    // Check GSM OGM message
    if (!gsmOgm.IsEmpty()) {
    if ((g7231Ogm.Find("%s") == P_MAX_INDEX) && !PFile::Exists(gsmOgm)) {
    cout << "warning: cannot open GSM OGM file \"" << gsmOgm << "\"" << endl;
    gsmOgm = "";
    }
    }
    if (gsmOgm.IsEmpty())
    cout << "No GSM outgoing message set\n";
    else {
    cout << "Using \"" << gsmOgm << "\" as GSM outgoing message\n";
    }

    // Check G.711 OGM message
    if (!g711Ogm.IsEmpty()) {
    if ((g7231Ogm.Find("%s") == P_MAX_INDEX) && !PFile::Exists(g711Ogm)) {
    cout << "warning: cannot open G711 OGM file \"" << g711Ogm << "\"" << endl;
    g711Ogm = "";
    }
    }
    if (g711Ogm.IsEmpty())
    cout << "No G711 outgoing message set\n";
    else {
    cout << "Using \"" << g711Ogm << "\" as G.711 outgoing message\n";
    }

    // Check LPC10 OGM message
    if (!lpc10Ogm.IsEmpty()) {
    if ((g7231Ogm.Find("%s") == P_MAX_INDEX) && !PFile::Exists(lpc10Ogm)) {
    cout << "warning: cannot open LPC10 OGM file \"" << lpc10Ogm << "\"" << endl;
    lpc10Ogm = "";
    }
    }
    if (lpc10Ogm.IsEmpty())
    cout << "No LPC10 outgoing message set\n";
    else {
    cout << "Using \"" << lpc10Ogm << "\" as LPC10 outgoing message\n";
    }

    #ifdef SPEEX_CODEC
    // Check Speex OGM message
    if (!speexOgm.IsEmpty()) {
    if ((g7231Ogm.Find("%s") == P_MAX_INDEX) && !PFile::Exists(speexOgm)) {
    cout << "warning: cannot open Speex OGM file \"" << speexOgm << "\"" << endl;
    speexOgm = "";
    }
    }
    if (speexOgm.IsEmpty())
    cout << "No Speex outgoing message set\n";
    else {
    cout << "Using \"" << speexOgm << "\" as Speex outgoing message\n";
    }
    #endif


    if (g7231Ogm.IsEmpty() && gsmOgm.IsEmpty() && g711Ogm.IsEmpty()
    && lpc10Ogm.IsEmpty()
    #ifdef SPEEX_CODEC
    && speexOgm.IsEmpty()
    #endif
    ) {
    cerr << "Must specify at least one outgoing message" << endl;
    return FALSE;
    }

    if (!g7231Ogm.IsEmpty())
    SetCapability(0, 0, new G7231_File_Capability);

    if (!gsmOgm.IsEmpty())
    SetCapability(0, 0, new H323_GSM0610Capability);

    if (!gsmOgm.IsEmpty())
    SetCapability(0, 0, new MicrosoftGSMAudioCapability);

    //这是 AM Endpoint的能力 支持g711的alaw和ulaw
    if (!g711Ogm.IsEmpty())
    SetCapability(0, 0, new H323_G711Capability(H323_G711Capability::muLaw, H323_G711Capability::At64k));

    if (!g711Ogm.IsEmpty())
    SetCapability(0, 0, new H323_G711Capability(H323_G711Capability::ALaw, H323_G711Capability::At64k));

    //没有用了
    if (!lpc10Ogm.IsEmpty())
    SetCapability(0, 0, new H323_LPC10Capability(*this));

    #ifdef SPEEX_CODEC
    if (!speexOgm.IsEmpty())
    SetCapability(0, 0, new SpeexNarrow3AudioCapability());
    #endif

    capabilities.Remove(args.GetOptionString(D).Lines());
    capabilities.Reorder(args.GetOptionString(P).Lines());

    cout << "Codecs (in preference order):\n" << setprecision(2) << capabilities << endl;

    return TRUE;
    }

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

    //录音通道, 解码对象所附着的通道
    //写文件
    PCM_RecordFile::PCM_RecordFile(MyH323Connection & _conn, const PFilePath & _fn, unsigned _callLimit)
    : conn(_conn), fn(_fn), callLimit(_callLimit)
    {
    recordStarted = FALSE;
    timeLimitExceeded = FALSE;
    closed = FALSE;
    dataWritten = FALSE;

    // If the file name ends in .wav then open the output as a WAV file.
    // Otherwise open it as a raw file.
    if ((_fn.Right(4)).ToLower() == ".wav")
    fileclass = new PWAVFile(_fn, PFile::WriteOnly,
    PFile::ModeDefault,PWAVFile::PCM_WavFile);
    else
    fileclass = new PFile(_fn, PFile::WriteOnly);
    }

    void PCM_RecordFile::StartRecording()
    {
    PWaitAndSignal mutex(pcmrecordMutex);

    if (recordStarted)
    return;

    PTRACE(1, "Starting recording to " << fn);

    PTime now;
    recordStarted = TRUE;
    finishTime = now + (callLimit * 1000);
    }

    BOOL PCM_RecordFile::Close()
    {
    PWaitAndSignal mutex(pcmrecordMutex);

    closed = TRUE;
    return fileclass->Close();
    }

    BOOL PCM_RecordFile::Write(const void * buf, PINDEX len)
    {
    // Wait for the mutex, and Signal it at the end of this function
    PWaitAndSignal mutex(pcmrecordMutex);

    // If the record file has been closed, or if the time limit has
    // been exceeded, then return immediatly.
    if (closed || timeLimitExceeded)
    return FALSE;

    if (!recordStarted) {
    DelayFrame(len);
    return TRUE;
    }

    PTime now;
    if ((callLimit != 0) && (now >= finishTime)) {
    PTRACE(1, "Terminating call due to timeout");
    conn.ClearCall();
    timeLimitExceeded = TRUE;
    return TRUE;
    }

    DelayFrame(len);

    dataWritten = TRUE;

    return WriteFrame(buf, len);
    }

    BOOL PCM_RecordFile::WriteFrame(const void * buf, PINDEX len)
    {
    //cerr << "Writing PCM " << len << endl;
    return fileclass->Write(buf, len);
    }

    void PCM_RecordFile::DelayFrame(PINDEX len)
    {
    delay.Delay(len/16);
    }

    PCM_RecordFile::~PCM_RecordFile()
    {
    PWaitAndSignal mutex(pcmrecordMutex);

    if (!dataWritten) {
    PTRACE(1, "Deleting " << fn << " as no data recorded");
    fileclass->Remove(fn);
    }

    delete fileclass;
    }

    ///////////////////////////////////////////////////////////////
    // Override some of the PCM_RecordFile functions to write
    // G723.1 data instead of PCM data.

    G7231_RecordFile::G7231_RecordFile(MyH323Connection & _conn, const PFilePath & _fn, unsigned _callLimit)
    : PCM_RecordFile(_conn, _fn, _callLimit)
    {
    // If the record file is a .wav file, we need to close the file
    // that PCM_RecordFile will have opened, and reopen it as a G.723.1 Wav file.
    if ((_fn.Right(4)).ToLower() == ".wav") {
    fileclass->Remove(_fn);
    delete fileclass;
    fileclass = new PWAVFile(_fn, PFile::WriteOnly,
    PFile::ModeDefault,PWAVFile::G7231_WavFile);
    }
    }

    BOOL G7231_RecordFile::WriteFrame(const void * buf, PINDEX /*len*/)
    {
    int frameLen = G7231_File_Codec::GetFrameLen(*(BYTE *)buf);
    // cerr << "Writing G7231 " << frameLen << endl;
    return fileclass->Write(buf, frameLen);
    }

    void G7231_RecordFile::DelayFrame(PINDEX /*len*/)
    {
    // Ignore the len parameter as that is the compressed size.
    // We must delay by the actual sample time.
    delay.Delay((G7231_SAMPLES_PER_BLOCK*2)/16);
    }

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

    static BOOL MatchString(const PString & str1, const PString str2)
    {
    if (str1.GetLength() != str2.GetLength())
    return FALSE;

    PINDEX len = str1.GetLength();

    PINDEX i;
    for (i = 0; i < len; i++)
    if ((str1[i] != ?) && (str2[i] != ?) && (str1[i] != str2[i]))
    return FALSE;

    return TRUE;
    }

    static PINDEX FindMatch(const PStringList & list, const PString & key)
    {
    PINDEX maxKeyLen = 0;
    PINDEX i;

    PINDEX keyLen = key.GetLength();
    PINDEX listLen = list.GetSize();

    for (i = 0; i < listLen; i++)
    maxKeyLen = PMAX(maxKeyLen, list[i].GetLength());

    if (keyLen == 0 || maxKeyLen == 0)
    return P_MAX_INDEX;

    if (keyLen > maxKeyLen)
    return P_MAX_INDEX;

    PINDEX len = 1;
    while (len <= keyLen) {
    PString subStr = key.Left(len);

    PINDEX matches = 0;
    PINDEX lastMatch = P_MAX_INDEX;
    PINDEX i;

    // look for a match to the substring
    for (i = 0; i < list.GetSize(); i++) {
    if ((list[i].GetLength() >= keyLen) && MatchString(list[i].Left(len), subStr)) {
    matches++;
    lastMatch = i;
    }
    }

    // if we got ONE match, we have a winner
    if (matches == 1)
    return lastMatch+1;

    // if we have no matches, then there is no point continuing
    if (matches == 0)
    return P_MAX_INDEX;

    // if we have more than one match, try the next char
    len++;
    }

    // too many matches
    return 0;
    }


    MyH323Connection::MyH323Connection(MyH323EndPoint & _ep, unsigned callReference)
    : H323Connection(_ep, callReference), ep(_ep)
    {
    basename = psprintf("%04i%02i%02i_%02i%02i%02i", callStartTime.GetYear(), callStartTime.GetMonth(), callStartTime.GetDay(),
    callStartTime.GetHour(), callStartTime.GetMinute(), callStartTime.GetSecond());
    recordFile = NULL;
    ogmChannel = NULL;

    receiveCodecName = transmitCodecName = "none";

    cout << "Opening connection" << endl;

    currentMenu = 0;
    digits = "";

    PConfig config;
    PStringList sections = config.GetSections();
    PINDEX i;
    for (i = 0; i < sections.GetSize(); i++) {
    if (sections[i].Find(MENU_PREFIX) == 0)
    menuNames.AppendString(sections[i]);
    }

    }

    MyH323Connection::~MyH323Connection()
    {
    cout << "Closing connection" << endl;

    PTime now;
    PTimeInterval interval = now - recordStartTime;
    PString addr = GetControlChannel().GetRemoteAddress();

    PString codecStr = receiveCodecName + "/" + transmitCodecName;
    unsigned duration = (unsigned)((interval.GetMilliSeconds()+999)/1000);

    LogCall(recordFn, addr, GetRemotePartyName(), duration, codecStr, product);

    if ((recordFile!= NULL) && (recordFile->WasRecordStarted()) && !ep.GetRunCmd().IsEmpty()) {
    PString cmdStr = ep.GetRunCmd() &
    recordFn &
    "\" + addr + "\" &
    "\"" + GetRemotePartyName() + "\"" &
    PString(PString::Unsigned, duration) &
    "\"" + codecStr + "\"" &
    "\"" + product + "\"";
    PTRACE(1, "Executing : " << cmdStr);
    system((const char *)cmdStr);
    } else {
    PTRACE(1, "No action to perform at end of record");
    }

    if (ogmChannel != NULL)
    delete ogmChannel;

    if (recordFile != NULL)
    delete recordFile;

    if (ep.GetDeleteAfterRecord()) {
    PTRACE(1, "Removing " << recordFn << " as requested by option");
    PFile::Remove(recordFn);
    }
    }

    H323Connection::AnswerCallResponse
    MyH323Connection::OnAnswerCall(const PString & caller,
    const H323SignalPDU & setupPDU,
    H323SignalPDU & /*connectPDU*/)
    {
    product = "Unknown";

    const H225_Setup_UUIE & setup = setupPDU.m_h323_uu_pdu.m_h323_message_body;
    const H225_EndpointType & epInfo = setup.m_sourceInfo;

    if (epInfo.HasOptionalField(H225_EndpointType::e_vendor)) {
    const H225_VendorIdentifier & vendorInfo = epInfo.m_vendor;
    if (vendorInfo.HasOptionalField(H225_VendorIdentifier::e_productId))
    product = vendorInfo.m_productId.AsString();
    if (vendorInfo.HasOptionalField(H225_VendorIdentifier::e_versionId))
    product = product + "/" + vendorInfo.m_versionId.AsString();
    }

    cout << "Accepting call from " << caller << " using " << product << endl;

    return AnswerCallNow;
    }
    //
    //关键的东西都在这里
    // 从传入的codec的的类别来判断H323 Endpoint正在使用什么样的codec进行数据的编码解码
    //显然我们一开始的设定影响了现在的codec, 我们设定H323 Endpoint 的能力是G711,
    // 所以这里应该是IsDescendant from H323_muLawCodec::Class() 或者H323_ALawCodec::Class().
    //
    BOOL MyH323Connection::OpenAudioChannel(BOOL isEncoding,
    unsigned /* bufferSize */,
    H323AudioCodec & codec)
    {
    codec.SetSilenceDetectionMode(H323AudioCodec::NoSilenceDetection);
    PStringStream codecName;
    codecName << codec;

    PString ogm;
    BOOL isPCM = FALSE;

    if (codec.IsDescendant(G7231_File_Codec::Class())) {
    ogm = ep.GetG7231OGM();
    isPCM = FALSE;
    } else if (codec.IsDescendant(H323_GSM0610Codec::Class())) {
    ogm = ep.GetGSMOGM();
    isPCM = TRUE;
    } else if (codec.IsDescendant(MicrosoftGSMCodec::Class())) {
    ogm = ep.GetGSMOGM();
    isPCM = TRUE;
    } else if (codec.IsDescendant(H323_muLawCodec::Class())) {
    ogm = ep.GetG711OGM();
    isPCM = TRUE;
    } else if (codec.IsDescendant(H323_ALawCodec::Class())) {
    ogm = ep.GetG711OGM();
    isPCM = TRUE;
    } else if (codec.IsDescendant(H323_LPC10Codec::Class())) {
    ogm = ep.GetLPC10OGM();
    isPCM = TRUE;
    #ifdef SPEEX_CODEC
    } else if (codec.IsDescendant(SpeexCodec::Class())) {
    ogm = ep.GetSPEEXOGM();
    isPCM = TRUE;
    #endif
    } else {
    cerr << "Unknown codec \"" << codecName << endl;
    return FALSE;
    }


    PWaitAndSignal mutex(connMutex);

    if ((recordFile == NULL) && (isEncoding == FALSE)) {
    if (isPCM) {
    if (ep.GetRecordWav() == TRUE)
    recordFn = ep.GetDirectory() + (basename + ".wav");
    else
    recordFn = ep.GetDirectory() + (basename + ".sw");
    recordFile = new PCM_RecordFile (*this, recordFn, ep.GetCallLimit());
    } else {
    if (ep.GetRecordWav() == TRUE)
    recordFn = ep.GetDirectory() + (basename + ".wav");
    else
    recordFn = ep.GetDirectory() + (basename + ".g723");
    recordFile = new G7231_RecordFile(*this, recordFn, ep.GetCallLimit());
    }
    }

    // 这里创建了我们想用的通道
    if ((ogmChannel == NULL) && (isEncoding == TRUE)) {
    if (isPCM)
    ogmChannel = new PCM_OGMChannel(*this);
    else
    ogmChannel = new G7231_OGMChannel(*this);
    }

    if (isEncoding) {

    if (ep.GetHangupAfterPlay())
    ogmChannel->SetPlayOnce();

    if (ogm.Find("%s"))
    ogm.Replace("%s", e164Number);

    transmitCodecName = codecName;
    if (!StartMenu(0)) {
    if (!PFile::Exists(ogm))
    cerr << "error: cannot find OGM \"" << ogm << "\"" << endl;
    else
    ogmChannel->QueueFile(ogm);
    if (!ep.GetNoRecordG7231())
    ogmChannel->SetRecordTrigger();
    }

    //这里讲通道附着在codec上, 放音
    codec.AttachChannel(ogmChannel, FALSE);
    } else {
    receiveCodecName = codecName;
    //这里讲通道附着在codec上. 录音
    codec.AttachChannel(recordFile, FALSE);
    }
    return TRUE;
    }

    BOOL MyH323Connection::OnStartLogicalChannel(H323Channel & channel)
    {
    if (!H323Connection::OnStartLogicalChannel(channel))
    return FALSE;

    cout << "Started logical channel: ";

    switch (channel.GetDirection()) {
    case H323Channel::IsT
    发布人:dingguijin 来自: