~ubuntu-branches/ubuntu/wily/sflphone/wily

« back to all changes in this revision

Viewing changes to daemon/libs/pjproject-2.1.0/pjsip-apps/src/samples/pjsip-perf.c

  • Committer: Package Import Robot
  • Author(s): Mark Purcell
  • Date: 2014-01-28 18:23:36 UTC
  • mfrom: (1.1.11)
  • mto: This revision was merged to the branch mainline in revision 24.
  • Revision ID: package-import@ubuntu.com-20140128182336-3xenud1kbnwmf3mz
* New upstream release 
  - Fixes "New Upstream Release" (Closes: #735846)
  - Fixes "Ringtone does not stop" (Closes: #727164)
  - Fixes "[sflphone-kde] crash on startup" (Closes: #718178)
  - Fixes "sflphone GUI crashes when call is hung up" (Closes: #736583)
* Build-Depends: ensure GnuTLS 2.6
  - libucommon-dev (>= 6.0.7-1.1), libccrtp-dev (>= 2.0.6-3)
  - Fixes "FTBFS Build-Depends libgnutls{26,28}-dev" (Closes: #722040)
* Fix "boost 1.49 is going away" unversioned Build-Depends: (Closes: #736746)
* Add Build-Depends: libsndfile-dev, nepomuk-core-dev

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* $Id: pjsip-perf.c 4370 2013-02-26 05:30:00Z nanang $ */
 
2
/* 
 
3
 * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
 
4
 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
 
5
 *
 
6
 * This program is free software; you can redistribute it and/or modify
 
7
 * it under the terms of the GNU General Public License as published by
 
8
 * the Free Software Foundation; either version 2 of the License, or
 
9
 * (at your option) any later version.
 
10
 *
 
11
 * This program is distributed in the hope that it will be useful,
 
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
14
 * GNU General Public License for more details.
 
15
 *
 
16
 * You should have received a copy of the GNU General Public License
 
17
 * along with this program; if not, write to the Free Software
 
18
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
 
19
 */
 
20
 
 
21
 
 
22
/**
 
23
 * \page page_pjsip_perf_c Samples: SIP Performance Benchmark
 
24
 *
 
25
 * <b>pjsip-perf</b> is a complete program to measure the
 
26
 * performance of PJSIP or other SIP endpoints. It consists of two
 
27
 * parts:
 
28
 *  - the server, to respond incoming requests, and
 
29
 *  - the client, who actively submits requests and measure the
 
30
 *     performance of the server.
 
31
 *
 
32
 * Both server and client part can run simultaneously, to measure the
 
33
 * performance when both endpoints are co-located in a single program.
 
34
 * 
 
35
 * The server accepts both INVITE and non-INVITE requests.
 
36
 * The server exports several different types of URL, which would
 
37
 * control how the request would be handled by the server:
 
38
 *  - URL with "0" as the user part will be handled statelessly.
 
39
 *    It should not be used with INVITE method.
 
40
 *  - URL with "1" as the user part will be handled statefully.
 
41
 *    If the request is an INVITE request, INVITE transaction will
 
42
 *    be created and 200/OK response will be sent, along with a valid
 
43
 *    SDP body. However, the SDP is just a static text body, and
 
44
 *    is not a proper SDP generated by PJMEDIA.
 
45
 *  - URL with "2" as the user part is only meaningful for INVITE
 
46
 *    requests, as it would be handled <b>call-statefully</b> by the
 
47
 *    server. For this URL, the server also would generate SDP dynamically
 
48
 *    and perform a proper SDP negotiation for the incoming call.
 
49
 *    Also for every call, server will limit the call duration to
 
50
 *    10 seconds, on which the call will be terminated if the client
 
51
 *    doesn't hangup the call.
 
52
 *    
 
53
 *
 
54
 *
 
55
 * This file is pjsip-apps/src/samples/pjsip-perf.c
 
56
 *
 
57
 * \includelineno pjsip-perf.c
 
58
 */
 
59
 
 
60
/* Include all headers. */
 
61
#include <pjsip.h>
 
62
#include <pjmedia.h>
 
63
#include <pjmedia-codec.h>
 
64
#include <pjsip_ua.h>
 
65
#include <pjsip_simple.h>
 
66
#include <pjlib-util.h>
 
67
#include <pjlib.h>
 
68
#include <stdio.h>
 
69
 
 
70
#if defined(PJ_WIN32) && PJ_WIN32!=0
 
71
#  include <windows.h>
 
72
#endif
 
73
 
 
74
#define THIS_FILE           "pjsip-perf.c"
 
75
#define DEFAULT_COUNT       (pjsip_cfg()->tsx.max_count/2>10000?10000:pjsip_cfg()->tsx.max_count/2)
 
76
#define JOB_WINDOW          1000
 
77
#define TERMINATE_TSX(x,c)
 
78
 
 
79
 
 
80
#ifndef CACHING_POOL_SIZE
 
81
#   define CACHING_POOL_SIZE   (256*1024*1024)
 
82
#endif
 
83
 
 
84
 
 
85
/* Static message body for INVITE, when stateful processing is
 
86
 * invoked (instead of call-stateful, where SDP is generated
 
87
 * dynamically.
 
88
 */
 
89
static pj_str_t dummy_sdp_str = 
 
90
{
 
91
    "v=0\r\n"
 
92
    "o=- 3360842071 3360842071 IN IP4 192.168.0.68\r\n"
 
93
    "s=pjmedia\r\n"
 
94
    "c=IN IP4 192.168.0.68\r\n"
 
95
    "t=0 0\r\n"
 
96
    "m=audio 4000 RTP/AVP 0 8 3 103 102 101\r\n"
 
97
    "a=rtcp:4001 IN IP4 192.168.0.68\r\n"
 
98
    "a=rtpmap:103 speex/16000\r\n"
 
99
    "a=rtpmap:102 speex/8000\r\n"
 
100
    "a=rtpmap:3 GSM/8000\r\n"
 
101
    "a=rtpmap:0 PCMU/8000\r\n"
 
102
    "a=rtpmap:8 PCMA/8000\r\n"
 
103
    "a=sendrecv\r\n"
 
104
    "a=rtpmap:101 telephone-event/8000\r\n"
 
105
    "a=fmtp:101 0-15\r\n",
 
106
    0
 
107
};
 
108
 
 
109
static pj_str_t mime_application = { "application", 11};
 
110
static pj_str_t mime_sdp = {"sdp", 3};
 
111
 
 
112
 
 
113
struct srv_state
 
114
{
 
115
    unsigned        stateless_cnt;
 
116
    unsigned        stateful_cnt;
 
117
    unsigned        call_cnt;
 
118
};
 
119
 
 
120
 
 
121
struct app
 
122
{
 
123
    pj_caching_pool      cp;
 
124
    pj_pool_t           *pool;
 
125
    pj_bool_t            use_tcp;
 
126
    pj_str_t             local_addr;
 
127
    int                  local_port;
 
128
    pjsip_endpoint      *sip_endpt;
 
129
    pjmedia_endpt       *med_endpt;
 
130
    pj_str_t             local_uri;
 
131
    pj_str_t             local_contact;
 
132
    unsigned             skinfo_cnt;
 
133
    pjmedia_sock_info    skinfo[8];
 
134
 
 
135
    pj_bool_t            thread_quit;
 
136
    unsigned             thread_count;
 
137
    pj_thread_t         *thread[16];
 
138
 
 
139
    pj_bool_t            real_sdp;
 
140
    pjmedia_sdp_session *dummy_sdp;
 
141
 
 
142
    int                  log_level;
 
143
 
 
144
    struct {
 
145
        pjsip_method         method;
 
146
        pj_str_t             dst_uri;
 
147
        pj_bool_t            stateless;
 
148
        unsigned             timeout;
 
149
        unsigned             job_count,
 
150
                             job_submitted, 
 
151
                             job_finished,
 
152
                             job_window;
 
153
        unsigned             stat_max_window;
 
154
        pj_time_val          first_request;
 
155
        pj_time_val          requests_sent;
 
156
        pj_time_val          last_completion;
 
157
        unsigned             total_responses;
 
158
        unsigned             response_codes[800];
 
159
    } client;
 
160
 
 
161
    struct {
 
162
        pj_bool_t send_trying;
 
163
        pj_bool_t send_ringing;
 
164
        unsigned delay;
 
165
        struct srv_state prev_state;
 
166
        struct srv_state cur_state;
 
167
    } server;
 
168
 
 
169
 
 
170
} app;
 
171
 
 
172
struct call
 
173
{
 
174
    pjsip_inv_session   *inv;
 
175
    pj_timer_entry       ans_timer;
 
176
};
 
177
 
 
178
 
 
179
static void app_perror(const char *sender, const char *title, 
 
180
                       pj_status_t status)
 
181
{
 
182
    char errmsg[PJ_ERR_MSG_SIZE];
 
183
 
 
184
    pj_strerror(status, errmsg, sizeof(errmsg));
 
185
    PJ_LOG(1,(sender, "%s: %s [code=%d]", title, errmsg, status));
 
186
}
 
187
 
 
188
 
 
189
/**************************************************************************
 
190
 * STATELESS SERVER
 
191
 */
 
192
static pj_bool_t mod_stateless_on_rx_request(pjsip_rx_data *rdata);
 
193
 
 
194
/* Module to handle incoming requests statelessly.
 
195
 */
 
196
static pjsip_module mod_stateless_server =
 
197
{
 
198
    NULL, NULL,                     /* prev, next.              */
 
199
    { "mod-stateless-server", 20 }, /* Name.                    */
 
200
    -1,                             /* Id                       */
 
201
    PJSIP_MOD_PRIORITY_APPLICATION, /* Priority                 */
 
202
    NULL,                           /* load()                   */
 
203
    NULL,                           /* start()                  */
 
204
    NULL,                           /* stop()                   */
 
205
    NULL,                           /* unload()                 */
 
206
    &mod_stateless_on_rx_request,   /* on_rx_request()          */
 
207
    NULL,                           /* on_rx_response()         */
 
208
    NULL,                           /* on_tx_request.           */
 
209
    NULL,                           /* on_tx_response()         */
 
210
    NULL,                           /* on_tsx_state()           */
 
211
};
 
212
 
 
213
 
 
214
static pj_bool_t mod_stateless_on_rx_request(pjsip_rx_data *rdata)
 
215
{
 
216
    const pj_str_t stateless_user = { "0", 1 };
 
217
    pjsip_uri *uri;
 
218
    pjsip_sip_uri *sip_uri;
 
219
 
 
220
    uri = pjsip_uri_get_uri(rdata->msg_info.msg->line.req.uri);
 
221
 
 
222
    /* Only want to receive SIP/SIPS scheme */
 
223
    if (!PJSIP_URI_SCHEME_IS_SIP(uri) && !PJSIP_URI_SCHEME_IS_SIPS(uri))
 
224
        return PJ_FALSE;
 
225
 
 
226
    sip_uri = (pjsip_sip_uri*) uri;
 
227
 
 
228
    /* Check for matching user part */
 
229
    if (pj_strcmp(&sip_uri->user, &stateless_user)!=0)
 
230
        return PJ_FALSE;
 
231
 
 
232
    /*
 
233
     * Yes, this is for us.
 
234
     */
 
235
 
 
236
    /* Ignore ACK request */
 
237
    if (rdata->msg_info.msg->line.req.method.id == PJSIP_ACK_METHOD)
 
238
        return PJ_TRUE;
 
239
 
 
240
    /*
 
241
     * Respond statelessly with 200/OK.
 
242
     */
 
243
    pjsip_endpt_respond_stateless(app.sip_endpt, rdata, 200, NULL,
 
244
                                  NULL, NULL);
 
245
    app.server.cur_state.stateless_cnt++;
 
246
    return PJ_TRUE;
 
247
}
 
248
 
 
249
 
 
250
/**************************************************************************
 
251
 * STATEFUL SERVER
 
252
 */
 
253
static pj_bool_t mod_stateful_on_rx_request(pjsip_rx_data *rdata);
 
254
 
 
255
/* Module to handle incoming requests statefully.
 
256
 */
 
257
static pjsip_module mod_stateful_server =
 
258
{
 
259
    NULL, NULL,                     /* prev, next.              */
 
260
    { "mod-stateful-server", 19 },  /* Name.                    */
 
261
    -1,                             /* Id                       */
 
262
    PJSIP_MOD_PRIORITY_APPLICATION, /* Priority                 */
 
263
    NULL,                           /* load()                   */
 
264
    NULL,                           /* start()                  */
 
265
    NULL,                           /* stop()                   */
 
266
    NULL,                           /* unload()                 */
 
267
    &mod_stateful_on_rx_request,   /* on_rx_request()           */
 
268
    NULL,                           /* on_rx_response()         */
 
269
    NULL,                           /* on_tx_request.           */
 
270
    NULL,                           /* on_tx_response()         */
 
271
    NULL,                           /* on_tsx_state()           */
 
272
};
 
273
 
 
274
 
 
275
static pj_bool_t mod_stateful_on_rx_request(pjsip_rx_data *rdata)
 
276
{
 
277
    const pj_str_t stateful_user = { "1", 1 };
 
278
    pjsip_uri *uri;
 
279
    pjsip_sip_uri *sip_uri;
 
280
 
 
281
    uri = pjsip_uri_get_uri(rdata->msg_info.msg->line.req.uri);
 
282
 
 
283
    /* Only want to receive SIP/SIPS scheme */
 
284
    if (!PJSIP_URI_SCHEME_IS_SIP(uri) && !PJSIP_URI_SCHEME_IS_SIPS(uri))
 
285
        return PJ_FALSE;
 
286
 
 
287
    sip_uri = (pjsip_sip_uri*) uri;
 
288
 
 
289
    /* Check for matching user part */
 
290
    if (pj_strcmp(&sip_uri->user, &stateful_user)!=0)
 
291
        return PJ_FALSE;
 
292
 
 
293
    /*
 
294
     * Yes, this is for us.
 
295
     * Respond statefully with 200/OK.
 
296
     */
 
297
    switch (rdata->msg_info.msg->line.req.method.id) {
 
298
    case PJSIP_INVITE_METHOD:
 
299
        {
 
300
            pjsip_msg_body *body;
 
301
 
 
302
            if (dummy_sdp_str.slen == 0)
 
303
                dummy_sdp_str.slen = pj_ansi_strlen(dummy_sdp_str.ptr);
 
304
 
 
305
            body = pjsip_msg_body_create(rdata->tp_info.pool, 
 
306
                                         &mime_application, &mime_sdp, 
 
307
                                         &dummy_sdp_str);
 
308
            pjsip_endpt_respond(app.sip_endpt, &mod_stateful_server, rdata,
 
309
                                200, NULL, NULL, body, NULL);
 
310
        }
 
311
        break;
 
312
    case PJSIP_ACK_METHOD:
 
313
        return PJ_TRUE;
 
314
    default:
 
315
        pjsip_endpt_respond(app.sip_endpt, &mod_stateful_server, rdata,
 
316
                            200, NULL, NULL, NULL, NULL);
 
317
        break;
 
318
    }
 
319
 
 
320
    app.server.cur_state.stateful_cnt++;
 
321
    return PJ_TRUE;
 
322
}
 
323
 
 
324
 
 
325
/**************************************************************************
 
326
 * CALL SERVER
 
327
 */
 
328
static pj_bool_t mod_call_on_rx_request(pjsip_rx_data *rdata);
 
329
 
 
330
/* Module to handle incoming requests callly.
 
331
 */
 
332
static pjsip_module mod_call_server =
 
333
{
 
334
    NULL, NULL,                     /* prev, next.              */
 
335
    { "mod-call-server", 15 },      /* Name.                    */
 
336
    -1,                             /* Id                       */
 
337
    PJSIP_MOD_PRIORITY_APPLICATION, /* Priority                 */
 
338
    NULL,                           /* load()                   */
 
339
    NULL,                           /* start()                  */
 
340
    NULL,                           /* stop()                   */
 
341
    NULL,                           /* unload()                 */
 
342
    &mod_call_on_rx_request,        /* on_rx_request()          */
 
343
    NULL,                           /* on_rx_response()         */
 
344
    NULL,                           /* on_tx_request.           */
 
345
    NULL,                           /* on_tx_response()         */
 
346
    NULL,                           /* on_tsx_state()           */
 
347
};
 
348
 
 
349
 
 
350
static pj_status_t send_response(pjsip_inv_session *inv, 
 
351
                                 pjsip_rx_data *rdata,
 
352
                                 int code,
 
353
                                 pj_bool_t *has_initial)
 
354
{
 
355
    pjsip_tx_data *tdata;
 
356
    pj_status_t status;
 
357
 
 
358
    if (*has_initial) {
 
359
        status = pjsip_inv_answer(inv, code, NULL, NULL, &tdata);
 
360
    } else {
 
361
        status = pjsip_inv_initial_answer(inv, rdata, code, 
 
362
                                          NULL, NULL, &tdata);
 
363
    }
 
364
 
 
365
    if (status != PJ_SUCCESS) {
 
366
        if (*has_initial) {
 
367
            status = pjsip_inv_answer(inv, PJSIP_SC_NOT_ACCEPTABLE, 
 
368
                                      NULL, NULL, &tdata);
 
369
        } else {
 
370
            status = pjsip_inv_initial_answer(inv, rdata, 
 
371
                                              PJSIP_SC_NOT_ACCEPTABLE,
 
372
                                              NULL, NULL, &tdata);
 
373
        }
 
374
 
 
375
        if (status == PJ_SUCCESS) {
 
376
            *has_initial = PJ_TRUE;
 
377
            pjsip_inv_send_msg(inv, tdata); 
 
378
        } else {
 
379
            pjsip_inv_terminate(inv, 500, PJ_FALSE);
 
380
            return -1;
 
381
        }
 
382
    } else {
 
383
        *has_initial = PJ_TRUE;
 
384
 
 
385
        status = pjsip_inv_send_msg(inv, tdata); 
 
386
        if (status != PJ_SUCCESS) {
 
387
            pjsip_tx_data_dec_ref(tdata);
 
388
            return status;
 
389
        }
 
390
    }
 
391
 
 
392
    return status;
 
393
}
 
394
 
 
395
static void answer_timer_cb(pj_timer_heap_t *h, pj_timer_entry *entry)
 
396
{
 
397
    struct call *call = entry->user_data;
 
398
    pj_bool_t has_initial = PJ_TRUE;
 
399
 
 
400
    PJ_UNUSED_ARG(h);
 
401
 
 
402
    entry->id = 0;
 
403
    send_response(call->inv, NULL, 200, &has_initial);
 
404
}
 
405
 
 
406
static pj_bool_t mod_call_on_rx_request(pjsip_rx_data *rdata)
 
407
{
 
408
    const pj_str_t call_user = { "2", 1 };
 
409
    pjsip_uri *uri;
 
410
    pjsip_sip_uri *sip_uri;
 
411
    struct call *call;
 
412
    pjsip_dialog *dlg;
 
413
    pjmedia_sdp_session *sdp;
 
414
    pjsip_tx_data *tdata;
 
415
    pj_bool_t has_initial = PJ_FALSE;
 
416
    pj_status_t status;
 
417
 
 
418
    uri = pjsip_uri_get_uri(rdata->msg_info.msg->line.req.uri);
 
419
 
 
420
    /* Only want to receive SIP/SIPS scheme */
 
421
    if (!PJSIP_URI_SCHEME_IS_SIP(uri) && !PJSIP_URI_SCHEME_IS_SIPS(uri))
 
422
        return PJ_FALSE;
 
423
 
 
424
    sip_uri = (pjsip_sip_uri*) uri;
 
425
 
 
426
    /* Only want to handle INVITE requests. */
 
427
    if (rdata->msg_info.msg->line.req.method.id != PJSIP_INVITE_METHOD) {
 
428
        return PJ_FALSE;
 
429
    }
 
430
 
 
431
 
 
432
    /* Check for matching user part. Incoming requests will be handled 
 
433
     * call-statefully if:
 
434
     *  - user part is "2", or
 
435
     *  - user part is not "0" nor "1" and method is INVITE.
 
436
     */
 
437
    if (pj_strcmp(&sip_uri->user, &call_user) == 0 ||
 
438
        sip_uri->user.slen != 1 ||
 
439
        (*sip_uri->user.ptr != '0' && *sip_uri->user.ptr != '1'))
 
440
    {
 
441
        /* Match */
 
442
 
 
443
    } else {
 
444
        return PJ_FALSE;
 
445
    }
 
446
 
 
447
 
 
448
    /* Verify that we can handle the request. */
 
449
    if (app.real_sdp) {
 
450
        unsigned options = 0;
 
451
        status = pjsip_inv_verify_request(rdata, &options, NULL, NULL,
 
452
                                          app.sip_endpt, &tdata);
 
453
        if (status != PJ_SUCCESS) {
 
454
 
 
455
            /*
 
456
             * No we can't handle the incoming INVITE request.
 
457
             */
 
458
 
 
459
            if (tdata) {
 
460
                pjsip_response_addr res_addr;
 
461
 
 
462
                pjsip_get_response_addr(tdata->pool, rdata, &res_addr);
 
463
                pjsip_endpt_send_response(app.sip_endpt, &res_addr, tdata, 
 
464
                                          NULL, NULL);
 
465
 
 
466
            } else {
 
467
 
 
468
                /* Respond with 500 (Internal Server Error) */
 
469
                pjsip_endpt_respond_stateless(app.sip_endpt, rdata, 500, NULL,
 
470
                                              NULL, NULL);
 
471
            }
 
472
 
 
473
            return PJ_TRUE;
 
474
        } 
 
475
    }
 
476
 
 
477
    /* Create UAS dialog */
 
478
    status = pjsip_dlg_create_uas( pjsip_ua_instance(), rdata,
 
479
                                   &app.local_contact, &dlg);
 
480
    if (status != PJ_SUCCESS) {
 
481
        const pj_str_t reason = pj_str("Unable to create dialog");
 
482
        pjsip_endpt_respond_stateless( app.sip_endpt, rdata, 
 
483
                                       500, &reason,
 
484
                                       NULL, NULL);
 
485
        return PJ_TRUE;
 
486
    }
 
487
 
 
488
    /* Alloc call structure. */
 
489
    call = pj_pool_zalloc(dlg->pool, sizeof(struct call));
 
490
 
 
491
    /* Create SDP from PJMEDIA */
 
492
    if (app.real_sdp) {
 
493
        status = pjmedia_endpt_create_sdp(app.med_endpt, rdata->tp_info.pool, 
 
494
                                          app.skinfo_cnt, app.skinfo, 
 
495
                                          &sdp);
 
496
    } else {
 
497
        sdp = app.dummy_sdp;
 
498
    }
 
499
 
 
500
    /* Create UAS invite session */
 
501
    status = pjsip_inv_create_uas( dlg, rdata, sdp, 0, &call->inv);
 
502
    if (status != PJ_SUCCESS) {
 
503
        pjsip_dlg_create_response(dlg, rdata, 500, NULL, &tdata);
 
504
        pjsip_dlg_send_response(dlg, pjsip_rdata_get_tsx(rdata), tdata);
 
505
        return PJ_TRUE;
 
506
    }
 
507
    
 
508
    /* Send 100/Trying if needed */
 
509
    if (app.server.send_trying) {
 
510
        status = send_response(call->inv, rdata, 100, &has_initial);
 
511
        if (status != PJ_SUCCESS)
 
512
            return PJ_TRUE;
 
513
    }
 
514
 
 
515
    /* Send 180/Ringing if needed */
 
516
    if (app.server.send_ringing) {
 
517
        status = send_response(call->inv, rdata, 180, &has_initial);
 
518
        if (status != PJ_SUCCESS)
 
519
            return PJ_TRUE;
 
520
    }
 
521
 
 
522
    /* Simulate call processing delay */
 
523
    if (app.server.delay) {
 
524
        pj_time_val delay;
 
525
 
 
526
        call->ans_timer.id = 1;
 
527
        call->ans_timer.user_data = call;
 
528
        call->ans_timer.cb = &answer_timer_cb;
 
529
        
 
530
        delay.sec = 0;
 
531
        delay.msec = app.server.delay;
 
532
        pj_time_val_normalize(&delay);
 
533
 
 
534
        pjsip_endpt_schedule_timer(app.sip_endpt, &call->ans_timer, &delay);
 
535
 
 
536
    } else {
 
537
        /* Send the 200 response immediately . */  
 
538
        status = send_response(call->inv, rdata, 200, &has_initial);
 
539
        PJ_ASSERT_ON_FAIL(status == PJ_SUCCESS, return PJ_TRUE);
 
540
    }
 
541
 
 
542
    /* Done */
 
543
    app.server.cur_state.call_cnt++;
 
544
 
 
545
    return PJ_TRUE;
 
546
}
 
547
 
 
548
 
 
549
 
 
550
/**************************************************************************
 
551
 * Default handler when incoming request is not handled by any other
 
552
 * modules.
 
553
 */
 
554
static pj_bool_t mod_responder_on_rx_request(pjsip_rx_data *rdata);
 
555
 
 
556
/* Module to handle incoming requests statelessly.
 
557
 */
 
558
static pjsip_module mod_responder =
 
559
{
 
560
    NULL, NULL,                     /* prev, next.              */
 
561
    { "mod-responder", 13 },        /* Name.                    */
 
562
    -1,                             /* Id                       */
 
563
    PJSIP_MOD_PRIORITY_APPLICATION+1, /* Priority               */
 
564
    NULL,                           /* load()                   */
 
565
    NULL,                           /* start()                  */
 
566
    NULL,                           /* stop()                   */
 
567
    NULL,                           /* unload()                 */
 
568
    &mod_responder_on_rx_request,   /* on_rx_request()          */
 
569
    NULL,                           /* on_rx_response()         */
 
570
    NULL,                           /* on_tx_request.           */
 
571
    NULL,                           /* on_tx_response()         */
 
572
    NULL,                           /* on_tsx_state()           */
 
573
};
 
574
 
 
575
 
 
576
static pj_bool_t mod_responder_on_rx_request(pjsip_rx_data *rdata)
 
577
{
 
578
    const pj_str_t reason = pj_str("Not expecting request at this URI");
 
579
 
 
580
    /*
 
581
     * Respond any requests (except ACK!) with 500.
 
582
     */
 
583
    if (rdata->msg_info.msg->line.req.method.id != PJSIP_ACK_METHOD) {
 
584
        pjsip_endpt_respond_stateless(app.sip_endpt, rdata, 500, &reason,
 
585
                                      NULL, NULL);
 
586
    }
 
587
 
 
588
    return PJ_TRUE;
 
589
}
 
590
 
 
591
 
 
592
 
 
593
/*****************************************************************************
 
594
 * Below is a simple module to log all incoming and outgoing SIP messages
 
595
 */
 
596
 
 
597
 
 
598
/* Notification on incoming messages */
 
599
static pj_bool_t logger_on_rx_msg(pjsip_rx_data *rdata)
 
600
{
 
601
    PJ_LOG(3,(THIS_FILE, "RX %d bytes %s from %s %s:%d:\n"
 
602
                         "%.*s\n"
 
603
                         "--end msg--",
 
604
                         rdata->msg_info.len,
 
605
                         pjsip_rx_data_get_info(rdata),
 
606
                         rdata->tp_info.transport->type_name,
 
607
                         rdata->pkt_info.src_name,
 
608
                         rdata->pkt_info.src_port,
 
609
                         (int)rdata->msg_info.len,
 
610
                         rdata->msg_info.msg_buf));
 
611
    
 
612
    /* Always return false, otherwise messages will not get processed! */
 
613
    return PJ_FALSE;
 
614
}
 
615
 
 
616
/* Notification on outgoing messages */
 
617
static pj_status_t logger_on_tx_msg(pjsip_tx_data *tdata)
 
618
{
 
619
    
 
620
    /* Important note:
 
621
     *  tp_info field is only valid after outgoing messages has passed
 
622
     *  transport layer. So don't try to access tp_info when the module
 
623
     *  has lower priority than transport layer.
 
624
     */
 
625
 
 
626
    PJ_LOG(3,(THIS_FILE, "TX %d bytes %s to %s %s:%d:\n"
 
627
                         "%.*s\n"
 
628
                         "--end msg--",
 
629
                         (tdata->buf.cur - tdata->buf.start),
 
630
                         pjsip_tx_data_get_info(tdata),
 
631
                         tdata->tp_info.transport->type_name,
 
632
                         tdata->tp_info.dst_name,
 
633
                         tdata->tp_info.dst_port,
 
634
                         (int)(tdata->buf.cur - tdata->buf.start),
 
635
                         tdata->buf.start));
 
636
 
 
637
    /* Always return success, otherwise message will not get sent! */
 
638
    return PJ_SUCCESS;
 
639
}
 
640
 
 
641
/* The module instance. */
 
642
static pjsip_module msg_logger = 
 
643
{
 
644
    NULL, NULL,                         /* prev, next.          */
 
645
    { "mod-siprtp-log", 14 },           /* Name.                */
 
646
    -1,                                 /* Id                   */
 
647
    PJSIP_MOD_PRIORITY_TRANSPORT_LAYER-1,/* Priority            */
 
648
    NULL,                               /* load()               */
 
649
    NULL,                               /* start()              */
 
650
    NULL,                               /* stop()               */
 
651
    NULL,                               /* unload()             */
 
652
    &logger_on_rx_msg,                  /* on_rx_request()      */
 
653
    &logger_on_rx_msg,                  /* on_rx_response()     */
 
654
    &logger_on_tx_msg,                  /* on_tx_request.       */
 
655
    &logger_on_tx_msg,                  /* on_tx_response()     */
 
656
    NULL,                               /* on_tsx_state()       */
 
657
 
 
658
};
 
659
 
 
660
 
 
661
 
 
662
/**************************************************************************
 
663
 * Test Client.
 
664
 */
 
665
 
 
666
static pj_bool_t mod_test_on_rx_response(pjsip_rx_data *rdata);
 
667
 
 
668
static void call_on_media_update( pjsip_inv_session *inv,
 
669
                                  pj_status_t status);
 
670
static void call_on_state_changed( pjsip_inv_session *inv, 
 
671
                                   pjsip_event *e);
 
672
static void call_on_forked(pjsip_inv_session *inv, pjsip_event *e);
 
673
 
 
674
 
 
675
/* Module to handle incoming requests callly.
 
676
 */
 
677
static pjsip_module mod_test =
 
678
{
 
679
    NULL, NULL,                     /* prev, next.              */
 
680
    { "mod-test", 8 },              /* Name.                    */
 
681
    -1,                             /* Id                       */
 
682
    PJSIP_MOD_PRIORITY_APPLICATION, /* Priority                 */
 
683
    NULL,                           /* load()                   */
 
684
    NULL,                           /* start()                  */
 
685
    NULL,                           /* stop()                   */
 
686
    NULL,                           /* unload()                 */
 
687
    NULL,                           /* on_rx_request()          */
 
688
    &mod_test_on_rx_response,       /* on_rx_response()         */
 
689
    NULL,                           /* on_tx_request.           */
 
690
    NULL,                           /* on_tx_response()         */
 
691
    NULL,                           /* on_tsx_state()           */
 
692
};
 
693
 
 
694
 
 
695
static void report_completion(int status_code)
 
696
{
 
697
    app.client.job_finished++;
 
698
    if (status_code >= 200 && status_code < 800)
 
699
        app.client.response_codes[status_code]++;
 
700
    app.client.total_responses++;
 
701
    pj_gettimeofday(&app.client.last_completion);
 
702
}
 
703
 
 
704
 
 
705
/* Handler when response is received. */
 
706
static pj_bool_t mod_test_on_rx_response(pjsip_rx_data *rdata)
 
707
{
 
708
    if (pjsip_rdata_get_tsx(rdata) == NULL) {
 
709
        report_completion(rdata->msg_info.msg->line.status.code);
 
710
    }
 
711
 
 
712
    return PJ_TRUE;
 
713
}
 
714
 
 
715
 
 
716
/*
 
717
 * Create app
 
718
 */
 
719
static pj_status_t create_app(void)
 
720
{
 
721
    pj_status_t status;
 
722
 
 
723
    status = pj_init();
 
724
    if (status != PJ_SUCCESS) {
 
725
        app_perror(THIS_FILE, "Error initializing pjlib", status);
 
726
        return status;
 
727
    }
 
728
 
 
729
    /* init PJLIB-UTIL: */
 
730
    status = pjlib_util_init();
 
731
    PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
 
732
 
 
733
    /* Must create a pool factory before we can allocate any memory. */
 
734
    pj_caching_pool_init(&app.cp, &pj_pool_factory_default_policy, 
 
735
                         CACHING_POOL_SIZE);
 
736
 
 
737
    /* Create application pool for misc. */
 
738
    app.pool = pj_pool_create(&app.cp.factory, "app", 1000, 1000, NULL);
 
739
 
 
740
    /* Create the endpoint: */
 
741
    status = pjsip_endpt_create(&app.cp.factory, pj_gethostname()->ptr, 
 
742
                                &app.sip_endpt);
 
743
    PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
 
744
 
 
745
 
 
746
    return status;
 
747
}
 
748
 
 
749
 
 
750
/*
 
751
 * Init SIP stack
 
752
 */
 
753
static pj_status_t init_sip()
 
754
{
 
755
    pj_status_t status = -1;
 
756
 
 
757
    /* Add UDP/TCP transport. */
 
758
    {
 
759
        pj_sockaddr_in addr;
 
760
        pjsip_host_port addrname;
 
761
        const char *transport_type = NULL;
 
762
 
 
763
        pj_bzero(&addr, sizeof(addr));
 
764
        addr.sin_family = pj_AF_INET();
 
765
        addr.sin_addr.s_addr = 0;
 
766
        addr.sin_port = pj_htons((pj_uint16_t)app.local_port);
 
767
 
 
768
        if (app.local_addr.slen) {
 
769
            addrname.host = app.local_addr;
 
770
            addrname.port = 5060;
 
771
        } 
 
772
        if (app.local_port != 0)
 
773
            addrname.port = app.local_port;
 
774
 
 
775
        if (0) {
 
776
#if defined(PJ_HAS_TCP) && PJ_HAS_TCP!=0
 
777
        } else if (app.use_tcp) {
 
778
            pj_sockaddr_in local_addr;
 
779
            pjsip_tpfactory *tpfactory;
 
780
            
 
781
            transport_type = "tcp";
 
782
            pj_sockaddr_in_init(&local_addr, 0, (pj_uint16_t)app.local_port);
 
783
            status = pjsip_tcp_transport_start(app.sip_endpt, &local_addr,
 
784
                                               app.thread_count, &tpfactory);
 
785
            if (status == PJ_SUCCESS) {
 
786
                app.local_addr = tpfactory->addr_name.host;
 
787
                app.local_port = tpfactory->addr_name.port;
 
788
            }
 
789
#endif
 
790
        } else {
 
791
            pjsip_transport *tp;
 
792
 
 
793
            transport_type = "udp";
 
794
            status = pjsip_udp_transport_start(app.sip_endpt, &addr, 
 
795
                                               (app.local_addr.slen ? &addrname:NULL),
 
796
                                               app.thread_count, &tp);
 
797
            if (status == PJ_SUCCESS) {
 
798
                app.local_addr = tp->local_name.host;
 
799
                app.local_port = tp->local_name.port;
 
800
            }
 
801
 
 
802
        }
 
803
        if (status != PJ_SUCCESS) {
 
804
            app_perror(THIS_FILE, "Unable to start transport", status);
 
805
            return status;
 
806
        }
 
807
 
 
808
        app.local_uri.ptr = pj_pool_alloc(app.pool, 128);
 
809
        app.local_uri.slen = pj_ansi_sprintf(app.local_uri.ptr, 
 
810
                                             "<sip:pjsip-perf@%.*s:%d;transport=%s>",
 
811
                                             (int)app.local_addr.slen,
 
812
                                             app.local_addr.ptr,
 
813
                                             app.local_port,
 
814
                                             transport_type);
 
815
 
 
816
        app.local_contact = app.local_uri;
 
817
    }
 
818
 
 
819
    /* 
 
820
     * Init transaction layer.
 
821
     * This will create/initialize transaction hash tables etc.
 
822
     */
 
823
    status = pjsip_tsx_layer_init_module(app.sip_endpt);
 
824
    PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
 
825
 
 
826
    /*  Initialize UA layer. */
 
827
    status = pjsip_ua_init_module( app.sip_endpt, NULL );
 
828
    PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
 
829
 
 
830
    /* Initialize 100rel support */
 
831
    status = pjsip_100rel_init_module(app.sip_endpt);
 
832
    PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
 
833
 
 
834
    /*  Init invite session module. */
 
835
    {
 
836
        pjsip_inv_callback inv_cb;
 
837
 
 
838
        /* Init the callback for INVITE session: */
 
839
        pj_bzero(&inv_cb, sizeof(inv_cb));
 
840
        inv_cb.on_state_changed = &call_on_state_changed;
 
841
        inv_cb.on_new_session = &call_on_forked;
 
842
        inv_cb.on_media_update = &call_on_media_update;
 
843
 
 
844
        /* Initialize invite session module:  */
 
845
        status = pjsip_inv_usage_init(app.sip_endpt, &inv_cb);
 
846
        PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
 
847
    }
 
848
 
 
849
    /* Register our module to receive incoming requests. */
 
850
    status = pjsip_endpt_register_module( app.sip_endpt, &mod_test);
 
851
    PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
 
852
 
 
853
 
 
854
    /* Register stateless server module */
 
855
    status = pjsip_endpt_register_module( app.sip_endpt, &mod_stateless_server);
 
856
    PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
 
857
 
 
858
    /* Register default responder module */
 
859
    status = pjsip_endpt_register_module( app.sip_endpt, &mod_responder);
 
860
    PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
 
861
 
 
862
    /* Register stateless server module */
 
863
    status = pjsip_endpt_register_module( app.sip_endpt, &mod_stateful_server);
 
864
    PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
 
865
 
 
866
 
 
867
    /* Register call server module */
 
868
    status = pjsip_endpt_register_module( app.sip_endpt, &mod_call_server);
 
869
    PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
 
870
 
 
871
 
 
872
    /* Done */
 
873
    return PJ_SUCCESS;
 
874
}
 
875
 
 
876
 
 
877
/*
 
878
 * Destroy SIP
 
879
 */
 
880
static void destroy_app()
 
881
{
 
882
    unsigned i;
 
883
 
 
884
    app.thread_quit = 1;
 
885
    for (i=0; i<app.thread_count; ++i) {
 
886
        if (app.thread[i]) {
 
887
            pj_thread_join(app.thread[i]);
 
888
            pj_thread_destroy(app.thread[i]);
 
889
            app.thread[i] = NULL;
 
890
        }
 
891
    }
 
892
 
 
893
    if (app.sip_endpt) {
 
894
        pjsip_endpt_destroy(app.sip_endpt);
 
895
        app.sip_endpt = NULL;
 
896
    }
 
897
 
 
898
    if (app.pool) {
 
899
        pj_pool_release(app.pool);
 
900
        app.pool = NULL;
 
901
        PJ_LOG(3,(THIS_FILE, "Peak memory size: %uMB",
 
902
                             app.cp.peak_used_size / 1000000));
 
903
        pj_caching_pool_destroy(&app.cp);
 
904
    }
 
905
 
 
906
    /* Shutdown PJLIB */
 
907
    pj_shutdown();
 
908
}
 
909
 
 
910
 
 
911
/*
 
912
 * Init media stack.
 
913
 */
 
914
static pj_status_t init_media()
 
915
{
 
916
    unsigned    i;
 
917
    pj_uint16_t rtp_port;
 
918
    pj_status_t status;
 
919
 
 
920
 
 
921
    /* Initialize media endpoint so that at least error subsystem is properly
 
922
     * initialized.
 
923
     */
 
924
    status = pjmedia_endpt_create(&app.cp.factory, 
 
925
                                  pjsip_endpt_get_ioqueue(app.sip_endpt), 0, 
 
926
                                  &app.med_endpt);
 
927
    PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
 
928
 
 
929
 
 
930
    /* Must register all codecs to be supported */
 
931
    pjmedia_codec_register_audio_codecs(app.med_endpt, NULL);
 
932
 
 
933
    /* Init dummy socket addresses */
 
934
    app.skinfo_cnt = 0;
 
935
    for (i=0, rtp_port=4000; i<PJ_ARRAY_SIZE(app.skinfo); ++i, rtp_port+=2) {
 
936
        pjmedia_sock_info *skinfo;
 
937
 
 
938
        skinfo = &app.skinfo[i];
 
939
        
 
940
        pj_sockaddr_in_init(&skinfo->rtp_addr_name.ipv4, &app.local_addr,
 
941
                            (pj_uint16_t)rtp_port);
 
942
        pj_sockaddr_in_init(&skinfo->rtp_addr_name.ipv4, &app.local_addr,
 
943
                            (pj_uint16_t)(rtp_port+1));
 
944
        app.skinfo_cnt++;
 
945
    }
 
946
 
 
947
    /* Generate dummy SDP */
 
948
    dummy_sdp_str.slen = pj_ansi_strlen(dummy_sdp_str.ptr);
 
949
    status = pjmedia_sdp_parse(app.pool, dummy_sdp_str.ptr, dummy_sdp_str.slen, 
 
950
                               &app.dummy_sdp);
 
951
    if (status != PJ_SUCCESS) {
 
952
        app_perror(THIS_FILE, "Error parsing dummy SDP", status);
 
953
        return status;
 
954
    }
 
955
 
 
956
 
 
957
    /* Done */
 
958
    return PJ_SUCCESS;
 
959
}
 
960
 
 
961
 
 
962
/* This is notification from the call about media negotiation
 
963
 * status. This is called for client calls only.
 
964
 */
 
965
static void call_on_media_update( pjsip_inv_session *inv,
 
966
                                  pj_status_t status)
 
967
{
 
968
    if (status != PJ_SUCCESS) {
 
969
        pjsip_tx_data *tdata;
 
970
        pj_status_t status;
 
971
 
 
972
        status = pjsip_inv_end_session(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE, 
 
973
                                       NULL, &tdata);
 
974
        if (status == PJ_SUCCESS && tdata)
 
975
            status = pjsip_inv_send_msg(inv, tdata);
 
976
    }
 
977
}
 
978
 
 
979
 
 
980
/* This is notification from the call when the call state has changed.
 
981
 * This is called for client calls only.
 
982
 */
 
983
static void call_on_state_changed( pjsip_inv_session *inv, 
 
984
                                   pjsip_event *e)
 
985
{
 
986
    PJ_UNUSED_ARG(e);
 
987
 
 
988
    /* Bail out if the session has been counted before */
 
989
    if (inv->mod_data[mod_test.id] != NULL)
 
990
        return;
 
991
 
 
992
    /* Bail out if this is not an outgoing call */
 
993
    if (inv->role != PJSIP_UAC_ROLE)
 
994
        return;
 
995
 
 
996
    if (inv->state == PJSIP_INV_STATE_CONFIRMED) {
 
997
        pjsip_tx_data *tdata;
 
998
        pj_status_t status;
 
999
 
 
1000
        //report_completion(200);
 
1001
        //inv->mod_data[mod_test.id] = (void*)1;
 
1002
 
 
1003
        status = pjsip_inv_end_session(inv, PJSIP_SC_OK, NULL, &tdata);
 
1004
        if (status == PJ_SUCCESS && tdata)
 
1005
            status = pjsip_inv_send_msg(inv, tdata);
 
1006
 
 
1007
    } else if (inv->state == PJSIP_INV_STATE_DISCONNECTED) {
 
1008
        report_completion(inv->cause);
 
1009
        inv->mod_data[mod_test.id] = (void*)1;
 
1010
    }
 
1011
}
 
1012
 
 
1013
 
 
1014
/* Not implemented for now */
 
1015
static void call_on_forked(pjsip_inv_session *inv, pjsip_event *e)
 
1016
{
 
1017
    /* Do nothing */
 
1018
    PJ_UNUSED_ARG(inv);
 
1019
    PJ_UNUSED_ARG(e);
 
1020
}
 
1021
 
 
1022
 
 
1023
/*
 
1024
 * Make outgoing call.
 
1025
 */
 
1026
static pj_status_t make_call(const pj_str_t *dst_uri)
 
1027
{
 
1028
    struct call *call;
 
1029
    pjsip_dialog *dlg;
 
1030
    pjmedia_sdp_session *sdp;
 
1031
    pjsip_tx_data *tdata;
 
1032
    pj_status_t status;
 
1033
 
 
1034
 
 
1035
    /* Create UAC dialog */
 
1036
    status = pjsip_dlg_create_uac( pjsip_ua_instance(), 
 
1037
                                   &app.local_uri,      /* local URI        */
 
1038
                                   &app.local_contact,  /* local Contact    */
 
1039
                                   dst_uri,             /* remote URI       */
 
1040
                                   dst_uri,             /* remote target    */
 
1041
                                   &dlg);               /* dialog           */
 
1042
    if (status != PJ_SUCCESS) {
 
1043
        return status;
 
1044
    }
 
1045
 
 
1046
    /* Create call */
 
1047
    call = pj_pool_zalloc(dlg->pool, sizeof(struct call));
 
1048
 
 
1049
    /* Create SDP */
 
1050
    if (app.real_sdp) {
 
1051
        status = pjmedia_endpt_create_sdp(app.med_endpt, dlg->pool, 1, 
 
1052
                                          app.skinfo, &sdp);
 
1053
        if (status != PJ_SUCCESS) {
 
1054
            pjsip_dlg_terminate(dlg);
 
1055
            return status;
 
1056
        }
 
1057
    } else
 
1058
        sdp = app.dummy_sdp;
 
1059
 
 
1060
    /* Create the INVITE session. */
 
1061
    status = pjsip_inv_create_uac( dlg, sdp, 0, &call->inv);
 
1062
    if (status != PJ_SUCCESS) {
 
1063
        pjsip_dlg_terminate(dlg);
 
1064
        return status;
 
1065
    }
 
1066
 
 
1067
 
 
1068
    /* Create initial INVITE request.
 
1069
     * This INVITE request will contain a perfectly good request and 
 
1070
     * an SDP body as well.
 
1071
     */
 
1072
    status = pjsip_inv_invite(call->inv, &tdata);
 
1073
    PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
 
1074
 
 
1075
 
 
1076
    /* Send initial INVITE request. 
 
1077
     * From now on, the invite session's state will be reported to us
 
1078
     * via the invite session callbacks.
 
1079
     */
 
1080
    status = pjsip_inv_send_msg(call->inv, tdata);
 
1081
    PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
 
1082
 
 
1083
 
 
1084
    return PJ_SUCCESS;
 
1085
}
 
1086
 
 
1087
 
 
1088
/*
 
1089
 * Verify that valid SIP url is given.
 
1090
 */
 
1091
static pj_status_t verify_sip_url(const char *c_url)
 
1092
{
 
1093
    pjsip_uri *p;
 
1094
    pj_pool_t *pool;
 
1095
    char *url;
 
1096
    int len = (c_url ? pj_ansi_strlen(c_url) : 0);
 
1097
 
 
1098
    if (!len) return -1;
 
1099
 
 
1100
    pool = pj_pool_create(&app.cp.factory, "check%p", 1024, 0, NULL);
 
1101
    if (!pool) return PJ_ENOMEM;
 
1102
 
 
1103
    url = pj_pool_alloc(pool, len+1);
 
1104
    pj_ansi_strcpy(url, c_url);
 
1105
    url[len] = '\0';
 
1106
 
 
1107
    p = pjsip_parse_uri(pool, url, len, 0);
 
1108
    if (!p || pj_stricmp2(pjsip_uri_get_scheme(p), "sip") != 0)
 
1109
        p = NULL;
 
1110
 
 
1111
    pj_pool_release(pool);
 
1112
    return p ? 0 : -1;
 
1113
}
 
1114
 
 
1115
 
 
1116
static void usage(void)
 
1117
{
 
1118
    printf(
 
1119
        "Usage:\n"
 
1120
        "   pjsip-perf [OPTIONS]        -- to start as server\n"
 
1121
        "   pjsip-perf [OPTIONS] URL    -- to call server (possibly itself)\n"
 
1122
        "\n"
 
1123
        "where:\n"
 
1124
        "   URL                     The SIP URL to be contacted.\n"
 
1125
        "\n"
 
1126
        "Client options:\n"
 
1127
        "   --method=METHOD, -m     Set test method (set to INVITE for call benchmark)\n"
 
1128
        "                           [default: OPTIONS]\n"
 
1129
        "   --count=N, -n           Set total number of requests to initiate\n"
 
1130
        "                           [default=%d]\n"
 
1131
        "   --stateless, -s         Set to operate in stateless mode\n"
 
1132
        "                           [default: stateful]\n"
 
1133
        "   --timeout=SEC, -t       Set client timeout [default=60 sec]\n"
 
1134
        "   --window=COUNT, -w      Set maximum outstanding job [default: %d]\n"
 
1135
        "\n"
 
1136
        "SDP options (client and server):\n"
 
1137
        "   --real-sdp              Generate real SDP from pjmedia, and also perform\n"
 
1138
        "                           proper SDP negotiation [default: dummy]\n"
 
1139
        "\n"
 
1140
        "Client and Server options:\n"
 
1141
        "   --local-port=PORT, -p   Set local port [default: 5060]\n"
 
1142
        "   --use-tcp, -T           Use TCP instead of UDP. Note that when started as\n"
 
1143
        "                           client, you must add ;transport=tcp parameter to URL\n"
 
1144
        "                           [default: no]\n"
 
1145
        "   --thread-count=N        Set number of worker threads [default=1]\n"
 
1146
        "   --trying                Send 100/Trying response (server, default no)\n"
 
1147
        "   --ringing               Send 180/Ringing response (server, default no)\n"
 
1148
        "   --delay=MS, -d          Delay answering call by MS (server, default no)\n"
 
1149
        "\n"
 
1150
        "Misc options:\n"
 
1151
        "   --help, -h              Display this screen\n"
 
1152
        "   --verbose, -v           Verbose logging (put more than once for even more)\n"
 
1153
        "\n"
 
1154
        "When started as server, pjsip-perf can be contacted on the following URIs:\n"
 
1155
        "   - sip:0@server-addr     To handle requests statelessly.\n"
 
1156
        "   - sip:1@server-addr     To handle requests statefully.\n"
 
1157
        "   - sip:2@server-addr     To handle INVITE call.\n",
 
1158
        DEFAULT_COUNT, JOB_WINDOW);
 
1159
}
 
1160
 
 
1161
 
 
1162
static int my_atoi(const char *s)
 
1163
{
 
1164
    pj_str_t ss = pj_str((char*)s);
 
1165
    return pj_strtoul(&ss);
 
1166
}
 
1167
 
 
1168
 
 
1169
static pj_status_t init_options(int argc, char *argv[])
 
1170
{
 
1171
    enum { OPT_THREAD_COUNT = 1, OPT_REAL_SDP, OPT_TRYING, OPT_RINGING };
 
1172
    struct pj_getopt_option long_options[] = {
 
1173
        { "local-port",     1, 0, 'p' },
 
1174
        { "count",          1, 0, 'c' },
 
1175
        { "thread-count",   1, 0, OPT_THREAD_COUNT },
 
1176
        { "method",         1, 0, 'm' },
 
1177
        { "help",           0, 0, 'h' },
 
1178
        { "stateless",      0, 0, 's' },
 
1179
        { "timeout",        1, 0, 't' },
 
1180
        { "real-sdp",       0, 0, OPT_REAL_SDP },
 
1181
        { "verbose",        0, 0, 'v' },
 
1182
        { "use-tcp",        0, 0, 'T' },
 
1183
        { "window",         1, 0, 'w' },
 
1184
        { "delay",          1, 0, 'd' },
 
1185
        { "trying",         0, 0, OPT_TRYING},
 
1186
        { "ringing",        0, 0, OPT_RINGING},
 
1187
        { NULL, 0, 0, 0 },
 
1188
    };
 
1189
    int c;
 
1190
    int option_index;
 
1191
 
 
1192
    /* Init default application configs */
 
1193
    app.local_port = 5060;
 
1194
    app.thread_count = 1;
 
1195
    app.client.job_count = DEFAULT_COUNT;
 
1196
    app.client.method = *pjsip_get_options_method();
 
1197
    app.client.job_window = c = JOB_WINDOW;
 
1198
    app.client.timeout = 60;
 
1199
    app.log_level = 3;
 
1200
 
 
1201
 
 
1202
    /* Parse options */
 
1203
    pj_optind = 0;
 
1204
    while((c=pj_getopt_long(argc,argv, "p:c:m:t:w:d:hsv", 
 
1205
                            long_options, &option_index))!=-1) 
 
1206
    {
 
1207
        switch (c) {
 
1208
        case 'p':
 
1209
            app.local_port = my_atoi(pj_optarg);
 
1210
            if (app.local_port < 0 || app.local_port > 65535) {
 
1211
                PJ_LOG(3,(THIS_FILE, "Invalid --local-port %s", pj_optarg));
 
1212
                return -1;
 
1213
            }
 
1214
            break;
 
1215
 
 
1216
        case 'c':
 
1217
            app.client.job_count = my_atoi(pj_optarg);
 
1218
            if (app.client.job_count < 0) {
 
1219
                PJ_LOG(3,(THIS_FILE, "Invalid --local-port %s", pj_optarg));
 
1220
                return -1;
 
1221
            }
 
1222
            if (app.client.job_count > pjsip_cfg()->tsx.max_count)
 
1223
                PJ_LOG(3,(THIS_FILE, 
 
1224
                          "Warning: --count value (%d) exceeds maximum "
 
1225
                          "transaction count (%d)", app.client.job_count,
 
1226
                          pjsip_cfg()->tsx.max_count));
 
1227
            break;
 
1228
 
 
1229
        case OPT_THREAD_COUNT:
 
1230
            app.thread_count = my_atoi(pj_optarg);
 
1231
            if (app.thread_count < 1 || app.thread_count > 16) {
 
1232
                PJ_LOG(3,(THIS_FILE, "Invalid --thread-count %s", pj_optarg));
 
1233
                return -1;
 
1234
            }
 
1235
            break;
 
1236
 
 
1237
        case 'm':
 
1238
            {
 
1239
                pj_str_t temp = pj_str((char*)pj_optarg);
 
1240
                pjsip_method_init_np(&app.client.method, &temp);
 
1241
            }
 
1242
            break;
 
1243
 
 
1244
        case 'h':
 
1245
            usage();
 
1246
            return -1;
 
1247
 
 
1248
        case 's':
 
1249
            app.client.stateless = PJ_TRUE;
 
1250
            break;
 
1251
 
 
1252
        case OPT_REAL_SDP:
 
1253
            app.real_sdp = 1;
 
1254
            break;
 
1255
 
 
1256
        case 'v':
 
1257
            app.log_level++;
 
1258
            break;
 
1259
 
 
1260
        case 't':
 
1261
            app.client.timeout = my_atoi(pj_optarg);
 
1262
            if (app.client.timeout < 0 || app.client.timeout > 600) {
 
1263
                PJ_LOG(3,(THIS_FILE, "Invalid --timeout %s", pj_optarg));
 
1264
                return -1;
 
1265
            }
 
1266
            break;
 
1267
 
 
1268
        case 'w':
 
1269
            app.client.job_window = my_atoi(pj_optarg);
 
1270
            if (app.client.job_window <= 0) {
 
1271
                PJ_LOG(3,(THIS_FILE, "Invalid --window %s", pj_optarg));
 
1272
                return -1;
 
1273
            }
 
1274
            break;
 
1275
 
 
1276
        case 'T':
 
1277
            app.use_tcp = PJ_TRUE;
 
1278
            break;
 
1279
 
 
1280
        case 'd':
 
1281
            app.server.delay = my_atoi(pj_optarg);
 
1282
            if (app.server.delay > 3600) {
 
1283
                PJ_LOG(3,(THIS_FILE, "I think --delay %s is too long", 
 
1284
                          pj_optarg));
 
1285
                return -1;
 
1286
            }
 
1287
            break;
 
1288
 
 
1289
        case OPT_TRYING:
 
1290
            app.server.send_trying = 1;
 
1291
            break;
 
1292
 
 
1293
        case OPT_RINGING:
 
1294
            app.server.send_ringing = 1;
 
1295
            break;
 
1296
 
 
1297
        default:
 
1298
            PJ_LOG(1,(THIS_FILE, 
 
1299
                      "Invalid argument. Use --help to see help"));
 
1300
            return -1;
 
1301
        }
 
1302
    }
 
1303
 
 
1304
    if (pj_optind != argc) {
 
1305
 
 
1306
        if (verify_sip_url(argv[pj_optind]) != PJ_SUCCESS) {
 
1307
            PJ_LOG(1,(THIS_FILE, "Invalid SIP URI %s", argv[pj_optind]));
 
1308
            return -1;
 
1309
        }
 
1310
        app.client.dst_uri = pj_str(argv[pj_optind]);
 
1311
        
 
1312
        pj_optind++;
 
1313
 
 
1314
    }
 
1315
 
 
1316
    if (pj_optind != argc) {
 
1317
        PJ_LOG(1,(THIS_FILE, "Error: unknown options %s", argv[pj_optind]));
 
1318
        return -1;
 
1319
    }
 
1320
 
 
1321
    return 0;
 
1322
}
 
1323
 
 
1324
 
 
1325
/* Send one stateless request */
 
1326
static pj_status_t submit_stateless_job(void)
 
1327
{
 
1328
    pjsip_tx_data *tdata;
 
1329
    pj_status_t status;
 
1330
 
 
1331
    status = pjsip_endpt_create_request(app.sip_endpt, &app.client.method, 
 
1332
                                        &app.client.dst_uri, &app.local_uri,
 
1333
                                        &app.client.dst_uri, &app.local_contact,
 
1334
                                        NULL, -1, NULL, &tdata);
 
1335
    if (status != PJ_SUCCESS) {
 
1336
        app_perror(THIS_FILE, "Error creating request", status);
 
1337
        report_completion(701);
 
1338
        return status;
 
1339
    }
 
1340
 
 
1341
    status = pjsip_endpt_send_request_stateless(app.sip_endpt, tdata, NULL,
 
1342
                                                NULL);
 
1343
    if (status != PJ_SUCCESS) {
 
1344
        pjsip_tx_data_dec_ref(tdata);
 
1345
        app_perror(THIS_FILE, "Error sending stateless request", status);
 
1346
        report_completion(701);
 
1347
        return status;
 
1348
    }
 
1349
 
 
1350
    return PJ_SUCCESS;
 
1351
}
 
1352
 
 
1353
 
 
1354
/* This callback is called when client transaction state has changed */
 
1355
static void tsx_completion_cb(void *token, pjsip_event *event)
 
1356
{
 
1357
    pjsip_transaction *tsx;
 
1358
 
 
1359
    PJ_UNUSED_ARG(token);
 
1360
 
 
1361
    if (event->type != PJSIP_EVENT_TSX_STATE)
 
1362
        return;
 
1363
 
 
1364
    tsx = event->body.tsx_state.tsx;
 
1365
 
 
1366
    if (tsx->mod_data[mod_test.id] != NULL) {
 
1367
        /* This transaction has been calculated before */
 
1368
        return;
 
1369
    }
 
1370
 
 
1371
    if (tsx->state==PJSIP_TSX_STATE_TERMINATED) {
 
1372
        report_completion(tsx->status_code);
 
1373
        tsx->mod_data[mod_test.id] = (void*)1;
 
1374
    }
 
1375
    else if (tsx->method.id == PJSIP_INVITE_METHOD &&
 
1376
             tsx->state == PJSIP_TSX_STATE_CONFIRMED) {
 
1377
 
 
1378
        report_completion(tsx->status_code);
 
1379
        tsx->mod_data[mod_test.id] = (void*)1;
 
1380
        
 
1381
    } else if (tsx->state == PJSIP_TSX_STATE_COMPLETED) {
 
1382
 
 
1383
        report_completion(tsx->status_code);
 
1384
        tsx->mod_data[mod_test.id] = (void*)1;
 
1385
 
 
1386
        TERMINATE_TSX(tsx, tsx->status_code);
 
1387
    }
 
1388
}
 
1389
 
 
1390
 
 
1391
/* Send one stateful request */
 
1392
static pj_status_t submit_job(void)
 
1393
{
 
1394
    pjsip_tx_data *tdata;
 
1395
    pj_status_t status;
 
1396
 
 
1397
    status = pjsip_endpt_create_request(app.sip_endpt, &app.client.method, 
 
1398
                                        &app.client.dst_uri, &app.local_uri,
 
1399
                                        &app.client.dst_uri, &app.local_contact,
 
1400
                                        NULL, -1, NULL, &tdata);
 
1401
    if (status != PJ_SUCCESS) {
 
1402
        app_perror(THIS_FILE, "Error creating request", status);
 
1403
        report_completion(701);
 
1404
        return status;
 
1405
    }
 
1406
 
 
1407
    status = pjsip_endpt_send_request(app.sip_endpt, tdata, -1, NULL, 
 
1408
                                      &tsx_completion_cb);
 
1409
    if (status != PJ_SUCCESS) {
 
1410
        app_perror(THIS_FILE, "Error sending stateful request", status);
 
1411
        //should have been reported by tsx_completion_cb().
 
1412
        //report_completion(701);
 
1413
        //No longer necessary (r777)
 
1414
        //pjsip_tx_data_dec_ref(tdata);
 
1415
    }
 
1416
    return status;
 
1417
}
 
1418
 
 
1419
 
 
1420
/* Client worker thread */
 
1421
static int client_thread(void *arg)
 
1422
{
 
1423
    pj_time_val end_time, last_report, now;
 
1424
    unsigned thread_index = (unsigned)(long)arg;
 
1425
    unsigned cycle = 0, last_cycle = 0;
 
1426
 
 
1427
    pj_thread_sleep(100);
 
1428
 
 
1429
    pj_gettimeofday(&end_time);
 
1430
    end_time.sec += app.client.timeout;
 
1431
 
 
1432
    pj_gettimeofday(&last_report);
 
1433
 
 
1434
    if (app.client.first_request.sec == 0) {
 
1435
        pj_gettimeofday(&app.client.first_request);
 
1436
    }
 
1437
 
 
1438
    /* Submit all jobs */
 
1439
    while (app.client.job_submitted < app.client.job_count && !app.thread_quit){
 
1440
        pj_time_val timeout = { 0, 1 };
 
1441
        unsigned i;
 
1442
        int outstanding;
 
1443
        pj_status_t status;
 
1444
 
 
1445
        /* Calculate current outstanding job */
 
1446
        outstanding = app.client.job_submitted - app.client.job_finished;
 
1447
 
 
1448
        /* Update stats on max outstanding jobs */
 
1449
        if (outstanding > (int)app.client.stat_max_window)
 
1450
            app.client.stat_max_window = outstanding;
 
1451
 
 
1452
        /* Wait if there are more pending jobs than allowed in the
 
1453
         * window. But spawn a new job anyway if no events are happening
 
1454
         * after we wait for some time.
 
1455
         */
 
1456
        for (i=0; outstanding > (int)app.client.job_window && i<1000; ++i) {
 
1457
            pj_time_val wait = { 0, 500 };
 
1458
            unsigned count = 0;
 
1459
 
 
1460
            pjsip_endpt_handle_events2(app.sip_endpt, &wait, &count);
 
1461
            outstanding = app.client.job_submitted - app.client.job_finished;
 
1462
 
 
1463
            if (count == 0)
 
1464
                break;
 
1465
 
 
1466
            ++cycle;
 
1467
        }
 
1468
 
 
1469
 
 
1470
        /* Submit one job */
 
1471
        if (app.client.method.id == PJSIP_INVITE_METHOD) {
 
1472
            status = make_call(&app.client.dst_uri);
 
1473
        } else if (app.client.stateless) {
 
1474
            status = submit_stateless_job();
 
1475
        } else {
 
1476
            status = submit_job();
 
1477
        }
 
1478
 
 
1479
        ++app.client.job_submitted;
 
1480
        ++cycle;
 
1481
 
 
1482
        /* Handle event */
 
1483
        pjsip_endpt_handle_events2(app.sip_endpt, &timeout, NULL);
 
1484
 
 
1485
        /* Check for time out, also print report */
 
1486
        if (cycle - last_cycle >= 500) {
 
1487
            pj_gettimeofday(&now);
 
1488
            if (PJ_TIME_VAL_GTE(now, end_time)) {
 
1489
                break;
 
1490
            }
 
1491
            last_cycle = cycle;
 
1492
 
 
1493
            
 
1494
            if (thread_index == 0 && now.sec-last_report.sec >= 2) {
 
1495
                printf("\r%d jobs started, %d completed...   ",
 
1496
                       app.client.job_submitted, app.client.job_finished);
 
1497
                fflush(stdout);
 
1498
                last_report = now;
 
1499
            }
 
1500
        }
 
1501
    }
 
1502
 
 
1503
    if (app.client.requests_sent.sec == 0) {
 
1504
        pj_gettimeofday(&app.client.requests_sent);
 
1505
    }
 
1506
 
 
1507
 
 
1508
    if (thread_index == 0) {
 
1509
        printf("\r%d jobs started, %d completed%s\n",
 
1510
               app.client.job_submitted, app.client.job_finished,
 
1511
               (app.client.job_submitted!=app.client.job_finished ? 
 
1512
                ", waiting..." : ".") );
 
1513
        fflush(stdout);
 
1514
    }
 
1515
 
 
1516
    /* Wait until all jobs completes, or timed out */
 
1517
    pj_gettimeofday(&now);
 
1518
    while (PJ_TIME_VAL_LT(now, end_time) && 
 
1519
           app.client.job_finished < app.client.job_count && 
 
1520
           !app.thread_quit) 
 
1521
    {
 
1522
        pj_time_val timeout = { 0, 1 };
 
1523
        unsigned i;
 
1524
 
 
1525
        for (i=0; i<1000; ++i) {
 
1526
            unsigned count;
 
1527
            count = 0;
 
1528
            pjsip_endpt_handle_events2(app.sip_endpt, &timeout, &count);
 
1529
            if (count == 0)
 
1530
                break;
 
1531
        }
 
1532
 
 
1533
        pj_gettimeofday(&now);
 
1534
    }
 
1535
 
 
1536
    /* Wait couple of seconds to let jobs completes (e.g. ACKs to be sent)  */
 
1537
    pj_gettimeofday(&now);
 
1538
    end_time = now;
 
1539
    end_time.sec += 2;
 
1540
    while (PJ_TIME_VAL_LT(now, end_time)) 
 
1541
    {
 
1542
        pj_time_val timeout = { 0, 1 };
 
1543
        unsigned i;
 
1544
 
 
1545
        for (i=0; i<1000; ++i) {
 
1546
            unsigned count;
 
1547
            count = 0;
 
1548
            pjsip_endpt_handle_events2(app.sip_endpt, &timeout, &count);
 
1549
            if (count == 0)
 
1550
                break;
 
1551
        }
 
1552
 
 
1553
        pj_gettimeofday(&now);
 
1554
    }
 
1555
 
 
1556
    return 0;
 
1557
}
 
1558
 
 
1559
 
 
1560
static const char *good_number(char *buf, pj_int32_t val)
 
1561
{
 
1562
    if (val < 1000) {
 
1563
        pj_ansi_sprintf(buf, "%d", val);
 
1564
    } else if (val < 1000000) {
 
1565
        pj_ansi_sprintf(buf, "%d.%dK", 
 
1566
                        val / 1000,
 
1567
                        (val % 1000) / 100);
 
1568
    } else {
 
1569
        pj_ansi_sprintf(buf, "%d.%02dM", 
 
1570
                        val / 1000000,
 
1571
                        (val % 1000000) / 10000);
 
1572
    }
 
1573
 
 
1574
    return buf;
 
1575
}
 
1576
 
 
1577
 
 
1578
static int server_thread(void *arg)
 
1579
{
 
1580
    pj_time_val timeout = { 0, 1 };
 
1581
    unsigned thread_index = (unsigned)(long)arg;
 
1582
    pj_time_val last_report, next_report;
 
1583
 
 
1584
    pj_gettimeofday(&last_report);
 
1585
    next_report = last_report;
 
1586
    next_report.sec++;
 
1587
 
 
1588
    while (!app.thread_quit) {
 
1589
        pj_time_val now;
 
1590
        unsigned i;
 
1591
 
 
1592
        for (i=0; i<100; ++i) {
 
1593
            unsigned count = 0;
 
1594
            pjsip_endpt_handle_events2(app.sip_endpt, &timeout, &count);
 
1595
            if (count == 0)
 
1596
                break;
 
1597
        }
 
1598
 
 
1599
        if (thread_index == 0) {
 
1600
            pj_gettimeofday(&now);
 
1601
 
 
1602
            if (PJ_TIME_VAL_GTE(now, next_report)) {
 
1603
                pj_time_val tmp;
 
1604
                unsigned msec;
 
1605
                unsigned stateless, stateful, call;
 
1606
                char str_stateless[32], str_stateful[32], str_call[32];
 
1607
 
 
1608
                tmp = now;
 
1609
                PJ_TIME_VAL_SUB(tmp, last_report);
 
1610
                msec = PJ_TIME_VAL_MSEC(tmp);
 
1611
 
 
1612
                last_report = now;
 
1613
                next_report = last_report;
 
1614
                next_report.sec++;
 
1615
 
 
1616
                stateless = app.server.cur_state.stateless_cnt - app.server.prev_state.stateless_cnt;
 
1617
                stateful = app.server.cur_state.stateful_cnt - app.server.prev_state.stateful_cnt;
 
1618
                call = app.server.cur_state.call_cnt - app.server.prev_state.call_cnt;
 
1619
 
 
1620
                good_number(str_stateless, app.server.cur_state.stateless_cnt);
 
1621
                good_number(str_stateful, app.server.cur_state.stateful_cnt);
 
1622
                good_number(str_call, app.server.cur_state.call_cnt);
 
1623
 
 
1624
                printf("Total(rate): stateless:%s (%d/s), statefull:%s (%d/s), call:%s (%d/s)       \r",
 
1625
                       str_stateless, stateless*1000/msec,
 
1626
                       str_stateful, stateful*1000/msec,
 
1627
                       str_call, call*1000/msec);
 
1628
                fflush(stdout);
 
1629
 
 
1630
                app.server.prev_state = app.server.cur_state;
 
1631
            }
 
1632
        }
 
1633
    }
 
1634
 
 
1635
    return 0;
 
1636
}
 
1637
 
 
1638
static void write_report(const char *msg)
 
1639
{
 
1640
    puts(msg);
 
1641
 
 
1642
#if defined(PJ_WIN32) && PJ_WIN32!=0
 
1643
    OutputDebugString(msg);
 
1644
    OutputDebugString("\n");
 
1645
#endif
 
1646
}
 
1647
 
 
1648
 
 
1649
int main(int argc, char *argv[])
 
1650
{
 
1651
    static char report[1024];
 
1652
 
 
1653
    printf("PJSIP Performance Measurement Tool v%s\n"
 
1654
           "(c)2006 pjsip.org\n\n",
 
1655
           PJ_VERSION);
 
1656
 
 
1657
    if (create_app() != 0)
 
1658
        return 1;
 
1659
 
 
1660
    if (init_options(argc, argv) != 0)
 
1661
        return 1;
 
1662
 
 
1663
    if (init_sip() != 0)
 
1664
        return 1;
 
1665
 
 
1666
    if (init_media() != 0)
 
1667
        return 1;
 
1668
 
 
1669
    pj_log_set_level(app.log_level);
 
1670
 
 
1671
    if (app.log_level > 4) {
 
1672
        pjsip_endpt_register_module(app.sip_endpt, &msg_logger);
 
1673
    }
 
1674
 
 
1675
 
 
1676
    /* Misc infos */
 
1677
    if (app.client.dst_uri.slen != 0) {
 
1678
        if (app.client.method.id == PJSIP_INVITE_METHOD) {
 
1679
            if (app.client.stateless) {
 
1680
                PJ_LOG(3,(THIS_FILE, 
 
1681
                          "Info: --stateless option makes no sense for INVITE,"
 
1682
                          " ignored."));
 
1683
            }
 
1684
        }
 
1685
 
 
1686
    }
 
1687
 
 
1688
 
 
1689
 
 
1690
    if (app.client.dst_uri.slen) {
 
1691
        /* Client mode */
 
1692
        pj_status_t status;
 
1693
        char test_type[64];
 
1694
        unsigned msec_req, msec_res;
 
1695
        unsigned i;
 
1696
 
 
1697
        /* Get the job name */
 
1698
        if (app.client.method.id == PJSIP_INVITE_METHOD) {
 
1699
            pj_ansi_strcpy(test_type, "INVITE calls");
 
1700
        } else if (app.client.stateless) {
 
1701
            pj_ansi_sprintf(test_type, "stateless %.*s requests",
 
1702
                            (int)app.client.method.name.slen,
 
1703
                            app.client.method.name.ptr);
 
1704
        } else {
 
1705
            pj_ansi_sprintf(test_type, "stateful %.*s requests",
 
1706
                            (int)app.client.method.name.slen,
 
1707
                            app.client.method.name.ptr);
 
1708
        }
 
1709
        
 
1710
 
 
1711
        printf("Sending %d %s to '%.*s' with %d maximum outstanding jobs, please wait..\n", 
 
1712
                  app.client.job_count, test_type,
 
1713
                  (int)app.client.dst_uri.slen, app.client.dst_uri.ptr,
 
1714
                  app.client.job_window);
 
1715
 
 
1716
        for (i=0; i<app.thread_count; ++i) {
 
1717
            status = pj_thread_create(app.pool, NULL, &client_thread, 
 
1718
                                      (void*)(long)i, 0, 0, &app.thread[i]);
 
1719
            if (status != PJ_SUCCESS) {
 
1720
                app_perror(THIS_FILE, "Unable to create thread", status);
 
1721
                return 1;
 
1722
            }
 
1723
        }
 
1724
 
 
1725
        for (i=0; i<app.thread_count; ++i) {
 
1726
            pj_thread_join(app.thread[i]);
 
1727
            app.thread[i] = NULL;
 
1728
        }
 
1729
 
 
1730
        if (app.client.last_completion.sec) {
 
1731
            pj_time_val duration;
 
1732
            duration = app.client.last_completion;
 
1733
            PJ_TIME_VAL_SUB(duration, app.client.first_request);
 
1734
            msec_res = PJ_TIME_VAL_MSEC(duration);
 
1735
        } else {
 
1736
            msec_res = app.client.timeout * 1000;
 
1737
        }
 
1738
 
 
1739
        if (msec_res == 0) msec_res = 1;
 
1740
 
 
1741
        if (app.client.requests_sent.sec) {
 
1742
            pj_time_val duration;
 
1743
            duration = app.client.requests_sent;
 
1744
            PJ_TIME_VAL_SUB(duration, app.client.first_request);
 
1745
            msec_req = PJ_TIME_VAL_MSEC(duration);
 
1746
        } else {
 
1747
            msec_req = app.client.timeout * 1000;
 
1748
        }
 
1749
 
 
1750
        if (msec_req == 0) msec_req = 1;
 
1751
 
 
1752
        if (app.client.job_submitted < app.client.job_count)
 
1753
            puts("\ntimed-out!\n");
 
1754
        else
 
1755
            puts("\ndone.\n");
 
1756
 
 
1757
        pj_ansi_snprintf(
 
1758
            report, sizeof(report),
 
1759
            "Total %d %s sent in %d ms at rate of %d/sec\n"
 
1760
            "Total %d responses receieved in %d ms at rate of %d/sec:",
 
1761
            app.client.job_submitted, test_type, msec_req, 
 
1762
            app.client.job_submitted * 1000 / msec_req,
 
1763
            app.client.total_responses, msec_res,
 
1764
            app.client.total_responses*1000/msec_res);
 
1765
        write_report(report);
 
1766
 
 
1767
        /* Print detailed response code received */
 
1768
        pj_ansi_sprintf(report, "\nDetailed responses received:");
 
1769
        write_report(report);
 
1770
 
 
1771
        for (i=0; i<PJ_ARRAY_SIZE(app.client.response_codes); ++i) {
 
1772
            const pj_str_t *reason;
 
1773
 
 
1774
            if (app.client.response_codes[i] == 0)
 
1775
                continue;
 
1776
 
 
1777
            reason = pjsip_get_status_text(i);
 
1778
            pj_ansi_snprintf( report, sizeof(report),
 
1779
                              " - %d responses:  %7d     (%.*s)",
 
1780
                              i, app.client.response_codes[i],
 
1781
                              (int)reason->slen, reason->ptr);
 
1782
            write_report(report);
 
1783
        }
 
1784
 
 
1785
        /* Total responses and rate */
 
1786
        pj_ansi_snprintf( report, sizeof(report),
 
1787
            "                    ------\n"
 
1788
            " TOTAL responses:  %7d (rate=%d/sec)\n",
 
1789
            app.client.total_responses, 
 
1790
            app.client.total_responses*1000/msec_res);
 
1791
 
 
1792
        write_report(report);
 
1793
 
 
1794
        pj_ansi_sprintf(report, "Maximum outstanding job: %d", 
 
1795
                        app.client.stat_max_window);
 
1796
        write_report(report);
 
1797
 
 
1798
 
 
1799
    } else {
 
1800
        /* Server mode */
 
1801
        char s[10], *unused;
 
1802
        pj_status_t status;
 
1803
        unsigned i;
 
1804
 
 
1805
        puts("pjsip-perf started in server-mode");
 
1806
 
 
1807
        printf("Receiving requests on the following URIs:\n"
 
1808
               "  sip:0@%.*s:%d%s    for stateless handling\n"
 
1809
               "  sip:1@%.*s:%d%s    for stateful handling\n"
 
1810
               "  sip:2@%.*s:%d%s    for call handling\n",
 
1811
               (int)app.local_addr.slen,
 
1812
               app.local_addr.ptr,
 
1813
               app.local_port,
 
1814
               (app.use_tcp ? ";transport=tcp" : ""),
 
1815
               (int)app.local_addr.slen,
 
1816
               app.local_addr.ptr,
 
1817
               app.local_port,
 
1818
               (app.use_tcp ? ";transport=tcp" : ""),
 
1819
               (int)app.local_addr.slen,
 
1820
               app.local_addr.ptr,
 
1821
               app.local_port,
 
1822
               (app.use_tcp ? ";transport=tcp" : ""));
 
1823
        printf("INVITE with non-matching user part will be handled call-statefully\n");
 
1824
 
 
1825
        for (i=0; i<app.thread_count; ++i) {
 
1826
            status = pj_thread_create(app.pool, NULL, &server_thread, 
 
1827
                                      (void*)(long)i, 0, 0, &app.thread[i]);
 
1828
            if (status != PJ_SUCCESS) {
 
1829
                app_perror(THIS_FILE, "Unable to create thread", status);
 
1830
                return 1;
 
1831
            }
 
1832
        }
 
1833
 
 
1834
        puts("\nPress <ENTER> to quit\n");
 
1835
        fflush(stdout);
 
1836
        unused = fgets(s, sizeof(s), stdin);
 
1837
        PJ_UNUSED_ARG(unused);
 
1838
 
 
1839
        app.thread_quit = PJ_TRUE;
 
1840
        for (i=0; i<app.thread_count; ++i) {
 
1841
            pj_thread_join(app.thread[i]);
 
1842
            app.thread[i] = NULL;
 
1843
        }
 
1844
 
 
1845
        puts("");
 
1846
    }
 
1847
 
 
1848
 
 
1849
    destroy_app();
 
1850
 
 
1851
    return 0;
 
1852
}
 
1853