📄 pjsip-perf.c
字号:
#if defined(PJMEDIA_HAS_GSM_CODEC) && PJMEDIA_HAS_GSM_CODEC!=0
pjmedia_codec_gsm_init(app.med_endpt);
#endif
#if defined(PJMEDIA_HAS_SPEEX_CODEC) && PJMEDIA_HAS_SPEEX_CODEC!=0
pjmedia_codec_speex_init(app.med_endpt, PJMEDIA_SPEEX_NO_UWB, 3, 3);
#endif
/* Init dummy socket addresses */
app.skinfo_cnt = 0;
for (i=0, rtp_port=4000; i<PJ_ARRAY_SIZE(app.skinfo); ++i, rtp_port+=2) {
pjmedia_sock_info *skinfo;
skinfo = &app.skinfo[i];
pj_sockaddr_in_init(&skinfo->rtp_addr_name, &app.local_addr,
(pj_uint16_t)rtp_port);
pj_sockaddr_in_init(&skinfo->rtp_addr_name, &app.local_addr,
(pj_uint16_t)(rtp_port+1));
app.skinfo_cnt++;
}
/* Generate dummy SDP */
dummy_sdp_str.slen = pj_ansi_strlen(dummy_sdp_str.ptr);
status = pjmedia_sdp_parse(app.pool, dummy_sdp_str.ptr, dummy_sdp_str.slen,
&app.dummy_sdp);
if (status != PJ_SUCCESS) {
app_perror(THIS_FILE, "Error parsing dummy SDP", status);
return status;
}
/* Done */
return PJ_SUCCESS;
}
/* This is notification from the call about media negotiation
* status. This is called for client calls only.
*/
static void call_on_media_update( pjsip_inv_session *inv,
pj_status_t status)
{
if (status != PJ_SUCCESS) {
pjsip_tx_data *tdata;
pj_status_t status;
status = pjsip_inv_end_session(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE,
NULL, &tdata);
if (status == PJ_SUCCESS && tdata)
status = pjsip_inv_send_msg(inv, tdata);
}
}
/* This is notification from the call when the call state has changed.
* This is called for client calls only.
*/
static void call_on_state_changed( pjsip_inv_session *inv,
pjsip_event *e)
{
PJ_UNUSED_ARG(e);
/* Bail out if the session has been counted before */
if (inv->mod_data[mod_test.id] != NULL)
return;
/* Bail out if this is not an outgoing call */
if (inv->role != PJSIP_UAC_ROLE)
return;
if (inv->state == PJSIP_INV_STATE_CONFIRMED) {
pjsip_tx_data *tdata;
pj_status_t status;
//report_completion(200);
//inv->mod_data[mod_test.id] = (void*)1;
status = pjsip_inv_end_session(inv, PJSIP_SC_OK, NULL, &tdata);
if (status == PJ_SUCCESS && tdata)
status = pjsip_inv_send_msg(inv, tdata);
} else if (inv->state == PJSIP_INV_STATE_DISCONNECTED) {
report_completion(inv->cause);
inv->mod_data[mod_test.id] = (void*)1;
}
}
/* Not implemented for now */
static void call_on_forked(pjsip_inv_session *inv, pjsip_event *e)
{
/* Do nothing */
PJ_UNUSED_ARG(inv);
PJ_UNUSED_ARG(e);
}
/*
* Make outgoing call.
*/
static pj_status_t make_call(const pj_str_t *dst_uri)
{
struct call *call;
pjsip_dialog *dlg;
pjmedia_sdp_session *sdp;
pjsip_tx_data *tdata;
pj_status_t status;
/* Create UAC dialog */
status = pjsip_dlg_create_uac( pjsip_ua_instance(),
&app.local_uri, /* local URI */
&app.local_contact, /* local Contact */
dst_uri, /* remote URI */
dst_uri, /* remote target */
&dlg); /* dialog */
if (status != PJ_SUCCESS) {
return status;
}
/* Create call */
call = pj_pool_zalloc(dlg->pool, sizeof(struct call));
/* Create SDP */
if (app.real_sdp) {
status = pjmedia_endpt_create_sdp(app.med_endpt, dlg->pool, 1,
app.skinfo, &sdp);
if (status != PJ_SUCCESS) {
pjsip_dlg_terminate(dlg);
return status;
}
} else
sdp = app.dummy_sdp;
/* Create the INVITE session. */
status = pjsip_inv_create_uac( dlg, sdp, 0, &call->inv);
if (status != PJ_SUCCESS) {
pjsip_dlg_terminate(dlg);
return status;
}
/* Create initial INVITE request.
* This INVITE request will contain a perfectly good request and
* an SDP body as well.
*/
status = pjsip_inv_invite(call->inv, &tdata);
PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
/* Send initial INVITE request.
* From now on, the invite session's state will be reported to us
* via the invite session callbacks.
*/
status = pjsip_inv_send_msg(call->inv, tdata);
PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
return PJ_SUCCESS;
}
/*
* Verify that valid SIP url is given.
*/
static pj_status_t verify_sip_url(const char *c_url)
{
pjsip_uri *p;
pj_pool_t *pool;
char *url;
int len = (c_url ? pj_ansi_strlen(c_url) : 0);
if (!len) return -1;
pool = pj_pool_create(&app.cp.factory, "check%p", 1024, 0, NULL);
if (!pool) return PJ_ENOMEM;
url = pj_pool_alloc(pool, len+1);
pj_ansi_strcpy(url, c_url);
url[len] = '\0';
p = pjsip_parse_uri(pool, url, len, 0);
if (!p || pj_stricmp2(pjsip_uri_get_scheme(p), "sip") != 0)
p = NULL;
pj_pool_release(pool);
return p ? 0 : -1;
}
static void usage(void)
{
printf(
"Usage:\n"
" pjsip-perf [OPTIONS] -- to start as server\n"
" pjsip-perf [OPTIONS] URL -- to call server (possibly itself)\n"
"\n"
"where:\n"
" URL The SIP URL to be contacted.\n"
"\n"
"Client options:\n"
" --method=METHOD, -m Set test method (set to INVITE for call benchmark)\n"
" [default: OPTIONS]\n"
" --count=N, -n Set total number of requests to initiate\n"
" [default=%d]\n"
" --stateless, -s Set to operate in stateless mode\n"
" [default: stateful]\n"
" --timeout=SEC, -t Set client timeout [default=60 sec]\n"
" --window=COUNT, -w Set maximum outstanding job [default: %d]\n"
"\n"
"SDP options (client and server):\n"
" --real-sdp Generate real SDP from pjmedia, and also perform\n"
" proper SDP negotiation [default: dummy]\n"
"\n"
"Client and Server options:\n"
" --local-port=PORT, -p Set local port [default: 5060]\n"
" --use-tcp, -T Use TCP instead of UDP. Note that when started as\n"
" client, you must add ;transport=tcp parameter to URL\n"
" [default: no]\n"
" --thread-count=N Set number of worker threads [default=1]\n"
" --trying Send 100/Trying response (server, default no)\n"
" --ringing Send 180/Ringing response (server, default no)\n"
" --delay=MS, -d Delay answering call by MS (server, default no)\n"
"\n"
"Misc options:\n"
" --help, -h Display this screen\n"
" --verbose, -v Verbose logging (put more than once for even more)\n"
"\n"
"When started as server, pjsip-perf can be contacted on the following URIs:\n"
" - sip:0@server-addr To handle requests statelessly.\n"
" - sip:1@server-addr To handle requests statefully.\n"
" - sip:2@server-addr To handle INVITE call.\n",
DEFAULT_COUNT, JOB_WINDOW);
}
static int my_atoi(const char *s)
{
pj_str_t ss = pj_str((char*)s);
return pj_strtoul(&ss);
}
static pj_status_t init_options(int argc, char *argv[])
{
enum { OPT_THREAD_COUNT = 1, OPT_REAL_SDP, OPT_TRYING, OPT_RINGING };
struct pj_getopt_option long_options[] = {
{ "local-port", 1, 0, 'p' },
{ "count", 1, 0, 'c' },
{ "thread-count", 1, 0, OPT_THREAD_COUNT },
{ "method", 1, 0, 'm' },
{ "help", 0, 0, 'h' },
{ "stateless", 0, 0, 's' },
{ "timeout", 1, 0, 't' },
{ "real-sdp", 0, 0, OPT_REAL_SDP },
{ "verbose", 0, 0, 'v' },
{ "use-tcp", 0, 0, 'T' },
{ "window", 1, 0, 'w' },
{ "delay", 1, 0, 'd' },
{ "trying", 0, 0, OPT_TRYING},
{ "ringing", 0, 0, OPT_RINGING},
{ NULL, 0, 0, 0 },
};
int c;
int option_index;
/* Init default application configs */
app.local_port = 5060;
app.thread_count = 1;
app.client.job_count = DEFAULT_COUNT;
app.client.method = pjsip_options_method;
app.client.job_window = c = JOB_WINDOW;
app.client.timeout = 60;
app.log_level = 3;
/* Parse options */
pj_optind = 0;
while((c=pj_getopt_long(argc,argv, "p:c:m:t:w:d:hsv",
long_options, &option_index))!=-1)
{
switch (c) {
case 'p':
app.local_port = my_atoi(pj_optarg);
if (app.local_port < 0 || app.local_port > 65535) {
PJ_LOG(3,(THIS_FILE, "Invalid --local-port %s", pj_optarg));
return -1;
}
break;
case 'c':
app.client.job_count = my_atoi(pj_optarg);
if (app.client.job_count < 0) {
PJ_LOG(3,(THIS_FILE, "Invalid --local-port %s", pj_optarg));
return -1;
}
if (app.client.job_count > PJSIP_MAX_TSX_COUNT)
PJ_LOG(3,(THIS_FILE,
"Warning: --count value (%d) exceeds maximum "
"transaction count (%d)", app.client.job_count,
PJSIP_MAX_TSX_COUNT));
break;
case OPT_THREAD_COUNT:
app.thread_count = my_atoi(pj_optarg);
if (app.thread_count < 1 || app.thread_count > 16) {
PJ_LOG(3,(THIS_FILE, "Invalid --thread-count %s", pj_optarg));
return -1;
}
break;
case 'm':
{
pj_str_t temp = pj_str((char*)pj_optarg);
pjsip_method_init_np(&app.client.method, &temp);
}
break;
case 'h':
usage();
return -1;
case 's':
app.client.stateless = PJ_TRUE;
break;
case OPT_REAL_SDP:
app.real_sdp = 1;
break;
case 'v':
app.log_level++;
break;
case 't':
app.client.timeout = my_atoi(pj_optarg);
if (app.client.timeout < 0 || app.client.timeout > 600) {
PJ_LOG(3,(THIS_FILE, "Invalid --timeout %s", pj_optarg));
return -1;
}
break;
case 'w':
app.client.job_window = my_atoi(pj_optarg);
if (app.client.job_window <= 0) {
PJ_LOG(3,(THIS_FILE, "Invalid --window %s", pj_optarg));
return -1;
}
break;
case 'T':
app.use_tcp = PJ_TRUE;
break;
case 'd':
app.server.delay = my_atoi(pj_optarg);
if (app.server.delay > 3600) {
PJ_LOG(3,(THIS_FILE, "I think --delay %s is too long",
pj_optarg));
return -1;
}
break;
case OPT_TRYING:
app.server.send_trying = 1;
break;
case OPT_RINGING:
app.server.send_ringing = 1;
break;
default:
PJ_LOG(1,(THIS_FILE,
"Invalid argument. Use --help to see help"));
return -1;
}
}
if (pj_optind != argc) {
if (verify_sip_url(argv[pj_optind]) != PJ_SUCCESS) {
PJ_LOG(1,(THIS_FILE, "Invalid SIP URI %s", argv[pj_optind]));
return -1;
}
app.client.dst_uri = pj_str(argv[pj_optind]);
pj_optind++;
}
if (pj_optind != argc) {
PJ_LOG(1,(THIS_FILE, "Error: unknown options %s", argv[pj_optind]));
return -1;
}
return 0;
}
/* Send one stateless request */
static pj_status_t submit_stateless_job(void)
{
pjsip_tx_data *tdata;
pj_status_t status;
status = pjsip_endpt_create_request(app.sip_endpt, &app.client.method,
&app.client.dst_uri, &app.local_uri,
&app.client.dst_uri, &app.local_contact,
NULL, -1, NULL, &tdata);
if (status != PJ_SUCCESS) {
app_perror(THIS_FILE, "Error creating request", status);
report_completion(701);
return status;
}
status = pjsip_endpt_send_request_stateless(app.sip_endpt, tdata, NULL,
NULL);
if (status != PJ_SUCCESS) {
pjsip_tx_data_dec_ref(tdata);
app_perror(THIS_FILE, "Error sending stateless request", status);
report_completion(701);
return status;
}
return PJ_SUCCESS;
}
/* This callback is called when client transaction state has changed */
static void tsx_completion_cb(void *token, pjsip_event *event)
{
pjsip_transaction *tsx;
PJ_UNUSED_ARG(token);
if (event->type != PJSIP_EVENT_TSX_STATE)
return;
tsx = event->body.tsx_state.tsx;
if (tsx->mod_data[mod_test.id] != NULL) {
/* This transaction has been calculated before */
return;
}
if (tsx->state==PJSIP_TSX_STATE_TERMINATED) {
report_completion(tsx->status_code);
tsx->mod_data[mod_test.id] = (void*)1;
}
else if (tsx->method.id == PJSIP_INVITE_METHOD &&
tsx->state == PJSIP_TSX_STATE_CONFIRMED) {
report_completion(tsx->status_code);
tsx->mod_data[mod_test.id] = (void*)1;
} else if (tsx->state == PJSIP_TSX_STATE_COMPLETED) {
report_completion(tsx->status_code);
tsx->mod_data[mod_test.id] = (void*)1;
TERMINATE_TSX(tsx, tsx->status_code);
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -