📄 pjsua_app.c
字号:
if (call_id == PJSUA_INVALID_ID) {
PJ_LOG(1,(THIS_FILE, "Invalid call ID in timer callback"));
return;
}
/* Add warning header */
pjsua_msg_data_init(&msg_data);
pjsip_generic_string_hdr_init2(&warn, &hname, &hvalue);
pj_list_push_back(&msg_data.hdr_list, &warn);
/* Call duration has been exceeded; disconnect the call */
PJ_LOG(3,(THIS_FILE, "Duration (%d seconds) has been exceeded "
"for call %d, disconnecting the call",
app_config.duration, call_id));
entry->id = PJSUA_INVALID_ID;
pjsua_call_hangup(call_id, 200, NULL, &msg_data);
}
/*
* Handler when invite state has changed.
*/
static void on_call_state(pjsua_call_id call_id, pjsip_event *e)
{
pjsua_call_info call_info;
PJ_UNUSED_ARG(e);
pjsua_call_get_info(call_id, &call_info);
if (call_info.state == PJSIP_INV_STATE_DISCONNECTED) {
/* Cancel duration timer, if any */
if (app_config.call_data[call_id].timer.id != PJSUA_INVALID_ID) {
struct call_data *cd = &app_config.call_data[call_id];
pjsip_endpoint *endpt = pjsua_get_pjsip_endpt();
cd->timer.id = PJSUA_INVALID_ID;
pjsip_endpt_cancel_timer(endpt, &cd->timer);
}
PJ_LOG(3,(THIS_FILE, "Call %d is DISCONNECTED [reason=%d (%s)]",
call_id,
call_info.last_status,
call_info.last_status_text.ptr));
if (call_id == current_call) {
find_next_call();
}
/* Dump media state upon disconnected */
if (1) {
char buf[1024];
pjsua_call_dump(call_id, PJ_TRUE, buf,
sizeof(buf), " ");
PJ_LOG(5,(THIS_FILE,
"Call %d disconnected, dumping media stats\n%s",
call_id, buf));
}
} else {
if (app_config.duration!=NO_LIMIT &&
call_info.state == PJSIP_INV_STATE_CONFIRMED)
{
/* Schedule timer to hangup call after the specified duration */
struct call_data *cd = &app_config.call_data[call_id];
pjsip_endpoint *endpt = pjsua_get_pjsip_endpt();
pj_time_val delay;
cd->timer.id = call_id;
delay.sec = app_config.duration;
delay.msec = 0;
pjsip_endpt_schedule_timer(endpt, &cd->timer, &delay);
}
if (call_info.state == PJSIP_INV_STATE_EARLY) {
int code;
pj_str_t reason;
pjsip_msg *msg;
/* This can only occur because of TX or RX message */
pj_assert(e->type == PJSIP_EVENT_TSX_STATE);
if (e->body.tsx_state.type == PJSIP_EVENT_RX_MSG) {
msg = e->body.tsx_state.src.rdata->msg_info.msg;
} else {
msg = e->body.tsx_state.src.tdata->msg;
}
code = msg->line.status.code;
reason = msg->line.status.reason;
PJ_LOG(3,(THIS_FILE, "Call %d state changed to %s (%d %.*s)",
call_id, call_info.state_text.ptr,
code, (int)reason.slen, reason.ptr));
} else {
PJ_LOG(3,(THIS_FILE, "Call %d state changed to %s",
call_id,
call_info.state_text.ptr));
}
if (current_call==PJSUA_INVALID_ID)
current_call = call_id;
}
}
/**
* Handler when there is incoming call.
*/
static void on_incoming_call(pjsua_acc_id acc_id, pjsua_call_id call_id,
pjsip_rx_data *rdata)
{
pjsua_call_info call_info;
PJ_UNUSED_ARG(acc_id);
PJ_UNUSED_ARG(rdata);
pjsua_call_get_info(call_id, &call_info);
if (app_config.auto_answer > 0) {
pjsua_call_answer(call_id, app_config.auto_answer, NULL, NULL);
}
if (app_config.auto_answer < 200) {
PJ_LOG(3,(THIS_FILE,
"Incoming call for account %d!\n"
"From: %s\n"
"To: %s\n"
"Press a to answer or h to reject call",
acc_id,
call_info.remote_info.ptr,
call_info.local_info.ptr));
}
}
/*
* Callback on media state changed event.
* The action may connect the call to sound device, to file, or
* to loop the call.
*/
static void on_call_media_state(pjsua_call_id call_id)
{
pjsua_call_info call_info;
pjsua_call_get_info(call_id, &call_info);
if (call_info.media_status == PJSUA_CALL_MEDIA_ACTIVE) {
pj_bool_t connect_sound = PJ_TRUE;
/* Loopback sound, if desired */
if (app_config.auto_loop) {
pjsua_conf_connect(call_info.conf_slot, call_info.conf_slot);
connect_sound = PJ_FALSE;
/* Automatically record conversation, if desired */
if (app_config.auto_rec && app_config.rec_port != PJSUA_INVALID_ID) {
pjsua_conf_connect(call_info.conf_slot, app_config.rec_port);
}
}
/* Stream a file, if desired */
if (app_config.auto_play && app_config.wav_port != PJSUA_INVALID_ID) {
pjsua_conf_connect(app_config.wav_port, call_info.conf_slot);
connect_sound = PJ_FALSE;
}
/* Put call in conference with other calls, if desired */
if (app_config.auto_conf) {
pjsua_call_id call_ids[PJSUA_MAX_CALLS];
unsigned call_cnt=PJ_ARRAY_SIZE(call_ids);
unsigned i;
/* Get all calls, and establish media connection between
* this call and other calls.
*/
pjsua_enum_calls(call_ids, &call_cnt);
for (i=0; i<call_cnt; ++i) {
if (call_ids[i] == call_id)
continue;
if (!pjsua_call_has_media(call_ids[i]))
continue;
pjsua_conf_connect(call_info.conf_slot,
pjsua_call_get_conf_port(call_ids[i]));
pjsua_conf_connect(pjsua_call_get_conf_port(call_ids[i]),
call_info.conf_slot);
/* Automatically record conversation, if desired */
if (app_config.auto_rec && app_config.rec_port != PJSUA_INVALID_ID) {
pjsua_conf_connect(pjsua_call_get_conf_port(call_ids[i]),
app_config.rec_port);
}
}
/* Also connect call to local sound device */
connect_sound = PJ_TRUE;
}
/* Otherwise connect to sound device */
if (connect_sound) {
pjsua_conf_connect(call_info.conf_slot, 0);
pjsua_conf_connect(0, call_info.conf_slot);
/* Automatically record conversation, if desired */
if (app_config.auto_rec && app_config.rec_port != PJSUA_INVALID_ID) {
pjsua_conf_connect(call_info.conf_slot, app_config.rec_port);
pjsua_conf_connect(0, app_config.rec_port);
}
}
PJ_LOG(3,(THIS_FILE, "Media for call %d is active", call_id));
} else if (call_info.media_status == PJSUA_CALL_MEDIA_LOCAL_HOLD) {
PJ_LOG(3,(THIS_FILE, "Media for call %d is suspended (hold) by local",
call_id));
} else if (call_info.media_status == PJSUA_CALL_MEDIA_REMOTE_HOLD) {
PJ_LOG(3,(THIS_FILE,
"Media for call %d is suspended (hold) by remote",
call_id));
} else {
PJ_LOG(3,(THIS_FILE,
"Media for call %d is inactive",
call_id));
}
}
/*
* DTMF callback.
*/
static void call_on_dtmf_callback(pjsua_call_id call_id, int dtmf)
{
PJ_LOG(3,(THIS_FILE, "Incoming DTMF on call %d: %c", call_id, dtmf));
}
/*
* Handler registration status has changed.
*/
static void on_reg_state(pjsua_acc_id acc_id)
{
PJ_UNUSED_ARG(acc_id);
// Log already written.
}
/*
* Handler on buddy state changed.
*/
static void on_buddy_state(pjsua_buddy_id buddy_id)
{
pjsua_buddy_info info;
pjsua_buddy_get_info(buddy_id, &info);
PJ_LOG(3,(THIS_FILE, "%.*s status is %.*s",
(int)info.uri.slen,
info.uri.ptr,
(int)info.status_text.slen,
info.status_text.ptr));
}
/**
* Incoming IM message (i.e. MESSAGE request)!
*/
static void on_pager(pjsua_call_id call_id, const pj_str_t *from,
const pj_str_t *to, const pj_str_t *contact,
const pj_str_t *mime_type, const pj_str_t *text)
{
/* Note: call index may be -1 */
PJ_UNUSED_ARG(call_id);
PJ_UNUSED_ARG(to);
PJ_UNUSED_ARG(contact);
PJ_UNUSED_ARG(mime_type);
PJ_LOG(3,(THIS_FILE,"MESSAGE from %.*s: %.*s (%.*s)",
(int)from->slen, from->ptr,
(int)text->slen, text->ptr,
(int)mime_type->slen, mime_type->ptr));
}
/**
* Received typing indication
*/
static void on_typing(pjsua_call_id call_id, const pj_str_t *from,
const pj_str_t *to, const pj_str_t *contact,
pj_bool_t is_typing)
{
PJ_UNUSED_ARG(call_id);
PJ_UNUSED_ARG(to);
PJ_UNUSED_ARG(contact);
PJ_LOG(3,(THIS_FILE, "IM indication: %.*s %s",
(int)from->slen, from->ptr,
(is_typing?"is typing..":"has stopped typing")));
}
/**
* Call transfer request status.
*/
static void on_call_transfer_status(pjsua_call_id call_id,
int status_code,
const pj_str_t *status_text,
pj_bool_t final,
pj_bool_t *p_cont)
{
PJ_LOG(3,(THIS_FILE, "Call %d: transfer status=%d (%.*s) %s",
call_id, status_code,
(int)status_text->slen, status_text->ptr,
(final ? "[final]" : "")));
if (status_code/100 == 2) {
PJ_LOG(3,(THIS_FILE,
"Call %d: call transfered successfully, disconnecting call",
call_id));
pjsua_call_hangup(call_id, PJSIP_SC_GONE, NULL, NULL);
*p_cont = PJ_FALSE;
}
}
/*
* Notification that call is being replaced.
*/
static void on_call_replaced(pjsua_call_id old_call_id,
pjsua_call_id new_call_id)
{
pjsua_call_info old_ci, new_ci;
pjsua_call_get_info(old_call_id, &old_ci);
pjsua_call_get_info(new_call_id, &new_ci);
PJ_LOG(3,(THIS_FILE, "Call %d with %.*s is being replaced by "
"call %d with %.*s",
old_call_id,
(int)old_ci.remote_info.slen, old_ci.remote_info.ptr,
new_call_id,
(int)new_ci.remote_info.slen, new_ci.remote_info.ptr));
}
/*
* Print buddy list.
*/
static void print_buddy_list(void)
{
pjsua_buddy_id ids[64];
int i;
unsigned count = PJ_ARRAY_SIZE(ids);
puts("Buddy list:");
pjsua_enum_buddies(ids, &count);
if (count == 0)
puts(" -none-");
else {
for (i=0; i<(int)count; ++i) {
pjsua_buddy_info info;
if (pjsua_buddy_get_info(ids[i], &info) != PJ_SUCCESS)
continue;
printf(" [%2d] <%7s> %.*s\n",
ids[i]+1, info.status_text.ptr,
(int)info.uri.slen,
info.uri.ptr);
}
}
puts("");
}
/*
* Print account status.
*/
static void print_acc_status(int acc_id)
{
char buf[80];
pjsua_acc_info info;
pjsua_acc_get_info(acc_id, &info);
if (!info.has_registration) {
pj_ansi_snprintf(buf, sizeof(buf), "%.*s",
(int)info.status_text.slen,
info.status_text.ptr);
} else {
pj_ansi_snprintf(buf, sizeof(buf),
"%d/%.*s (expires=%d)",
info.status,
(int)info.status_text.slen,
info.status_text.ptr,
info.expires);
}
printf(" %c[%2d] %.*s: %s\n", (acc_id==current_acc?'*':' '),
acc_id, (int)info.acc_uri.slen, info.acc_uri.ptr, buf);
printf(" Online status: %s\n",
(info.online_status ? "Online" : "Invisible"));
}
/*
* Show a bit of help.
*/
static void keystroke_help(void)
{
pjsua_acc_id acc_ids[16];
unsigned count = PJ_ARRAY_SIZE(acc_ids);
int i;
printf(">>>>\n");
pjsua_enum_accs(acc_ids, &count);
printf("Account list:\n");
for (i=0; i<(int)count; ++i)
print_acc_status(acc_ids[i]);
print_buddy_list();
//puts("Commands:");
puts("+=============================================================================+");
puts("| Call Commands: | Buddy, IM & Presence: | Account: |");
puts("| | | |");
puts("| m Make new call | +b Add new buddy .| +a Add new accnt |");
puts("| M Make multiple calls | -b Delete buddy | -a Delete accnt. |");
puts("| a Answer call | !b Modify buddy | !a Modify accnt. |");
puts("| h Hangup call (ha=all) | i Send IM | rr (Re-)register |");
puts("| H Hold call | s Subscribe presence | ru Unregister |");
puts("| v re-inVite (release hold) | u Unsubscribe presence | > Cycle next ac.|");
puts("| ] Select next dialog | t ToGgle Online status | < Cycle prev ac.|");
puts("| [ Select previous dialog +--------------------------+-------------------+");
puts("| x Xfer call | Media Commands: | Status & Config: |");
puts("| X Xfer with Replaces | | |");
puts("| # Send DTMF string | cl List ports | d Dump status |");
puts("| dq Dump curr. call quality | cc Connect port | dd Dump detailed |");
puts("| | cd Disconnect port | dc Dump config |");
puts("| S Send arbitrary REQUEST | V Adjust audio Volume | f Save config |");
puts("+------------------------------+--------------------------+-------------------+");
puts("| q QUIT sleep N: console sleep for N ms |");
puts("+=============================================================================+");
i = pjsua_call_get_count();
printf("You have %d active call%s\n", i, (i>1?"s":""));
if (current_call != PJSUA_INVALID_ID) {
pjsua_call_info ci;
if (pjsua_call_get_info(current_call, &ci)==PJ_SUCCESS)
printf("Current call id=%d to %.*s [%.*s]\n", current_call,
(int)ci.remote_info.slen, ci.remote_info.ptr,
(int)ci.state_text.slen, ci.state_text.ptr);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -