首页 > 编程语言 >Opal 教程之二 SipIM 例子源码分析

Opal 教程之二 SipIM 例子源码分析

时间:2023-05-05 10:15:12浏览次数:38  
标签:SIP SipIM Opal args sipim manager 源码

在互联网上.极少有关于Opal的中文教程.因此敝人决定把学习Opal的过程记录下来
勉励自己.抑或给后来者留下一些入门的资料.
最近感觉学习Opal已经慢慢摸出了门道.前几个星期.摸索在 VS2005编译Opal 花了我好多天的时间.
把我的耐心都磨得差不多快要放弃研究Opal.作为初学者.学习的难度被一些小问题无限放大.
很少能找到关于Opal 的资料.
呵呵.所以我决定要为Opal 在中国程序员 普及做出贡献(是不是大言不惭呢?欢迎拍砖哦.).


上面的文字.做为我Opal学习之路的前奏。


先从 SiPIM这个例子开始吧

刚刚开始的时候.我对这个名字有点疑惑.究竟是Simple IM 的缩写
还是SIP 协议的IM 呢.(IM 是即时通讯的意思).答案是后者
这个例子.演示了两个SipIM 程序 如何通讯.主要是文字信息的传递
非常合适初学者学习.


还是分析源码吧.程序员就喜欢比较直接一点

在ptlib框架开发的控制台程序一定要有继承PProcess的子类.控制台程序
从该子类的Main函数入口.

main.h 文件
class SipIM : public PProcess
{
  //这是规范.在框架Ptlib下面的类都这样声明
  PCLASSINFO(SipIM, PProcess)

  public:
    SipIM();
    ~SipIM();

    //定义了三者传输协议,
    //1.MSRP 协议
    //2.SIP 协议
    //3.T140 协议
    enum Mode {
      Use_MSRP,
      Use_SIPIM,
      Use_T140
    };
   
    //控制台程序入口
    virtual void Main();

  private:
    //SimIM 程序的核心管理类
    MyManager * m_manager;
};


main.CXX 文件

//声明控制台程序从SimIM 入口.这是为了确保 ptlib 的初始化类的顺序不出现问题
PCREATE_PROCESS(SipIM);


//构造函数.并把成员函数m_manager指向NULL
//OPAL_MAJOR, OPAL_MINOR, ReleaseCode, OPAL_BUILD 这些都是Opal 版本号标示
SipIM::SipIM()
  : PProcess("OPAL SIP IM", "SipIM", OPAL_MAJOR, OPAL_MINOR, ReleaseCode, OPAL_BUILD)
  , m_manager(NULL)
{
}


SipIM::~SipIM()
{
  delete m_manager;
}


//该例子的主要初始化和处理逻辑都放在Main函数里面

void SipIM::Main()
{
  //获取控制台程序的输入参数引用,本人不仅仅Opal 是初学者.连C++ 也是半桶水
  //怎么输入控制台一些参数设置呢?呵呵.打开命令控制台.把本程序sipim.exe 拖到控制台
  //在该路径后面空一格. 然后输入参数按Enter键即可. 注意控制台是按空格来区分参数的个数.
  //例如 G:\Source\开源\opal-3.8.0-src\opal\bin\sipim\Debug\sipim.exe -uhsinxubin --sipim
  //在普通的控制台程序中.参数输入应该是 sipim.exe -uhsinxubin --sipim 这三个参数.
  //但是在ptlib 中.则把sipim.exe 这个参数去除掉.变成了两个参数.在上面的例子

  PArgList & args = GetArguments();

  //args[0] == -uhsinxubin
  //args[1] == --sipim
  //但是在VS2005 Debug的模式运行.怎么设置输入参数呢?
  //答案是SetArgs()函数.例如
  //args.SetArgs("-uhsinxubin --sipim");
 
  //args.Parse 函数则是对输入参数进行分析.
  //帮助文档 Parse the standard C program arguments into an argument of options and parameters.
  //把命令行参数变成了Options 和 parameters.我也不知道怎么翻译. 感觉很拗口.
  args.Parse(
             "u-user:"
             "h-help."
             "-sipim."
             "-t140."
             "-msrp."
#if PTRACING
             "o-output:"             "-no-output."
             "t-trace."              "-no-trace."
#endif
             , FALSE);

  // 例如 args.SetArgs("-uhsinxubin --sipim"); 经过Parse 分析后.
  // 则得到这样的结果  
  // args.HasOption('u') == true;
  // args.GetOptionString('u') ==BinBin;
  // args.HasOption("sipim") == PTrue;
  // args.HasOption("msrp") == PFalse;

  //请注意在分析字符串中  
  //          "u-user:"
  //           "h-help."
  //           "-sipim."
  //           "-t140."
  //           "-msrp."
  // 包含":" 和"."  符号和"-" 开头.这个两个符号有不同的含义(本人就被这些含义 困惑好久了).
  // ":" 表示选项后面带有参数. 例如  -uhslinxubin (u是选项,hslinxubin 是参数)
  // "." 表示只有选项没有参数  例如  --sipim      (sipim 是选项.没有参数)
  // "-" 表示要想获取该选项.  则输入参数必须多加"-" 例如 --sipim
 
 
  //这段是测试用的.基本可以忽略
  if (args.HasOption('h')) {
    return;
  }


  //实例化核心控制类.记得之前的构造函数? m_manager被设置为NULL
  MyManager m_manager;
 
  //设置用户名称 例如 args.GetOptionString('u') == hslinxubin (呵呵)
  if (args.HasOption('u'))
    m_manager.SetDefaultUserName(args.GetOptionString('u'));
 
  // 设置信令协议.在本例子中 .以后再介绍本例子中的三种协议T140,sipim,msrp
  //   mode == Use_SIPIM;
  //   m_manager.m_imFormat == OpalSIPIM;

  Mode mode;
  if (args.HasOption("t140")) {
    mode = Use_T140;
    m_manager.m_imFormat = OpalT140;
  }
#if OPAL_HAS_SIPIM
  else if (args.HasOption("sipim")) {
    mode = Use_SIPIM;
    m_manager.m_imFormat = OpalSIPIM;
  }
#endif
#if OPAL_HAS_MSRP
  else if (args.HasOption("msrp")) {
    mode = Use_MSRP;
    m_manager.m_imFormat = OpalMSRP;
  }
#endif
  else {
    cout << "error: must select IM mode" << endl;
    return;
  }

  //媒体格式列表,
  OpalMediaFormatList allMediaFormats;

  //初始化SIPEndPoint终端,并打开SIP 监听
  SIPEndPoint * sip  = new SIPEndPoint(m_manager);
  if (!sip->StartListeners(PStringArray())) {
    cerr << "Could not start SIP listeners." << endl;
    return;
  }
 
  //获取SIP 协议能操作的媒体格式列表.
  //例如 UserInput/RFC2833,G.711-uLaw-64k,G.711-ALaw-64k,RFC4175_YCbCr-4:2:0,RFC4175_RGB,
  //SIP-IM,NamedSignalEvent,YUV420P,MSRP,T.140,H.224/H323AnnexQ,H.224/HDLCTunneling
  allMediaFormats += sip->GetMediaFormats();

  //初始化计算机声卡终端. 并获取声卡能支持的媒体格式列表
  //例如 PCM-16-48kHz,PCM-16-32kHz,PCM-16-16kHz,PCM-16
  MyPCSSEndPoint * pcss = new MyPCSSEndPoint(m_manager);
  allMediaFormats += pcss->GetMediaFormats();
 
  //设置声卡的录音和播放设备
  pcss->SetSoundChannelPlayDevice("NULL");
  pcss->SetSoundChannelRecordDevice("NULL");

  //获取能传输的媒体格式。例如 RGB24
  allMediaFormats = OpalTranscoder::GetPossibleFormats(allMediaFormats); // Add transcoders

  //移除掉不能通过线进行传输的媒体格式
  //最后只剩下 UserInput/RFC2833,G.711-uLaw-64k,G.711-ALaw-64k,RFC4175_YCbCr-4:2:0,
  // RFC4175_RGB,SIP-IM,NamedSignalEvent,MSRP,T.140,H.224/H323AnnexQ

  for (PINDEX i = 0; i < allMediaFormats.GetSize(); i++) {
    if (!allMediaFormats[i].IsTransportable())
      allMediaFormats.RemoveAt(i--); // Don't show media formats that are not used over the wire
  }

  m_manager.AddRouteEntry("sip:.*\t.*=pc:*");
  m_manager.AddRouteEntry("pc:.*\t.*=sip:<da>");
  cout << "Available codecs: " << setfill(',') << allMediaFormats << setfill(' ') << endl;

 
  PString imFormatMask = PString("!") + (const char *)m_manager.m_imFormat;
  //设置核心管理类的 所要屏蔽的媒体格式
  // 例如 imFormatMask == "!SIP-IM " 表示屏蔽所有非SIP-IM的媒体格式
  m_manager.SetMediaFormatMask(imFormatMask);

  //把所有非SIP-IM的媒体格式移除
  allMediaFormats.Remove(imFormatMask);
 
  cout << "Codecs to be used: " << setfill(',') << allMediaFormats << setfill(' ') << endl;

  OpalConnection::StringOptions options;
  options.SetAt(OPAL_OPT_AUTO_START, m_manager.m_imFormat.GetMediaType() + ":exclusive");

  //按照正常的顺序.到了这一步.arg.getCount ==0
  //所以一定是监听的状态.如果想测试SipIM 互相发信息,
  // 则必须编译两次.并且在不同的机器上分别运行这个两个程序
  //假如是监听程序.则不用改
  //假如是主动拨打的话.则必须执行下面的
  //args.SetArgs("192.168.0.109");
 
  if (args.GetCount() == 0)
    cout << "Awaiting incoming call ..." << flush;
  else {
   // 本地呼叫 args[0]
    if (!m_manager.SetUpCall("pc:", args[0], m_manager.m_callToken, NULL, 0, &options)) {
      cerr << "Could not start IM to \"" << args[0] << '"' << endl;
      return;
    }
  }

  //等待对方的响应、
  m_manager.m_connected.Wait();

  // 应该是一种实时协议吧
  RFC4103Context rfc4103(m_manager.m_imFormat);

  int count = 1;
  for (;;) {
    PThread::Sleep(5000);
    const char * textData = "Hello, world";

    if (count > 0) {
      // 获取和远程终端建立连接的Call.
      PSafePtr<OpalCall> call = m_manager.FindCallWithLock(m_manager.m_callToken);
      if (call != NULL) {
      // 从Call中获取 Connection .一个Call可以包含多个Connection
        PSafePtr<OpalPCSSConnection> conn = call->GetConnectionAs<OpalPCSSConnection>();
        if (conn != NULL) {

          RTP_DataFrameList frameList = rfc4103.ConvertToFrames("text/plain", textData);
          OpalMediaFormat fmt(m_manager.m_imFormat);
         //把 "Hello world " 这个字符串发送出去.
          for (PINDEX i = 0; i < frameList.GetSize(); ++i) {
            conn->TransmitInternalIM(fmt, (RTP_IMFrame &)frameList[i]);
          }
        }
      }
      --count;
    }
  }

  // Wait for call to come in and finish
  m_manager.m_completed.Wait();
  cout << " completed.";
}

//上面好多概念。我现在也搞不清楚.再让我研究一下吧.再详细的描述.敬请期待哦

标签:SIP,SipIM,Opal,args,sipim,manager,源码
From: https://www.cnblogs.com/kn-zheng/p/17373262.html

相关文章

  • 一个完整的编译Ptlib.Opal 的教程
    想到以前你第一次到我家的情景.有点伤感.还是把它埋在内心深处吧好好的生活.努力把音视频的知识打扎实。接下来的时间就好好研究ptlib和Opal的知识。并把学习的过程记录下来1.从开源中国下载Ptlib最新的版本.ptlib-2.8.0-src.zip2.先单独编译Configure分别用Debug和Release的......
  • java基于springboot+vue的宿舍管理系统、学生宿舍管理系统、高校宿舍管理系统,附源码+
    1、项目介绍java基于springboot+vue的宿舍管理系统、学生宿舍管理系统、高校宿舍管理系统,实现管理员:首页、个人中心、公告信息管理、院系管理、班级管理、学生管理、宿舍信息管理、宿舍安排管理、卫生检查管理、报修信息管理、报修处理管理、缴费信息管理,学生;首页、个人中心、公......
  • Spring源码:Bean生命周期(三)
    前言在之前的文章中,我们已经对bean的准备工作进行了讲解,包括bean定义和FactoryBean判断等。在这个基础上,我们可以更加深入地理解getBean方法的实现逻辑,并在后续的学习中更好地掌握createBean方法的实现细节。getBean用法讲解getBean方法之前,我们先来看看他有几种常见......
  • STL源码分析读书笔记
    主要是关于标准库容器的整理空间配置器主要看SGI的实现,有两个空间配置器_malloc_alloc_template<0>__default_alloc_template<...>用户可以选择单独使用第一个分配器,或者一起使用两个分配器。当用户选择使用两个分配器时,编译器会分别将上述两个分配器typedef成malloc_a......
  • springboot 分析源码欢迎页和图标-> thymeleaf模板引擎常用语法->扩展
    欢迎页: icon: 注意点: thymeleaf模板引擎1.使用thymeleaf模板引擎前要导入对应依赖包2.阅读源码:根据源码说明我们可以将html文件放置在templates目录下,然后通过controller进行跳转即可 controller类://在templates下的东西需要通过controller类来跳转,//需要导入......
  • 从案例中详解go-errgroup-源码
    一、背景某次会议上发表了errorgroup包,一个g失败,其他的g会同时失败的错误言论(看了一下源码中的一句话Thefirstcalltoreturnanon-nilerrorcancelsthegroup,没进一步看其他源码,片面理解了)。//Thefirstcalltoreturnanon-nilerrorcancelsthegroup'scontext......
  • JUC并发编程原理精讲(源码分析)
    1.JUC前言知识JUC即java.util.concurrent涉及三个包:java.util.concurrentjava.util.concurrent.atomicjava.util.concurrent.locks普通的线程代码:ThreadRunnable没有返回值、效率相比入Callable相对较低!Callable有返回值!【工作常用】1.1进程和线程进程:是......
  • 互联网医院系统源码:数据安全与隐私保护问题如何解决?
    当下,互联网医院系统源码已经走进了很多人的视野中,它的作用和好处小编就不用强调了,今天我们来聊另一个话题——隐私与数据安全。在智慧医疗行业,安全问题更是重中之重,这也自然而然成为了老生常谈的一个问题。本文小编将从互联网医院系统源码的数据安全与隐私保护的意义、当前面临的挑......
  • 在线直播系统源码,默认倒计时,自定义输入时间倒计时
    在线直播系统源码,默认倒计时,自定义输入时间倒计时html部分代码 <divid="app">  <inputtype="num"v-model="time">  <inputtype="button" @click="click_input(time)"value="点击">  <div>{{get_cod......
  • @Import注解原理源码分析
    文章结构@Import注解源码的入口位置@Import注解原理收集@Import注解处理收集的imports@Import注解源码的入口位置源码的入口位置在ConfigurationClassParser#doProcessConfigurationClass方法中,至于为什么是这个位置,先按下不表后续会填坑完善。Spring如何解析配置类先简单看下Spr......