在上一篇学习笔记 从simple_pjsua.c示例程序了解PJSUA-LIB的基本使用流程中,使用了PJSUA层的

pjsua_call_make_call来发起一个呼叫,那么这个发起呼叫的流程是怎样的呢?先来看看这个函数:

[cpp] view plaincopy
  1. /*
  2. * Make outgoing call to the specified URI using the specified account.
  3. */
  4. PJ_DEF(pj_status_t) pjsua_call_make_call(pjsua_acc_id acc_id,
  5. const pj_str_t *dest_uri,
  6. const pjsua_call_setting *opt,
  7. void *user_data,
  8. const pjsua_msg_data *msg_data,
  9. pjsua_call_id *p_call_id)
  10. {
  11. pj_pool_t *tmp_pool = NULL;
  12. pjsip_dialog *dlg = NULL;
  13. pjsua_acc *acc;
  14. pjsua_call *call;
  15. int call_id = -1;
  16. pj_str_t contact;
  17. pj_status_t status;
  18. /* Check that account is valid */
  19. PJ_ASSERT_RETURN(acc_id>=0 || acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc),
  20. PJ_EINVAL);
  21. /* Check arguments */
  22. PJ_ASSERT_RETURN(dest_uri, PJ_EINVAL);
  23. PJ_LOG(4,(THIS_FILE, "Making call with acc #%d to %.*s", acc_id,
  24. (int)dest_uri->slen, dest_uri->ptr));
  25. pj_log_push_indent();
  26. PJSUA_LOCK();
  27. //    创建声音设备
  28. /* Create sound port if none is instantiated, to check if sound device
  29. * can be used. But only do this with the conference bridge, as with
  30. * audio switchboard (i.e. APS-Direct), we can only open the sound
  31. * device once the correct format has been known
  32. */
  33. if (!pjsua_var.is_mswitch && pjsua_var.snd_port==NULL &&
  34. pjsua_var.null_snd==NULL && !pjsua_var.no_snd)
  35. {
  36. status = pjsua_set_snd_dev(pjsua_var.cap_dev, pjsua_var.play_dev);
  37. if (status != PJ_SUCCESS)
  38. goto on_error;
  39. }
  40. //    检查SIP帐号
  41. acc = &pjsua_var.acc[acc_id];
  42. if (!acc->valid) {
  43. pjsua_perror(THIS_FILE, "Unable to make call because account "
  44. "is not valid", PJ_EINVALIDOP);
  45. status = PJ_EINVALIDOP;
  46. goto on_error;
  47. }
  48. //    创建呼叫标识
  49. /* Find free call slot. */
  50. call_id = alloc_call_id();
  51. if (call_id == PJSUA_INVALID_ID) {
  52. pjsua_perror(THIS_FILE, "Error making call", PJ_ETOOMANY);
  53. status = PJ_ETOOMANY;
  54. goto on_error;
  55. }
  56. //    复位呼叫参数
  57. /* Clear call descriptor */
  58. reset_call(call_id);
  59. call = &pjsua_var.calls[call_id];
  60. /* Associate session with account */
  61. call->acc_id = acc_id;
  62. call->call_hold_type = acc->cfg.call_hold_type;
  63. //    设置呼叫参数
  64. /* Apply call setting */
  65. status = apply_call_setting(call, opt, NULL);
  66. if (status != PJ_SUCCESS) {
  67. pjsua_perror(THIS_FILE, "Failed to apply call setting", status);
  68. goto on_error;
  69. }
  70. /* Create temporary pool */
  71. tmp_pool = pjsua_pool_create("tmpcall10", 512, 256);
  72. /* Verify that destination URI is valid before calling
  73. * pjsua_acc_create_uac_contact, or otherwise there
  74. * a misleading "Invalid Contact URI" error will be printed
  75. * when pjsua_acc_create_uac_contact() fails.
  76. */
  77. if (1) {
  78. pjsip_uri *uri;
  79. pj_str_t dup;
  80. //        分析被叫SIP号码
  81. pj_strdup_with_null(tmp_pool, &dup, dest_uri);
  82. uri = pjsip_parse_uri(tmp_pool, dup.ptr, dup.slen, 0);
  83. if (uri == NULL) {
  84. pjsua_perror(THIS_FILE, "Unable to make call",
  85. PJSIP_EINVALIDREQURI);
  86. status = PJSIP_EINVALIDREQURI;
  87. goto on_error;
  88. }
  89. }
  90. /* Mark call start time. */
  91. pj_gettimeofday(&call->start_time);
  92. /* Reset first response time */
  93. call->res_time.sec = 0;
  94. //    创建Contact头域
  95. /* Create suitable Contact header unless a Contact header has been
  96. * set in the account.
  97. */
  98. if (acc->contact.slen) {
  99. contact = acc->contact;
  100. } else {
  101. status = pjsua_acc_create_uac_contact(tmp_pool, &contact,
  102. acc_id, dest_uri);
  103. if (status != PJ_SUCCESS) {
  104. pjsua_perror(THIS_FILE, "Unable to generate Contact header",
  105. status);
  106. goto on_error;
  107. }
  108. }
  109. //    创建SIP对话(Dialog)
  110. /* Create outgoing dialog: */
  111. status = pjsip_dlg_create_uac( pjsip_ua_instance(),
  112. &acc->cfg.id, &contact,
  113. dest_uri, dest_uri, &dlg);
  114. if (status != PJ_SUCCESS) {
  115. pjsua_perror(THIS_FILE, "Dialog creation failed", status);
  116. goto on_error;
  117. }
  118. /* Increment the dialog's lock otherwise when invite session creation
  119. * fails the dialog will be destroyed prematurely.
  120. */
  121. pjsip_dlg_inc_lock(dlg);
  122. //    设置Via头域
  123. if (acc->cfg.allow_via_rewrite && acc->via_addr.host.slen > 0)
  124. pjsip_dlg_set_via_sent_by(dlg, &acc->via_addr, acc->via_tp);
  125. //    设置安全级别,安全级别有何作用?
  126. /* Calculate call's secure level */
  127. call->secure_level = get_secure_level(acc_id, dest_uri);
  128. //    设置用户数据,用户数据是什么?
  129. /* Attach user data */
  130. call->user_data = user_data;
  131. //    复制消息数据,消息数据有何作用?
  132. /* Store variables required for the callback after the async
  133. * media transport creation is completed.
  134. */
  135. if (msg_data) {
  136. call->async_call.call_var.out_call.msg_data = pjsua_msg_data_clone(
  137. dlg->pool, msg_data);
  138. }
  139. //    保存对话信息
  140. call->async_call.dlg = dlg;
  141. /* Temporarily increment dialog session. Without this, dialog will be
  142. * prematurely destroyed if dec_lock() is called on the dialog before
  143. * the invite session is created.
  144. */
  145. pjsip_dlg_inc_session(dlg, &pjsua_var.mod);
  146. //    初始化媒体通道
  147. /* Init media channel */
  148. status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAC,
  149. call->secure_level, dlg->pool,
  150. NULL, NULL, PJ_TRUE,
  151. &on_make_call_med_tp_complete);
  152. //    调用媒体传输回调函数
  153. if (status == PJ_SUCCESS) {
  154. status = on_make_call_med_tp_complete(call->index, NULL);
  155. if (status != PJ_SUCCESS)
  156. goto on_error;
  157. } else if (status != PJ_EPENDING) {
  158. pjsua_perror(THIS_FILE, "Error initializing media channel", status);
  159. pjsip_dlg_dec_session(dlg, &pjsua_var.mod);
  160. goto on_error;
  161. }
  162. /* Done. */
  163. if (p_call_id)
  164. *p_call_id = call_id;
  165. pjsip_dlg_dec_lock(dlg);
  166. pj_pool_release(tmp_pool);
  167. PJSUA_UNLOCK();
  168. pj_log_pop_indent();
  169. return PJ_SUCCESS;
  170. on_error:
  171. if (dlg) {
  172. /* This may destroy the dialog */
  173. pjsip_dlg_dec_lock(dlg);
  174. }
  175. if (call_id != -1) {
  176. pjsua_media_channel_deinit(call_id);
  177. reset_call(call_id);
  178. }
  179. pjsua_check_snd_dev_idle();
  180. if (tmp_pool)
  181. pj_pool_release(tmp_pool);
  182. PJSUA_UNLOCK();
  183. pj_log_pop_indent();
  184. return status;
  185. }

我们先来看看如何分配一个呼叫标识:

[cpp] view plaincopy
  1. /* Allocate one call id */
  2. static pjsua_call_id alloc_call_id(void)
  3. {
  4. pjsua_call_id cid;
  5. #if 1
  6. /* New algorithm: round-robin */
  7. if (pjsua_var.next_call_id >= (int)pjsua_var.ua_cfg.max_calls ||
  8. pjsua_var.next_call_id < 0)
  9. {
  10. pjsua_var.next_call_id = 0;
  11. }
  12. //    从next_call_id到max_calls之间找一个空闲的calls数组元素
  13. for (cid=pjsua_var.next_call_id;
  14. cid<(int)pjsua_var.ua_cfg.max_calls;
  15. ++cid)
  16. {
  17. if (pjsua_var.calls[cid].inv == NULL &&
  18. pjsua_var.calls[cid].async_call.dlg == NULL)
  19. {
  20. ++pjsua_var.next_call_id;
  21. return cid;
  22. }
  23. }
  24. //    从0到next_call_id之间找一个空闲的calls数组元素
  25. for (cid=0; cid < pjsua_var.next_call_id; ++cid) {
  26. if (pjsua_var.calls[cid].inv == NULL &&
  27. pjsua_var.calls[cid].async_call.dlg == NULL)
  28. {
  29. ++pjsua_var.next_call_id;
  30. return cid;
  31. }
  32. }
  33. #else
  34. /* Old algorithm */
  35. for (cid=0; cid<(int)pjsua_var.ua_cfg.max_calls; ++cid) {
  36. if (pjsua_var.calls[cid].inv == NULL)
  37. return cid;
  38. }
  39. #endif
  40. return PJSUA_INVALID_ID;
  41. }

