📄 pjsua_app.c
字号:
return PJ_FALSE;}/* Callback from timer when the maximum call duration has been * exceeded. */static void call_timeout_callback(pj_timer_heap_t *timer_heap, struct pj_timer_entry *entry){ pjsua_call_id call_id = entry->id; pjsua_msg_data msg_data; pjsip_generic_string_hdr warn; pj_str_t hname = pj_str("Warning"); pj_str_t hvalue = pj_str("399 pjsua \"Call duration exceeded\""); PJ_UNUSED_ARG(timer_heap); 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 + -