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

发布时间:2016-12-8 16:02:05 编辑:www.fx114.net 分享查询网我要评论
本篇文章主要介绍了"PJSIP学习笔记——PJSUA层发起呼叫的主要流程",主要涉及到PJSIP学习笔记——PJSUA层发起呼叫的主要流程方面的内容,对于PJSIP学习笔记——PJSUA层发起呼叫的主要流程感兴趣的同学可以参考一下。

在上一篇学习笔记从simple_pjsua.c示例程序了解PJSUA-LIB的基本使用流程中,使用了PJSUA层的 pjsua_call_make_call来发起一个呼叫,那么这个发起呼叫的流程是怎样的呢?先来看看这个函数: [cpp] view plaincopy /*  * Make outgoing call to the specified URI using the specified account.  */  PJ_DEF(pj_status_t) pjsua_call_make_call(pjsua_acc_id acc_id,                       const pj_str_t *dest_uri,                       const pjsua_call_setting *opt,                       void *user_data,                       const pjsua_msg_data *msg_data,                       pjsua_call_id *p_call_id)  {      pj_pool_t *tmp_pool = NULL;      pjsip_dialog *dlg = NULL;      pjsua_acc *acc;      pjsua_call *call;      int call_id = -1;      pj_str_t contact;      pj_status_t status;          /* Check that account is valid */      PJ_ASSERT_RETURN(acc_id>=0 || acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc),                PJ_EINVAL);        /* Check arguments */      PJ_ASSERT_RETURN(dest_uri, PJ_EINVAL);        PJ_LOG(4,(THIS_FILE, "Making call with acc #%d to %.*s", acc_id,            (int)dest_uri->slen, dest_uri->ptr));        pj_log_push_indent();        PJSUA_LOCK();    //    创建声音设备      /* Create sound port if none is instantiated, to check if sound device      * can be used. But only do this with the conference bridge, as with       * audio switchboard (i.e. APS-Direct), we can only open the sound       * device once the correct format has been known      */      if (!pjsua_var.is_mswitch && pjsua_var.snd_port==NULL &&       pjsua_var.null_snd==NULL && !pjsua_var.no_snd)       {      status = pjsua_set_snd_dev(pjsua_var.cap_dev, pjsua_var.play_dev);      if (status != PJ_SUCCESS)          goto on_error;      }    //    检查SIP帐号      acc = &pjsua_var.acc[acc_id];      if (!acc->valid) {      pjsua_perror(THIS_FILE, "Unable to make call because account "               "is not valid", PJ_EINVALIDOP);      status = PJ_EINVALIDOP;      goto on_error;      }    //    创建呼叫标识      /* Find free call slot. */      call_id = alloc_call_id();        if (call_id == PJSUA_INVALID_ID) {      pjsua_perror(THIS_FILE, "Error making call", PJ_ETOOMANY);      status = PJ_ETOOMANY;      goto on_error;      }    //    复位呼叫参数      /* Clear call descriptor */      reset_call(call_id);        call = &pjsua_var.calls[call_id];        /* Associate session with account */      call->acc_id = acc_id;      call->call_hold_type = acc->cfg.call_hold_type;    //    设置呼叫参数      /* Apply call setting */      status = apply_call_setting(call, opt, NULL);      if (status != PJ_SUCCESS) {      pjsua_perror(THIS_FILE, "Failed to apply call setting", status);      goto on_error;      }        /* Create temporary pool */      tmp_pool = pjsua_pool_create("tmpcall10", 512, 256);        /* Verify that destination URI is valid before calling       * pjsua_acc_create_uac_contact, or otherwise there        * a misleading "Invalid Contact URI" error will be printed      * when pjsua_acc_create_uac_contact() fails.      */      if (1) {      pjsip_uri *uri;      pj_str_t dup;    //        分析被叫SIP号码      pj_strdup_with_null(tmp_pool, &dup, dest_uri);      uri = pjsip_parse_uri(tmp_pool, dup.ptr, dup.slen, 0);        if (uri == NULL) {          pjsua_perror(THIS_FILE, "Unable to make call",                PJSIP_EINVALIDREQURI);          status = PJSIP_EINVALIDREQURI;          goto on_error;      }      }        /* Mark call start time. */      pj_gettimeofday(&call->start_time);        /* Reset first response time */      call->res_time.sec = 0;    //    创建Contact头域      /* Create suitable Contact header unless a Contact header has been      * set in the account.      */      if (acc->contact.slen) {      contact = acc->contact;      } else {      status = pjsua_acc_create_uac_contact(tmp_pool, &contact,                            acc_id, dest_uri);      if (status != PJ_SUCCESS) {          pjsua_perror(THIS_FILE, "Unable to generate Contact header",                status);          goto on_error;      }      }    //    创建SIP对话(Dialog)      /* Create outgoing dialog: */      status = pjsip_dlg_create_uac( pjsip_ua_instance(),                      &acc->cfg.id, &contact,                     dest_uri, dest_uri, &dlg);      if (status != PJ_SUCCESS) {      pjsua_perror(THIS_FILE, "Dialog creation failed", status);      goto on_error;      }        /* Increment the dialog's lock otherwise when invite session creation      * fails the dialog will be destroyed prematurely.      */      pjsip_dlg_inc_lock(dlg);    //    设置Via头域      if (acc->cfg.allow_via_rewrite && acc->via_addr.host.slen > 0)          pjsip_dlg_set_via_sent_by(dlg, &acc->via_addr, acc->via_tp);    //    设置安全级别,安全级别有何作用?      /* Calculate call's secure level */      call->secure_level = get_secure_level(acc_id, dest_uri);    //    设置用户数据,用户数据是什么?      /* Attach user data */      call->user_data = user_data;        //    复制消息数据,消息数据有何作用?      /* Store variables required for the callback after the async      * media transport creation is completed.      */      if (msg_data) {      call->async_call.call_var.out_call.msg_data = pjsua_msg_data_clone(                                                            dlg->pool, msg_data);      }  //    保存对话信息      call->async_call.dlg = dlg;        /* Temporarily increment dialog session. Without this, dialog will be      * prematurely destroyed if dec_lock() is called on the dialog before      * the invite session is created.      */      pjsip_dlg_inc_session(dlg, &pjsua_var.mod);    //    初始化媒体通道      /* Init media channel */      status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAC,                         call->secure_level, dlg->pool,                        NULL, NULL, PJ_TRUE,                                        &on_make_call_med_tp_complete);  //    调用媒体传输回调函数      if (status == PJ_SUCCESS) {          status = on_make_call_med_tp_complete(call->index, NULL);          if (status != PJ_SUCCESS)          goto on_error;      } else if (status != PJ_EPENDING) {      pjsua_perror(THIS_FILE, "Error initializing media channel", status);          pjsip_dlg_dec_session(dlg, &pjsua_var.mod);      goto on_error;      }        /* Done. */        if (p_call_id)      *p_call_id = call_id;        pjsip_dlg_dec_lock(dlg);      pj_pool_release(tmp_pool);      PJSUA_UNLOCK();        pj_log_pop_indent();        return PJ_SUCCESS;      on_error:      if (dlg) {      /* This may destroy the dialog */      pjsip_dlg_dec_lock(dlg);      }        if (call_id != -1) {      pjsua_media_channel_deinit(call_id);      reset_call(call_id);      }        pjsua_check_snd_dev_idle();        if (tmp_pool)      pj_pool_release(tmp_pool);      PJSUA_UNLOCK();        pj_log_pop_indent();      return status;  }   我们先来看看如何分配一个呼叫标识: [cpp] view plaincopy /* Allocate one call id */  static pjsua_call_id alloc_call_id(void)  {      pjsua_call_id cid;    #if 1      /* New algorithm: round-robin */      if (pjsua_var.next_call_id >= (int)pjsua_var.ua_cfg.max_calls ||       pjsua_var.next_call_id < 0)      {      pjsua_var.next_call_id = 0;      }    //    从next_call_id到max_calls之间找一个空闲的calls数组元素      for (cid=pjsua_var.next_call_id;       cid<(int)pjsua_var.ua_cfg.max_calls;        ++cid)       {      if (pjsua_var.calls[cid].inv == NULL &&              pjsua_var.calls[cid].async_call.dlg == NULL)          {          ++pjsua_var.next_call_id;          return cid;      }      }    //    从0到next_call_id之间找一个空闲的calls数组元素      for (cid=0; cid < pjsua_var.next_call_id; ++cid) {      if (pjsua_var.calls[cid].inv == NULL &&              pjsua_var.calls[cid].async_call.dlg == NULL)          {          ++pjsua_var.next_call_id;          return cid;      }      }    #else      /* Old algorithm */      for (cid=0; cid<(int)pjsua_var.ua_cfg.max_calls; ++cid) {      if (pjsua_var.calls[cid].inv == NULL)          return cid;      }  #endif        return PJSUA_INVALID_ID;  }   从上面的函数来看,这里的分配呼叫标识只是在calls数据中寻找一个空闲的单元(用于存放呼叫数据),这个呼叫标识并不是SIP协议里面的CALL ID的概念。 reset_call函数就是将呼叫参数设置为0值: [cpp] view plaincopy *   * Reset call descriptor.   */  static void reset_call(pjsua_call_id id)  {      pjsua_call *call = &pjsua_var.calls[id];      unsigned i;        pj_bzero(call, sizeof(*call));      call->index = id;      call->last_text.ptr = call->last_text_buf_;      for (i=0; i<PJ_ARRAY_SIZE(call->media); ++i) {      pjsua_call_media *call_med = &call->media[i];      call_med->ssrc = pj_rand();      call_med->strm.a.conf_slot = PJSUA_INVALID_ID;      call_med->strm.v.cap_win_id = PJSUA_INVALID_ID;      call_med->strm.v.rdr_win_id = PJSUA_INVALID_ID;      call_med->call = call;      call_med->idx = i;      call_med->tp_auto_del = PJ_TRUE;      }      pjsua_call_setting_default(&call->opt);      pj_timer_entry_init(&call->reinv_timer, PJ_FALSE,              (void*)(pj_size_t)id, &reinv_timer_cb);  }   设置呼叫参数: [cpp] view plaincopy static pj_status_t apply_call_setting(pjsua_call *call,                        const pjsua_call_setting *opt,                        const pjmedia_sdp_session *rem_sdp)  {      pj_assert(call);        if (!opt)      return PJ_SUCCESS;    #if !PJMEDIA_HAS_VIDEO      pj_assert(opt->vid_cnt == 0);  #endif        call->opt = *opt;    //    如果呼叫已建立,则设置本端的对话角色  //    如果有远端SDP,则本端为UAS(User Agent Server),否则为UAC      /* If call is established, reinit media channel */      if (call->inv && call->inv->state == PJSIP_INV_STATE_CONFIRMED) {      pjsip_role_e role = rem_sdp? PJSIP_ROLE_UAS : PJSIP_ROLE_UAC;      pj_status_t status;    //        初始化媒体通道      status = pjsua_media_channel_init(call->index, role,                        call->secure_level,                        call->inv->pool_prov,                        rem_sdp, NULL,                        PJ_FALSE, NULL);      if (status != PJ_SUCCESS) {          pjsua_perror(THIS_FILE, "Error re-initializing media channel",               status);          return status;      }      }        return PJ_SUCCESS;  }  

上一篇:Android-数据库详解之一:SQLite关键字以及语法
下一篇:java--基于socket的网络传输开发

相关文章

相关评论