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

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


我们先来看看如何分配一个呼叫标识:
[cpp] view plaincopy
  1. /* Allocate one call id */  
  2. static pjsua_call_id alloc_call_id(void)  
  3. {  
  4.     pjsua_call_id cid;  
  5.   
  6. #if 1  
  7.     /* New algorithm: round-robin */  
  8.     if (pjsua_var.next_call_id >= (int)pjsua_var.ua_cfg.max_calls ||   
  9.     pjsua_var.next_call_id < 0)  
  10.     {  
  11.     pjsua_var.next_call_id = 0;  
  12.     }  
  13.   
  14. //    从next_call_id到max_calls之间找一个空闲的calls数组元素  
  15.     for (cid=pjsua_var.next_call_id;  
  16.      cid<(int)pjsua_var.ua_cfg.max_calls;   
  17.      ++cid)   
  18.     {  
  19.     if (pjsua_var.calls[cid].inv == NULL &&  
  20.             pjsua_var.calls[cid].async_call.dlg == NULL)  
  21.         {  
  22.         ++pjsua_var.next_call_id;  
  23.         return cid;  
  24.     }  
  25.     }  
  26.   
  27. //    从0到next_call_id之间找一个空闲的calls数组元素  
  28.     for (cid=0; cid < pjsua_var.next_call_id; ++cid) {  
  29.     if (pjsua_var.calls[cid].inv == NULL &&  
  30.             pjsua_var.calls[cid].async_call.dlg == NULL)  
  31.         {  
  32.         ++pjsua_var.next_call_id;  
  33.         return cid;  
  34.     }  
  35.     }  
  36.   
  37. #else  
  38.     /* Old algorithm */  
  39.     for (cid=0; cid<(int)pjsua_var.ua_cfg.max_calls; ++cid) {  
  40.     if (pjsua_var.calls[cid].inv == NULL)  
  41.         return cid;  
  42.     }  
  43. #endif  
  44.   
  45.     return PJSUA_INVALID_ID;  
  46. }  
从上面的函数来看,这里的分配呼叫标识只是在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.   
  9.     pj_bzero(call, sizeof(*call));  
  10.     call->index = id;  
  11.     call->last_text.ptr = call->last_text_buf_;  
  12.     for (i=0; i<PJ_ARRAY_SIZE(call->media); ++i) {  
  13.     pjsua_call_media *call_med = &call->media[i];  
  14.     call_med->ssrc = pj_rand();  
  15.     call_med->strm.a.conf_slot = PJSUA_INVALID_ID;  
  16.     call_med->strm.v.cap_win_id = PJSUA_INVALID_ID;  
  17.     call_med->strm.v.rdr_win_id = PJSUA_INVALID_ID;  
  18.     call_med->call = call;  
  19.     call_med->idx = i;  
  20.     call_med->tp_auto_del = PJ_TRUE;  
  21.     }  
  22.     pjsua_call_setting_default(&call->opt);  
  23.     pj_timer_entry_init(&call->reinv_timer, PJ_FALSE,  
  24.             (void*)(pj_size_t)id, &reinv_timer_cb);  
  25. }  

设置呼叫参数:

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


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/434952.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Jquery Money 验证,转换成千分位

