pjsip源码解析之pjsip 中 reinvite 的处理

本文主要讲解 pjsip 中对于 reinvite 的处理方式,其中将主要展现 sdp 的重新协商过程,至于其它的部分暂时不做详细讲解

欢迎进入q群761341723,大家一起讨论问题。hpng该网站为我自己网站,一些想法也会发到这里

pjsua_call_reinvite2

pjsua_call_reinvite2 函数的定义位于 pjsip/src/pjsua-lib/pjsua_call.c 中,如下所示:

/*
 * Send re-INVITE (to release hold).
 */
PJ_DEF(pj_status_t) pjsua_call_reinvite2(pjsua_call_id call_id,
                                         const pjsua_call_setting *opt,
                                         const pjsua_msg_data *msg_data)
{
    pjmedia_sdp_session *sdp = NULL;
    pj_str_t *new_contact = NULL;
    pjsip_tx_data *tdata;
    pjsua_call *call;
    pjsip_dialog *dlg = NULL;
    pj_status_t status;


    PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
                     PJ_EINVAL);

    PJ_LOG(4,(THIS_FILE, "Sending re-INVITE on call %d", call_id));
    pj_log_push_indent();

    status = acquire_call("pjsua_call_reinvite2()", call_id, &call, &dlg);
    if (status != PJ_SUCCESS)
        goto on_return;

    if (pjsua_call_media_is_changing(call)) {
        PJ_LOG(1,(THIS_FILE, "Unable to reinvite" ERR_MEDIA_CHANGING));
        status = PJ_EINVALIDOP;
        goto on_return;
    }

    if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
        PJ_LOG(3,(THIS_FILE, "Can not re-INVITE call that is not confirmed"));
        status = PJSIP_ESESSIONSTATE;
        goto on_return;
    }

    status = apply_call_setting(call, opt, NULL);
    if (status != PJ_SUCCESS) {
        pjsua_perror(THIS_FILE, "Failed to apply call setting", status);
        goto on_return;
    }

    /* Create SDP */
    if (call->local_hold && (call->opt.flag & PJSUA_CALL_UNHOLD)==0) {
        status = create_sdp_of_call_hold(call, &sdp);
    } else if ((call->opt.flag & PJSUA_CALL_NO_SDP_OFFER) == 0) {
        status = pjsua_media_channel_create_sdp(call->index,
                                                call->inv->pool_prov,
                                                NULL, &sdp, NULL);
    }
    if (status != PJ_SUCCESS) {
        pjsua_perror(THIS_FILE, "Unable to get SDP from media endpoint",
                     status);
        goto on_return;
    }

    if ((call->opt.flag & PJSUA_CALL_UPDATE_CONTACT) &&
            pjsua_acc_is_valid(call->acc_id))
    {
        call_update_contact(call, &new_contact);
    }

    if ((call->opt.flag & PJSUA_CALL_UPDATE_VIA) &&
        pjsua_acc_is_valid(call->acc_id))
    {
        dlg_set_via(call->inv->dlg, &pjsua_var.acc[call->acc_id]);
    }

    if ((call->opt.flag & PJSUA_CALL_UPDATE_TARGET) &&
        msg_data && msg_data->target_uri.slen)
    {
        status = dlg_set_target(dlg, &msg_data->target_uri);
        if (status != PJ_SUCCESS) {
            pjsua_perror(THIS_FILE, "Unable to set new target", status);
            goto on_return;
        }
    }

    /* Create re-INVITE with new offer */
    status = pjsip_inv_reinvite( call->inv, new_contact, sdp, &tdata);
    if (status != PJ_SUCCESS) {
        pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
        goto on_return;
    }

    /* Add additional headers etc */
    pjsua_process_msg_data( tdata, msg_data);

    /* Send the request */
    call->med_update_success = PJ_FALSE;
    status = pjsip_inv_send_msg( call->inv, tdata);
    if (status == PJ_SUCCESS &&
        ((call->opt.flag & PJSUA_CALL_UNHOLD) &&
         (call->opt.flag & PJSUA_CALL_NO_SDP_OFFER) == 0))
    {
        call->local_hold = PJ_FALSE;
    } else if (status != PJ_SUCCESS) {
        pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
        goto on_return;
    }