从上面的函数来看,这里的分配呼叫标识只是在calls数据中寻找一个空闲的单元(用于存放呼叫数据),这个呼叫标识并不是SIP协议里面的CALL ID的概念。

reset_call函数就是将呼叫参数设置为0值:

[cpp] view plaincopy
  1. *
  2. * Reset call descriptor.
  3. */
  4. static void reset_call(pjsua_call_id id)
  5. {
  6. pjsua_call *call = &pjsua_var.calls[id];
  7. unsigned i;
  8. pj_bzero(call, sizeof(*call));
  9. call->index = id;
  10. call->last_text.ptr = call->last_text_buf_;
  11. for (i=0; i<PJ_ARRAY_SIZE(call->media); ++i) {
  12. pjsua_call_media *call_med = &call->media[i];
  13. call_med->ssrc = pj_rand();
  14. call_med->strm.a.conf_slot = PJSUA_INVALID_ID;
  15. call_med->strm.v.cap_win_id = PJSUA_INVALID_ID;
  16. call_med->strm.v.rdr_win_id = PJSUA_INVALID_ID;
  17. call_med->call = call;
  18. call_med->idx = i;
  19. call_med->tp_auto_del = PJ_TRUE;
  20. }
  21. pjsua_call_setting_default(&call->opt);
  22. pj_timer_entry_init(&call->reinv_timer, PJ_FALSE,
  23. (void*)(pj_size_t)id, &reinv_timer_cb);
  24. }

设置呼叫参数:

[cpp] view plaincopy
  1. static pj_status_t apply_call_setting(pjsua_call *call,
  2. const pjsua_call_setting *opt,
  3. const pjmedia_sdp_session *rem_sdp)
  4. {
  5. pj_assert(call);
  6. if (!opt)
  7. return PJ_SUCCESS;
  8. #if !PJMEDIA_HAS_VIDEO
  9. pj_assert(opt->vid_cnt == 0);
  10. #endif
  11. call->opt = *opt;
  12. //    如果呼叫已建立,则设置本端的对话角色
  13. //    如果有远端SDP,则本端为UAS(User Agent Server),否则为UAC
  14. /* If call is established, reinit media channel */
  15. if (call->inv && call->inv->state == PJSIP_INV_STATE_CONFIRMED) {
  16. pjsip_role_e role = rem_sdp? PJSIP_ROLE_UAS : PJSIP_ROLE_UAC;
  17. pj_status_t status;
  18. //        初始化媒体通道
  19. status = pjsua_media_channel_init(call->index, role,
  20. call->secure_level,
  21. call->inv->pool_prov,
  22. rem_sdp, NULL,
  23. PJ_FALSE, NULL);
  24. if (status != PJ_SUCCESS) {
  25. pjsua_perror(THIS_FILE, "Error re-initializing media channel",
  26. status);
  27. return status;
  28. }
  29. }
  30. return PJ_SUCCESS;
  31. }

PJSIP学习笔记——PJSUA层发起呼叫的主要流程相关推荐

  1. 面向对象的编程思想写单片机程序——(3)学习笔记 之 程序分层、数据产生流程

    系列文章目录 面向对象的编程思想写单片机程序--(1)学习笔记 之 程序设计 面向对象的编程思想写单片机程序--(2)学习笔记 之 怎么抽象出结构体 面向对象的编程思想写单片机程序--(3)学习笔记 ...

  2. 软件调试学习笔记(四)—— 异常的处理流程

    软件调试学习笔记(四)-- 异常的处理流程 要点回顾 异常的处理流程 实验1:理解调试器与异常的关系 未处理异常:最后一道防线 实验2:理解UnhandledExceptionFilter执行流程 实 ...

  3. 学习笔记(一)(x264编码流程)

    学习笔记(一)(x264编码流程) 作者 张士辉 11月 2, 2007 <script type=text/javascript></script> <script s ...

  4. CoAP学习笔记——服务器端繁忙时的处理请求流程

    CoAP学习笔记--服务器端繁忙时的处理请求流程 CoAP是一个简单的请求响应机制,对于一个给定的请求便有一个相应的响应. 很多时候,如果服务器不能立即响应一个CON请求,服务器只能返回一个空应答,这 ...

  5. 1.3)深度学习笔记------浅层神经网络

    目录 1)Neural Network Overview 2)Neural Network Representation 3)Computing a Neural Network's Output(重 ...

  6. AMBA CHI协议学习笔记2-Link层

    [本系列文章是我自己对CHI协议的学习笔记,仅供参考,欢迎指正.] [文中的红色字体是我自己的标注] 一.CHI的六种通道 CHI的通道,有收/发两个方向,在发送方向上,有三个通道分别是REQ,WDA ...

  7. 移动网优大神VoLTE学习笔记(三):注册信令流程

    文/张阳,本文来源于微信公众号:网优小谈(wireless_talk) 对于一个网络工程师而言,解读电信网络的信令是分析定位网络问题的有效手段之一.所谓兵马未动,粮草先行,网络信令就是先于业务流程的& ...

  8. Java 虚拟机学习笔记 | 类加载过程和对象的创建流程

    前言 创建对象是 Java 语言绕不开的话题,那么对象是如何创建出来的呢?我们今天就来聊一聊.对象创建第一步就是检查类是否加载,而类的加载又牵扯到类的加载过程.如果单说对象的创建而绕开类的加载过程,感 ...

  9. PJSIP学习笔记15 -- PJSUA应用程序中的会议桥

    注: 上面的tpport,是我为了帮助理解对应关系,为app_config增加的成员变量, 源码中并无这个成员. 可以使用PJSUA程序控制台命令cl来查看系统中的conference_port 未拨 ...

最新文章

  1. centos7+ansible自动化工具使用
  2. php route取值,route命令详解
  3. 用python画烟花-python 实现漂亮的烟花,樱花,玫瑰花
  4. qooxdoo学习笔记一
  5. Java 8 - 收集器Collectors
  6. 关键字提取_从杂乱无章的表格中找出关键字,批量提取字符
  7. ubuntu查看系统位数,版本号——百度
  8. Qt QWidget实现手势缩放和平移(一)
  9. SQL Server 性能调优(cpu)
  10. php判断值是否为空然后定义,判断php变量是不是定义,是否为空
  11. MemCache对PHP页面的缓存加速优化
  12. AttributeError: 'str' object has no attribute 'decode'
  13. HDP SandBox 安装与初步配置
  14. python显示图片_python 一个figure上显示多个图像的实例
  15. 16进制字符串转字节数组
  16. HTML:如何创建表格
  17. 百家讲坛-郦波评说曾国藩家训
  18. MobaXterm上方工具栏显示
  19. 一文搞懂AWS EC2, IGW, RT, NAT, SG 基础篇下
  20. 树形结构的处理——组合模式(四)

热门文章

  1. java错位_java – 如何保护自己免受参数错位的影响
  2. Java开发神器Lombok使用详解
  3. oracle19c xp安装 客户端_windows下安装oracle19c
  4. vs设置html的模板快
  5. 201521123014 《Java程序设计》第11周学习总结
  6. AutoLayout--masonry使用
  7. 彼得原理(The Peter Principle)
  8. HTML5中lineCap端点样式遇到closePath()
  9. PHP编写命令行脚本和后台运行程序的注意事项
  10. jquery动画 -- 1.加载指示器