pjsip logo pjsip.org
Open source SIP stack and media stack for presence, im/instant messaging, and multimedia communication

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

Samples: SIP Performance Benchmark

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 /* $Id: pjsip-perf.c 1870 2008-03-17 14:07:53Z bennylp $ */
00002 /* 
00003  * Copyright (C) 2003-2007 Benny Prijono <benny@prijono.org>
00004  *
00005  * This program is free software; you can redistribute it and/or modify
00006  * it under the terms of the GNU General Public License as published by
00007  * the Free Software Foundation; either version 2 of the License, or
00008  * (at your option) any later version.
00009  *
00010  * This program is distributed in the hope that it will be useful,
00011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  * GNU General Public License for more details.
00014  *
00015  * You should have received a copy of the GNU General Public License
00016  * along with this program; if not, write to the Free Software
00017  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
00018  */
00019 
00020 
00059 /* Include all headers. */
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 /* Static message body for INVITE, when stateful processing is
00085  * invoked (instead of call-stateful, where SDP is generated
00086  * dynamically.
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  * STATELESS SERVER
00190  */
00191 static pj_bool_t mod_stateless_on_rx_request(pjsip_rx_data *rdata);
00192 
00193 /* Module to handle incoming requests statelessly.
00194  */
00195 static pjsip_module mod_stateless_server =
00196 {
00197     NULL, NULL,                     /* prev, next.              */
00198     { "mod-stateless-server", 20 }, /* Name.                    */
00199     -1,                             /* Id                       */
00200     PJSIP_MOD_PRIORITY_APPLICATION, /* Priority                 */
00201     NULL,                           /* load()                   */
00202     NULL,                           /* start()                  */
00203     NULL,                           /* stop()                   */
00204     NULL,                           /* unload()                 */
00205     &mod_stateless_on_rx_request,   /* on_rx_request()          */
00206     NULL,                           /* on_rx_response()         */
00207     NULL,                           /* on_tx_request.           */
00208     NULL,                           /* on_tx_response()         */
00209     NULL,                           /* on_tsx_state()           */
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     /* Only want to receive SIP scheme */
00222     if (!PJSIP_URI_SCHEME_IS_SIP(uri))
00223         return PJ_FALSE;
00224 
00225     sip_uri = (pjsip_sip_uri*) uri;
00226 
00227     /* Check for matching user part */
00228     if (pj_strcmp(&sip_uri->user, &stateless_user)!=0)
00229         return PJ_FALSE;
00230 
00231     /*
00232      * Yes, this is for us.
00233      */
00234 
00235     /* Ignore ACK request */
00236     if (rdata->msg_info.msg->line.req.method.id == PJSIP_ACK_METHOD)
00237         return PJ_TRUE;
00238 
00239     /*
00240      * Respond statelessly with 200/OK.
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  * STATEFUL SERVER
00251  */
00252 static pj_bool_t mod_stateful_on_rx_request(pjsip_rx_data *rdata);
00253 
00254 /* Module to handle incoming requests statefully.
00255  */
00256 static pjsip_module mod_stateful_server =
00257 {
00258     NULL, NULL,                     /* prev, next.              */
00259     { "mod-stateful-server", 19 },  /* Name.                    */
00260     -1,                             /* Id                       */
00261     PJSIP_MOD_PRIORITY_APPLICATION, /* Priority                 */
00262     NULL,                           /* load()                   */
00263     NULL,                           /* start()                  */
00264     NULL,                           /* stop()                   */
00265     NULL,                           /* unload()                 */
00266     &mod_stateful_on_rx_request,   /* on_rx_request()           */
00267     NULL,                           /* on_rx_response()         */
00268     NULL,                           /* on_tx_request.           */
00269     NULL,                           /* on_tx_response()         */
00270     NULL,                           /* on_tsx_state()           */
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     /* Only want to receive SIP scheme */
00283     if (!PJSIP_URI_SCHEME_IS_SIP(uri))
00284         return PJ_FALSE;
00285 
00286     sip_uri = (pjsip_sip_uri*) uri;
00287 
00288     /* Check for matching user part */
00289     if (pj_strcmp(&sip_uri->user, &stateful_user)!=0)
00290         return PJ_FALSE;
00291 
00292     /*
00293      * Yes, this is for us.
00294      * Respond statefully with 200/OK.
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  * CALL SERVER
00326  */
00327 static pj_bool_t mod_call_on_rx_request(pjsip_rx_data *rdata);
00328 
00329 /* Module to handle incoming requests callly.
00330  */
00331 static pjsip_module mod_call_server =
00332 {
00333     NULL, NULL,                     /* prev, next.              */
00334     { "mod-call-server", 15 },      /* Name.                    */
00335     -1,                             /* Id                       */
00336     PJSIP_MOD_PRIORITY_APPLICATION, /* Priority                 */
00337     NULL,                           /* load()                   */
00338     NULL,                           /* start()                  */
00339     NULL,                           /* stop()                   */
00340     NULL,                           /* unload()                 */
00341     &mod_call_on_rx_request,        /* on_rx_request()          */
00342     NULL,                           /* on_rx_response()         */
00343     NULL,                           /* on_tx_request.           */
00344     NULL,                           /* on_tx_response()         */
00345     NULL,                           /* on_tsx_state()           */
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     /* Only want to receive SIP scheme */
00420     if (!PJSIP_URI_SCHEME_IS_SIP(uri))
00421         return PJ_FALSE;
00422 
00423     sip_uri = (pjsip_sip_uri*) uri;
00424 
00425     /* Only want to handle INVITE requests. */
00426     if (rdata->msg_info.msg->line.req.method.id != PJSIP_INVITE_METHOD) {
00427         return PJ_FALSE;
00428     }
00429 
00430 
00431     /* Check for matching user part. Incoming requests will be handled 
00432      * call-statefully if:
00433      *  - user part is "2", or
00434      *  - user part is not "0" nor "1" and method is INVITE.
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         /* Match */
00441 
00442     } else {
00443         return PJ_FALSE;
00444     }
00445 
00446 
00447     /* Verify that we can handle the request. */
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              * No we can't handle the incoming INVITE request.
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                 /* Respond with 500 (Internal Server Error) */
00468                 pjsip_endpt_respond_stateless(app.sip_endpt, rdata, 500, NULL,
00469                                               NULL, NULL);
00470             }
00471 
00472             return PJ_TRUE;
00473         } 
00474     }
00475 
00476     /* Create UAS dialog */
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     /* Alloc call structure. */
00488     call = pj_pool_zalloc(dlg->pool, sizeof(struct call));
00489 
00490     /* Create SDP from PJMEDIA */
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     /* Create UAS invite session */
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     /* Send 100/Trying if needed */
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     /* Send 180/Ringing if needed */
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     /* Simulate call processing delay */
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         /* Send the 200 response immediately . */  
00537         status = send_response(call->inv, rdata, 200, &has_initial);
00538         PJ_ASSERT_ON_FAIL(status == PJ_SUCCESS, return PJ_TRUE);
00539     }
00540 
00541     /* Done */
00542     app.server.cur_state.call_cnt++;
00543 
00544     return PJ_TRUE;
00545 }
00546 
00547 
00548 
00549 /**************************************************************************
00550  * Default handler when incoming request is not handled by any other
00551  * modules.
00552  */
00553 static pj_bool_t mod_responder_on_rx_request(pjsip_rx_data *rdata);
00554 
00555 /* Module to handle incoming requests statelessly.
00556  */
00557 static pjsip_module mod_responder =
00558 {
00559     NULL, NULL,                     /* prev, next.              */
00560     { "mod-responder", 13 },        /* Name.                    */
00561     -1,                             /* Id                       */
00562     PJSIP_MOD_PRIORITY_APPLICATION+1, /* Priority               */
00563     NULL,                           /* load()                   */
00564     NULL,                           /* start()                  */
00565     NULL,                           /* stop()                   */
00566     NULL,                           /* unload()                 */
00567     &mod_responder_on_rx_request,   /* on_rx_request()          */
00568     NULL,                           /* on_rx_response()         */
00569     NULL,                           /* on_tx_request.           */
00570     NULL,                           /* on_tx_response()         */
00571     NULL,                           /* on_tsx_state()           */
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      * Respond any requests (except ACK!) with 500.
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  * Below is a simple module to log all incoming and outgoing SIP messages
00594  */
00595 
00596 
00597 /* Notification on incoming messages */
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     /* Always return false, otherwise messages will not get processed! */
00612     return PJ_FALSE;
00613 }
00614 
00615 /* Notification on outgoing messages */
00616 static pj_status_t logger_on_tx_msg(pjsip_tx_data *tdata)
00617 {
00618     
00619     /* Important note:
00620      *  tp_info field is only valid after outgoing messages has passed
00621      *  transport layer. So don't try to access tp_info when the module
00622      *  has lower priority than transport layer.
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     /* Always return success, otherwise message will not get sent! */
00637     return PJ_SUCCESS;
00638 }
00639 
00640 /* The module instance. */
00641 static pjsip_module msg_logger = 
00642 {
00643     NULL, NULL,                         /* prev, next.          */
00644     { "mod-siprtp-log", 14 },           /* Name.                */
00645     -1,                                 /* Id                   */
00646     PJSIP_MOD_PRIORITY_TRANSPORT_LAYER-1,/* Priority            */
00647     NULL,                               /* load()               */
00648     NULL,                               /* start()              */
00649     NULL,                               /* stop()               */
00650     NULL,                               /* unload()             */
00651     &logger_on_rx_msg,                  /* on_rx_request()      */
00652     &logger_on_rx_msg,                  /* on_rx_response()     */
00653     &logger_on_tx_msg,                  /* on_tx_request.       */
00654     &logger_on_tx_msg,                  /* on_tx_response()     */
00655     NULL,                               /* on_tsx_state()       */
00656 
00657 };
00658 
00659 
00660 
00661 /**************************************************************************
00662  * Test Client.
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 /* Module to handle incoming requests callly.
00675  */
00676 static pjsip_module mod_test =
00677 {
00678     NULL, NULL,                     /* prev, next.              */
00679     { "mod-test", 8 },              /* Name.                    */
00680     -1,                             /* Id                       */
00681     PJSIP_MOD_PRIORITY_APPLICATION, /* Priority                 */
00682     NULL,                           /* load()                   */
00683     NULL,                           /* start()                  */
00684     NULL,                           /* stop()                   */
00685     NULL,                           /* unload()                 */
00686     NULL,                           /* on_rx_request()          */
00687     &mod_test_on_rx_response,       /* on_rx_response()         */
00688     NULL,                           /* on_tx_request.           */
00689     NULL,                           /* on_tx_response()         */
00690     NULL,                           /* on_tsx_state()           */
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 /* Handler when response is received. */
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  * Create app
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     /* init PJLIB-UTIL: */
00729     status = pjlib_util_init();
00730     PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
00731 
00732     /* Must create a pool factory before we can allocate any memory. */
00733     pj_caching_pool_init(&app.cp, &pj_pool_factory_default_policy, 
00734                          CACHING_POOL_SIZE);
00735 
00736     /* Create application pool for misc. */
00737     app.pool = pj_pool_create(&app.cp.factory, "app", 1000, 1000, NULL);
00738 
00739     /* Create the endpoint: */
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  * Init SIP stack
00751  */
00752 static pj_status_t init_sip()
00753 {
00754     pj_status_t status = -1;
00755 
00756     /* Add UDP/TCP transport. */
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      * Init transaction layer.
00820      * This will create/initialize transaction hash tables etc.
00821      */
00822     status = pjsip_tsx_layer_init_module(app.sip_endpt);
00823     PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
00824 
00825     /*  Initialize UA layer. */
00826     status = pjsip_ua_init_module( app.sip_endpt, NULL );
00827     PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
00828 
00829     /* Initialize 100rel support */
00830     status = pjsip_100rel_init_module(app.sip_endpt);
00831     PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
00832 
00833     /*  Init invite session module. */
00834     {
00835         pjsip_inv_callback inv_cb;
00836 
00837         /* Init the callback for INVITE session: */
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         /* Initialize invite session module:  */
00844         status = pjsip_inv_usage_init(app.sip_endpt, &inv_cb);
00845         PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
00846     }
00847 
00848     /* Register our module to receive incoming requests. */
00849     status = pjsip_endpt_register_module( app.sip_endpt, &mod_test);
00850     PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
00851 
00852 
00853     /* Register stateless server module */
00854     status = pjsip_endpt_register_module( app.sip_endpt, &mod_stateless_server);
00855     PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
00856 
00857     /* Register default responder module */
00858     status = pjsip_endpt_register_module( app.sip_endpt, &mod_responder);
00859     PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
00860 
00861     /* Register stateless server module */
00862     status = pjsip_endpt_register_module( app.sip_endpt, &mod_stateful_server);
00863     PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
00864 
00865 
00866     /* Register call server module */
00867     status = pjsip_endpt_register_module( app.sip_endpt, &mod_call_server);
00868     PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
00869 
00870 
00871     /* Done */
00872     return PJ_SUCCESS;
00873 }
00874 
00875 
00876 /*
00877  * Destroy SIP
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     /* Shutdown PJLIB */
00906     pj_shutdown();
00907 }
00908 
00909 
00910 /*
00911  * Init media stack.
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     /* Initialize media endpoint so that at least error subsystem is properly
00921      * initialized.
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     /* Must register all codecs to be supported */
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     /* Init dummy socket addresses */
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     /* Generate dummy SDP */
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     /* Done */
00968     return PJ_SUCCESS;
00969 }
00970 
00971 
00972 /* This is notification from the call about media negotiation
00973  * status. This is called for client calls only.
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 /* This is notification from the call when the call state has changed.
00991  * This is called for client calls only.
00992  */
00993 static void call_on_state_changed( pjsip_inv_session *inv, 
00994                                    pjsip_event *e)
00995 {
00996     PJ_UNUSED_ARG(e);
00997 
00998     /* Bail out if the session has been counted before */
00999     if (inv->mod_data[mod_test.id] != NULL)
01000         return;
01001 
01002     /* Bail out if this is not an outgoing call */
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         //report_completion(200);
01011         //inv->mod_data[mod_test.id] = (void*)1;
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 /* Not implemented for now */
01025 static void call_on_forked(pjsip_inv_session *inv, pjsip_event *e)
01026 {
01027     /* Do nothing */
01028     PJ_UNUSED_ARG(inv);
01029     PJ_UNUSED_ARG(e);
01030 }
01031 
01032 
01033 /*
01034  * Make outgoing call.
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     /* Create UAC dialog */
01046     status = pjsip_dlg_create_uac( pjsip_ua_instance(), 
01047                                    &app.local_uri,      /* local URI        */
01048                                    &app.local_contact,  /* local Contact    */
01049                                    dst_uri,             /* remote URI       */
01050                                    dst_uri,             /* remote target    */
01051                                    &dlg);               /* dialog           */
01052     if (status != PJ_SUCCESS) {
01053         return status;
01054     }
01055 
01056     /* Create call */
01057     call = pj_pool_zalloc(dlg->pool, sizeof(struct call));
01058 
01059     /* Create SDP */
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     /* Create the INVITE session. */
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     /* Create initial INVITE request.
01079      * This INVITE request will contain a perfectly good request and 
01080      * an SDP body as well.
01081      */
01082     status = pjsip_inv_invite(call->inv, &tdata);
01083     PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
01084 
01085 
01086     /* Send initial INVITE request. 
01087      * From now on, the invite session's state will be reported to us
01088      * via the invite session callbacks.
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  * Verify that valid SIP url is given.
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     /* Init default application configs */
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     /* Parse options */
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 =