function Convert(amtStr) {var a, renum ;var j 0;var a1 , a2 , a3 ;var tes /^-/;a amtStr.replace(/,/g, "");a a.replace(/[^-\.,0-9]/g, ""); //删除无效字符a a.replace(/(^\s*)|(\s*$)/g, ""); //trimif (tes.test(a)) a1 -;e…

离职证明电子版_离职证明中说劳动者因违纪离职的怎么办?

作者&#xff1a;吕武茂 衣尚民 范涛1.律师有话说&#xff1a;根据法律规定&#xff0c;解除或终止劳动合同后&#xff0c;给员工开离职证明是企业的法定义务&#xff0c;离职证明内容仅包括&#xff1a;劳动合同期限、解除或者终止劳动合同的日期、工作岗位、在本单位的工作年…

sip消息概念(一)

SIP 也是类似 HTTP 的一个协议集合&#xff0c;在网上搜索了一下相关的信息&#xff0c;摘录如下&#xff1a; SIP消息的第一行包含消息的类型和所使用的SIP版本&#xff08;2.0&#xff09;。在请求中&#xff0c;这一行还包含一个叫做SIP URI的地址。这代表消息的目的地。 这…

python特征递归消除

一、基础知识了解 特征递归消除官方给了两者方法 1.RFE 2.RFECV 一.RFE 官方解释 链接&#xff1a;sklearn.feature_selection.RFE — scikit-learn 1.0.2 documentationhttps://scikit-learn.org/stable/modules/generated/sklearn.feature_selection.RFE.html?highligh…

SIP 中的Dialog,call,session 和 transaction .

如果你对Sip协议中Call, Dialog, Transaction和Message之间的关系感觉到迷惑,那么,那么我可以告诉你,你并不孤单,因为大多数初学者对于这些名词之间的关系都会感到疑惑.Messages(消息) 消息是在服务器和客户端之间交换的独立文本, 有两种类型的消息,分别是请求(Requests)和响应…

JS 获取当前日期时间(兼容IE FF)

以前在页面中获得当前时间的方法如下&#xff1a; function SelectTodayClient() {var d new Date();var taday d.getYear() "-" (d.getMonth() 1) "-" d.getDate(); alert($(taday);} IE运行正常&#xff0c;FF运行如下&#xff1a; 这…

再次携号转网_潍坊一小伙欲携号转网屡被拒 联通客服:试运营状态不支持携转...

携号转网&#xff0c;也就是在手机号码保持不变的情况下&#xff0c;可以更换运营商&#xff0c;潍坊市民夏先生给记者打来电话说&#xff0c;他有一张联通的电话卡&#xff0c;想要办理携号转网业务&#xff0c;十几天过去了&#xff0c;号没转出去&#xff0c;还生了一肚子气…

机器学习参数优化数据改用所有数据还是训练集

参数优化过程中所用数据应该用训练集 &#xff08;占所有数据的一部分&#xff09;&#xff0c;如果用所有数据会导致模型评估的所有结果都偏高&#xff0c;因为这些评估都涉及测试集&#xff0c;如果用所有数据进行训练&#xff0c;导致测试集预测结果大部分都正确&#xff0c…

《当程序员的那些狗日日子》(三十六)无名的配角

人事助理姐姐帮我办理完续签手续后&#xff0c;我也回到原来的工作状态&#xff0c;继续处理手上的工作。 之前洪协助我完成了客服后台的一些新功能&#xff0c;但是在投入使用后&#xff0c;我发现这些功能还是存在较大的问题&#xff0c;本来我以为就要离开公司了&#xff0c…

8个树莓派超级计算机_6 个可以尝试的树莓派教程

这些树莓派项目均旨在简化你的生活并提高生产力。-- Lauren Pritchett(作者)没有什么比体验树莓派创作结果更令人兴奋了。经过数小时的编程、测试和徒手构建&#xff0c;你的项目终于开始成形&#xff0c;你不禁大喊 “哇哦&#xff01;”树莓派可以带给日常生活的可能性让我着…

SIP应答消息状态码与功能

SIP应答消息状态码与功能 类型 状态码 状态说明 临时应答(1XX) 100 Trying 正在处理中 180 Ringing 振铃 181 call being forwarder 呼叫正在前向 182 queue 排队 181* session progress 会话进行 会话成功(2XX) 200 OK 会话成功 重定向(3XX) 300 multiple 多重选择 301 moved …

sklearn的逻辑回归

官方逻辑回归链接 sklearn.linear_model.LogisticRegression — scikit-learn 1.0.2 documentationhttps://scikit-learn.org/stable/modules/generated/sklearn.linear_model.LogisticRegression.html#sklearn.linear_model.LogisticRegression 一&#xff0c;参数说明 1.1…

Ext scope 学习

首先&#xff0c;用一句话来概括scope的作用&#xff1a;scope就是用来解决 js 中 this 的指向问题。 下面进行讨论&#xff1a; 1、 关于JavaScript中this的使用&#xff0c;这是一个由来已久的问题了。我们这里就不介绍它的发展历史了&#xff0c;只结合具体的例子&#xff…

python某公司为员工发放奖品_python 练习2

#######假定有下面这样的列表:names [fentiao, fendai, fensi, apple]输出结果为:I have fentiao, fendai, fensi and apple.names [fentiao, fendai, fensi, apple]print I have ,.join(names[:-1]) and names[-1]######系统里面有用户 用户有密码users [root,westos…

SIP 协议 理解

SIP主要支持以下5个方面信令技术功能&#xff1a; 用户定位&#xff1a;确定通信所使用的终端系统位置。主要是和sip服务器实体中的注册服务器和非sip实体的 位置服务器相关&#xff0c;每个用户在上线的所在的sip实体&#xff0c;会将该用户的sip号&#xff08;sip域中的唯一…

sklearn中的支持向量机SVC

官方链接 sklearn.svm.SVC — scikit-learn 1.0.2 documentationhttps://scikit-learn.org/stable/modules/generated/sklearn.svm.SVC.html#sklearn.svm.SVC该方法是基于libsvm&#xff08;支持向量机库&#xff09;实现的 libsvm官网 LIBSVM -- A Library for Support Vec…

python学习正则表达式_Python学习教程(Python学习路线):正则表达式—第二讲

Python学习教程(Python学习路线)&#xff1a;正则表达式&#xff01;前面也有跟大家分享关于正则表达式的Python学习教程&#xff0c;但是有伙伴说还是没完全吸收&#xff01;这些是很正常的&#xff0c;正则表达式难吗&#xff1f;肯定难&#xff01;不是一次教程就能完全掌握…

ArcGIS之GP服务发布

模型构建器官方解释&#xff1a; 链接 什么是模型构建器&#xff1f; 1.模型构建器是一个用来创建、编辑和管理模型的应用程序。模型是将一系列地理处理工具串联在一起的工作流&#xff0c;它将其中一个工具的输出作为另一个工具的输入。也可以将模型构建器看成是用于构建工…

认知与设计:理解UI设计准则——序

交互计算机系统的设计不仅仅是门艺术&#xff0c;也是&#xff08;至少追求成为&#xff09;一门科学。好吧&#xff0c;实际上不是科学&#xff0c;但可以说是一门计算机和认知学的交叉工程学科&#xff0c;基于科学的技术方法创造满足指定需求的交互系统。   就像汽车、建筑…