📄 pjsua_call.c
字号:
on_return: pjsip_dlg_dec_lock(dlg); return status;}/* * Send IM typing indication inside INVITE session. */PJ_DEF(pj_status_t) pjsua_call_send_typing_ind( pjsua_call_id call_id, pj_bool_t is_typing, const pjsua_msg_data*msg_data){ pjsua_call *call; pjsip_dialog *dlg; pjsip_tx_data *tdata; pj_status_t status; PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls, PJ_EINVAL); status = acquire_call("pjsua_call_send_typing_ind", call_id, &call, &dlg); if (status != PJ_SUCCESS) return status; /* Create request message. */ status = pjsip_dlg_create_request( call->inv->dlg, &pjsip_message_method, -1, &tdata); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Unable to create MESSAGE request", status); goto on_return; } /* Create "application/im-iscomposing+xml" msg body. */ tdata->msg->body = pjsip_iscomposing_create_body(tdata->pool, is_typing, NULL, NULL, -1); /* Add additional headers etc */ pjsua_process_msg_data( tdata, msg_data); /* Send the request. */ status = pjsip_dlg_send_request( call->inv->dlg, tdata, -1, NULL); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Unable to send MESSAGE request", status); goto on_return; }on_return: pjsip_dlg_dec_lock(dlg); return status;}/* * Terminate all calls. */PJ_DEF(void) pjsua_call_hangup_all(void){ unsigned i; PJSUA_LOCK(); for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) { if (pjsua_var.calls[i].inv) pjsua_call_hangup(i, 0, NULL, NULL); } PJSUA_UNLOCK();}static const char *good_number(char *buf, pj_int32_t val){ if (val < 1000) { pj_ansi_sprintf(buf, "%d", val); } else if (val < 1000000) { pj_ansi_sprintf(buf, "%d.%dK", val / 1000, (val % 1000) / 100); } else { pj_ansi_sprintf(buf, "%d.%02dM", val / 1000000, (val % 1000000) / 10000); } return buf;}/* Dump media session */static void dump_media_session(const char *indent, char *buf, unsigned maxlen, pjmedia_session *session){ unsigned i; char *p = buf, *end = buf+maxlen; int len; pjmedia_session_info info; pjmedia_session_get_info(session, &info); for (i=0; i<info.stream_cnt; ++i) { pjmedia_rtcp_stat stat; const char *rem_addr; int rem_port; const char *dir; char last_update[64]; char packets[32], bytes[32], ipbytes[32], avg_bps[32]; pj_time_val media_duration, now; pjmedia_session_get_stream_stat(session, i, &stat); rem_addr = pj_inet_ntoa(info.stream_info[i].rem_addr.sin_addr); rem_port = pj_ntohs(info.stream_info[i].rem_addr.sin_port); if (info.stream_info[i].dir == PJMEDIA_DIR_ENCODING) dir = "sendonly"; else if (info.stream_info[i].dir == PJMEDIA_DIR_DECODING) dir = "recvonly"; else if (info.stream_info[i].dir == PJMEDIA_DIR_ENCODING_DECODING) dir = "sendrecv"; else dir = "inactive"; len = pj_ansi_snprintf(buf, end-p, "%s #%d %.*s @%dKHz, %s, peer=%s:%d", indent, i, (int)info.stream_info[i].fmt.encoding_name.slen, info.stream_info[i].fmt.encoding_name.ptr, info.stream_info[i].fmt.clock_rate / 1000, dir, rem_addr, rem_port); if (len < 1 || len > end-p) { *p = '\0'; return; } p += len; *p++ = '\n'; *p = '\0'; if (stat.rx.update_cnt == 0) strcpy(last_update, "never"); else { pj_gettimeofday(&now); PJ_TIME_VAL_SUB(now, stat.rx.update); sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago", now.sec / 3600, (now.sec % 3600) / 60, now.sec % 60, now.msec); } pj_gettimeofday(&media_duration); PJ_TIME_VAL_SUB(media_duration, stat.start); if (PJ_TIME_VAL_MSEC(media_duration) == 0) media_duration.msec = 1; len = pj_ansi_snprintf(p, end-p, "%s RX pt=%d, stat last update: %s\n" "%s total %spkt %sB (%sB +IP hdr) @avg=%sbps\n" "%s pkt loss=%d (%3.1f%%), dup=%d (%3.1f%%), reorder=%d (%3.1f%%)\n" "%s (msec) min avg max last\n" "%s loss period: %7.3f %7.3f %7.3f %7.3f\n" "%s jitter : %7.3f %7.3f %7.3f %7.3f%s", indent, info.stream_info[i].fmt.pt, last_update, indent, good_number(packets, stat.rx.pkt), good_number(bytes, stat.rx.bytes), good_number(ipbytes, stat.rx.bytes + stat.rx.pkt * 32), good_number(avg_bps, stat.rx.bytes * 8 * 1000 / PJ_TIME_VAL_MSEC(media_duration)), indent, stat.rx.loss, stat.rx.loss * 100.0 / (stat.rx.pkt + stat.rx.loss), stat.rx.dup, stat.rx.dup * 100.0 / (stat.rx.pkt + stat.rx.loss), stat.rx.reorder, stat.rx.reorder * 100.0 / (stat.rx.pkt + stat.rx.loss), indent, indent, stat.rx.loss_period.min / 1000.0, stat.rx.loss_period.avg / 1000.0, stat.rx.loss_period.max / 1000.0, stat.rx.loss_period.last / 1000.0, indent, stat.rx.jitter.min / 1000.0, stat.rx.jitter.avg / 1000.0, stat.rx.jitter.max / 1000.0, stat.rx.jitter.last / 1000.0, "" ); if (len < 1 || len > end-p) { *p = '\0'; return; } p += len; *p++ = '\n'; *p = '\0'; if (stat.tx.update_cnt == 0) strcpy(last_update, "never"); else { pj_gettimeofday(&now); PJ_TIME_VAL_SUB(now, stat.tx.update); sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago", now.sec / 3600, (now.sec % 3600) / 60, now.sec % 60, now.msec); } len = pj_ansi_snprintf(p, end-p, "%s TX pt=%d, ptime=%dms, stat last update: %s\n" "%s total %spkt %sB (%sB +IP hdr) @avg %sbps\n" "%s pkt loss=%d (%3.1f%%), dup=%d (%3.1f%%), reorder=%d (%3.1f%%)\n" "%s (msec) min avg max last\n" "%s loss period: %7.3f %7.3f %7.3f %7.3f\n" "%s jitter : %7.3f %7.3f %7.3f %7.3f%s", indent, info.stream_info[i].tx_pt, info.stream_info[i].param->info.frm_ptime * info.stream_info[i].param->setting.frm_per_pkt, last_update, indent, good_number(packets, stat.tx.pkt), good_number(bytes, stat.tx.bytes), good_number(ipbytes, stat.tx.bytes + stat.tx.pkt * 32), good_number(avg_bps, stat.tx.bytes * 8 * 1000 / PJ_TIME_VAL_MSEC(media_duration)), indent, stat.tx.loss, stat.tx.loss * 100.0 / (stat.tx.pkt + stat.tx.loss), stat.tx.dup, stat.tx.dup * 100.0 / (stat.tx.pkt + stat.tx.loss), stat.tx.reorder, stat.tx.reorder * 100.0 / (stat.tx.pkt + stat.tx.loss), indent, indent, stat.tx.loss_period.min / 1000.0, stat.tx.loss_period.avg / 1000.0, stat.tx.loss_period.max / 1000.0, stat.tx.loss_period.last / 1000.0, indent, stat.tx.jitter.min / 1000.0, stat.tx.jitter.avg / 1000.0, stat.tx.jitter.max / 1000.0, stat.tx.jitter.last / 1000.0, "" ); if (len < 1 || len > end-p) { *p = '\0'; return; } p += len; *p++ = '\n'; *p = '\0'; len = pj_ansi_snprintf(p, end-p, "%s RTT msec : %7.3f %7.3f %7.3f %7.3f", indent, stat.rtt.min / 1000.0, stat.rtt.avg / 1000.0, stat.rtt.max / 1000.0, stat.rtt.last / 1000.0 ); if (len < 1 || len > end-p) { *p = '\0'; return; } p += len; *p++ = '\n'; *p = '\0'; }}/* Print call info */static void print_call(const char *title, int call_id, char *buf, pj_size_t size){ int len; pjsip_inv_session *inv = pjsua_var.calls[call_id].inv; pjsip_dialog *dlg = inv->dlg; char userinfo[128]; /* Dump invite sesion info. */ len = pjsip_hdr_print_on(dlg->remote.info, userinfo, sizeof(userinfo)); if (len < 1) pj_ansi_strcpy(userinfo, "<--uri too long-->"); else userinfo[len] = '\0'; len = pj_ansi_snprintf(buf, size, "%s[%s] %s", title, pjsip_inv_state_name(inv->state), userinfo); if (len < 1 || len >= (int)size) { pj_ansi_strcpy(buf, "<--uri too long-->"); len = 18; } else buf[len] = '\0';}/* * Dump call and media statistics to string. */PJ_DEF(pj_status_t) pjsua_call_dump( pjsua_call_id call_id, pj_bool_t with_media, char *buffer, unsigned maxlen, const char *indent){ pjsua_call *call; pjsip_dialog *dlg; pj_time_val duration, res_delay, con_delay; char tmp[128]; char *p, *end; pj_status_t status; int len; PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls, PJ_EINVAL); status = acquire_call("pjsua_call_dump()", call_id, &call, &dlg); if (status != PJ_SUCCESS) return status; *buffer = '\0'; p = buffer; end = buffer + maxlen; len = 0; print_call(indent, call_id, tmp, sizeof(tmp)); len = pj_ansi_strlen(tmp); pj_ansi_strcpy(buffer, tmp); p += len; *p++ = '\r'; *p++ = '\n'; /* Calculate call duration */ if (call->conn_time.sec != 0) { pj_gettimeofday(&duration); PJ_TIME_VAL_SUB(duration, call->conn_time); con_delay = call->conn_time; PJ_TIME_VAL_SUB(con_delay, call->start_time); } else { duration.sec = duration.msec = 0; con_delay.sec = con_delay.msec = 0; } /* Calculate first response delay */ if (call->res_time.sec != 0) { res_delay = call->res_time; PJ_TIME_VAL_SUB(res_delay, call->start_time); } else { res_delay.sec = res_delay.msec = 0; } /* Print duration */ len = pj_ansi_snprintf(p, end-p, "%s Call time: %02dh:%02dm:%02ds, " "1st res in %d ms, conn in %dms", indent, (int)(duration.sec / 3600), (int)((duration.sec % 3600)/60), (int)(duration.sec % 60), (int)PJ_TIME_VAL_MSEC(res_delay), (int)PJ_TIME_VAL_MSEC(con_delay)); if (len > 0 && len < end-p) { p += len; *p++ = '\n'; *p = '\0'; } /* Dump session statistics */ if (with_media && call->session) dump_media_session(indent, p, end-p, call->session); pjsip_dlg_dec_lock(dlg); return PJ_SUCCESS;}/* * Destroy the call's media */static pj_status_t call_destroy_media(int call_id){ pjsua_call *call = &pjsua_var.calls[call_id]; if (call->conf_slot != PJSUA_INVALID_ID) { pjmedia_conf_remove_port(pjsua_var.mconf, call->conf_slot); call->conf_slot = PJSUA_INVALID_ID; } if (call->session) { /* Destroy session (this will also close RTP/RTCP sockets). */ pjmedia_session_destroy(call->session); call->session = NULL; PJ_LOG(4,(THIS_FILE, "Media session for call %d is destroyed", call_id)); } call->media_st = PJSUA_CALL_MEDIA_NONE; return PJ_SUCCESS;}/* * This callback receives notification from invite session when the * session state has changed. */static void pjsua_call_on_state_changed(pjsip_inv_session *inv, pjsip_event *e){ pjsua_call *call; PJSUA_LOCK(); call = inv->dlg->mod_data[pjsua_var.mod.id]; if (!call) { PJSUA_UNLOCK(); return; } /* Get call times */ switch (inv->state) { case PJSIP_INV_STATE_EARLY: case PJSIP_INV_STATE_CONNECTING: if (call->res_time.sec == 0) pj_gettimeofday(&call->res_time); call->last_code = e->body.tsx_state.tsx->status_code; pj_strncpy(&call->last_text, &e->body.tsx_state.tsx->status_text, sizeof(call->last_text_buf_)); break; case PJSIP_INV_STATE_CONFIRMED: pj_gettimeofday(&call->conn_time); break; case PJSIP_INV_STATE_DISCONNECTED: pj_gettimeofday(&call->dis_time); if (call->res_time.sec == 0) pj_gettimeofday(&call->res_time); if (e->body.tsx_state.tsx->status_code > call->last_code) { call->last_code = e->body.tsx_state.tsx->status_code; pj_strncpy(&call->last_text, &e->body.tsx_state.tsx->status_text, sizeof(call->last_text_buf_)); } break; default: call->last_code = e->body.tsx_state.tsx->status_code; pj_strncpy(&call->last_text, &e->body.tsx_state.tsx->status_text, sizeof(call->last_text_buf_)); break; } /* If this is an outgoing INVITE that was created because of * REFER/transfer, send NOTIFY to transferer. */ if (call->xfer_sub && e->type==PJSIP_EVENT_TSX_STATE) { int st_code = -1; pjsip_evsub_state ev_state = PJSIP_EVSUB_STATE_ACTIVE; switch (call->inv->state) { case PJSIP_INV_STATE_NULL: case PJSIP_INV_STATE_CALLING: /* Do nothing */ break; case PJSIP_INV_STATE_EARLY: case PJSIP_INV_STATE_CONNECTING: st_code = e->body.tsx_state.tsx->status_code; ev_state = PJSIP_EVSUB_STATE_ACTIVE; break; case PJSIP_INV_STATE_CONFIRMED: /* When state is confirmed, send the final 200/OK and terminate * subscription.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -