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

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

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

  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. }  


我们先来看看如何分配一个呼叫标识:
  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值:

  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. }  

设置呼叫参数:

  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. }  


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值