on_return:
    if (dlg) pjsip_dlg_dec_lock(dlg);
    pj_log_pop_indent();
    return status;
}2024-03-02 21:10:23 星期六
  • create_sdp_of_call_hold:创建 hold 的 sdp 内容
  • pjsua_media_channel_create_sdp: 没有 offer 的情况下创建 sdp
  • pjsua_process_msg_data: 创建并处理一些额外的 header 数据
  • pjsip_inv_send_msg: 发送 sdp 并修改 sdp 内容

reset_call

reset_call 函数的定义位于 pjsip/src/pjsua-lib/pjsua_call.c 中,如下所示:

/*
 * Reset call descriptor.
 */
static void reset_call(pjsua_call_id id)
{
    pjsua_call *call = &pjsua_var.calls[id];
    unsigned i;

    if (call->incoming_data) {
        pjsip_rx_data_free_cloned(call->incoming_data);
        call->incoming_data = NULL;
    }
    pj_bzero(call, sizeof(*call));
    call->index = id;
    call->last_text.ptr = call->last_text_buf_;
    call->cname.ptr = call->cname_buf;
    call->cname.slen = sizeof(call->cname_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->strm.v.strm_dec_slot = PJSUA_INVALID_ID;
        call_med->strm.v.strm_enc_slot = 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);
    pj_bzero(&call->trickle_ice, sizeof(call->trickle_ice));
    pj_timer_entry_init(&call->trickle_ice.timer, 0, call,
                        &trickle_ice_send_sip_info);
}
  • reinv_timer_cb: 用于重置 re-invite

reinv_timer_cb

reinv_timer_cb 函数的定义位于 pjsip/src/pjsua-lib/pjsua_call.c 中,如下所示:

/* Timer callback to send re-INVITE/UPDATE to lock codec or ICE update */
static void reinv_timer_cb(pj_timer_heap_t *th, pj_timer_entry *entry)
{
    pjsua_call_id call_id = (pjsua_call_id)(pj_size_t)entry->user_data;
    pjsip_dialog *dlg;
    pjsua_call *call;
    pj_status_t status;

    PJ_UNUSED_ARG(th);

    pjsua_var.calls[call_id].reinv_timer.id = PJ_FALSE;

    pj_log_push_indent();

    status = acquire_call("reinv_timer_cb()", call_id, &call, &dlg);
    if (status != PJ_SUCCESS) {
        pj_log_pop_indent();
        return;
    }

    process_pending_reinvite(call);

    pjsip_dlg_dec_lock(dlg);

    pj_log_pop_indent();
}
  • process_pending_reinvite: 对 reinvite 或者 update 进行处理,主要是处理 sdp

process_pending_reinvite

process_pending_reinvite 函数的定义位于 pjsip/src/pjsua-lib/pjsua_call.c 中,如下所示:


//4444
    if (rem_can_update) {
        status = pjsip_inv_update(inv, NULL, new_offer, &tdata);
    } else {
        status = pjsip_inv_reinvite(inv, NULL, new_offer, &tdata);
    }

    if (status==PJ_EINVALIDOP &&
        ++call->lock_codec.retry_cnt < LOCK_CODEC_MAX_RETRY)
    {
        /* Ups, let's reschedule again */
        pjsua_call_schedule_reinvite_check(call, LOCK_CODEC_RETRY_INTERVAL);
        return PJ_SUCCESS;
    } else if (status != PJ_SUCCESS) {
        pjsua_perror(THIS_FILE, "Error creating UPDATE/re-INVITE",
                     status);
        return status;
    }

    /* Send the UPDATE/re-INVITE request */
    status = pjsip_inv_send_msg(inv, tdata);
    if (status != PJ_SUCCESS) {
        pjsua_perror(THIS_FILE, "Error sending UPDATE/re-INVITE",
                     status);
        return status;
    }

  • pjsip_inv_update: 处理 update 的 sdp 信息
  • pjsip_inv_reinvite:处理 reinvite 的 sdp 信息
  • pjsip_inv_send_msg: 发送 re-invite 到服务器并修改 sdp

pjsip_inv_update

pjsip_inv_update 函数的定义位于 pjsip/src/pjsip-ua/sip_inv.c 中,如下所示:

/*
 * Create UPDATE.
 */
PJ_DEF(pj_status_t) pjsip_inv_update (  pjsip_inv_session *inv,
                                        const pj_str_t *new_contact,
                                        const pjmedia_sdp_session *offer,
                                        pjsip_tx_data **p_tdata )
{
    pjsip_contact_hdr *contact_hdr = NULL;
    pjsip_tx_data *tdata = NULL;
    pjmedia_sdp_session *sdp_copy;
    const pjsip_hdr *hdr;
    pjsip_supported_hdr *sup_hdr = NULL;
    pj_status_t status = PJ_SUCCESS;

    /* Verify arguments. */
    PJ_ASSERT_RETURN(inv && p_tdata, PJ_EINVAL);

    /* Dialog must have been established */
    PJ_ASSERT_RETURN(inv->dlg->state == PJSIP_DIALOG_STATE_ESTABLISHED,
                     PJ_EINVALIDOP);

    /* Invite session must not have been disconnected */
    PJ_ASSERT_RETURN(inv->state < PJSIP_INV_STATE_DISCONNECTED,
                     PJ_EINVALIDOP);

    pj_log_push_indent();

    /* Lock dialog. */
    pjsip_dlg_inc_lock(inv->dlg);

    /* Process offer, if any */
    if (offer) {
        if (inv->state == PJSIP_INV_STATE_EARLY && !inv->sdp_done_early_rel) {
            PJ_LOG(4,(inv->dlg->obj_name,
                      "RFC 3311 section 5.1 recommends against sending UPDATE"
                      " without reliable prov response"));
#if PJSIP_INV_UPDATE_EARLY_CHECK_RELIABLE
            status = PJ_EINVALIDOP;
            goto on_error;
#endif
        }

        if (pjmedia_sdp_neg_get_state(inv->neg)!=PJMEDIA_SDP_NEG_STATE_DONE) {
            PJ_LOG(4,(inv->dlg->obj_name,
                      "Invalid SDP offer/answer state for UPDATE"));
            status = PJ_EINVALIDOP;
            goto on_error;
        }

        /* Notify negotiator about the new offer. This will fix the offer
         * with correct SDP origin.
         */
        status = pjmedia_sdp_neg_modify_local_offer2(inv->pool_prov, inv->neg,
                                                     inv->sdp_neg_flags, offer);
        if (status != PJ_SUCCESS)
            goto on_error;

        /* Retrieve the "fixed" offer from negotiator */
        pjmedia_sdp_neg_get_neg_local(inv->neg, &offer);
    }

    /* Update Contact if required */
    if (new_contact) {
        pj_str_t tmp;
        const pj_str_t STR_CONTACT = { "Contact", 7 };

        pj_strdup_with_null(inv->dlg->pool, &tmp, new_contact);
        contact_hdr = (pjsip_contact_hdr*)
                      pjsip_parse_hdr(inv->dlg->pool, &STR_CONTACT, 
                                      tmp.ptr, tmp.slen, NULL);
        if (!contact_hdr) {
            status = PJSIP_EINVALIDURI;
            goto on_error;
        }

        inv->dlg->local.contact = contact_hdr;
    }

    /* Create request */
    status = pjsip_dlg_create_request(inv->dlg, &pjsip_update_method,
                                      -1, &tdata);
    if (status != PJ_SUCCESS)
            goto on_error;

    /* Attach SDP body */
    if (offer) {
        sdp_copy = pjmedia_sdp_session_clone(tdata->pool, offer);
        pjsip_create_sdp_body(tdata->pool, sdp_copy, &tdata->msg->body);
    }

    /* Session Timers spec (RFC 4028) says that Supported header MUST be put
     * in refresh requests. So here we'll just put the Supported header in
     * all cases regardless of whether session timers is used or not, just
     * in case this is a common behavior.
     */
    hdr = pjsip_endpt_get_capability(inv->dlg->endpt, PJSIP_H_SUPPORTED, NULL);
    if (hdr) {
        sup_hdr = (pjsip_supported_hdr*)
                   pjsip_hdr_shallow_clone(tdata->pool, hdr);
        pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)sup_hdr);
    }

    status = pjsip_timer_update_req(inv, tdata);
    if (status != PJ_SUCCESS)
        goto on_error;

    /* Cleanup Allow & Supported headers from disabled extensions */
    cleanup_allow_sup_hdr(inv->options, NULL, NULL, sup_hdr);

    /* Unlock dialog. */
    pjsip_dlg_dec_lock(inv->dlg);

    *p_tdata = tdata;

    pj_log_pop_indent();
    return PJ_SUCCESS;

on_error:
    if (tdata)
        pjsip_tx_data_dec_ref(tdata);

    /* Unlock dialog. */
    pjsip_dlg_dec_lock(inv->dlg);

    pj_log_pop_indent();
    return status;
}
  • pjmedia_sdp_neg_modify_local_offer2: 修改 sdp 中的 origin,
  • pjsip_create_sdp_body: 创建 sdp 的 body 内容

pjsip_inv_reinvte

pjsip_inv_reinvte 函数的定义位于 pjsip/src/pjsip-ua/sip_inv.c 中,如下所示:

/*
 * Create re-INVITE.
 */
PJ_DEF(pj_status_t) pjsip_inv_reinvite( pjsip_inv_session *inv,
                                        const pj_str_t *new_contact,
                                        const pjmedia_sdp_session *new_offer,
                                        pjsip_tx_data **p_tdata )
{
    pj_status_t status;
    pjsip_contact_hdr *contact_hdr = NULL;

    /* Check arguments. */
    PJ_ASSERT_RETURN(inv && p_tdata, PJ_EINVAL);

    /* Must NOT have a pending INVITE transaction */
    if (inv->invite_tsx!=NULL)
        return PJ_EINVALIDOP;

    pj_log_push_indent();

    pjsip_dlg_inc_lock(inv->dlg);

    if (new_contact) {
        pj_str_t tmp;
        const pj_str_t STR_CONTACT = { "Contact", 7 };

        pj_strdup_with_null(inv->dlg->pool, &tmp, new_contact);
        contact_hdr = (pjsip_contact_hdr*)
                      pjsip_parse_hdr(inv->dlg->pool, &STR_CONTACT, 
                                      tmp.ptr, tmp.slen, NULL);
        if (!contact_hdr) {
            status = PJSIP_EINVALIDURI;
            goto on_return;
        }
    }


    if (new_offer) {
        if (!inv->neg) {
            status = pjmedia_sdp_neg_create_w_local_offer(inv->pool, 
                                                          new_offer,
                                                          &inv->neg);
            if (status != PJ_SUCCESS)
                goto on_return;

        } else switch (pjmedia_sdp_neg_get_state(inv->neg)) {

            case PJMEDIA_SDP_NEG_STATE_NULL:
                pj_assert(!"Unexpected SDP neg state NULL");
                status = PJ_EBUG;
                goto on_return;

            case PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER:
                PJ_LOG(4,(inv->obj_name, 
                          "pjsip_inv_reinvite: already have an offer, new "
                          "offer is ignored"));
                break;

            case PJMEDIA_SDP_NEG_STATE_REMOTE_OFFER:
                status = pjmedia_sdp_neg_set_local_answer(inv->pool_prov, 
                                                          inv->neg,
                                                          new_offer);
                if (status != PJ_SUCCESS)
                    goto on_return;
                break;

            case PJMEDIA_SDP_NEG_STATE_WAIT_NEGO:
                PJ_LOG(4,(inv->obj_name, 
                          "pjsip_inv_reinvite: SDP in WAIT_NEGO state, new "
                          "offer is ignored"));
                break;

            case PJMEDIA_SDP_NEG_STATE_DONE:
                status = pjmedia_sdp_neg_modify_local_offer2(
                             inv->pool_prov, inv->neg,
                             inv->sdp_neg_flags, new_offer);
                if (status != PJ_SUCCESS)
                    goto on_return;
                break;
        }
    }

    if (contact_hdr)
        inv->dlg->local.contact = contact_hdr;

    status = pjsip_inv_invite(inv, p_tdata);

on_return:
    pjsip_dlg_dec_lock(inv->dlg);
    pj_log_pop_indent();
    return status;
}

此处是没有 offer 或者 answer 就创建对应的内容,当然也有可能在等待协商。

pjsip_inv_invite

pjsip_inv_invite 函数的定义位于 pjsip/src/pjsip-ua/sip_inv.c 中,如下所示:

/*
 * Create initial INVITE request.
 */
PJ_DEF(pj_status_t) pjsip_inv_invite( pjsip_inv_session *inv,
                                      pjsip_tx_data **p_tdata )
{
    pjsip_tx_data *tdata;
    const pjsip_hdr *hdr;
    pjsip_allow_hdr *allow_hdr = NULL;
    pjsip_supported_hdr *sup_hdr = NULL;
    pj_bool_t has_sdp;
    pj_status_t status;

    /* Verify arguments. */
    PJ_ASSERT_RETURN(inv && p_tdata, PJ_EINVAL);

    /* State MUST be NULL or CONFIRMED. */
    PJ_ASSERT_RETURN(inv->state == PJSIP_INV_STATE_NULL ||
                     inv->state == PJSIP_INV_STATE_CONFIRMED, 
                     PJ_EINVALIDOP);

    /* Lock dialog. */
    pjsip_dlg_inc_lock(inv->dlg);

    /* Create the INVITE request. */
    status = pjsip_dlg_create_request(inv->dlg, pjsip_get_invite_method(), -1,
                                      &tdata);
    if (status != PJ_SUCCESS)
        goto on_return;


    /* If this is the first INVITE, then copy the headers from inv_hdr.
     * These are the headers parsed from the request URI when the
     * dialog was created.
     */
    if (inv->state == PJSIP_INV_STATE_NULL) {
        hdr = inv->dlg->inv_hdr.next;

        while (hdr != &inv->dlg->inv_hdr) {
            pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)
                              pjsip_hdr_shallow_clone(tdata->pool, hdr));
            hdr = hdr->next;
        }
    }

    /* See if we have SDP to send. */
    if (inv->neg) {
        pjmedia_sdp_neg_state neg_state;

        neg_state = pjmedia_sdp_neg_get_state(inv->neg);

        has_sdp = (neg_state == PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER ||
                   (neg_state == PJMEDIA_SDP_NEG_STATE_WAIT_NEGO &&
                    pjmedia_sdp_neg_has_local_answer(inv->neg)));


    } else {
        has_sdp = PJ_FALSE;
    }

    /* Add SDP, if any. */
    if (has_sdp) {
        const pjmedia_sdp_session *offer;

        status = pjmedia_sdp_neg_get_neg_local(inv->neg, &offer);
        if (status != PJ_SUCCESS) {
            pjsip_tx_data_dec_ref(tdata);
            goto on_return;
        }

        tdata->msg->body = create_sdp_body(tdata->pool, offer);
    }

    /* Add Allow header. */
    if (inv->dlg->add_allow) {
        hdr = pjsip_endpt_get_capability(inv->dlg->endpt, PJSIP_H_ALLOW, NULL);
        if (hdr) {
            allow_hdr = (pjsip_allow_hdr*)
                        pjsip_hdr_shallow_clone(tdata->pool, hdr);
            pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)allow_hdr);
        }
    }

    /* Add Supported header */
    hdr = pjsip_endpt_get_capability(inv->dlg->endpt, PJSIP_H_SUPPORTED, NULL);
    if (hdr) {
        sup_hdr = (pjsip_supported_hdr*)
                   pjsip_hdr_shallow_clone(tdata->pool, hdr);
        pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)sup_hdr);
    }

    /* Cleanup Allow & Supported headers from disabled extensions */
    cleanup_allow_sup_hdr(inv->options, NULL, allow_hdr, sup_hdr);

    /* Add Require header. */
    if ((inv->options & PJSIP_INV_REQUIRE_100REL) ||
        (inv->options & PJSIP_INV_REQUIRE_TIMER) ||
        (inv->options & PJSIP_INV_REQUIRE_TRICKLE_ICE))
    {
        pjsip_require_hdr *hreq;

        hreq = pjsip_require_hdr_create(tdata->pool);

        if (inv->options & PJSIP_INV_REQUIRE_100REL)
            hreq->values[hreq->count++] = pj_str("100rel");
        if (inv->options & PJSIP_INV_REQUIRE_TIMER)
            hreq->values[hreq->count++] = pj_str("timer");
        if (inv->options & PJSIP_INV_REQUIRE_TRICKLE_ICE)
            hreq->values[hreq->count++] = pj_str("trickle-ice");

        pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*) hreq);
    }

    status = pjsip_timer_update_req(inv, tdata);
    if (status != PJ_SUCCESS)
        goto on_return;

    /* Done. */
    *p_tdata = tdata;


on_return:
    pjsip_dlg_dec_lock(inv->dlg);
    return status;
}
  • create_sdp_body: 创建对应的 sdp 的 body 内容

pjmedia_sdp_neg_modify_local_offer2

pjmedia_sdp_neg_modify_local_offer2 函数的定义位于 pjmedia/src/pjmedia/sdp_neg.c 中,如下所示:

PJ_DEF(pj_status_t) pjmedia_sdp_neg_modify_local_offer2(
                                    pj_pool_t *pool,
                                    pjmedia_sdp_neg *neg,
                                    unsigned flags,
                                    const pjmedia_sdp_session *local)
{
    pjmedia_sdp_session *new_offer;
    pjmedia_sdp_session *old_offer;
    unsigned oi; /* old offer media index */
    pj_status_t status;

    /* Check arguments are valid. */
    PJ_ASSERT_RETURN(pool && neg && local, PJ_EINVAL);

    /* Can only do this in STATE_DONE. */
    PJ_ASSERT_RETURN(neg->state == PJMEDIA_SDP_NEG_STATE_DONE, 
                     PJMEDIA_SDPNEG_EINSTATE);

    /* Validate the new offer */
    status = pjmedia_sdp_validate(local);
    if (status != PJ_SUCCESS)
        return status;

    /* Change state to STATE_LOCAL_OFFER */
    neg->state = PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER;

    /* When there is no active local SDP in state PJMEDIA_SDP_NEG_STATE_DONE,
     * it means that the previous initial SDP nego must have been failed,
     * so we'll just set the local SDP offer here.
     */
    if (!neg->active_local_sdp) {
        neg->initial_sdp_tmp = NULL;
        neg->initial_sdp = pjmedia_sdp_session_clone(pool, local);

        /* Assign PT numbers for our offer and update the mapping. */
        assign_pt_and_update_map(pool, neg, neg->initial_sdp,
                                 PJ_TRUE, PJ_FALSE);
        neg->neg_local_sdp = pjmedia_sdp_session_clone(pool, neg->initial_sdp);

        if (pjmedia_sdp_session_cmp(neg->last_sent, neg->neg_local_sdp, 0) !=
            PJ_SUCCESS)
        {
            ++neg->neg_local_sdp->origin.version;
        }
        neg->last_sent = neg->neg_local_sdp;

        return PJ_SUCCESS;
    }

    /* Init vars */
    old_offer = neg->active_local_sdp;
    new_offer = pjmedia_sdp_session_clone(pool, local);

    /* RFC 3264 Section 8: When issuing an offer that modifies the session,
     * the "o=" line of the new SDP MUST be identical to that in the
     * previous SDP, except that the version in the origin field MUST
     * increment by one from the previous SDP.
     */
    pj_strdup(pool, &new_offer->origin.user, &old_offer->origin.user);
    new_offer->origin.id = old_offer->origin.id;

    pj_strdup(pool, &new_offer->origin.net_type, &old_offer->origin.net_type);
    pj_strdup(pool, &new_offer->origin.addr_type,&old_offer->origin.addr_type);
    pj_strdup(pool, &new_offer->origin.addr, &old_offer->origin.addr);

    if ((flags & PJMEDIA_SDP_NEG_ALLOW_MEDIA_CHANGE) == 0) {
       /* Generating the new offer, in the case media lines doesn't match the
        * active SDP (e.g. current/active SDP's have m=audio and m=video lines,
        * and the new offer only has m=audio line), the negotiator will fix 
        * the new offer by reordering and adding the missing media line with 
        * port number set to zero.
        */
        for (oi = 0; oi < old_offer->media_count; ++oi) {
            pjmedia_sdp_media *om;
            pjmedia_sdp_media *nm;
            unsigned ni; /* new offer media index */
            pj_bool_t found = PJ_FALSE;

            om = old_offer->media[oi];
            for (ni = oi; ni < new_offer->media_count; ++ni) {
                nm = new_offer->media[ni];
                if (pj_strcmp(&nm->desc.media, &om->desc.media) == 0) {
                    if (ni != oi) {
                        /* The same media found but the position unmatched to
                         * the old offer, so let's put this media in the right
                         * place, and keep the order of the rest.
                         */
                        pj_array_insert(
                            new_offer->media,            /* array    */
                            sizeof(new_offer->media[0]), /* elmt size*/
                            ni,                          /* count    */
                            oi,                          /* pos      */
                            &nm);                        /* new elmt */
                    }
                    found = PJ_TRUE;
                    break;
                }
            }
            if (!found) {
                pjmedia_sdp_media *m;

                m = sdp_media_clone_deactivate(pool, om, om, local);

                pj_array_insert(new_offer->media, sizeof(new_offer->media[0]),
                                new_offer->media_count++, oi, &m);
            }
        }
    } else {
        /* If media type change is allowed, the negotiator only needs to fix 
         * the new offer by adding the missing media line(s) with port number
         * set to zero.
         */
        for (oi = new_offer->media_count; oi < old_offer->media_count; ++oi) {
            pjmedia_sdp_media *m;

            m = sdp_media_clone_deactivate(pool, old_offer->media[oi],
                                           old_offer->media[oi], local);

            pj_array_insert(new_offer->media, sizeof(new_offer->media[0]),
                            new_offer->media_count++, oi, &m);

        }
    }

    /* New_offer fixed */
    new_offer->origin.version = old_offer->origin.version;

    /* Assign PT numbers for our offer and update the mapping. */
    assign_pt_and_update_map(pool, neg, new_offer, PJ_TRUE, PJ_FALSE);

    if (pjmedia_sdp_session_cmp(neg->last_sent, new_offer, 0) != PJ_SUCCESS) {
        ++new_offer->origin.version;
    }
    neg->initial_sdp_tmp = neg->initial_sdp;
    neg->initial_sdp = new_offer;
    neg->neg_local_sdp = pjmedia_sdp_session_clone(pool, new_offer);
    neg->last_sent = neg->neg_local_sdp;

    return PJ_SUCCESS;
}

pjsip_inv_send_msg

pjsip_inv_send_msg 函数的定义位于 pjsip/src/pjsip-ua/sip_inv.c 中,如下所示:

/*
 * Send a request or response message.
 */
PJ_DEF(pj_status_t) pjsip_inv_send_msg( pjsip_inv_session *inv,
                                        pjsip_tx_data *tdata)
{
    pj_status_t status;

    /* Verify arguments. */
    PJ_ASSERT_RETURN(inv && tdata, PJ_EINVAL);

    pj_log_push_indent();

    PJ_LOG(5,(inv->obj_name, "Sending %s", 
              pjsip_tx_data_get_info(tdata)));

    if (tdata->msg->type == PJSIP_REQUEST_MSG) {
        struct tsx_inv_data *tsx_inv_data;

        pjsip_dlg_inc_lock(inv->dlg);

        /* Check again that we didn't receive incoming re-INVITE */
        if (tdata->msg->line.req.method.id==PJSIP_INVITE_METHOD && 
            inv->invite_tsx) 
        {
            pjsip_tx_data_dec_ref(tdata);
            pjsip_dlg_dec_lock(inv->dlg);
            status = PJ_EINVALIDOP;
            goto on_error;
        }

        /* Don't send BYE before ACK is received
         * https://github.com/pjsip/pjproject/issues/1712
         */
        if (tdata->msg->line.req.method.id == PJSIP_BYE_METHOD &&
            inv->role == PJSIP_ROLE_UAS &&
            inv->state == PJSIP_INV_STATE_CONNECTING &&
            inv->cause != PJSIP_SC_REQUEST_TIMEOUT &&
            inv->cause != PJSIP_SC_TSX_TRANSPORT_ERROR)
        {
            if (inv->pending_bye)
                pjsip_tx_data_dec_ref(inv->pending_bye);

            inv->pending_bye = tdata;
            PJ_LOG(4, (inv->obj_name, "Delaying BYE request until "
                       "ACK is received"));
            pjsip_dlg_dec_lock(inv->dlg);
            goto on_return;
        }

        /* Associate our data in outgoing invite transaction */
        tsx_inv_data = PJ_POOL_ZALLOC_T(inv->pool, struct tsx_inv_data);
        tsx_inv_data->inv = inv;
        tsx_inv_data->has_sdp = tx_data_has_sdp(tdata);

        pjsip_dlg_dec_lock(inv->dlg);

        status = pjsip_dlg_send_request(inv->dlg, tdata, mod_inv.mod.id, 
                                        tsx_inv_data);
        if (status != PJ_SUCCESS) {
            goto on_error;
        }

        /* Check if this is delayed manual ACK (see #416) */
        if (mod_inv.cb.on_send_ack &&
            tdata->msg->line.req.method.id == PJSIP_ACK_METHOD &&
            tdata == inv->last_ack)
        {
            pjsip_dlg_inc_lock(inv->dlg);

            /* Set state to CONFIRMED (if we're not in CONFIRMED yet).
             * But don't set it to CONFIRMED if we're already DISCONNECTED
             * (this may have been a late 200/OK response.
             */
            if (inv->state < PJSIP_INV_STATE_CONFIRMED) {
                pjsip_event ack_e;
                PJSIP_EVENT_INIT_TX_MSG(ack_e, inv->last_ack);
                inv_set_state(inv, PJSIP_INV_STATE_CONFIRMED, &ack_e);
            } else if (inv->state == PJSIP_INV_STATE_DISCONNECTED) {
                /* Avoid possible leaked tdata when invite session is
                 * already destroyed.
                 * https://github.com/pjsip/pjproject/pull/2432
                 */
                pjsip_tx_data_dec_ref(inv->last_ack);
                inv->last_ack = NULL;
            }

            pjsip_dlg_dec_lock(inv->dlg);
        }

    } else {
        pjsip_cseq_hdr *cseq;

        /* Can only do this to send response to original INVITE
         * request.
         */
        cseq = (pjsip_cseq_hdr*)pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL);
        PJ_ASSERT_ON_FAIL(cseq != NULL
                          && (inv->invite_tsx && cseq->cseq == inv->invite_tsx->cseq),
                          { pjsip_tx_data_dec_ref(tdata); return PJ_EINVALIDOP; });

        if (inv->options & PJSIP_INV_REQUIRE_100REL) {
            status = pjsip_100rel_tx_response(inv, tdata);
        } else 
        {
            status = pjsip_dlg_send_response(inv->dlg, inv->invite_tsx, tdata);
        }

        if (status != PJ_SUCCESS) {
            goto on_error;
        }
    }

    /* Done */
on_return:
    pj_log_pop_indent();
    return PJ_SUCCESS;

on_error:
    pj_log_pop_indent();
    return status;
}
  • tx_data_has_sdp: 判断是否有 sdp
  • pjsip_dlg_send_request: 将 reinvte 发送出去
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值