📄 pjsua_call.c
字号:
if (status != PJ_SUCCESS)
return status;
/* Set default media type if none is specified */
if (mime_type == NULL) {
mime_type = &mime_text_plain;
}
/* 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;
}
/* Add accept header. */
pjsip_msg_add_hdr( tdata->msg,
(pjsip_hdr*)pjsua_im_create_accept(tdata->pool));
/* Parse MIME type */
pjsua_parse_media_type(tdata->pool, mime_type, &ctype);
/* Create "text/plain" message body. */
tdata->msg->body = pjsip_msg_body_create( tdata->pool, &ctype.type,
&ctype.subtype, content);
if (tdata->msg->body == NULL) {
pjsua_perror(THIS_FILE, "Unable to create msg body", PJ_ENOMEM);
pjsip_tx_data_dec_ref(tdata);
goto on_return;
}
/* Add additional headers etc */
pjsua_process_msg_data( tdata, msg_data);
/* Create IM data and attach to the request. */
im_data = pj_pool_zalloc(tdata->pool, sizeof(*im_data));
im_data->acc_id = call->acc_id;
im_data->call_id = call_id;
im_data->to = call->inv->dlg->remote.info_str;
pj_strdup_with_null(tdata->pool, &im_data->body, content);
im_data->user_data = user_data;
/* Send the request. */
status = pjsip_dlg_send_request( call->inv->dlg, tdata,
pjsua_var.mod.id, im_data);
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;
}
/*
* 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)
{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -