📄 pjsua_call.c
字号:
*/ st_code = e->body.tsx_state.tsx->status_code; ev_state = PJSIP_EVSUB_STATE_TERMINATED; break; case PJSIP_INV_STATE_DISCONNECTED: st_code = e->body.tsx_state.tsx->status_code; ev_state = PJSIP_EVSUB_STATE_TERMINATED; break; case PJSIP_INV_STATE_INCOMING: /* Nothing to do. Just to keep gcc from complaining about * unused enums. */ break; } if (st_code != -1) { pjsip_tx_data *tdata; pj_status_t status; status = pjsip_xfer_notify( call->xfer_sub, ev_state, st_code, NULL, &tdata); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Unable to create NOTIFY", status); } else { status = pjsip_xfer_send_request(call->xfer_sub, tdata); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Unable to send NOTIFY", status); } } } } if (pjsua_var.ua_cfg.cb.on_call_state) (*pjsua_var.ua_cfg.cb.on_call_state)(call->index, e); /* call->inv may be NULL now */ /* Destroy media session when invite session is disconnected. */ if (inv->state == PJSIP_INV_STATE_DISCONNECTED) { pj_assert(call != NULL); if (call) call_destroy_media(call->index); /* Free call */ call->inv = NULL; --pjsua_var.call_cnt; /* Reset call */ reset_call(call->index); } PJSUA_UNLOCK();}/* * This callback is called by invite session framework when UAC session * has forked. */static void pjsua_call_on_forked( pjsip_inv_session *inv, pjsip_event *e){ PJ_UNUSED_ARG(inv); PJ_UNUSED_ARG(e); PJ_TODO(HANDLE_FORKED_DIALOG);}/* * Disconnect call upon error. */static void call_disconnect( pjsip_inv_session *inv, int code ){ pjsip_tx_data *tdata; pj_status_t status; status = pjsip_inv_end_session(inv, code, NULL, &tdata); if (status == PJ_SUCCESS) pjsip_inv_send_msg(inv, tdata);}/* * DTMF callback from the stream. */static void dtmf_callback(pjmedia_stream *strm, void *user_data, int digit){ PJ_UNUSED_ARG(strm); if (pjsua_var.ua_cfg.cb.on_dtmf_digit) { pjsua_call_id call_id; call_id = (pjsua_call_id)user_data; pjsua_var.ua_cfg.cb.on_dtmf_digit(call_id, digit); }}/* * Callback to be called when SDP offer/answer negotiation has just completed * in the session. This function will start/update media if negotiation * has succeeded. */static void pjsua_call_on_media_update(pjsip_inv_session *inv, pj_status_t status){ int prev_media_st = 0; pjsua_call *call; pjmedia_session_info sess_info; const pjmedia_sdp_session *local_sdp; const pjmedia_sdp_session *remote_sdp; pjmedia_port *media_port; pj_str_t port_name; char tmp[PJSIP_MAX_URL_SIZE]; PJSUA_LOCK(); call = inv->dlg->mod_data[pjsua_var.mod.id]; if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "SDP negotiation has failed", status); /* Stop/destroy media, if any */ call_destroy_media(call->index); /* Disconnect call if we're not in the middle of initializing an * UAS dialog and if this is not a re-INVITE */ if (inv->state != PJSIP_INV_STATE_NULL && inv->state != PJSIP_INV_STATE_CONFIRMED) { //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE); } PJSUA_UNLOCK(); return; } /* Destroy existing media session, if any. */ if (call) { prev_media_st = call->media_st; call_destroy_media(call->index); } /* Get local and remote SDP */ status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &local_sdp); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Unable to retrieve currently active local SDP", status); //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE); PJSUA_UNLOCK(); return; } status = pjmedia_sdp_neg_get_active_remote(call->inv->neg, &remote_sdp); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Unable to retrieve currently active remote SDP", status); //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE); PJSUA_UNLOCK(); return; } /* Create media session info based on SDP parameters. * We only support one stream per session at the moment */ status = pjmedia_session_info_from_sdp( call->inv->dlg->pool, pjsua_var.med_endpt, 1,&sess_info, local_sdp, remote_sdp); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Unable to create media session", status); call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE); PJSUA_UNLOCK(); return; } /* Check if media is put on-hold */ if (sess_info.stream_cnt == 0 || sess_info.stream_info[0].dir == PJMEDIA_DIR_NONE) { /* Determine who puts the call on-hold */ if (prev_media_st == PJSUA_CALL_MEDIA_ACTIVE) { if (pjmedia_sdp_neg_was_answer_remote(call->inv->neg)) { /* It was local who offer hold */ call->media_st = PJSUA_CALL_MEDIA_LOCAL_HOLD; } else { call->media_st = PJSUA_CALL_MEDIA_REMOTE_HOLD; } } call->media_dir = PJMEDIA_DIR_NONE; } else { /* Override ptime, if this option is specified. */ if (pjsua_var.media_cfg.ptime != 0) { sess_info.stream_info[0].param->setting.frm_per_pkt = (pj_uint8_t) (pjsua_var.media_cfg.ptime / sess_info.stream_info[0].param->info.frm_ptime); if (sess_info.stream_info[0].param->setting.frm_per_pkt == 0) sess_info.stream_info[0].param->setting.frm_per_pkt = 1; } /* Disable VAD, if this option is specified. */ if (pjsua_var.media_cfg.no_vad) { sess_info.stream_info[0].param->setting.vad = 0; } /* Optionally, application may modify other stream settings here * (such as jitter buffer parameters, codec ptime, etc.) */ sess_info.stream_info[0].jb_init = pjsua_var.media_cfg.jb_init; sess_info.stream_info[0].jb_min_pre = pjsua_var.media_cfg.jb_min_pre; sess_info.stream_info[0].jb_max_pre = pjsua_var.media_cfg.jb_max_pre; sess_info.stream_info[0].jb_max = pjsua_var.media_cfg.jb_max; /* Create session based on session info. */ status = pjmedia_session_create( pjsua_var.med_endpt, &sess_info, &call->med_tp, call, &call->session ); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Unable to create media session", status); //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE); PJSUA_UNLOCK(); return; } /* If DTMF callback is installed by application, install our * callback to the session. */ if (pjsua_var.ua_cfg.cb.on_dtmf_digit) { pjmedia_session_set_dtmf_callback(call->session, 0, &dtmf_callback, (void*)(call->index)); } /* Get the port interface of the first stream in the session. * We need the port interface to add to the conference bridge. */ pjmedia_session_get_port(call->session, 0, &media_port); /* * Add the call to conference bridge. */ port_name.ptr = tmp; port_name.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, call->inv->dlg->remote.info->uri, tmp, sizeof(tmp)); if (port_name.slen < 1) { port_name = pj_str("call"); } status = pjmedia_conf_add_port( pjsua_var.mconf, call->inv->pool, media_port, &port_name, (unsigned*)&call->conf_slot); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Unable to create conference slot", status); call_destroy_media(call->index); //call_disconnect(inv, PJSIP_SC_INTERNAL_SERVER_ERROR); PJSUA_UNLOCK(); return; } /* Call's media state is active */ call->media_st = PJSUA_CALL_MEDIA_ACTIVE; call->media_dir = sess_info.stream_info[0].dir; } /* Print info. */ { char info[80]; int info_len = 0; unsigned i; for (i=0; i<sess_info.stream_cnt; ++i) { int len; const char *dir; pjmedia_stream_info *strm_info = &sess_info.stream_info[i]; switch (strm_info->dir) { case PJMEDIA_DIR_NONE: dir = "inactive"; break; case PJMEDIA_DIR_ENCODING: dir = "sendonly"; break; case PJMEDIA_DIR_DECODING: dir = "recvonly"; break; case PJMEDIA_DIR_ENCODING_DECODING: dir = "sendrecv"; break; default: dir = "unknown"; break; } len = pj_ansi_sprintf( info+info_len, ", stream #%d: %.*s (%s)", i, (int)strm_info->fmt.encoding_name.slen, strm_info->fmt.encoding_name.ptr, dir); if (len > 0) info_len += len; } PJ_LOG(4,(THIS_FILE,"Media updates%s", info)); } /* Call application callback, if any */ if (pjsua_var.ua_cfg.cb.on_call_media_state) pjsua_var.ua_cfg.cb.on_call_media_state(call->index); PJSUA_UNLOCK();}/* * Create inactive SDP for call hold. */static pj_status_t create_inactive_sdp(pjsua_call *call, pjmedia_sdp_session **p_answer){ pj_status_t status; pjmedia_sdp_conn *conn; pjmedia_sdp_attr *attr; pjmedia_sdp_session *sdp; /* Create new offer */ status = pjmedia_endpt_create_sdp(pjsua_var.med_endpt, pjsua_var.pool, 1, &call->skinfo, &sdp); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Unable to create local SDP", status); return status; } /* Get SDP media connection line */ conn = sdp->media[0]->conn; if (!conn) conn = sdp->conn; /* Modify address */ conn->addr = pj_str("0.0.0.0"); /* Remove existing directions attributes */ pjmedia_sdp_media_remove_all_attr(sdp->media[0], "sendrecv"); pjmedia_sdp_media_remove_all_attr(sdp->media[0], "sendonly"); pjmedia_sdp_media_remove_all_attr(sdp->media[0], "recvonly"); pjmedia_sdp_media_remove_all_attr(sdp->media[0], "inactive"); /* Add inactive attribute */ attr = pjmedia_sdp_attr_create(pjsua_var.pool, "inactive", NULL); pjmedia_sdp_media_add_attr(sdp->media[0], attr); *p_answer = sdp; return status;}/* * Called when session received new offer. */static void pjsua_call_on_rx_offer(pjsip_inv_session *inv, const pjmedia_sdp_session *offer){ const char *remote_state; pjsua_call *call; pjmedia_sdp_conn *conn; pjmedia_sdp_session *answer; pj_bool_t is_remote_active; pj_status_t status; PJSUA_LOCK(); call = inv->dlg->mod_data[pjsua_var.mod.id]; /* * See if remote is offering active media (i.e. not on-hold) */ is_remote_active = PJ_TRUE; conn = offer->media[0]->conn; if (!conn) conn = offer->conn; if (pj_strcmp2(&conn->addr, "0.0.0.0")==0 || pj_strcmp2(&conn->addr, "0")==0) { is_remote_active = PJ_FALSE; } else if (pjmedia_sdp_media_find_attr2(offer->media[0], "inactive", NULL)) { is_remote_active = PJ_FALSE; } remote_state = (is_remote_active ? "active" : "inactive"); /* Supply candidate answer */ if (call->media_st == PJSUA_CALL_MEDIA_LOCAL_HOLD || !is_remote_active) { PJ_LOG(4,(THIS_FILE, "Call %d: RX new media offer, creating inactive SDP " "(media in offer is %s)", call->index, remote_state)); status = create_inactive_sdp( call, &answer ); } else { PJ_LOG(4,(THIS_FILE, "Call %d: received updated media offer", call->index)); status = pjmedia_endpt_create_sdp( pjsua_var.med_endpt, call->inv->pool, 1, &call->skinfo, &answer); } if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Unable to create local SDP", status); PJSUA_UNLOCK(); return; } status = pjsip_inv_set_sdp_answer(call->inv, answer); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Unable to set answer", status); PJSUA_UNLOCK(); return; } PJSUA_UNLOCK();}/* * Callback called by event framework when the xfer subscription state * has changed. */static void xfer_client_on_evsub_state( pjsip_evsub *sub, pjsip_event *event){ PJ_UNUSED_ARG(event); /* * When subscription is accepted (got 200/OK to REFER), check if * subscription suppressed. */ if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_ACCEPTED) { pjsip_rx_data *rdata; pjsip_generic_string_hdr *refer_sub; const pj_str_t REFER_SUB = { "Refer-Sub", 9 }; pjsua_call *call; call = pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id); /* Must be receipt of response message */ pj_assert(event->type == PJSIP_EVENT_TSX_STATE && event->body.tsx_state.type == PJSIP_EVENT_RX_MSG); rdata = event->body.tsx_state.src.rdata; /* Find Refer-Sub header */ refer_sub = (pjsip_generic_string_hdr*) pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &REFER_SUB, NULL); /* Check if subscription is suppressed */ if (refer_sub && pj_stricmp2(&refer_sub->hvalue, "false")==0) { /* Since no subscription is desired, assume that call has been * transfered successfully. */ if (call && pjsua_var.ua_cfg.cb.on_call_transfer_status) { const pj_str_t ACCEPTED = { "Accepted", 8 }; pj_bool_t cont = PJ_FALSE; (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index, 200, &ACCEPTED, PJ_TRUE, &cont); }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -