|
HOME
SIP/media Features
High Performance SIP
Small Footprint SIP
Symbian Port
FAQ
Documentation
Licensing
Download
Development (Trac)
Projects using pjsip
Mailing List
Open Source Links
About: PJLIB, PJLIB-UTIL, PJSIP, and PJMEDIA are created by: Benny Prijono <bennylp pjsip.org>
|
|
Home --> Documentations --> PJMEDIA Reference
pjsip-perf is a complete program to measure the performance of PJSIP or other SIP endpoints. It consists of two parts:
- the server, to respond incoming requests, and
- the client, who actively submits requests and measure the performance of the server.
Both server and client part can run simultaneously, to measure the performance when both endpoints are co-located in a single program.
The server accepts both INVITE and non-INVITE requests. The server exports several different types of URL, which would control how the request would be handled by the server:
- URL with "0" as the user part will be handled statelessly. It should not be used with INVITE method.
- URL with "1" as the user part will be handled statefully. If the request is an INVITE request, INVITE transaction will be created and 200/OK response will be sent, along with a valid SDP body. However, the SDP is just a static text body, and is not a proper SDP generated by PJMEDIA.
- URL with "2" as the user part is only meaningful for INVITE requests, as it would be handled call-statefully by the server. For this URL, the server also would generate SDP dynamically and perform a proper SDP negotiation for the incoming call. Also for every call, server will limit the call duration to 10 seconds, on which the call will be terminated if the client doesn't hangup the call.
This file is pjsip-apps/src/samples/pjsip-perf.c
00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00059
00060 #include <pjsip.h>
00061 #include <pjmedia.h>
00062 #include <pjmedia-codec.h>
00063 #include <pjsip_ua.h>
00064 #include <pjsip_simple.h>
00065 #include <pjlib-util.h>
00066 #include <pjlib.h>
00067 #include <stdio.h>
00068
00069 #if defined(PJ_WIN32) && PJ_WIN32!=0
00070 # include <windows.h>
00071 #endif
00072
00073 #define THIS_FILE "pjsip-perf.c"
00074 #define DEFAULT_COUNT (pjsip_cfg()->tsx.max_count/2>10000?10000:pjsip_cfg()->tsx.max_count/2)
00075 #define JOB_WINDOW 1000
00076 #define TERMINATE_TSX(x,c)
00077
00078
00079 #ifndef CACHING_POOL_SIZE
00080 # define CACHING_POOL_SIZE (256*1024*1024)
00081 #endif
00082
00083
00084
00085
00086
00087
00088 static pj_str_t dummy_sdp_str =
00089 {
00090 "v=0\r\n"
00091 "o=- 3360842071 3360842071 IN IP4 192.168.0.68\r\n"
00092 "s=pjmedia\r\n"
00093 "c=IN IP4 192.168.0.68\r\n"
00094 "t=0 0\r\n"
00095 "m=audio 4000 RTP/AVP 0 8 3 103 102 101\r\n"
00096 "a=rtcp:4001 IN IP4 192.168.0.68\r\n"
00097 "a=rtpmap:103 speex/16000\r\n"
00098 "a=rtpmap:102 speex/8000\r\n"
00099 "a=rtpmap:3 GSM/8000\r\n"
00100 "a=rtpmap:0 PCMU/8000\r\n"
00101 "a=rtpmap:8 PCMA/8000\r\n"
00102 "a=sendrecv\r\n"
00103 "a=rtpmap:101 telephone-event/8000\r\n"
00104 "a=fmtp:101 0-15\r\n",
00105 0
00106 };
00107
00108 static pj_str_t mime_application = { "application", 11};
00109 static pj_str_t mime_sdp = {"sdp", 3};
00110
00111
00112 struct srv_state
00113 {
00114 unsigned stateless_cnt;
00115 unsigned stateful_cnt;
00116 unsigned call_cnt;
00117 };
00118
00119
00120 struct app
00121 {
00122 pj_caching_pool cp;
00123 pj_pool_t *pool;
00124 pj_bool_t use_tcp;
00125 pj_str_t local_addr;
00126 int local_port;
00127 pjsip_endpoint *sip_endpt;
00128 pjmedia_endpt *med_endpt;
00129 pj_str_t local_uri;
00130 pj_str_t local_contact;
00131 unsigned skinfo_cnt;
00132 pjmedia_sock_info skinfo[8];
00133
00134 pj_bool_t thread_quit;
00135 unsigned thread_count;
00136 pj_thread_t *thread[16];
00137
00138 pj_bool_t real_sdp;
00139 pjmedia_sdp_session *dummy_sdp;
00140
00141 int log_level;
00142
00143 struct {
00144 pjsip_method method;
00145 pj_str_t dst_uri;
00146 pj_bool_t stateless;
00147 unsigned timeout;
00148 unsigned job_count,
00149 job_submitted,
00150 job_finished,
00151 job_window;
00152 unsigned stat_max_window;
00153 pj_time_val first_request;
00154 pj_time_val requests_sent;
00155 pj_time_val last_completion;
00156 unsigned total_responses;
00157 unsigned response_codes[800];
00158 } client;
00159
00160 struct {
00161 pj_bool_t send_trying;
00162 pj_bool_t send_ringing;
00163 unsigned delay;
00164 struct srv_state prev_state;
00165 struct srv_state cur_state;
00166 } server;
00167
00168
00169 } app;
00170
00171 struct call
00172 {
00173 pjsip_inv_session *inv;
00174 pj_timer_entry ans_timer;
00175 };
00176
00177
00178 static void app_perror(const char *sender, const char *title,
00179 pj_status_t status)
00180 {
00181 char errmsg[PJ_ERR_MSG_SIZE];
00182
00183 pj_strerror(status, errmsg, sizeof(errmsg));
00184 PJ_LOG(1,(sender, "%s: %s [code=%d]", title, errmsg, status));
00185 }
00186
00187
00188
00189
00190
00191 static pj_bool_t mod_stateless_on_rx_request(pjsip_rx_data *rdata);
00192
00193
00194
00195 static pjsip_module mod_stateless_server =
00196 {
00197 NULL, NULL,
00198 { "mod-stateless-server", 20 },
00199 -1,
00200 PJSIP_MOD_PRIORITY_APPLICATION,
00201 NULL,
00202 NULL,
00203 NULL,
00204 NULL,
00205 &mod_stateless_on_rx_request,
00206 NULL,
00207 NULL,
00208 NULL,
00209 NULL,
00210 };
00211
00212
00213 static pj_bool_t mod_stateless_on_rx_request(pjsip_rx_data *rdata)
00214 {
00215 const pj_str_t stateless_user = { "0", 1 };
00216 pjsip_uri *uri;
00217 pjsip_sip_uri *sip_uri;
00218
00219 uri = pjsip_uri_get_uri(rdata->msg_info.msg->line.req.uri);
00220
00221
00222 if (!PJSIP_URI_SCHEME_IS_SIP(uri))
00223 return PJ_FALSE;
00224
00225 sip_uri = (pjsip_sip_uri*) uri;
00226
00227
00228 if (pj_strcmp(&sip_uri->user, &stateless_user)!=0)
00229 return PJ_FALSE;
00230
00231
00232
00233
00234
00235
00236 if (rdata->msg_info.msg->line.req.method.id == PJSIP_ACK_METHOD)
00237 return PJ_TRUE;
00238
00239
00240
00241
00242 pjsip_endpt_respond_stateless(app.sip_endpt, rdata, 200, NULL,
00243 NULL, NULL);
00244 app.server.cur_state.stateless_cnt++;
00245 return PJ_TRUE;
00246 }
00247
00248
00249
00250
00251
00252 static pj_bool_t mod_stateful_on_rx_request(pjsip_rx_data *rdata);
00253
00254
00255
00256 static pjsip_module mod_stateful_server =
00257 {
00258 NULL, NULL,
00259 { "mod-stateful-server", 19 },
00260 -1,
00261 PJSIP_MOD_PRIORITY_APPLICATION,
00262 NULL,
00263 NULL,
00264 NULL,
00265 NULL,
00266 &mod_stateful_on_rx_request,
00267 NULL,
00268 NULL,
00269 NULL,
00270 NULL,
00271 };
00272
00273
00274 static pj_bool_t mod_stateful_on_rx_request(pjsip_rx_data *rdata)
00275 {
00276 const pj_str_t stateful_user = { "1", 1 };
00277 pjsip_uri *uri;
00278 pjsip_sip_uri *sip_uri;
00279
00280 uri = pjsip_uri_get_uri(rdata->msg_info.msg->line.req.uri);
00281
00282
00283 if (!PJSIP_URI_SCHEME_IS_SIP(uri))
00284 return PJ_FALSE;
00285
00286 sip_uri = (pjsip_sip_uri*) uri;
00287
00288
00289 if (pj_strcmp(&sip_uri->user, &stateful_user)!=0)
00290 return PJ_FALSE;
00291
00292
00293
00294
00295
00296 switch (rdata->msg_info.msg->line.req.method.id) {
00297 case PJSIP_INVITE_METHOD:
00298 {
00299 pjsip_msg_body *body;
00300
00301 if (dummy_sdp_str.slen == 0)
00302 dummy_sdp_str.slen = pj_ansi_strlen(dummy_sdp_str.ptr);
00303
00304 body = pjsip_msg_body_create(rdata->tp_info.pool,
00305 &mime_application, &mime_sdp,
00306 &dummy_sdp_str);
00307 pjsip_endpt_respond(app.sip_endpt, &mod_stateful_server, rdata,
00308 200, NULL, NULL, body, NULL);
00309 }
00310 break;
00311 case PJSIP_ACK_METHOD:
00312 return PJ_TRUE;
00313 default:
00314 pjsip_endpt_respond(app.sip_endpt, &mod_stateful_server, rdata,
00315 200, NULL, NULL, NULL, NULL);
00316 break;
00317 }
00318
00319 app.server.cur_state.stateful_cnt++;
00320 return PJ_TRUE;
00321 }
00322
00323
00324
00325
00326
00327 static pj_bool_t mod_call_on_rx_request(pjsip_rx_data *rdata);
00328
00329
00330
00331 static pjsip_module mod_call_server =
00332 {
00333 NULL, NULL,
00334 { "mod-call-server", 15 },
00335 -1,
00336 PJSIP_MOD_PRIORITY_APPLICATION,
00337 NULL,
00338 NULL,
00339 NULL,
00340 NULL,
00341 &mod_call_on_rx_request,
00342 NULL,
00343 NULL,
00344 NULL,
00345 NULL,
00346 };
00347
00348
00349 static pj_status_t send_response(pjsip_inv_session *inv,
00350 pjsip_rx_data *rdata,
00351 int code,
00352 pj_bool_t *has_initial)
00353 {
00354 pjsip_tx_data *tdata;
00355 pj_status_t status;
00356
00357 if (*has_initial) {
00358 status = pjsip_inv_answer(inv, code, NULL, NULL, &tdata);
00359 } else {
00360 status = pjsip_inv_initial_answer(inv, rdata, code,
00361 NULL, NULL, &tdata);
00362 }
00363
00364 if (status != PJ_SUCCESS) {
00365 if (*has_initial) {
00366 status = pjsip_inv_answer(inv, PJSIP_SC_NOT_ACCEPTABLE,
00367 NULL, NULL, &tdata);
00368 } else {
00369 status = pjsip_inv_initial_answer(inv, rdata,
00370 PJSIP_SC_NOT_ACCEPTABLE,
00371 NULL, NULL, &tdata);
00372 }
00373
00374 if (status == PJ_SUCCESS) {
00375 *has_initial = PJ_TRUE;
00376 pjsip_inv_send_msg(inv, tdata);
00377 } else {
00378 pjsip_inv_terminate(inv, 500, PJ_FALSE);
00379 return -1;
00380 }
00381 } else {
00382 *has_initial = PJ_TRUE;
00383
00384 status = pjsip_inv_send_msg(inv, tdata);
00385 if (status != PJ_SUCCESS) {
00386 pjsip_tx_data_dec_ref(tdata);
00387 return status;
00388 }
00389 }
00390
00391 return status;
00392 }
00393
00394 static void answer_timer_cb(pj_timer_heap_t *h, pj_timer_entry *entry)
00395 {
00396 struct call *call = entry->user_data;
00397 pj_bool_t has_initial = PJ_TRUE;
00398
00399 PJ_UNUSED_ARG(h);
00400
00401 entry->id = 0;
00402 send_response(call->inv, NULL, 200, &has_initial);
00403 }
00404
00405 static pj_bool_t mod_call_on_rx_request(pjsip_rx_data *rdata)
00406 {
00407 const pj_str_t call_user = { "2", 1 };
00408 pjsip_uri *uri;
00409 pjsip_sip_uri *sip_uri;
00410 struct call *call;
00411 pjsip_dialog *dlg;
00412 pjmedia_sdp_session *sdp;
00413 pjsip_tx_data *tdata;
00414 pj_bool_t has_initial = PJ_FALSE;
00415 pj_status_t status;
00416
00417 uri = pjsip_uri_get_uri(rdata->msg_info.msg->line.req.uri);
00418
00419
00420 if (!PJSIP_URI_SCHEME_IS_SIP(uri))
00421 return PJ_FALSE;
00422
00423 sip_uri = (pjsip_sip_uri*) uri;
00424
00425
00426 if (rdata->msg_info.msg->line.req.method.id != PJSIP_INVITE_METHOD) {
00427 return PJ_FALSE;
00428 }
00429
00430
00431
00432
00433
00434
00435
00436 if (pj_strcmp(&sip_uri->user, &call_user) == 0 ||
00437 sip_uri->user.slen != 1 ||
00438 (*sip_uri->user.ptr != '0' && *sip_uri->user.ptr != '1'))
00439 {
00440
00441
00442 } else {
00443 return PJ_FALSE;
00444 }
00445
00446
00447
00448 if (app.real_sdp) {
00449 unsigned options = 0;
00450 status = pjsip_inv_verify_request(rdata, &options, NULL, NULL,
00451 app.sip_endpt, &tdata);
00452 if (status != PJ_SUCCESS) {
00453
00454
00455
00456
00457
00458 if (tdata) {
00459 pjsip_response_addr res_addr;
00460
00461 pjsip_get_response_addr(tdata->pool, rdata, &res_addr);
00462 pjsip_endpt_send_response(app.sip_endpt, &res_addr, tdata,
00463 NULL, NULL);
00464
00465 } else {
00466
00467
00468 pjsip_endpt_respond_stateless(app.sip_endpt, rdata, 500, NULL,
00469 NULL, NULL);
00470 }
00471
00472 return PJ_TRUE;
00473 }
00474 }
00475
00476
00477 status = pjsip_dlg_create_uas( pjsip_ua_instance(), rdata,
00478 &app.local_contact, &dlg);
00479 if (status != PJ_SUCCESS) {
00480 const pj_str_t reason = pj_str("Unable to create dialog");
00481 pjsip_endpt_respond_stateless( app.sip_endpt, rdata,
00482 500, &reason,
00483 NULL, NULL);
00484 return PJ_TRUE;
00485 }
00486
00487
00488 call = pj_pool_zalloc(dlg->pool, sizeof(struct call));
00489
00490
00491 if (app.real_sdp) {
00492 status = pjmedia_endpt_create_sdp(app.med_endpt, rdata->tp_info.pool,
00493 app.skinfo_cnt, app.skinfo,
00494 &sdp);
00495 } else {
00496 sdp = app.dummy_sdp;
00497 }
00498
00499
00500 status = pjsip_inv_create_uas( dlg, rdata, sdp, 0, &call->inv);
00501 if (status != PJ_SUCCESS) {
00502 pjsip_dlg_create_response(dlg, rdata, 500, NULL, &tdata);
00503 pjsip_dlg_send_response(dlg, pjsip_rdata_get_tsx(rdata), tdata);
00504 return PJ_TRUE;
00505 }
00506
00507
00508 if (app.server.send_trying) {
00509 status = send_response(call->inv, rdata, 100, &has_initial);
00510 if (status != PJ_SUCCESS)
00511 return PJ_TRUE;
00512 }
00513
00514
00515 if (app.server.send_ringing) {
00516 status = send_response(call->inv, rdata, 180, &has_initial);
00517 if (status != PJ_SUCCESS)
00518 return PJ_TRUE;
00519 }
00520
00521
00522 if (app.server.delay) {
00523 pj_time_val delay;
00524
00525 call->ans_timer.id = 1;
00526 call->ans_timer.user_data = call;
00527 call->ans_timer.cb = &answer_timer_cb;
00528
00529 delay.sec = 0;
00530 delay.msec = app.server.delay;
00531 pj_time_val_normalize(&delay);
00532
00533 pjsip_endpt_schedule_timer(app.sip_endpt, &call->ans_timer, &delay);
00534
00535 } else {
00536
00537 status = send_response(call->inv, rdata, 200, &has_initial);
00538 PJ_ASSERT_ON_FAIL(status == PJ_SUCCESS, return PJ_TRUE);
00539 }
00540
00541
00542 app.server.cur_state.call_cnt++;
00543
00544 return PJ_TRUE;
00545 }
00546
00547
00548
00549
00550
00551
00552
00553 static pj_bool_t mod_responder_on_rx_request(pjsip_rx_data *rdata);
00554
00555
00556
00557 static pjsip_module mod_responder =
00558 {
00559 NULL, NULL,
00560 { "mod-responder", 13 },
00561 -1,
00562 PJSIP_MOD_PRIORITY_APPLICATION+1,
00563 NULL,
00564 NULL,
00565 NULL,
00566 NULL,
00567 &mod_responder_on_rx_request,
00568 NULL,
00569 NULL,
00570 NULL,
00571 NULL,
00572 };
00573
00574
00575 static pj_bool_t mod_responder_on_rx_request(pjsip_rx_data *rdata)
00576 {
00577 const pj_str_t reason = pj_str("Not expecting request at this URI");
00578
00579
00580
00581
00582 if (rdata->msg_info.msg->line.req.method.id != PJSIP_ACK_METHOD) {
00583 pjsip_endpt_respond_stateless(app.sip_endpt, rdata, 500, &reason,
00584 NULL, NULL);
00585 }
00586
00587 return PJ_TRUE;
00588 }
00589
00590
00591
00592
00593
00594
00595
00596
00597
00598 static pj_bool_t logger_on_rx_msg(pjsip_rx_data *rdata)
00599 {
00600 PJ_LOG(3,(THIS_FILE, "RX %d bytes %s from %s %s:%d:\n"
00601 "%.*s\n"
00602 "--end msg--",
00603 rdata->msg_info.len,
00604 pjsip_rx_data_get_info(rdata),
00605 rdata->tp_info.transport->type_name,
00606 rdata->pkt_info.src_name,
00607 rdata->pkt_info.src_port,
00608 (int)rdata->msg_info.len,
00609 rdata->msg_info.msg_buf));
00610
00611
00612 return PJ_FALSE;
00613 }
00614
00615
00616 static pj_status_t logger_on_tx_msg(pjsip_tx_data *tdata)
00617 {
00618
00619
00620
00621
00622
00623
00624
00625 PJ_LOG(3,(THIS_FILE, "TX %d bytes %s to %s %s:%d:\n"
00626 "%.*s\n"
00627 "--end msg--",
00628 (tdata->buf.cur - tdata->buf.start),
00629 pjsip_tx_data_get_info(tdata),
00630 tdata->tp_info.transport->type_name,
00631 tdata->tp_info.dst_name,
00632 tdata->tp_info.dst_port,
00633 (int)(tdata->buf.cur - tdata->buf.start),
00634 tdata->buf.start));
00635
00636
00637 return PJ_SUCCESS;
00638 }
00639
00640
00641 static pjsip_module msg_logger =
00642 {
00643 NULL, NULL,
00644 { "mod-siprtp-log", 14 },
00645 -1,
00646 PJSIP_MOD_PRIORITY_TRANSPORT_LAYER-1,
00647 NULL,
00648 NULL,
00649 NULL,
00650 NULL,
00651 &logger_on_rx_msg,
00652 &logger_on_rx_msg,
00653 &logger_on_tx_msg,
00654 &logger_on_tx_msg,
00655 NULL,
00656
00657 };
00658
00659
00660
00661
00662
00663
00664
00665 static pj_bool_t mod_test_on_rx_response(pjsip_rx_data *rdata);
00666
00667 static void call_on_media_update( pjsip_inv_session *inv,
00668 pj_status_t status);
00669 static void call_on_state_changed( pjsip_inv_session *inv,
00670 pjsip_event *e);
00671 static void call_on_forked(pjsip_inv_session *inv, pjsip_event *e);
00672
00673
00674
00675
00676 static pjsip_module mod_test =
00677 {
00678 NULL, NULL,
00679 { "mod-test", 8 },
00680 -1,
00681 PJSIP_MOD_PRIORITY_APPLICATION,
00682 NULL,
00683 NULL,
00684 NULL,
00685 NULL,
00686 NULL,
00687 &mod_test_on_rx_response,
00688 NULL,
00689 NULL,
00690 NULL,
00691 };
00692
00693
00694 static void report_completion(int status_code)
00695 {
00696 app.client.job_finished++;
00697 if (status_code >= 200 && status_code < 800)
00698 app.client.response_codes[status_code]++;
00699 app.client.total_responses++;
00700 pj_gettimeofday(&app.client.last_completion);
00701 }
00702
00703
00704
00705 static pj_bool_t mod_test_on_rx_response(pjsip_rx_data *rdata)
00706 {
00707 if (pjsip_rdata_get_tsx(rdata) == NULL) {
00708 report_completion(rdata->msg_info.msg->line.status.code);
00709 }
00710
00711 return PJ_TRUE;
00712 }
00713
00714
00715
00716
00717
00718 static pj_status_t create_app(void)
00719 {
00720 pj_status_t status;
00721
00722 status = pj_init();
00723 if (status != PJ_SUCCESS) {
00724 app_perror(THIS_FILE, "Error initializing pjlib", status);
00725 return status;
00726 }
00727
00728
00729 status = pjlib_util_init();
00730 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
00731
00732
00733 pj_caching_pool_init(&app.cp, &pj_pool_factory_default_policy,
00734 CACHING_POOL_SIZE);
00735
00736
00737 app.pool = pj_pool_create(&app.cp.factory, "app", 1000, 1000, NULL);
00738
00739
00740 status = pjsip_endpt_create(&app.cp.factory, pj_gethostname()->ptr,
00741 &app.sip_endpt);
00742 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
00743
00744
00745 return status;
00746 }
00747
00748
00749
00750
00751
00752 static pj_status_t init_sip()
00753 {
00754 pj_status_t status = -1;
00755
00756
00757 {
00758 pj_sockaddr_in addr;
00759 pjsip_host_port addrname;
00760 const char *transport_type = NULL;
00761
00762 pj_bzero(&addr, sizeof(addr));
00763 addr.sin_family = pj_AF_INET();
00764 addr.sin_addr.s_addr = 0;
00765 addr.sin_port = pj_htons((pj_uint16_t)app.local_port);
00766
00767 if (app.local_addr.slen) {
00768 addrname.host = app.local_addr;
00769 addrname.port = 5060;
00770 }
00771 if (app.local_port != 0)
00772 addrname.port = app.local_port;
00773
00774 if (0) {
00775 #if defined(PJ_HAS_TCP) && PJ_HAS_TCP!=0
00776 } else if (app.use_tcp) {
00777 pj_sockaddr_in local_addr;
00778 pjsip_tpfactory *tpfactory;
00779
00780 transport_type = "tcp";
00781 pj_sockaddr_in_init(&local_addr, 0, (pj_uint16_t)app.local_port);
00782 status = pjsip_tcp_transport_start(app.sip_endpt, &local_addr,
00783 app.thread_count, &tpfactory);
00784 if (status == PJ_SUCCESS) {
00785 app.local_addr = tpfactory->addr_name.host;
00786 app.local_port = tpfactory->addr_name.port;
00787 }
00788 #endif
00789 } else {
00790 pjsip_transport *tp;
00791
00792 transport_type = "udp";
00793 status = pjsip_udp_transport_start(app.sip_endpt, &addr,
00794 (app.local_addr.slen ? &addrname:NULL),
00795 app.thread_count, &tp);
00796 if (status == PJ_SUCCESS) {
00797 app.local_addr = tp->local_name.host;
00798 app.local_port = tp->local_name.port;
00799 }
00800
00801 }
00802 if (status != PJ_SUCCESS) {
00803 app_perror(THIS_FILE, "Unable to start transport", status);
00804 return status;
00805 }
00806
00807 app.local_uri.ptr = pj_pool_alloc(app.pool, 128);
00808 app.local_uri.slen = pj_ansi_sprintf(app.local_uri.ptr,
00809 "<sip:pjsip-perf@%.*s:%d;transport=%s>",
00810 (int)app.local_addr.slen,
00811 app.local_addr.ptr,
00812 app.local_port,
00813 transport_type);
00814
00815 app.local_contact = app.local_uri;
00816 }
00817
00818
00819
00820
00821
00822 status = pjsip_tsx_layer_init_module(app.sip_endpt);
00823 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
00824
00825
00826 status = pjsip_ua_init_module( app.sip_endpt, NULL );
00827 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
00828
00829
00830 status = pjsip_100rel_init_module(app.sip_endpt);
00831 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
00832
00833
00834 {
00835 pjsip_inv_callback inv_cb;
00836
00837
00838 pj_bzero(&inv_cb, sizeof(inv_cb));
00839 inv_cb.on_state_changed = &call_on_state_changed;
00840 inv_cb.on_new_session = &call_on_forked;
00841 inv_cb.on_media_update = &call_on_media_update;
00842
00843
00844 status = pjsip_inv_usage_init(app.sip_endpt, &inv_cb);
00845 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
00846 }
00847
00848
00849 status = pjsip_endpt_register_module( app.sip_endpt, &mod_test);
00850 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
00851
00852
00853
00854 status = pjsip_endpt_register_module( app.sip_endpt, &mod_stateless_server);
00855 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
00856
00857
00858 status = pjsip_endpt_register_module( app.sip_endpt, &mod_responder);
00859 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
00860
00861
00862 status = pjsip_endpt_register_module( app.sip_endpt, &mod_stateful_server);
00863 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
00864
00865
00866
00867 status = pjsip_endpt_register_module( app.sip_endpt, &mod_call_server);
00868 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
00869
00870
00871
00872 return PJ_SUCCESS;
00873 }
00874
00875
00876
00877
00878
00879 static void destroy_app()
00880 {
00881 unsigned i;
00882
00883 app.thread_quit = 1;
00884 for (i=0; i<app.thread_count; ++i) {
00885 if (app.thread[i]) {
00886 pj_thread_join(app.thread[i]);
00887 pj_thread_destroy(app.thread[i]);
00888 app.thread[i] = NULL;
00889 }
00890 }
00891
00892 if (app.sip_endpt) {
00893 pjsip_endpt_destroy(app.sip_endpt);
00894 app.sip_endpt = NULL;
00895 }
00896
00897 if (app.pool) {
00898 pj_pool_release(app.pool);
00899 app.pool = NULL;
00900 PJ_LOG(3,(THIS_FILE, "Peak memory size: %uMB",
00901 app.cp.peak_used_size / 1000000));
00902 pj_caching_pool_destroy(&app.cp);
00903 }
00904
00905
00906 pj_shutdown();
00907 }
00908
00909
00910
00911
00912
00913 static pj_status_t init_media()
00914 {
00915 unsigned i;
00916 pj_uint16_t rtp_port;
00917 pj_status_t status;
00918
00919
00920
00921
00922
00923 status = pjmedia_endpt_create(&app.cp.factory,
00924 pjsip_endpt_get_ioqueue(app.sip_endpt), 0,
00925 &app.med_endpt);
00926 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
00927
00928
00929
00930 #if defined(PJMEDIA_HAS_G711_CODEC) && PJMEDIA_HAS_G711_CODEC!=0
00931 pjmedia_codec_g711_init(app.med_endpt);
00932 #endif
00933 #if defined(PJMEDIA_HAS_GSM_CODEC) && PJMEDIA_HAS_GSM_CODEC!=0
00934 pjmedia_codec_gsm_init(app.med_endpt);
00935 #endif
00936 #if defined(PJMEDIA_HAS_SPEEX_CODEC) && PJMEDIA_HAS_SPEEX_CODEC!=0
00937 pjmedia_codec_speex_init(app.med_endpt, PJMEDIA_SPEEX_NO_UWB, 3, 3);
00938 #endif
00939 #if defined(PJMEDIA_HAS_G722_CODEC) && PJMEDIA_HAS_G722_CODEC!=0
00940 pjmedia_codec_g722_init(app.med_endpt);
00941 #endif
00942
00943
00944 app.skinfo_cnt = 0;
00945 for (i=0, rtp_port=4000; i<PJ_ARRAY_SIZE(app.skinfo); ++i, rtp_port+=2) {
00946 pjmedia_sock_info *skinfo;
00947
00948 skinfo = &app.skinfo[i];
00949
00950 pj_sockaddr_in_init(&skinfo->rtp_addr_name.ipv4, &app.local_addr,
00951 (pj_uint16_t)rtp_port);
00952 pj_sockaddr_in_init(&skinfo->rtp_addr_name.ipv4, &app.local_addr,
00953 (pj_uint16_t)(rtp_port+1));
00954 app.skinfo_cnt++;
00955 }
00956
00957
00958 dummy_sdp_str.slen = pj_ansi_strlen(dummy_sdp_str.ptr);
00959 status = pjmedia_sdp_parse(app.pool, dummy_sdp_str.ptr, dummy_sdp_str.slen,
00960 &app.dummy_sdp);
00961 if (status != PJ_SUCCESS) {
00962 app_perror(THIS_FILE, "Error parsing dummy SDP", status);
00963 return status;
00964 }
00965
00966
00967
00968 return PJ_SUCCESS;
00969 }
00970
00971
00972
00973
00974
00975 static void call_on_media_update( pjsip_inv_session *inv,
00976 pj_status_t status)
00977 {
00978 if (status != PJ_SUCCESS) {
00979 pjsip_tx_data *tdata;
00980 pj_status_t status;
00981
00982 status = pjsip_inv_end_session(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE,
00983 NULL, &tdata);
00984 if (status == PJ_SUCCESS && tdata)
00985 status = pjsip_inv_send_msg(inv, tdata);
00986 }
00987 }
00988
00989
00990
00991
00992
00993 static void call_on_state_changed( pjsip_inv_session *inv,
00994 pjsip_event *e)
00995 {
00996 PJ_UNUSED_ARG(e);
00997
00998
00999 if (inv->mod_data[mod_test.id] != NULL)
01000 return;
01001
01002
01003 if (inv->role != PJSIP_UAC_ROLE)
01004 return;
01005
01006 if (inv->state == PJSIP_INV_STATE_CONFIRMED) {
01007 pjsip_tx_data *tdata;
01008 pj_status_t status;
01009
01010
01011
01012
01013 status = pjsip_inv_end_session(inv, PJSIP_SC_OK, NULL, &tdata);
01014 if (status == PJ_SUCCESS && tdata)
01015 status = pjsip_inv_send_msg(inv, tdata);
01016
01017 } else if (inv->state == PJSIP_INV_STATE_DISCONNECTED) {
01018 report_completion(inv->cause);
01019 inv->mod_data[mod_test.id] = (void*)1;
01020 }
01021 }
01022
01023
01024
01025 static void call_on_forked(pjsip_inv_session *inv, pjsip_event *e)
01026 {
01027
01028 PJ_UNUSED_ARG(inv);
01029 PJ_UNUSED_ARG(e);
01030 }
01031
01032
01033
01034
01035
01036 static pj_status_t make_call(const pj_str_t *dst_uri)
01037 {
01038 struct call *call;
01039 pjsip_dialog *dlg;
01040 pjmedia_sdp_session *sdp;
01041 pjsip_tx_data *tdata;
01042 pj_status_t status;
01043
01044
01045
01046 status = pjsip_dlg_create_uac( pjsip_ua_instance(),
01047 &app.local_uri,
01048 &app.local_contact,
01049 dst_uri,
01050 dst_uri,
01051 &dlg);
01052 if (status != PJ_SUCCESS) {
01053 return status;
01054 }
01055
01056
01057 call = pj_pool_zalloc(dlg->pool, sizeof(struct call));
01058
01059
01060 if (app.real_sdp) {
01061 status = pjmedia_endpt_create_sdp(app.med_endpt, dlg->pool, 1,
01062 app.skinfo, &sdp);
01063 if (status != PJ_SUCCESS) {
01064 pjsip_dlg_terminate(dlg);
01065 return status;
01066 }
01067 } else
01068 sdp = app.dummy_sdp;
01069
01070
01071 status = pjsip_inv_create_uac( dlg, sdp, 0, &call->inv);
01072 if (status != PJ_SUCCESS) {
01073 pjsip_dlg_terminate(dlg);
01074 return status;
01075 }
01076
01077
01078
01079
01080
01081
01082 status = pjsip_inv_invite(call->inv, &tdata);
01083 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
01084
01085
01086
01087
01088
01089
01090 status = pjsip_inv_send_msg(call->inv, tdata);
01091 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
01092
01093
01094 return PJ_SUCCESS;
01095 }
01096
01097
01098
01099
01100
01101 static pj_status_t verify_sip_url(const char *c_url)
01102 {
01103 pjsip_uri *p;
01104 pj_pool_t *pool;
01105 char *url;
01106 int len = (c_url ? pj_ansi_strlen(c_url) : 0);
01107
01108 if (!len) return -1;
01109
01110 pool = pj_pool_create(&app.cp.factory, "check%p", 1024, 0, NULL);
01111 if (!pool) return PJ_ENOMEM;
01112
01113 url = pj_pool_alloc(pool, len+1);
01114 pj_ansi_strcpy(url, c_url);
01115 url[len] = '\0';
01116
01117 p = pjsip_parse_uri(pool, url, len, 0);
01118 if (!p || pj_stricmp2(pjsip_uri_get_scheme(p), "sip") != 0)
01119 p = NULL;
01120
01121 pj_pool_release(pool);
01122 return p ? 0 : -1;
01123 }
01124
01125
01126 static void usage(void)
01127 {
01128 printf(
01129 "Usage:\n"
01130 " pjsip-perf [OPTIONS] -- to start as server\n"
01131 " pjsip-perf [OPTIONS] URL -- to call server (possibly itself)\n"
01132 "\n"
01133 "where:\n"
01134 " URL The SIP URL to be contacted.\n"
01135 "\n"
01136 "Client options:\n"
01137 " --method=METHOD, -m Set test method (set to INVITE for call benchmark)\n"
01138 " [default: OPTIONS]\n"
01139 " --count=N, -n Set total number of requests to initiate\n"
01140 " [default=%d]\n"
01141 " --stateless, -s Set to operate in stateless mode\n"
01142 " [default: stateful]\n"
01143 " --timeout=SEC, -t Set client timeout [default=60 sec]\n"
01144 " --window=COUNT, -w Set maximum outstanding job [default: %d]\n"
01145 "\n"
01146 "SDP options (client and server):\n"
01147 " --real-sdp Generate real SDP from pjmedia, and also perform\n"
01148 " proper SDP negotiation [default: dummy]\n"
01149 "\n"
01150 "Client and Server options:\n"
01151 " --local-port=PORT, -p Set local port [default: 5060]\n"
01152 " --use-tcp, -T Use TCP instead of UDP. Note that when started as\n"
01153 " client, you must add ;transport=tcp parameter to URL\n"
01154 " [default: no]\n"
01155 " --thread-count=N Set number of worker threads [default=1]\n"
01156 " --trying Send 100/Trying response (server, default no)\n"
01157 " --ringing Send 180/Ringing response (server, default no)\n"
01158 " --delay=MS, -d Delay answering call by MS (server, default no)\n"
01159 "\n"
01160 "Misc options:\n"
01161 " --help, -h Display this screen\n"
01162 " --verbose, -v Verbose logging (put more than once for even more)\n"
01163 "\n"
01164 "When started as server, pjsip-perf can be contacted on the following URIs:\n"
01165 " - sip:0@server-addr To handle requests statelessly.\n"
01166 " - sip:1@server-addr To handle requests statefully.\n"
01167 " - sip:2@server-addr To handle INVITE call.\n",
01168 DEFAULT_COUNT, JOB_WINDOW);
01169 }
01170
01171
01172 static int my_atoi(const char *s)
01173 {
01174 pj_str_t ss = pj_str((char*)s);
01175 return pj_strtoul(&ss);
01176 }
01177
01178
01179 static pj_status_t init_options(int argc, char *argv[])
01180 {
01181 enum { OPT_THREAD_COUNT = 1, OPT_REAL_SDP, OPT_TRYING, OPT_RINGING };
01182 struct pj_getopt_option long_options[] = {
01183 { "local-port", 1, 0, 'p' },
01184 { "count", 1, 0, 'c' },
01185 { "thread-count", 1, 0, OPT_THREAD_COUNT },
01186 { "method", 1, 0, 'm' },
01187 { "help", 0, 0, 'h' },
01188 { "stateless", 0, 0, 's' },
01189 { "timeout", 1, 0, 't' },
01190 { "real-sdp", 0, 0, OPT_REAL_SDP },
01191 { "verbose", 0, 0, 'v' },
01192 { "use-tcp", 0, 0, 'T' },
01193 { "window", 1, 0, 'w' },
01194 { "delay", 1, 0, 'd' },
01195 { "trying", 0, 0, OPT_TRYING},
01196 { "ringing", 0, 0, OPT_RINGING},
01197 { NULL, 0, 0, 0 },
01198 };
01199 int c;
01200 int option_index;
01201
01202
01203 app.local_port = 5060;
01204 app.thread_count = 1;
01205 app.client.job_count = DEFAULT_COUNT;
01206 app.client.method = *pjsip_get_options_method();
01207 app.client.job_window = c = JOB_WINDOW;
01208 app.client.timeout = 60;
01209 app.log_level = 3;
01210
01211
01212
01213 pj_optind = 0;
01214 while((c=pj_getopt_long(argc,argv, "p:c:m:t:w:d:hsv",
01215 long_options, &option_index))!=-1)
01216 {
01217 switch (c) {
01218 case 'p':
01219 app.local_port = my_atoi(pj_optarg);
01220 if (app.local_port < 0 || app.local_port > 65535) {
01221 PJ_LOG(3,(THIS_FILE, "Invalid --local-port %s", pj_optarg));
01222 return -1;
01223 }
01224 break;
01225
01226 case 'c':
01227 app.client.job_count = my_atoi(pj_optarg);
01228 if (app.client.job_count < 0) {
01229 PJ_LOG(3,(THIS_FILE, "Invalid --local-port %s", pj_optarg));
01230 return -1;
01231 }
01232 if (app.client.job_count > pjsip_cfg()->tsx.max_count)
01233 PJ_LOG(3,(THIS_FILE,
01234 "Warning: --count value (%d) exceeds maximum "
01235 "transaction count (%d)", app.client.job_count,
01236 pjsip_cfg()->tsx.max_count));
01237 break;
01238
01239 case OPT_THREAD_COUNT:
01240 app.thread_count = my_atoi(pj_optarg);
01241 if (app.thread_count < 1 || app.thread_count > 16) {
01242 PJ_LOG(3,(THIS_FILE, "Invalid --thread-count %s", pj_optarg));
01243 return -1;
01244 }
01245 break;
01246
01247 case 'm':
01248 {
01249 pj_str_t temp = pj_str((char*)pj_optarg);
01250 pjsip_method_init_np(&app.client.method, &temp);
01251 }
01252 break;
01253
01254 case 'h':
01255 usage();
01256 return -1;
01257
01258 case 's':
01259 app.client.stateless = |