~ubuntu-branches/ubuntu/trusty/sflphone/trusty

« back to all changes in this revision

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

  • Committer: Package Import Robot
  • Author(s): Mark Purcell
  • Date: 2014-01-28 18:23:36 UTC
  • mfrom: (4.3.4 sid)
  • Revision ID: package-import@ubuntu.com-20140128182336-jrsv0k9u6cawc068
Tags: 1.3.0-1
* 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: siprtp.c 3553 2011-05-05 06:14:19Z 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
 
 
24
 
 
25
/* Usage */
 
26
static const char *USAGE = 
 
27
" PURPOSE:                                                                  \n"
 
28
"   This program establishes SIP INVITE session and media, and calculate    \n"
 
29
"   the media quality (packet lost, jitter, rtt, etc.). Unlike normal       \n"
 
30
"   pjmedia applications, this program bypasses all pjmedia stream          \n"
 
31
"   framework and transmit encoded RTP packets manually using own thread.   \n"
 
32
"\n"
 
33
" USAGE:\n"
 
34
"   siprtp [options]        => to start in server mode\n"
 
35
"   siprtp [options] URL    => to start in client mode\n"
 
36
"\n"
 
37
" Program options:\n"
 
38
"   --count=N,        -c    Set number of calls to create (default:1) \n"
 
39
"   --gap=N           -g    Set call gapping to N msec (default:0)\n"
 
40
"   --duration=SEC,   -d    Set maximum call duration (default:unlimited) \n"
 
41
"   --auto-quit,      -q    Quit when calls have been completed (default:no)\n"
 
42
"   --call-report     -R    Display report on call termination (default:yes)\n"
 
43
"\n"
 
44
" Address and ports options:\n"
 
45
"   --local-port=PORT,-p    Set local SIP port (default: 5060)\n"
 
46
"   --rtp-port=PORT,  -r    Set start of RTP port (default: 4000)\n"
 
47
"   --ip-addr=IP,     -i    Set local IP address to use (otherwise it will\n"
 
48
"                           try to determine local IP address from hostname)\n"
 
49
"\n"
 
50
" Logging Options:\n"
 
51
"   --log-level=N,    -l    Set log verbosity level (default=5)\n"
 
52
"   --app-log-level=N       Set app screen log verbosity (default=3)\n"
 
53
"   --log-file=FILE         Write log to file FILE\n"
 
54
"   --report-file=FILE      Write report to file FILE\n"
 
55
"\n"
 
56
/* Don't support this anymore, because codec is properly examined in
 
57
   pjmedia_session_info_from_sdp() function.
 
58
 
 
59
" Codec Options:\n"
 
60
"   --a-pt=PT               Set audio payload type to PT (default=0)\n"
 
61
"   --a-name=NAME           Set audio codec name to NAME (default=pcmu)\n"
 
62
"   --a-clock=RATE          Set audio codec rate to RATE Hz (default=8000Hz)\n"
 
63
"   --a-bitrate=BPS         Set audio codec bitrate to BPS (default=64000bps)\n"
 
64
"   --a-ptime=MS            Set audio frame time to MS msec (default=20ms)\n"
 
65
*/
 
66
;
 
67
 
 
68
 
 
69
/* Include all headers. */
 
70
#include <pjsip.h>
 
71
#include <pjmedia.h>
 
72
#include <pjmedia-codec.h>
 
73
#include <pjsip_ua.h>
 
74
#include <pjsip_simple.h>
 
75
#include <pjlib-util.h>
 
76
#include <pjlib.h>
 
77
 
 
78
#include <stdlib.h>
 
79
 
 
80
/* Uncomment these to disable threads.
 
81
 * NOTE:
 
82
 *   when threading is disabled, siprtp won't transmit any
 
83
 *   RTP packets.
 
84
 */
 
85
/*
 
86
#undef PJ_HAS_THREADS
 
87
#define PJ_HAS_THREADS 0
 
88
*/
 
89
 
 
90
 
 
91
#if PJ_HAS_HIGH_RES_TIMER==0
 
92
#   error "High resolution timer is needed for this sample"
 
93
#endif
 
94
 
 
95
#define THIS_FILE       "siprtp.c"
 
96
#define MAX_CALLS       1024
 
97
#define RTP_START_PORT  4000
 
98
 
 
99
 
 
100
/* Codec descriptor: */
 
101
struct codec
 
102
{
 
103
    unsigned    pt;
 
104
    char*       name;
 
105
    unsigned    clock_rate;
 
106
    unsigned    bit_rate;
 
107
    unsigned    ptime;
 
108
    char*       description;
 
109
};
 
110
 
 
111
 
 
112
/* A bidirectional media stream created when the call is active. */
 
113
struct media_stream
 
114
{
 
115
    /* Static: */
 
116
    unsigned             call_index;        /* Call owner.              */
 
117
    unsigned             media_index;       /* Media index in call.     */
 
118
    pjmedia_transport   *transport;         /* To send/recv RTP/RTCP    */
 
119
 
 
120
    /* Active? */
 
121
    pj_bool_t            active;            /* Non-zero if is in call.  */
 
122
 
 
123
    /* Current stream info: */
 
124
    pjmedia_stream_info  si;                /* Current stream info.     */
 
125
 
 
126
    /* More info: */
 
127
    unsigned             clock_rate;        /* clock rate               */
 
128
    unsigned             samples_per_frame; /* samples per frame        */
 
129
    unsigned             bytes_per_frame;   /* frame size.              */
 
130
 
 
131
    /* RTP session: */
 
132
    pjmedia_rtp_session  out_sess;          /* outgoing RTP session     */
 
133
    pjmedia_rtp_session  in_sess;           /* incoming RTP session     */
 
134
 
 
135
    /* RTCP stats: */
 
136
    pjmedia_rtcp_session rtcp;              /* incoming RTCP session.   */
 
137
 
 
138
    /* Thread: */
 
139
    pj_bool_t            thread_quit_flag;  /* Stop media thread.       */
 
140
    pj_thread_t         *thread;            /* Media thread.            */
 
141
};
 
142
 
 
143
 
 
144
/* This is a call structure that is created when the application starts
 
145
 * and only destroyed when the application quits.
 
146
 */
 
147
struct call
 
148
{
 
149
    unsigned             index;
 
150
    pjsip_inv_session   *inv;
 
151
    unsigned             media_count;
 
152
    struct media_stream  media[1];
 
153
    pj_time_val          start_time;
 
154
    pj_time_val          response_time;
 
155
    pj_time_val          connect_time;
 
156
 
 
157
    pj_timer_entry       d_timer;           /**< Disconnect timer.      */
 
158
};
 
159
 
 
160
 
 
161
/* Application's global variables */
 
162
static struct app
 
163
{
 
164
    unsigned             max_calls;
 
165
    unsigned             call_gap;
 
166
    pj_bool_t            call_report;
 
167
    unsigned             uac_calls;
 
168
    unsigned             duration;
 
169
    pj_bool_t            auto_quit;
 
170
    unsigned             thread_count;
 
171
    int                  sip_port;
 
172
    int                  rtp_start_port;
 
173
    pj_str_t             local_addr;
 
174
    pj_str_t             local_uri;
 
175
    pj_str_t             local_contact;
 
176
    
 
177
    int                  app_log_level;
 
178
    int                  log_level;
 
179
    char                *log_filename;
 
180
    char                *report_filename;
 
181
 
 
182
    struct codec         audio_codec;
 
183
 
 
184
    pj_str_t             uri_to_call;
 
185
 
 
186
    pj_caching_pool      cp;
 
187
    pj_pool_t           *pool;
 
188
 
 
189
    pjsip_endpoint      *sip_endpt;
 
190
    pj_bool_t            thread_quit;
 
191
    pj_thread_t         *sip_thread[1];
 
192
 
 
193
    pjmedia_endpt       *med_endpt;
 
194
    struct call          call[MAX_CALLS];
 
195
} app;
 
196
 
 
197
 
 
198
 
 
199
/*
 
200
 * Prototypes:
 
201
 */
 
202
 
 
203
/* Callback to be called when SDP negotiation is done in the call: */
 
204
static void call_on_media_update( pjsip_inv_session *inv,
 
205
                                  pj_status_t status);
 
206
 
 
207
/* Callback to be called when invite session's state has changed: */
 
208
static void call_on_state_changed( pjsip_inv_session *inv, 
 
209
                                   pjsip_event *e);
 
210
 
 
211
/* Callback to be called when dialog has forked: */
 
212
static void call_on_forked(pjsip_inv_session *inv, pjsip_event *e);
 
213
 
 
214
/* Callback to be called to handle incoming requests outside dialogs: */
 
215
static pj_bool_t on_rx_request( pjsip_rx_data *rdata );
 
216
 
 
217
/* Worker thread prototype */
 
218
static int sip_worker_thread(void *arg);
 
219
 
 
220
/* Create SDP for call */
 
221
static pj_status_t create_sdp( pj_pool_t *pool,
 
222
                               struct call *call,
 
223
                               pjmedia_sdp_session **p_sdp);
 
224
 
 
225
/* Hangup call */
 
226
static void hangup_call(unsigned index);
 
227
 
 
228
/* Destroy the call's media */
 
229
static void destroy_call_media(unsigned call_index);
 
230
 
 
231
/* Destroy media. */
 
232
static void destroy_media();
 
233
 
 
234
/* This callback is called by media transport on receipt of RTP packet. */
 
235
static void on_rx_rtp(void *user_data, void *pkt, pj_ssize_t size);
 
236
 
 
237
/* This callback is called by media transport on receipt of RTCP packet. */
 
238
static void on_rx_rtcp(void *user_data, void *pkt, pj_ssize_t size);
 
239
 
 
240
/* Display error */
 
241
static void app_perror(const char *sender, const char *title, 
 
242
                       pj_status_t status);
 
243
 
 
244
/* Print call */
 
245
static void print_call(int call_index);
 
246
 
 
247
 
 
248
/* This is a PJSIP module to be registered by application to handle
 
249
 * incoming requests outside any dialogs/transactions. The main purpose
 
250
 * here is to handle incoming INVITE request message, where we will
 
251
 * create a dialog and INVITE session for it.
 
252
 */
 
253
static pjsip_module mod_siprtp =
 
254
{
 
255
    NULL, NULL,                     /* prev, next.              */
 
256
    { "mod-siprtpapp", 13 },        /* Name.                    */
 
257
    -1,                             /* Id                       */
 
258
    PJSIP_MOD_PRIORITY_APPLICATION, /* Priority                 */
 
259
    NULL,                           /* load()                   */
 
260
    NULL,                           /* start()                  */
 
261
    NULL,                           /* stop()                   */
 
262
    NULL,                           /* unload()                 */
 
263
    &on_rx_request,                 /* on_rx_request()          */
 
264
    NULL,                           /* on_rx_response()         */
 
265
    NULL,                           /* on_tx_request.           */
 
266
    NULL,                           /* on_tx_response()         */
 
267
    NULL,                           /* on_tsx_state()           */
 
268
};
 
269
 
 
270
 
 
271
/* Codec constants */
 
272
struct codec audio_codecs[] = 
 
273
{
 
274
    { 0,  "PCMU", 8000, 64000, 20, "G.711 ULaw" },
 
275
    { 3,  "GSM",  8000, 13200, 20, "GSM" },
 
276
    { 4,  "G723", 8000, 6400,  30, "G.723.1" },
 
277
    { 8,  "PCMA", 8000, 64000, 20, "G.711 ALaw" },
 
278
    { 18, "G729", 8000, 8000,  20, "G.729" },
 
279
};
 
280
 
 
281
 
 
282
/*
 
283
 * Init SIP stack
 
284
 */
 
285
static pj_status_t init_sip()
 
286
{
 
287
    unsigned i;
 
288
    pj_status_t status;
 
289
 
 
290
    /* init PJLIB-UTIL: */
 
291
    status = pjlib_util_init();
 
292
    PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
 
293
 
 
294
    /* Must create a pool factory before we can allocate any memory. */
 
295
    pj_caching_pool_init(&app.cp, &pj_pool_factory_default_policy, 0);
 
296
 
 
297
    /* Create application pool for misc. */
 
298
    app.pool = pj_pool_create(&app.cp.factory, "app", 1000, 1000, NULL);
 
299
 
 
300
    /* Create the endpoint: */
 
301
    status = pjsip_endpt_create(&app.cp.factory, pj_gethostname()->ptr, 
 
302
                                &app.sip_endpt);
 
303
    PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
 
304
 
 
305
 
 
306
    /* Add UDP transport. */
 
307
    {
 
308
        pj_sockaddr_in addr;
 
309
        pjsip_host_port addrname;
 
310
        pjsip_transport *tp;
 
311
 
 
312
        pj_bzero(&addr, sizeof(addr));
 
313
        addr.sin_family = pj_AF_INET();
 
314
        addr.sin_addr.s_addr = 0;
 
315
        addr.sin_port = pj_htons((pj_uint16_t)app.sip_port);
 
316
 
 
317
        if (app.local_addr.slen) {
 
318
 
 
319
            addrname.host = app.local_addr;
 
320
            addrname.port = app.sip_port;
 
321
 
 
322
            status = pj_sockaddr_in_init(&addr, &app.local_addr, 
 
323
                                         (pj_uint16_t)app.sip_port);
 
324
            if (status != PJ_SUCCESS) {
 
325
                app_perror(THIS_FILE, "Unable to resolve IP interface", status);
 
326
                return status;
 
327
            }
 
328
        }
 
329
 
 
330
        status = pjsip_udp_transport_start( app.sip_endpt, &addr, 
 
331
                                            (app.local_addr.slen ? &addrname:NULL),
 
332
                                            1, &tp);
 
333
        if (status != PJ_SUCCESS) {
 
334
            app_perror(THIS_FILE, "Unable to start UDP transport", status);
 
335
            return status;
 
336
        }
 
337
 
 
338
        PJ_LOG(3,(THIS_FILE, "SIP UDP listening on %.*s:%d",
 
339
                  (int)tp->local_name.host.slen, tp->local_name.host.ptr,
 
340
                  tp->local_name.port));
 
341
    }
 
342
 
 
343
    /* 
 
344
     * Init transaction layer.
 
345
     * This will create/initialize transaction hash tables etc.
 
346
     */
 
347
    status = pjsip_tsx_layer_init_module(app.sip_endpt);
 
348
    PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
 
349
 
 
350
    /*  Initialize UA layer. */
 
351
    status = pjsip_ua_init_module( app.sip_endpt, NULL );
 
352
    PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
 
353
 
 
354
    /* Initialize 100rel support */
 
355
    status = pjsip_100rel_init_module(app.sip_endpt);
 
356
    PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
 
357
 
 
358
    /*  Init invite session module. */
 
359
    {
 
360
        pjsip_inv_callback inv_cb;
 
361
 
 
362
        /* Init the callback for INVITE session: */
 
363
        pj_bzero(&inv_cb, sizeof(inv_cb));
 
364
        inv_cb.on_state_changed = &call_on_state_changed;
 
365
        inv_cb.on_new_session = &call_on_forked;
 
366
        inv_cb.on_media_update = &call_on_media_update;
 
367
 
 
368
        /* Initialize invite session module:  */
 
369
        status = pjsip_inv_usage_init(app.sip_endpt, &inv_cb);
 
370
        PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
 
371
    }
 
372
 
 
373
    /* Register our module to receive incoming requests. */
 
374
    status = pjsip_endpt_register_module( app.sip_endpt, &mod_siprtp);
 
375
    PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
 
376
 
 
377
    /* Init calls */
 
378
    for (i=0; i<app.max_calls; ++i)
 
379
        app.call[i].index = i;
 
380
 
 
381
    /* Done */
 
382
    return PJ_SUCCESS;
 
383
}
 
384
 
 
385
 
 
386
/*
 
387
 * Destroy SIP
 
388
 */
 
389
static void destroy_sip()
 
390
{
 
391
    unsigned i;
 
392
 
 
393
    app.thread_quit = 1;
 
394
    for (i=0; i<app.thread_count; ++i) {
 
395
        if (app.sip_thread[i]) {
 
396
            pj_thread_join(app.sip_thread[i]);
 
397
            pj_thread_destroy(app.sip_thread[i]);
 
398
            app.sip_thread[i] = NULL;
 
399
        }
 
400
    }
 
401
 
 
402
    if (app.sip_endpt) {
 
403
        pjsip_endpt_destroy(app.sip_endpt);
 
404
        app.sip_endpt = NULL;
 
405
    }
 
406
 
 
407
}
 
408
 
 
409
 
 
410
/*
 
411
 * Init media stack.
 
412
 */
 
413
static pj_status_t init_media()
 
414
{
 
415
    unsigned    i, count;
 
416
    pj_uint16_t rtp_port;
 
417
    pj_status_t status;
 
418
 
 
419
 
 
420
    /* Initialize media endpoint so that at least error subsystem is properly
 
421
     * initialized.
 
422
     */
 
423
#if PJ_HAS_THREADS
 
424
    status = pjmedia_endpt_create(&app.cp.factory, NULL, 1, &app.med_endpt);
 
425
#else
 
426
    status = pjmedia_endpt_create(&app.cp.factory, 
 
427
                                  pjsip_endpt_get_ioqueue(app.sip_endpt),
 
428
                                  0, &app.med_endpt);
 
429
#endif
 
430
    PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
 
431
 
 
432
 
 
433
    /* Must register codecs to be supported */
 
434
#if defined(PJMEDIA_HAS_G711_CODEC) && PJMEDIA_HAS_G711_CODEC!=0
 
435
    pjmedia_codec_g711_init(app.med_endpt);
 
436
#endif
 
437
 
 
438
    /* RTP port counter */
 
439
    rtp_port = (pj_uint16_t)(app.rtp_start_port & 0xFFFE);
 
440
 
 
441
    /* Init media transport for all calls. */
 
442
    for (i=0, count=0; i<app.max_calls; ++i, ++count) {
 
443
 
 
444
        unsigned j;
 
445
 
 
446
        /* Create transport for each media in the call */
 
447
        for (j=0; j<PJ_ARRAY_SIZE(app.call[0].media); ++j) {
 
448
            /* Repeat binding media socket to next port when fails to bind
 
449
             * to current port number.
 
450
             */
 
451
            int retry;
 
452
 
 
453
            app.call[i].media[j].call_index = i;
 
454
            app.call[i].media[j].media_index = j;
 
455
 
 
456
            status = -1;
 
457
            for (retry=0; retry<100; ++retry,rtp_port+=2)  {
 
458
                struct media_stream *m = &app.call[i].media[j];
 
459
                
 
460
                status = pjmedia_transport_udp_create2(app.med_endpt, 
 
461
                                                       "siprtp",
 
462
                                                       &app.local_addr,
 
463
                                                       rtp_port, 0, 
 
464
                                                       &m->transport);
 
465
                if (status == PJ_SUCCESS) {
 
466
                    rtp_port += 2;
 
467
                    break;
 
468
                }
 
469
            }
 
470
        }
 
471
 
 
472
        if (status != PJ_SUCCESS)
 
473
            goto on_error;
 
474
    }
 
475
 
 
476
    /* Done */
 
477
    return PJ_SUCCESS;
 
478
 
 
479
on_error:
 
480
    destroy_media();
 
481
    return status;
 
482
}
 
483
 
 
484
 
 
485
/*
 
486
 * Destroy media.
 
487
 */
 
488
static void destroy_media()
 
489
{
 
490
    unsigned i;
 
491
 
 
492
    for (i=0; i<app.max_calls; ++i) {
 
493
        unsigned j;
 
494
        for (j=0; j<PJ_ARRAY_SIZE(app.call[0].media); ++j) {
 
495
            struct media_stream *m = &app.call[i].media[j];
 
496
 
 
497
            if (m->transport) {
 
498
                pjmedia_transport_close(m->transport);
 
499
                m->transport = NULL;
 
500
            }
 
501
        }
 
502
    }
 
503
 
 
504
    if (app.med_endpt) {
 
505
        pjmedia_endpt_destroy(app.med_endpt);
 
506
        app.med_endpt = NULL;
 
507
    }
 
508
}
 
509
 
 
510
 
 
511
/*
 
512
 * Make outgoing call.
 
513
 */
 
514
static pj_status_t make_call(const pj_str_t *dst_uri)
 
515
{
 
516
    unsigned i;
 
517
    struct call *call;
 
518
    pjsip_dialog *dlg;
 
519
    pjmedia_sdp_session *sdp;
 
520
    pjsip_tx_data *tdata;
 
521
    pj_status_t status;
 
522
 
 
523
 
 
524
    /* Find unused call slot */
 
525
    for (i=0; i<app.max_calls; ++i) {
 
526
        if (app.call[i].inv == NULL)
 
527
            break;
 
528
    }
 
529
 
 
530
    if (i == app.max_calls)
 
531
        return PJ_ETOOMANY;
 
532
 
 
533
    call = &app.call[i];
 
534
 
 
535
    /* Create UAC dialog */
 
536
    status = pjsip_dlg_create_uac( pjsip_ua_instance(), 
 
537
                                   &app.local_uri,      /* local URI        */
 
538
                                   &app.local_contact,  /* local Contact    */
 
539
                                   dst_uri,             /* remote URI       */
 
540
                                   dst_uri,             /* remote target    */
 
541
                                   &dlg);               /* dialog           */
 
542
    if (status != PJ_SUCCESS) {
 
543
        ++app.uac_calls;
 
544
        return status;
 
545
    }
 
546
 
 
547
    /* Create SDP */
 
548
    create_sdp( dlg->pool, call, &sdp);
 
549
 
 
550
    /* Create the INVITE session. */
 
551
    status = pjsip_inv_create_uac( dlg, sdp, 0, &call->inv);
 
552
    if (status != PJ_SUCCESS) {
 
553
        pjsip_dlg_terminate(dlg);
 
554
        ++app.uac_calls;
 
555
        return status;
 
556
    }
 
557
 
 
558
 
 
559
    /* Attach call data to invite session */
 
560
    call->inv->mod_data[mod_siprtp.id] = call;
 
561
 
 
562
    /* Mark start of call */
 
563
    pj_gettimeofday(&call->start_time);
 
564
 
 
565
 
 
566
    /* Create initial INVITE request.
 
567
     * This INVITE request will contain a perfectly good request and 
 
568
     * an SDP body as well.
 
569
     */
 
570
    status = pjsip_inv_invite(call->inv, &tdata);
 
571
    PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
 
572
 
 
573
 
 
574
    /* Send initial INVITE request. 
 
575
     * From now on, the invite session's state will be reported to us
 
576
     * via the invite session callbacks.
 
577
     */
 
578
    status = pjsip_inv_send_msg(call->inv, tdata);
 
579
    PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
 
580
 
 
581
 
 
582
    return PJ_SUCCESS;
 
583
}
 
584
 
 
585
 
 
586
/*
 
587
 * Receive incoming call
 
588
 */
 
589
static void process_incoming_call(pjsip_rx_data *rdata)
 
590
{
 
591
    unsigned i, options;
 
592
    struct call *call;
 
593
    pjsip_dialog *dlg;
 
594
    pjmedia_sdp_session *sdp;
 
595
    pjsip_tx_data *tdata;
 
596
    pj_status_t status;
 
597
 
 
598
    /* Find free call slot */
 
599
    for (i=0; i<app.max_calls; ++i) {
 
600
        if (app.call[i].inv == NULL)
 
601
            break;
 
602
    }
 
603
 
 
604
    if (i == app.max_calls) {
 
605
        const pj_str_t reason = pj_str("Too many calls");
 
606
        pjsip_endpt_respond_stateless( app.sip_endpt, rdata, 
 
607
                                       500, &reason,
 
608
                                       NULL, NULL);
 
609
        return;
 
610
    }
 
611
 
 
612
    call = &app.call[i];
 
613
 
 
614
    /* Verify that we can handle the request. */
 
615
    options = 0;
 
616
    status = pjsip_inv_verify_request(rdata, &options, NULL, NULL,
 
617
                                   app.sip_endpt, &tdata);
 
618
    if (status != PJ_SUCCESS) {
 
619
        /*
 
620
         * No we can't handle the incoming INVITE request.
 
621
         */
 
622
        if (tdata) {
 
623
            pjsip_response_addr res_addr;
 
624
            
 
625
            pjsip_get_response_addr(tdata->pool, rdata, &res_addr);
 
626
            pjsip_endpt_send_response(app.sip_endpt, &res_addr, tdata,
 
627
                NULL, NULL);
 
628
            
 
629
        } else {
 
630
            
 
631
            /* Respond with 500 (Internal Server Error) */
 
632
            pjsip_endpt_respond_stateless(app.sip_endpt, rdata, 500, NULL,
 
633
                NULL, NULL);
 
634
        }
 
635
        
 
636
        return;
 
637
    }
 
638
 
 
639
    /* Create UAS dialog */
 
640
    status = pjsip_dlg_create_uas( pjsip_ua_instance(), rdata,
 
641
                                   &app.local_contact, &dlg);
 
642
    if (status != PJ_SUCCESS) {
 
643
        const pj_str_t reason = pj_str("Unable to create dialog");
 
644
        pjsip_endpt_respond_stateless( app.sip_endpt, rdata, 
 
645
                                       500, &reason,
 
646
                                       NULL, NULL);
 
647
        return;
 
648
    }
 
649
 
 
650
    /* Create SDP */
 
651
    create_sdp( dlg->pool, call, &sdp);
 
652
 
 
653
    /* Create UAS invite session */
 
654
    status = pjsip_inv_create_uas( dlg, rdata, sdp, 0, &call->inv);
 
655
    if (status != PJ_SUCCESS) {
 
656
        pjsip_dlg_create_response(dlg, rdata, 500, NULL, &tdata);
 
657
        pjsip_dlg_send_response(dlg, pjsip_rdata_get_tsx(rdata), tdata);
 
658
        return;
 
659
    }
 
660
    
 
661
 
 
662
    /* Attach call data to invite session */
 
663
    call->inv->mod_data[mod_siprtp.id] = call;
 
664
 
 
665
    /* Mark start of call */
 
666
    pj_gettimeofday(&call->start_time);
 
667
 
 
668
 
 
669
 
 
670
    /* Create 200 response .*/
 
671
    status = pjsip_inv_initial_answer(call->inv, rdata, 200, 
 
672
                                      NULL, NULL, &tdata);
 
673
    if (status != PJ_SUCCESS) {
 
674
        status = pjsip_inv_initial_answer(call->inv, rdata, 
 
675
                                          PJSIP_SC_NOT_ACCEPTABLE,
 
676
                                          NULL, NULL, &tdata);
 
677
        if (status == PJ_SUCCESS)
 
678
            pjsip_inv_send_msg(call->inv, tdata); 
 
679
        else
 
680
            pjsip_inv_terminate(call->inv, 500, PJ_FALSE);
 
681
        return;
 
682
    }
 
683
 
 
684
 
 
685
    /* Send the 200 response. */  
 
686
    status = pjsip_inv_send_msg(call->inv, tdata); 
 
687
    PJ_ASSERT_ON_FAIL(status == PJ_SUCCESS, return);
 
688
 
 
689
 
 
690
    /* Done */
 
691
}
 
692
 
 
693
 
 
694
/* Callback to be called when dialog has forked: */
 
695
static void call_on_forked(pjsip_inv_session *inv, pjsip_event *e)
 
696
{
 
697
    PJ_UNUSED_ARG(inv);
 
698
    PJ_UNUSED_ARG(e);
 
699
 
 
700
    PJ_TODO( HANDLE_FORKING );
 
701
}
 
702
 
 
703
 
 
704
/* Callback to be called to handle incoming requests outside dialogs: */
 
705
static pj_bool_t on_rx_request( pjsip_rx_data *rdata )
 
706
{
 
707
    /* Ignore strandled ACKs (must not send respone */
 
708
    if (rdata->msg_info.msg->line.req.method.id == PJSIP_ACK_METHOD)
 
709
        return PJ_FALSE;
 
710
 
 
711
    /* Respond (statelessly) any non-INVITE requests with 500  */
 
712
    if (rdata->msg_info.msg->line.req.method.id != PJSIP_INVITE_METHOD) {
 
713
        pj_str_t reason = pj_str("Unsupported Operation");
 
714
        pjsip_endpt_respond_stateless( app.sip_endpt, rdata, 
 
715
                                       500, &reason,
 
716
                                       NULL, NULL);
 
717
        return PJ_TRUE;
 
718
    }
 
719
 
 
720
    /* Handle incoming INVITE */
 
721
    process_incoming_call(rdata);
 
722
 
 
723
    /* Done */
 
724
    return PJ_TRUE;
 
725
}
 
726
 
 
727
 
 
728
/* Callback timer to disconnect call (limiting call duration) */
 
729
static void timer_disconnect_call( pj_timer_heap_t *timer_heap,
 
730
                                   struct pj_timer_entry *entry)
 
731
{
 
732
    struct call *call = entry->user_data;
 
733
 
 
734
    PJ_UNUSED_ARG(timer_heap);
 
735
 
 
736
    entry->id = 0;
 
737
    hangup_call(call->index);
 
738
}
 
739
 
 
740
 
 
741
/* Callback to be called when invite session's state has changed: */
 
742
static void call_on_state_changed( pjsip_inv_session *inv, 
 
743
                                   pjsip_event *e)
 
744
{
 
745
    struct call *call = inv->mod_data[mod_siprtp.id];
 
746
 
 
747
    PJ_UNUSED_ARG(e);
 
748
 
 
749
    if (!call)
 
750
        return;
 
751
 
 
752
    if (inv->state == PJSIP_INV_STATE_DISCONNECTED) {
 
753
        
 
754
        pj_time_val null_time = {0, 0};
 
755
 
 
756
        if (call->d_timer.id != 0) {
 
757
            pjsip_endpt_cancel_timer(app.sip_endpt, &call->d_timer);
 
758
            call->d_timer.id = 0;
 
759
        }
 
760
 
 
761
        PJ_LOG(3,(THIS_FILE, "Call #%d disconnected. Reason=%d (%.*s)",
 
762
                  call->index,
 
763
                  inv->cause,
 
764
                  (int)inv->cause_text.slen,
 
765
                  inv->cause_text.ptr));
 
766
 
 
767
        if (app.call_report) {
 
768
            PJ_LOG(3,(THIS_FILE, "Call #%d statistics:", call->index));
 
769
            print_call(call->index);
 
770
        }
 
771
 
 
772
 
 
773
        call->inv = NULL;
 
774
        inv->mod_data[mod_siprtp.id] = NULL;
 
775
 
 
776
        destroy_call_media(call->index);
 
777
 
 
778
        call->start_time = null_time;
 
779
        call->response_time = null_time;
 
780
        call->connect_time = null_time;
 
781
 
 
782
        ++app.uac_calls;
 
783
 
 
784
    } else if (inv->state == PJSIP_INV_STATE_CONFIRMED) {
 
785
 
 
786
        pj_time_val t;
 
787
 
 
788
        pj_gettimeofday(&call->connect_time);
 
789
        if (call->response_time.sec == 0)
 
790
            call->response_time = call->connect_time;
 
791
 
 
792
        t = call->connect_time;
 
793
        PJ_TIME_VAL_SUB(t, call->start_time);
 
794
 
 
795
        PJ_LOG(3,(THIS_FILE, "Call #%d connected in %d ms", call->index,
 
796
                  PJ_TIME_VAL_MSEC(t)));
 
797
 
 
798
        if (app.duration != 0) {
 
799
            call->d_timer.id = 1;
 
800
            call->d_timer.user_data = call;
 
801
            call->d_timer.cb = &timer_disconnect_call;
 
802
 
 
803
            t.sec = app.duration;
 
804
            t.msec = 0;
 
805
 
 
806
            pjsip_endpt_schedule_timer(app.sip_endpt, &call->d_timer, &t);
 
807
        }
 
808
 
 
809
    } else if ( inv->state == PJSIP_INV_STATE_EARLY ||
 
810
                inv->state == PJSIP_INV_STATE_CONNECTING) {
 
811
 
 
812
        if (call->response_time.sec == 0)
 
813
            pj_gettimeofday(&call->response_time);
 
814
 
 
815
    }
 
816
}
 
817
 
 
818
 
 
819
/* Utility */
 
820
static void app_perror(const char *sender, const char *title, 
 
821
                       pj_status_t status)
 
822
{
 
823
    char errmsg[PJ_ERR_MSG_SIZE];
 
824
 
 
825
    pj_strerror(status, errmsg, sizeof(errmsg));
 
826
    PJ_LOG(3,(sender, "%s: %s [status=%d]", title, errmsg, status));
 
827
}
 
828
 
 
829
 
 
830
/* Worker thread for SIP */
 
831
static int sip_worker_thread(void *arg)
 
832
{
 
833
    PJ_UNUSED_ARG(arg);
 
834
 
 
835
    while (!app.thread_quit) {
 
836
        pj_time_val timeout = {0, 10};
 
837
        pjsip_endpt_handle_events(app.sip_endpt, &timeout);
 
838
    }
 
839
 
 
840
    return 0;
 
841
}
 
842
 
 
843
 
 
844
/* Init application options */
 
845
static pj_status_t init_options(int argc, char *argv[])
 
846
{
 
847
    static char ip_addr[32];
 
848
    static char local_uri[64];
 
849
 
 
850
    enum { OPT_START,
 
851
           OPT_APP_LOG_LEVEL, OPT_LOG_FILE, 
 
852
           OPT_A_PT, OPT_A_NAME, OPT_A_CLOCK, OPT_A_BITRATE, OPT_A_PTIME,
 
853
           OPT_REPORT_FILE };
 
854
 
 
855
    struct pj_getopt_option long_options[] = {
 
856
        { "count",          1, 0, 'c' },
 
857
        { "gap",            1, 0, 'g' },
 
858
        { "call-report",    0, 0, 'R' },
 
859
        { "duration",       1, 0, 'd' },
 
860
        { "auto-quit",      0, 0, 'q' },
 
861
        { "local-port",     1, 0, 'p' },
 
862
        { "rtp-port",       1, 0, 'r' },
 
863
        { "ip-addr",        1, 0, 'i' },
 
864
 
 
865
        { "log-level",      1, 0, 'l' },
 
866
        { "app-log-level",  1, 0, OPT_APP_LOG_LEVEL },
 
867
        { "log-file",       1, 0, OPT_LOG_FILE },
 
868
 
 
869
        { "report-file",    1, 0, OPT_REPORT_FILE },
 
870
 
 
871
        /* Don't support this anymore, see comments in USAGE above.
 
872
        { "a-pt",           1, 0, OPT_A_PT },
 
873
        { "a-name",         1, 0, OPT_A_NAME },
 
874
        { "a-clock",        1, 0, OPT_A_CLOCK },
 
875
        { "a-bitrate",      1, 0, OPT_A_BITRATE },
 
876
        { "a-ptime",        1, 0, OPT_A_PTIME },
 
877
        */
 
878
 
 
879
        { NULL, 0, 0, 0 },
 
880
    };
 
881
    int c;
 
882
    int option_index;
 
883
 
 
884
    /* Get local IP address for the default IP address */
 
885
    {
 
886
        const pj_str_t *hostname;
 
887
        pj_sockaddr_in tmp_addr;
 
888
        char *addr;
 
889
 
 
890
        hostname = pj_gethostname();
 
891
        pj_sockaddr_in_init(&tmp_addr, hostname, 0);
 
892
        addr = pj_inet_ntoa(tmp_addr.sin_addr);
 
893
        pj_ansi_strcpy(ip_addr, addr);
 
894
    }
 
895
 
 
896
    /* Init defaults */
 
897
    app.max_calls = 1;
 
898
    app.thread_count = 1;
 
899
    app.sip_port = 5060;
 
900
    app.rtp_start_port = RTP_START_PORT;
 
901
    app.local_addr = pj_str(ip_addr);
 
902
    app.log_level = 5;
 
903
    app.app_log_level = 3;
 
904
    app.log_filename = NULL;
 
905
 
 
906
    /* Default codecs: */
 
907
    app.audio_codec = audio_codecs[0];
 
908
 
 
909
    /* Parse options */
 
910
    pj_optind = 0;
 
911
    while((c=pj_getopt_long(argc,argv, "c:d:p:r:i:l:g:qR", 
 
912
                            long_options, &option_index))!=-1) 
 
913
    {
 
914
        switch (c) {
 
915
        case 'c':
 
916
            app.max_calls = atoi(pj_optarg);
 
917
            if (app.max_calls < 0 || app.max_calls > MAX_CALLS) {
 
918
                PJ_LOG(3,(THIS_FILE, "Invalid max calls value %s", pj_optarg));
 
919
                return 1;
 
920
            }
 
921
            break;
 
922
        case 'g':
 
923
            app.call_gap = atoi(pj_optarg);
 
924
            break;
 
925
        case 'R':
 
926
            app.call_report = PJ_TRUE;
 
927
            break;
 
928
        case 'd':
 
929
            app.duration = atoi(pj_optarg);
 
930
            break;
 
931
        case 'q':
 
932
            app.auto_quit = 1;
 
933
            break;
 
934
 
 
935
        case 'p':
 
936
            app.sip_port = atoi(pj_optarg);
 
937
            break;
 
938
        case 'r':
 
939
            app.rtp_start_port = atoi(pj_optarg);
 
940
            break;
 
941
        case 'i':
 
942
            app.local_addr = pj_str(pj_optarg);
 
943
            break;
 
944
 
 
945
        case 'l':
 
946
            app.log_level = atoi(pj_optarg);
 
947
            break;
 
948
        case OPT_APP_LOG_LEVEL:
 
949
            app.app_log_level = atoi(pj_optarg);
 
950
            break;
 
951
        case OPT_LOG_FILE:
 
952
            app.log_filename = pj_optarg;
 
953
            break;
 
954
 
 
955
        case OPT_A_PT:
 
956
            app.audio_codec.pt = atoi(pj_optarg);
 
957
            break;
 
958
        case OPT_A_NAME:
 
959
            app.audio_codec.name = pj_optarg;
 
960
            break;
 
961
        case OPT_A_CLOCK:
 
962
            app.audio_codec.clock_rate = atoi(pj_optarg);
 
963
            break;
 
964
        case OPT_A_BITRATE:
 
965
            app.audio_codec.bit_rate = atoi(pj_optarg);
 
966
            break;
 
967
        case OPT_A_PTIME:
 
968
            app.audio_codec.ptime = atoi(pj_optarg);
 
969
            break;
 
970
        case OPT_REPORT_FILE:
 
971
            app.report_filename = pj_optarg;
 
972
            break;
 
973
 
 
974
        default:
 
975
            puts(USAGE);
 
976
            return 1;
 
977
        }
 
978
    }
 
979
 
 
980
    /* Check if URL is specified */
 
981
    if (pj_optind < argc)
 
982
        app.uri_to_call = pj_str(argv[pj_optind]);
 
983
 
 
984
    /* Build local URI and contact */
 
985
    pj_ansi_sprintf( local_uri, "sip:%s:%d", app.local_addr.ptr, app.sip_port);
 
986
    app.local_uri = pj_str(local_uri);
 
987
    app.local_contact = app.local_uri;
 
988
 
 
989
 
 
990
    return PJ_SUCCESS;
 
991
}
 
992
 
 
993
 
 
994
/*****************************************************************************
 
995
 * MEDIA STUFFS
 
996
 */
 
997
 
 
998
/*
 
999
 * Create SDP session for a call.
 
1000
 */
 
1001
static pj_status_t create_sdp( pj_pool_t *pool,
 
1002
                               struct call *call,
 
1003
                               pjmedia_sdp_session **p_sdp)
 
1004
{
 
1005
    pj_time_val tv;
 
1006
    pjmedia_sdp_session *sdp;
 
1007
    pjmedia_sdp_media *m;
 
1008
    pjmedia_sdp_attr *attr;
 
1009
    pjmedia_transport_info tpinfo;
 
1010
    struct media_stream *audio = &call->media[0];
 
1011
 
 
1012
    PJ_ASSERT_RETURN(pool && p_sdp, PJ_EINVAL);
 
1013
 
 
1014
 
 
1015
    /* Get transport info */
 
1016
    pjmedia_transport_info_init(&tpinfo);
 
1017
    pjmedia_transport_get_info(audio->transport, &tpinfo);
 
1018
 
 
1019
    /* Create and initialize basic SDP session */
 
1020
    sdp = pj_pool_zalloc (pool, sizeof(pjmedia_sdp_session));
 
1021
 
 
1022
    pj_gettimeofday(&tv);
 
1023
    sdp->origin.user = pj_str("pjsip-siprtp");
 
1024
    sdp->origin.version = sdp->origin.id = tv.sec + 2208988800UL;
 
1025
    sdp->origin.net_type = pj_str("IN");
 
1026
    sdp->origin.addr_type = pj_str("IP4");
 
1027
    sdp->origin.addr = *pj_gethostname();
 
1028
    sdp->name = pj_str("pjsip");
 
1029
 
 
1030
    /* Since we only support one media stream at present, put the
 
1031
     * SDP connection line in the session level.
 
1032
     */
 
1033
    sdp->conn = pj_pool_zalloc (pool, sizeof(pjmedia_sdp_conn));
 
1034
    sdp->conn->net_type = pj_str("IN");
 
1035
    sdp->conn->addr_type = pj_str("IP4");
 
1036
    sdp->conn->addr = app.local_addr;
 
1037
 
 
1038
 
 
1039
    /* SDP time and attributes. */
 
1040
    sdp->time.start = sdp->time.stop = 0;
 
1041
    sdp->attr_count = 0;
 
1042
 
 
1043
    /* Create media stream 0: */
 
1044
 
 
1045
    sdp->media_count = 1;
 
1046
    m = pj_pool_zalloc (pool, sizeof(pjmedia_sdp_media));
 
1047
    sdp->media[0] = m;
 
1048
 
 
1049
    /* Standard media info: */
 
1050
    m->desc.media = pj_str("audio");
 
1051
    m->desc.port = pj_ntohs(tpinfo.sock_info.rtp_addr_name.ipv4.sin_port);
 
1052
    m->desc.port_count = 1;
 
1053
    m->desc.transport = pj_str("RTP/AVP");
 
1054
 
 
1055
    /* Add format and rtpmap for each codec. */
 
1056
    m->desc.fmt_count = 1;
 
1057
    m->attr_count = 0;
 
1058
 
 
1059
    {
 
1060
        pjmedia_sdp_rtpmap rtpmap;
 
1061
        pjmedia_sdp_attr *attr;
 
1062
        char ptstr[10];
 
1063
 
 
1064
        sprintf(ptstr, "%d", app.audio_codec.pt);
 
1065
        pj_strdup2(pool, &m->desc.fmt[0], ptstr);
 
1066
        rtpmap.pt = m->desc.fmt[0];
 
1067
        rtpmap.clock_rate = app.audio_codec.clock_rate;
 
1068
        rtpmap.enc_name = pj_str(app.audio_codec.name);
 
1069
        rtpmap.param.slen = 0;
 
1070
 
 
1071
        pjmedia_sdp_rtpmap_to_attr(pool, &rtpmap, &attr);
 
1072
        m->attr[m->attr_count++] = attr;
 
1073
    }
 
1074
 
 
1075
    /* Add sendrecv attribute. */
 
1076
    attr = pj_pool_zalloc(pool, sizeof(pjmedia_sdp_attr));
 
1077
    attr->name = pj_str("sendrecv");
 
1078
    m->attr[m->attr_count++] = attr;
 
1079
 
 
1080
#if 1
 
1081
    /*
 
1082
     * Add support telephony event
 
1083
     */
 
1084
    m->desc.fmt[m->desc.fmt_count++] = pj_str("121");
 
1085
    /* Add rtpmap. */
 
1086
    attr = pj_pool_zalloc(pool, sizeof(pjmedia_sdp_attr));
 
1087
    attr->name = pj_str("rtpmap");
 
1088
    attr->value = pj_str("121 telephone-event/8000");
 
1089
    m->attr[m->attr_count++] = attr;
 
1090
    /* Add fmtp */
 
1091
    attr = pj_pool_zalloc(pool, sizeof(pjmedia_sdp_attr));
 
1092
    attr->name = pj_str("fmtp");
 
1093
    attr->value = pj_str("121 0-15");
 
1094
    m->attr[m->attr_count++] = attr;
 
1095
#endif
 
1096
 
 
1097
    /* Done */
 
1098
    *p_sdp = sdp;
 
1099
 
 
1100
    return PJ_SUCCESS;
 
1101
}
 
1102
 
 
1103
 
 
1104
#if defined(PJ_WIN32) && PJ_WIN32 != 0
 
1105
#include <windows.h>
 
1106
static void boost_priority(void)
 
1107
{
 
1108
    SetPriorityClass( GetCurrentProcess(), REALTIME_PRIORITY_CLASS);
 
1109
    SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
 
1110
}
 
1111
 
 
1112
#elif defined(PJ_LINUX) && PJ_LINUX != 0
 
1113
#include <pthread.h>
 
1114
static void boost_priority(void)
 
1115
{
 
1116
#define POLICY  SCHED_FIFO
 
1117
    struct sched_param tp;
 
1118
    int max_prio;
 
1119
    int policy;
 
1120
    int rc;
 
1121
 
 
1122
    if (sched_get_priority_min(POLICY) < sched_get_priority_max(POLICY))
 
1123
        max_prio = sched_get_priority_max(POLICY)-1;
 
1124
    else
 
1125
        max_prio = sched_get_priority_max(POLICY)+1;
 
1126
 
 
1127
    /*
 
1128
     * Adjust process scheduling algorithm and priority
 
1129
     */
 
1130
    rc = sched_getparam(0, &tp);
 
1131
    if (rc != 0) {
 
1132
        app_perror( THIS_FILE, "sched_getparam error",
 
1133
                    PJ_RETURN_OS_ERROR(rc));
 
1134
        return;
 
1135
    }
 
1136
    tp.__sched_priority = max_prio;
 
1137
 
 
1138
    rc = sched_setscheduler(0, POLICY, &tp);
 
1139
    if (rc != 0) {
 
1140
        app_perror( THIS_FILE, "sched_setscheduler error",
 
1141
                    PJ_RETURN_OS_ERROR(rc));
 
1142
    }
 
1143
 
 
1144
    PJ_LOG(4, (THIS_FILE, "New process policy=%d, priority=%d",
 
1145
              policy, tp.__sched_priority));
 
1146
 
 
1147
    /*
 
1148
     * Adjust thread scheduling algorithm and priority
 
1149
     */
 
1150
    rc = pthread_getschedparam(pthread_self(), &policy, &tp);
 
1151
    if (rc != 0) {
 
1152
        app_perror( THIS_FILE, "pthread_getschedparam error",
 
1153
                    PJ_RETURN_OS_ERROR(rc));
 
1154
        return;
 
1155
    }
 
1156
 
 
1157
    PJ_LOG(4, (THIS_FILE, "Old thread policy=%d, priority=%d",
 
1158
              policy, tp.__sched_priority));
 
1159
 
 
1160
    policy = POLICY;
 
1161
    tp.__sched_priority = max_prio;
 
1162
 
 
1163
    rc = pthread_setschedparam(pthread_self(), policy, &tp);
 
1164
    if (rc != 0) {
 
1165
        app_perror( THIS_FILE, "pthread_setschedparam error",
 
1166
                    PJ_RETURN_OS_ERROR(rc));
 
1167
        return;
 
1168
    }
 
1169
 
 
1170
    PJ_LOG(4, (THIS_FILE, "New thread policy=%d, priority=%d",
 
1171
              policy, tp.__sched_priority));
 
1172
}
 
1173
 
 
1174
#else
 
1175
#  define boost_priority()
 
1176
#endif
 
1177
 
 
1178
 
 
1179
/*
 
1180
 * This callback is called by media transport on receipt of RTP packet.
 
1181
 */
 
1182
static void on_rx_rtp(void *user_data, void *pkt, pj_ssize_t size)
 
1183
{
 
1184
    struct media_stream *strm;
 
1185
    pj_status_t status;
 
1186
    const pjmedia_rtp_hdr *hdr;
 
1187
    const void *payload;
 
1188
    unsigned payload_len;
 
1189
 
 
1190
    strm = user_data;
 
1191
 
 
1192
    /* Discard packet if media is inactive */
 
1193
    if (!strm->active)
 
1194
        return;
 
1195
 
 
1196
    /* Check for errors */
 
1197
    if (size < 0) {
 
1198
        app_perror(THIS_FILE, "RTP recv() error", -size);
 
1199
        return;
 
1200
    }
 
1201
 
 
1202
    /* Decode RTP packet. */
 
1203
    status = pjmedia_rtp_decode_rtp(&strm->in_sess, 
 
1204
                                    pkt, size, 
 
1205
                                    &hdr, &payload, &payload_len);
 
1206
    if (status != PJ_SUCCESS) {
 
1207
        app_perror(THIS_FILE, "RTP decode error", status);
 
1208
        return;
 
1209
    }
 
1210
 
 
1211
    //PJ_LOG(4,(THIS_FILE, "Rx seq=%d", pj_ntohs(hdr->seq)));
 
1212
 
 
1213
    /* Update the RTCP session. */
 
1214
    pjmedia_rtcp_rx_rtp(&strm->rtcp, pj_ntohs(hdr->seq),
 
1215
                        pj_ntohl(hdr->ts), payload_len);
 
1216
 
 
1217
    /* Update RTP session */
 
1218
    pjmedia_rtp_session_update(&strm->in_sess, hdr, NULL);
 
1219
 
 
1220
}
 
1221
 
 
1222
/*
 
1223
 * This callback is called by media transport on receipt of RTCP packet.
 
1224
 */
 
1225
static void on_rx_rtcp(void *user_data, void *pkt, pj_ssize_t size)
 
1226
{
 
1227
    struct media_stream *strm;
 
1228
 
 
1229
    strm = user_data;
 
1230
 
 
1231
    /* Discard packet if media is inactive */
 
1232
    if (!strm->active)
 
1233
        return;
 
1234
 
 
1235
    /* Check for errors */
 
1236
    if (size < 0) {
 
1237
        app_perror(THIS_FILE, "Error receiving RTCP packet", -size);
 
1238
        return;
 
1239
    }
 
1240
 
 
1241
    /* Update RTCP session */
 
1242
    pjmedia_rtcp_rx_rtcp(&strm->rtcp, pkt, size);
 
1243
}
 
1244
 
 
1245
 
 
1246
/* 
 
1247
 * Media thread 
 
1248
 *
 
1249
 * This is the thread to send and receive both RTP and RTCP packets.
 
1250
 */
 
1251
static int media_thread(void *arg)
 
1252
{
 
1253
    enum { RTCP_INTERVAL = 5000, RTCP_RAND = 2000 };
 
1254
    struct media_stream *strm = arg;
 
1255
    char packet[1500];
 
1256
    unsigned msec_interval;
 
1257
    pj_timestamp freq, next_rtp, next_rtcp;
 
1258
 
 
1259
 
 
1260
    /* Boost thread priority if necessary */
 
1261
    boost_priority();
 
1262
 
 
1263
    /* Let things settle */
 
1264
    pj_thread_sleep(100);
 
1265
 
 
1266
    msec_interval = strm->samples_per_frame * 1000 / strm->clock_rate;
 
1267
    pj_get_timestamp_freq(&freq);
 
1268
 
 
1269
    pj_get_timestamp(&next_rtp);
 
1270
    next_rtp.u64 += (freq.u64 * msec_interval / 1000);
 
1271
 
 
1272
    next_rtcp = next_rtp;
 
1273
    next_rtcp.u64 += (freq.u64 * (RTCP_INTERVAL+(pj_rand()%RTCP_RAND)) / 1000);
 
1274
 
 
1275
 
 
1276
    while (!strm->thread_quit_flag) {
 
1277
        pj_timestamp now, lesser;
 
1278
        pj_time_val timeout;
 
1279
        pj_bool_t send_rtp, send_rtcp;
 
1280
 
 
1281
        send_rtp = send_rtcp = PJ_FALSE;
 
1282
 
 
1283
        /* Determine how long to sleep */
 
1284
        if (next_rtp.u64 < next_rtcp.u64) {
 
1285
            lesser = next_rtp;
 
1286
            send_rtp = PJ_TRUE;
 
1287
        } else {
 
1288
            lesser = next_rtcp;
 
1289
            send_rtcp = PJ_TRUE;
 
1290
        }
 
1291
 
 
1292
        pj_get_timestamp(&now);
 
1293
        if (lesser.u64 <= now.u64) {
 
1294
            timeout.sec = timeout.msec = 0;
 
1295
            //printf("immediate "); fflush(stdout);
 
1296
        } else {
 
1297
            pj_uint64_t tick_delay;
 
1298
            tick_delay = lesser.u64 - now.u64;
 
1299
            timeout.sec = 0;
 
1300
            timeout.msec = (pj_uint32_t)(tick_delay * 1000 / freq.u64);
 
1301
            pj_time_val_normalize(&timeout);
 
1302
 
 
1303
            //printf("%d:%03d ", timeout.sec, timeout.msec); fflush(stdout);
 
1304
        }
 
1305
 
 
1306
        /* Wait for next interval */
 
1307
        //if (timeout.sec!=0 && timeout.msec!=0) {
 
1308
            pj_thread_sleep(PJ_TIME_VAL_MSEC(timeout));
 
1309
            if (strm->thread_quit_flag)
 
1310
                break;
 
1311
        //}
 
1312
 
 
1313
        pj_get_timestamp(&now);
 
1314
 
 
1315
        if (send_rtp || next_rtp.u64 <= now.u64) {
 
1316
            /*
 
1317
             * Time to send RTP packet.
 
1318
             */
 
1319
            pj_status_t status;
 
1320
            const void *p_hdr;
 
1321
            const pjmedia_rtp_hdr *hdr;
 
1322
            pj_ssize_t size;
 
1323
            int hdrlen;
 
1324
 
 
1325
            /* Format RTP header */
 
1326
            status = pjmedia_rtp_encode_rtp( &strm->out_sess, strm->si.tx_pt,
 
1327
                                             0, /* marker bit */
 
1328
                                             strm->bytes_per_frame, 
 
1329
                                             strm->samples_per_frame,
 
1330
                                             &p_hdr, &hdrlen);
 
1331
            if (status == PJ_SUCCESS) {
 
1332
 
 
1333
                //PJ_LOG(4,(THIS_FILE, "\t\tTx seq=%d", pj_ntohs(hdr->seq)));
 
1334
                
 
1335
                hdr = (const pjmedia_rtp_hdr*) p_hdr;
 
1336
 
 
1337
                /* Copy RTP header to packet */
 
1338
                pj_memcpy(packet, hdr, hdrlen);
 
1339
 
 
1340
                /* Zero the payload */
 
1341
                pj_bzero(packet+hdrlen, strm->bytes_per_frame);
 
1342
 
 
1343
                /* Send RTP packet */
 
1344
                size = hdrlen + strm->bytes_per_frame;
 
1345
                status = pjmedia_transport_send_rtp(strm->transport, 
 
1346
                                                    packet, size);
 
1347
                if (status != PJ_SUCCESS)
 
1348
                    app_perror(THIS_FILE, "Error sending RTP packet", status);
 
1349
 
 
1350
            } else {
 
1351
                pj_assert(!"RTP encode() error");
 
1352
            }
 
1353
 
 
1354
            /* Update RTCP SR */
 
1355
            pjmedia_rtcp_tx_rtp( &strm->rtcp, (pj_uint16_t)strm->bytes_per_frame);
 
1356
 
 
1357
            /* Schedule next send */
 
1358
            next_rtp.u64 += (msec_interval * freq.u64 / 1000);
 
1359
        }
 
1360
 
 
1361
 
 
1362
        if (send_rtcp || next_rtcp.u64 <= now.u64) {
 
1363
            /*
 
1364
             * Time to send RTCP packet.
 
1365
             */
 
1366
            void *rtcp_pkt;
 
1367
            int rtcp_len;
 
1368
            pj_ssize_t size;
 
1369
            pj_status_t status;
 
1370
 
 
1371
            /* Build RTCP packet */
 
1372
            pjmedia_rtcp_build_rtcp(&strm->rtcp, &rtcp_pkt, &rtcp_len);
 
1373
 
 
1374
    
 
1375
            /* Send packet */
 
1376
            size = rtcp_len;
 
1377
            status = pjmedia_transport_send_rtcp(strm->transport,
 
1378
                                                 rtcp_pkt, size);
 
1379
            if (status != PJ_SUCCESS) {
 
1380
                app_perror(THIS_FILE, "Error sending RTCP packet", status);
 
1381
            }
 
1382
            
 
1383
            /* Schedule next send */
 
1384
            next_rtcp.u64 += (freq.u64 * (RTCP_INTERVAL+(pj_rand()%RTCP_RAND)) /
 
1385
                              1000);
 
1386
        }
 
1387
    }
 
1388
 
 
1389
    return 0;
 
1390
}
 
1391
 
 
1392
 
 
1393
/* Callback to be called when SDP negotiation is done in the call: */
 
1394
static void call_on_media_update( pjsip_inv_session *inv,
 
1395
                                  pj_status_t status)
 
1396
{
 
1397
    struct call *call;
 
1398
    pj_pool_t *pool;
 
1399
    struct media_stream *audio;
 
1400
    const pjmedia_sdp_session *local_sdp, *remote_sdp;
 
1401
    struct codec *codec_desc = NULL;
 
1402
    unsigned i;
 
1403
 
 
1404
    call = inv->mod_data[mod_siprtp.id];
 
1405
    pool = inv->dlg->pool;
 
1406
    audio = &call->media[0];
 
1407
 
 
1408
    /* If this is a mid-call media update, then destroy existing media */
 
1409
    if (audio->thread != NULL)
 
1410
        destroy_call_media(call->index);
 
1411
 
 
1412
 
 
1413
    /* Do nothing if media negotiation has failed */
 
1414
    if (status != PJ_SUCCESS) {
 
1415
        app_perror(THIS_FILE, "SDP negotiation failed", status);
 
1416
        return;
 
1417
    }
 
1418
 
 
1419
    
 
1420
    /* Capture stream definition from the SDP */
 
1421
    pjmedia_sdp_neg_get_active_local(inv->neg, &local_sdp);
 
1422
    pjmedia_sdp_neg_get_active_remote(inv->neg, &remote_sdp);
 
1423
 
 
1424
    status = pjmedia_stream_info_from_sdp(&audio->si, inv->pool, app.med_endpt,
 
1425
                                          local_sdp, remote_sdp, 0);
 
1426
    if (status != PJ_SUCCESS) {
 
1427
        app_perror(THIS_FILE, "Error creating stream info from SDP", status);
 
1428
        return;
 
1429
    }
 
1430
 
 
1431
    /* Get the remainder of codec information from codec descriptor */
 
1432
    if (audio->si.fmt.pt == app.audio_codec.pt)
 
1433
        codec_desc = &app.audio_codec;
 
1434
    else {
 
1435
        /* Find the codec description in codec array */
 
1436
        for (i=0; i<PJ_ARRAY_SIZE(audio_codecs); ++i) {
 
1437
            if (audio_codecs[i].pt == audio->si.fmt.pt) {
 
1438
                codec_desc = &audio_codecs[i];
 
1439
                break;
 
1440
            }
 
1441
        }
 
1442
 
 
1443
        if (codec_desc == NULL) {
 
1444
            PJ_LOG(3, (THIS_FILE, "Error: Invalid codec payload type"));
 
1445
            return;
 
1446
        }
 
1447
    }
 
1448
 
 
1449
    audio->clock_rate = audio->si.fmt.clock_rate;
 
1450
    audio->samples_per_frame = audio->clock_rate * codec_desc->ptime / 1000;
 
1451
    audio->bytes_per_frame = codec_desc->bit_rate * codec_desc->ptime / 1000 / 8;
 
1452
 
 
1453
 
 
1454
    pjmedia_rtp_session_init(&audio->out_sess, audio->si.tx_pt, 
 
1455
                             pj_rand());
 
1456
    pjmedia_rtp_session_init(&audio->in_sess, audio->si.fmt.pt, 0);
 
1457
    pjmedia_rtcp_init(&audio->rtcp, "rtcp", audio->clock_rate, 
 
1458
                      audio->samples_per_frame, 0);
 
1459
 
 
1460
 
 
1461
    /* Attach media to transport */
 
1462
    status = pjmedia_transport_attach(audio->transport, audio, 
 
1463
                                      &audio->si.rem_addr, 
 
1464
                                      &audio->si.rem_rtcp, 
 
1465
                                      sizeof(pj_sockaddr_in),
 
1466
                                      &on_rx_rtp,
 
1467
                                      &on_rx_rtcp);
 
1468
    if (status != PJ_SUCCESS) {
 
1469
        app_perror(THIS_FILE, "Error on pjmedia_transport_attach()", status);
 
1470
        return;
 
1471
    }
 
1472
 
 
1473
    /* Start media thread. */
 
1474
    audio->thread_quit_flag = 0;
 
1475
#if PJ_HAS_THREADS
 
1476
    status = pj_thread_create( inv->pool, "media", &media_thread, audio,
 
1477
                               0, 0, &audio->thread);
 
1478
    if (status != PJ_SUCCESS) {
 
1479
        app_perror(THIS_FILE, "Error creating media thread", status);
 
1480
        return;
 
1481
    }
 
1482
#endif
 
1483
 
 
1484
    /* Set the media as active */
 
1485
    audio->active = PJ_TRUE;
 
1486
}
 
1487
 
 
1488
 
 
1489
 
 
1490
/* Destroy call's media */
 
1491
static void destroy_call_media(unsigned call_index)
 
1492
{
 
1493
    struct media_stream *audio = &app.call[call_index].media[0];
 
1494
 
 
1495
    if (audio) {
 
1496
        audio->active = PJ_FALSE;
 
1497
 
 
1498
        if (audio->thread) {
 
1499
            audio->thread_quit_flag = 1;
 
1500
            pj_thread_join(audio->thread);
 
1501
            pj_thread_destroy(audio->thread);
 
1502
            audio->thread = NULL;
 
1503
            audio->thread_quit_flag = 0;
 
1504
        }
 
1505
 
 
1506
        pjmedia_transport_detach(audio->transport, audio);
 
1507
    }
 
1508
}
 
1509
 
 
1510
 
 
1511
/*****************************************************************************
 
1512
 * USER INTERFACE STUFFS
 
1513
 */
 
1514
 
 
1515
static void call_get_duration(int call_index, pj_time_val *dur)
 
1516
{
 
1517
    struct call *call = &app.call[call_index];
 
1518
    pjsip_inv_session *inv;
 
1519
 
 
1520
    dur->sec = dur->msec = 0;
 
1521
 
 
1522
    if (!call)
 
1523
        return;
 
1524
 
 
1525
    inv = call->inv;
 
1526
    if (!inv)
 
1527
        return;
 
1528
 
 
1529
    if (inv->state >= PJSIP_INV_STATE_CONFIRMED && call->connect_time.sec) {
 
1530
 
 
1531
        pj_gettimeofday(dur);
 
1532
        PJ_TIME_VAL_SUB((*dur), call->connect_time);
 
1533
    }
 
1534
}
 
1535
 
 
1536
 
 
1537
static const char *good_number(char *buf, pj_int32_t val)
 
1538
{
 
1539
    if (val < 1000) {
 
1540
        pj_ansi_sprintf(buf, "%d", val);
 
1541
    } else if (val < 1000000) {
 
1542
        pj_ansi_sprintf(buf, "%d.%02dK", 
 
1543
                        val / 1000,
 
1544
                        (val % 1000) / 100);
 
1545
    } else {
 
1546
        pj_ansi_sprintf(buf, "%d.%02dM", 
 
1547
                        val / 1000000,
 
1548
                        (val % 1000000) / 10000);
 
1549
    }
 
1550
 
 
1551
    return buf;
 
1552
}
 
1553
 
 
1554
 
 
1555
 
 
1556
static void print_avg_stat(void)
 
1557
{
 
1558
#define MIN_(var,val)      if ((int)val < (int)var) var = val
 
1559
#define MAX_(var,val)      if ((int)val > (int)var) var = val
 
1560
#define AVG_(var,val)      var = ( ((var * count) + val) / (count+1) )
 
1561
#define BIGVAL              0x7FFFFFFFL
 
1562
    struct stat_entry
 
1563
    {
 
1564
        int min, avg, max;
 
1565
    };
 
1566
 
 
1567
    struct stat_entry call_dur, call_pdd;
 
1568
    pjmedia_rtcp_stat min_stat, avg_stat, max_stat;
 
1569
 
 
1570
    char srx_min[16], srx_avg[16], srx_max[16];
 
1571
    char brx_min[16], brx_avg[16], brx_max[16];
 
1572
    char stx_min[16], stx_avg[16], stx_max[16];
 
1573
    char btx_min[16], btx_avg[16], btx_max[16];
 
1574
 
 
1575
 
 
1576
    unsigned i, count;
 
1577
 
 
1578
    pj_bzero(&call_dur, sizeof(call_dur)); 
 
1579
    call_dur.min = BIGVAL;
 
1580
 
 
1581
    pj_bzero(&call_pdd, sizeof(call_pdd)); 
 
1582
    call_pdd.min = BIGVAL;
 
1583
 
 
1584
    pj_bzero(&min_stat, sizeof(min_stat));
 
1585
    min_stat.rx.pkt = min_stat.tx.pkt = BIGVAL;
 
1586
    min_stat.rx.bytes = min_stat.tx.bytes = BIGVAL;
 
1587
    min_stat.rx.loss = min_stat.tx.loss = BIGVAL;
 
1588
    min_stat.rx.dup = min_stat.tx.dup = BIGVAL;
 
1589
    min_stat.rx.reorder = min_stat.tx.reorder = BIGVAL;
 
1590
    min_stat.rx.jitter.min = min_stat.tx.jitter.min = BIGVAL;
 
1591
    min_stat.rtt.min = BIGVAL;
 
1592
 
 
1593
    pj_bzero(&avg_stat, sizeof(avg_stat));
 
1594
    pj_bzero(&max_stat, sizeof(max_stat));
 
1595
 
 
1596
 
 
1597
    for (i=0, count=0; i<app.max_calls; ++i) {
 
1598
 
 
1599
        struct call *call = &app.call[i];
 
1600
        struct media_stream *audio = &call->media[0];
 
1601
        pj_time_val dur;
 
1602
        unsigned msec_dur;
 
1603
 
 
1604
        if (call->inv == NULL || 
 
1605
            call->inv->state < PJSIP_INV_STATE_CONFIRMED ||
 
1606
            call->connect_time.sec == 0) 
 
1607
        {
 
1608
            continue;
 
1609
        }
 
1610
 
 
1611
        /* Duration */
 
1612
        call_get_duration(i, &dur);
 
1613
        msec_dur = PJ_TIME_VAL_MSEC(dur);
 
1614
 
 
1615
        MIN_(call_dur.min, msec_dur);
 
1616
        MAX_(call_dur.max, msec_dur);
 
1617
        AVG_(call_dur.avg, msec_dur);
 
1618
 
 
1619
        /* Connect delay */
 
1620
        if (call->connect_time.sec) {
 
1621
            pj_time_val t = call->connect_time;
 
1622
            PJ_TIME_VAL_SUB(t, call->start_time);
 
1623
            msec_dur = PJ_TIME_VAL_MSEC(t);
 
1624
        } else {
 
1625
            msec_dur = 10;
 
1626
        }
 
1627
 
 
1628
        MIN_(call_pdd.min, msec_dur);
 
1629
        MAX_(call_pdd.max, msec_dur);
 
1630
        AVG_(call_pdd.avg, msec_dur);
 
1631
 
 
1632
        /* RX Statistisc: */
 
1633
 
 
1634
        /* Packets */
 
1635
        MIN_(min_stat.rx.pkt, audio->rtcp.stat.rx.pkt);
 
1636
        MAX_(max_stat.rx.pkt, audio->rtcp.stat.rx.pkt);
 
1637
        AVG_(avg_stat.rx.pkt, audio->rtcp.stat.rx.pkt);
 
1638
 
 
1639
        /* Bytes */
 
1640
        MIN_(min_stat.rx.bytes, audio->rtcp.stat.rx.bytes);
 
1641
        MAX_(max_stat.rx.bytes, audio->rtcp.stat.rx.bytes);
 
1642
        AVG_(avg_stat.rx.bytes, audio->rtcp.stat.rx.bytes);
 
1643
 
 
1644
 
 
1645
        /* Packet loss */
 
1646
        MIN_(min_stat.rx.loss, audio->rtcp.stat.rx.loss);
 
1647
        MAX_(max_stat.rx.loss, audio->rtcp.stat.rx.loss);
 
1648
        AVG_(avg_stat.rx.loss, audio->rtcp.stat.rx.loss);
 
1649
 
 
1650
        /* Packet dup */
 
1651
        MIN_(min_stat.rx.dup, audio->rtcp.stat.rx.dup);
 
1652
        MAX_(max_stat.rx.dup, audio->rtcp.stat.rx.dup);
 
1653
        AVG_(avg_stat.rx.dup, audio->rtcp.stat.rx.dup);
 
1654
 
 
1655
        /* Packet reorder */
 
1656
        MIN_(min_stat.rx.reorder, audio->rtcp.stat.rx.reorder);
 
1657
        MAX_(max_stat.rx.reorder, audio->rtcp.stat.rx.reorder);
 
1658
        AVG_(avg_stat.rx.reorder, audio->rtcp.stat.rx.reorder);
 
1659
 
 
1660
        /* Jitter  */
 
1661
        MIN_(min_stat.rx.jitter.min, audio->rtcp.stat.rx.jitter.min);
 
1662
        MAX_(max_stat.rx.jitter.max, audio->rtcp.stat.rx.jitter.max);
 
1663
        AVG_(avg_stat.rx.jitter.mean, audio->rtcp.stat.rx.jitter.mean);
 
1664
 
 
1665
 
 
1666
        /* TX Statistisc: */
 
1667
 
 
1668
        /* Packets */
 
1669
        MIN_(min_stat.tx.pkt, audio->rtcp.stat.tx.pkt);
 
1670
        MAX_(max_stat.tx.pkt, audio->rtcp.stat.tx.pkt);
 
1671
        AVG_(avg_stat.tx.pkt, audio->rtcp.stat.tx.pkt);
 
1672
 
 
1673
        /* Bytes */
 
1674
        MIN_(min_stat.tx.bytes, audio->rtcp.stat.tx.bytes);
 
1675
        MAX_(max_stat.tx.bytes, audio->rtcp.stat.tx.bytes);
 
1676
        AVG_(avg_stat.tx.bytes, audio->rtcp.stat.tx.bytes);
 
1677
 
 
1678
        /* Packet loss */
 
1679
        MIN_(min_stat.tx.loss, audio->rtcp.stat.tx.loss);
 
1680
        MAX_(max_stat.tx.loss, audio->rtcp.stat.tx.loss);
 
1681
        AVG_(avg_stat.tx.loss, audio->rtcp.stat.tx.loss);
 
1682
 
 
1683
        /* Packet dup */
 
1684
        MIN_(min_stat.tx.dup, audio->rtcp.stat.tx.dup);
 
1685
        MAX_(max_stat.tx.dup, audio->rtcp.stat.tx.dup);
 
1686
        AVG_(avg_stat.tx.dup, audio->rtcp.stat.tx.dup);
 
1687
 
 
1688
        /* Packet reorder */
 
1689
        MIN_(min_stat.tx.reorder, audio->rtcp.stat.tx.reorder);
 
1690
        MAX_(max_stat.tx.reorder, audio->rtcp.stat.tx.reorder);
 
1691
        AVG_(avg_stat.tx.reorder, audio->rtcp.stat.tx.reorder);
 
1692
 
 
1693
        /* Jitter  */
 
1694
        MIN_(min_stat.tx.jitter.min, audio->rtcp.stat.tx.jitter.min);
 
1695
        MAX_(max_stat.tx.jitter.max, audio->rtcp.stat.tx.jitter.max);
 
1696
        AVG_(avg_stat.tx.jitter.mean, audio->rtcp.stat.tx.jitter.mean);
 
1697
 
 
1698
 
 
1699
        /* RTT */
 
1700
        MIN_(min_stat.rtt.min, audio->rtcp.stat.rtt.min);
 
1701
        MAX_(max_stat.rtt.max, audio->rtcp.stat.rtt.max);
 
1702
        AVG_(avg_stat.rtt.mean, audio->rtcp.stat.rtt.mean);
 
1703
 
 
1704
        ++count;
 
1705
    }
 
1706
 
 
1707
    if (count == 0) {
 
1708
        puts("No active calls");
 
1709
        return;
 
1710
    }
 
1711
 
 
1712
    printf("Total %d call(s) active.\n"
 
1713
           "                    Average Statistics\n"
 
1714
           "                    min     avg     max \n"
 
1715
           "                -----------------------\n"
 
1716
           " call duration: %7d %7d %7d %s\n"
 
1717
           " connect delay: %7d %7d %7d %s\n"
 
1718
           " RX stat:\n"
 
1719
           "       packets: %7s %7s %7s %s\n"
 
1720
           "       payload: %7s %7s %7s %s\n"
 
1721
           "          loss: %7d %7d %7d %s\n"
 
1722
           "  percent loss: %7.3f %7.3f %7.3f %s\n"
 
1723
           "           dup: %7d %7d %7d %s\n"
 
1724
           "       reorder: %7d %7d %7d %s\n"
 
1725
           "        jitter: %7.3f %7.3f %7.3f %s\n"
 
1726
           " TX stat:\n"
 
1727
           "       packets: %7s %7s %7s %s\n"
 
1728
           "       payload: %7s %7s %7s %s\n"
 
1729
           "          loss: %7d %7d %7d %s\n"
 
1730
           "  percent loss: %7.3f %7.3f %7.3f %s\n"
 
1731
           "           dup: %7d %7d %7d %s\n"
 
1732
           "       reorder: %7d %7d %7d %s\n"
 
1733
           "        jitter: %7.3f %7.3f %7.3f %s\n"
 
1734
           " RTT          : %7.3f %7.3f %7.3f %s\n"
 
1735
           ,
 
1736
           count,
 
1737
           call_dur.min/1000, call_dur.avg/1000, call_dur.max/1000, 
 
1738
           "seconds",
 
1739
 
 
1740
           call_pdd.min, call_pdd.avg, call_pdd.max, 
 
1741
           "ms",
 
1742
 
 
1743
           /* rx */
 
1744
 
 
1745
           good_number(srx_min, min_stat.rx.pkt),
 
1746
           good_number(srx_avg, avg_stat.rx.pkt),
 
1747
           good_number(srx_max, max_stat.rx.pkt),
 
1748
           "packets",
 
1749
 
 
1750
           good_number(brx_min, min_stat.rx.bytes),
 
1751
           good_number(brx_avg, avg_stat.rx.bytes),
 
1752
           good_number(brx_max, max_stat.rx.bytes),
 
1753
           "bytes",
 
1754
 
 
1755
           min_stat.rx.loss, avg_stat.rx.loss, max_stat.rx.loss,
 
1756
           "packets",
 
1757
           
 
1758
           min_stat.rx.loss*100.0/(min_stat.rx.pkt+min_stat.rx.loss),
 
1759
           avg_stat.rx.loss*100.0/(avg_stat.rx.pkt+avg_stat.rx.loss),
 
1760
           max_stat.rx.loss*100.0/(max_stat.rx.pkt+max_stat.rx.loss),
 
1761
           "%",
 
1762
 
 
1763
 
 
1764
           min_stat.rx.dup, avg_stat.rx.dup, max_stat.rx.dup,
 
1765
           "packets",
 
1766
 
 
1767
           min_stat.rx.reorder, avg_stat.rx.reorder, max_stat.rx.reorder,
 
1768
           "packets",
 
1769
 
 
1770
           min_stat.rx.jitter.min/1000.0, 
 
1771
           avg_stat.rx.jitter.mean/1000.0, 
 
1772
           max_stat.rx.jitter.max/1000.0,
 
1773
           "ms",
 
1774
        
 
1775
           /* tx */
 
1776
 
 
1777
           good_number(stx_min, min_stat.tx.pkt),
 
1778
           good_number(stx_avg, avg_stat.tx.pkt),
 
1779
           good_number(stx_max, max_stat.tx.pkt),
 
1780
           "packets",
 
1781
 
 
1782
           good_number(btx_min, min_stat.tx.bytes),
 
1783
           good_number(btx_avg, avg_stat.tx.bytes),
 
1784
           good_number(btx_max, max_stat.tx.bytes),
 
1785
           "bytes",
 
1786
 
 
1787
           min_stat.tx.loss, avg_stat.tx.loss, max_stat.tx.loss,
 
1788
           "packets",
 
1789
           
 
1790
           min_stat.tx.loss*100.0/(min_stat.tx.pkt+min_stat.tx.loss),
 
1791
           avg_stat.tx.loss*100.0/(avg_stat.tx.pkt+avg_stat.tx.loss),
 
1792
           max_stat.tx.loss*100.0/(max_stat.tx.pkt+max_stat.tx.loss),
 
1793
           "%",
 
1794
 
 
1795
           min_stat.tx.dup, avg_stat.tx.dup, max_stat.tx.dup,
 
1796
           "packets",
 
1797
 
 
1798
           min_stat.tx.reorder, avg_stat.tx.reorder, max_stat.tx.reorder,
 
1799
           "packets",
 
1800
 
 
1801
           min_stat.tx.jitter.min/1000.0, 
 
1802
           avg_stat.tx.jitter.mean/1000.0, 
 
1803
           max_stat.tx.jitter.max/1000.0,
 
1804
           "ms",
 
1805
 
 
1806
           /* rtt */
 
1807
           min_stat.rtt.min/1000.0, 
 
1808
           avg_stat.rtt.mean/1000.0, 
 
1809
           max_stat.rtt.max/1000.0,
 
1810
           "ms"
 
1811
           );
 
1812
 
 
1813
}
 
1814
 
 
1815
 
 
1816
#include "siprtp_report.c"
 
1817
 
 
1818
 
 
1819
static void list_calls()
 
1820
{
 
1821
    unsigned i;
 
1822
    puts("List all calls:");
 
1823
    for (i=0; i<app.max_calls; ++i) {
 
1824
        if (!app.call[i].inv)
 
1825
            continue;
 
1826
        print_call(i);
 
1827
    }
 
1828
}
 
1829
 
 
1830
static void hangup_call(unsigned index)
 
1831
{
 
1832
    pjsip_tx_data *tdata;
 
1833
    pj_status_t status;
 
1834
 
 
1835
    if (app.call[index].inv == NULL)
 
1836
        return;
 
1837
 
 
1838
    status = pjsip_inv_end_session(app.call[index].inv, 603, NULL, &tdata);
 
1839
    if (status==PJ_SUCCESS && tdata!=NULL)
 
1840
        pjsip_inv_send_msg(app.call[index].inv, tdata);
 
1841
}
 
1842
 
 
1843
static void hangup_all_calls()
 
1844
{
 
1845
    unsigned i;
 
1846
    for (i=0; i<app.max_calls; ++i) {
 
1847
        if (!app.call[i].inv)
 
1848
            continue;
 
1849
        hangup_call(i);
 
1850
        pj_thread_sleep(app.call_gap);
 
1851
    }
 
1852
    
 
1853
    /* Wait until all calls are terminated */
 
1854
    for (i=0; i<app.max_calls; ++i) {
 
1855
        while (app.call[i].inv)
 
1856
            pj_thread_sleep(10);
 
1857
    }
 
1858
}
 
1859
 
 
1860
static pj_bool_t simple_input(const char *title, char *buf, pj_size_t len)
 
1861
{
 
1862
    char *p;
 
1863
 
 
1864
    printf("%s (empty to cancel): ", title); fflush(stdout);
 
1865
    if (fgets(buf, len, stdin) == NULL)
 
1866
        return PJ_FALSE;
 
1867
 
 
1868
    /* Remove trailing newlines. */
 
1869
    for (p=buf; ; ++p) {
 
1870
        if (*p=='\r' || *p=='\n') *p='\0';
 
1871
        else if (!*p) break;
 
1872
    }
 
1873
 
 
1874
    if (!*buf)
 
1875
        return PJ_FALSE;
 
1876
    
 
1877
    return PJ_TRUE;
 
1878
}
 
1879
 
 
1880
 
 
1881
static const char *MENU =
 
1882
"\n"
 
1883
"Enter menu character:\n"
 
1884
"  s    Summary\n"
 
1885
"  l    List all calls\n"
 
1886
"  h    Hangup a call\n"
 
1887
"  H    Hangup all calls\n"
 
1888
"  q    Quit\n"
 
1889
"\n";
 
1890
 
 
1891
 
 
1892
/* Main screen menu */
 
1893
static void console_main()
 
1894
{
 
1895
    char input1[10];
 
1896
    unsigned i;
 
1897
 
 
1898
    printf("%s", MENU);
 
1899
 
 
1900
    for (;;) {
 
1901
        printf(">>> "); fflush(stdout);
 
1902
        if (fgets(input1, sizeof(input1), stdin) == NULL) {
 
1903
            puts("EOF while reading stdin, will quit now..");
 
1904
            break;
 
1905
        }
 
1906
 
 
1907
        switch (input1[0]) {
 
1908
 
 
1909
        case 's':
 
1910
            print_avg_stat();
 
1911
            break;
 
1912
 
 
1913
        case 'l':
 
1914
            list_calls();
 
1915
            break;
 
1916
 
 
1917
        case 'h':
 
1918
            if (!simple_input("Call number to hangup", input1, sizeof(input1)))
 
1919
                break;
 
1920
 
 
1921
            i = atoi(input1);
 
1922
            hangup_call(i);
 
1923
            break;
 
1924
 
 
1925
        case 'H':
 
1926
            hangup_all_calls();
 
1927
            break;
 
1928
 
 
1929
        case 'q':
 
1930
            goto on_exit;
 
1931
 
 
1932
        default:
 
1933
            puts("Invalid command");
 
1934
            printf("%s", MENU);
 
1935
            break;
 
1936
        }
 
1937
 
 
1938
        fflush(stdout);
 
1939
    }
 
1940
 
 
1941
on_exit:
 
1942
    hangup_all_calls();
 
1943
}
 
1944
 
 
1945
 
 
1946
/*****************************************************************************
 
1947
 * Below is a simple module to log all incoming and outgoing SIP messages
 
1948
 */
 
1949
 
 
1950
 
 
1951
/* Notification on incoming messages */
 
1952
static pj_bool_t logger_on_rx_msg(pjsip_rx_data *rdata)
 
1953
{
 
1954
    PJ_LOG(4,(THIS_FILE, "RX %d bytes %s from %s:%d:\n"
 
1955
                         "%s\n"
 
1956
                         "--end msg--",
 
1957
                         rdata->msg_info.len,
 
1958
                         pjsip_rx_data_get_info(rdata),
 
1959
                         rdata->pkt_info.src_name,
 
1960
                         rdata->pkt_info.src_port,
 
1961
                         rdata->msg_info.msg_buf));
 
1962
    
 
1963
    /* Always return false, otherwise messages will not get processed! */
 
1964
    return PJ_FALSE;
 
1965
}
 
1966
 
 
1967
/* Notification on outgoing messages */
 
1968
static pj_status_t logger_on_tx_msg(pjsip_tx_data *tdata)
 
1969
{
 
1970
    
 
1971
    /* Important note:
 
1972
     *  tp_info field is only valid after outgoing messages has passed
 
1973
     *  transport layer. So don't try to access tp_info when the module
 
1974
     *  has lower priority than transport layer.
 
1975
     */
 
1976
 
 
1977
    PJ_LOG(4,(THIS_FILE, "TX %d bytes %s to %s:%d:\n"
 
1978
                         "%s\n"
 
1979
                         "--end msg--",
 
1980
                         (tdata->buf.cur - tdata->buf.start),
 
1981
                         pjsip_tx_data_get_info(tdata),
 
1982
                         tdata->tp_info.dst_name,
 
1983
                         tdata->tp_info.dst_port,
 
1984
                         tdata->buf.start));
 
1985
 
 
1986
    /* Always return success, otherwise message will not get sent! */
 
1987
    return PJ_SUCCESS;
 
1988
}
 
1989
 
 
1990
/* The module instance. */
 
1991
static pjsip_module msg_logger = 
 
1992
{
 
1993
    NULL, NULL,                         /* prev, next.          */
 
1994
    { "mod-siprtp-log", 14 },           /* Name.                */
 
1995
    -1,                                 /* Id                   */
 
1996
    PJSIP_MOD_PRIORITY_TRANSPORT_LAYER-1,/* Priority            */
 
1997
    NULL,                               /* load()               */
 
1998
    NULL,                               /* start()              */
 
1999
    NULL,                               /* stop()               */
 
2000
    NULL,                               /* unload()             */
 
2001
    &logger_on_rx_msg,                  /* on_rx_request()      */
 
2002
    &logger_on_rx_msg,                  /* on_rx_response()     */
 
2003
    &logger_on_tx_msg,                  /* on_tx_request.       */
 
2004
    &logger_on_tx_msg,                  /* on_tx_response()     */
 
2005
    NULL,                               /* on_tsx_state()       */
 
2006
 
 
2007
};
 
2008
 
 
2009
 
 
2010
 
 
2011
/*****************************************************************************
 
2012
 * Console application custom logging:
 
2013
 */
 
2014
 
 
2015
 
 
2016
static FILE *log_file;
 
2017
 
 
2018
 
 
2019
static void app_log_writer(int level, const char *buffer, int len)
 
2020
{
 
2021
    /* Write to both stdout and file. */
 
2022
 
 
2023
    if (level <= app.app_log_level)
 
2024
        pj_log_write(level, buffer, len);
 
2025
 
 
2026
    if (log_file) {
 
2027
        int count = fwrite(buffer, len, 1, log_file);
 
2028
        PJ_UNUSED_ARG(count);
 
2029
        fflush(log_file);
 
2030
    }
 
2031
}
 
2032
 
 
2033
 
 
2034
pj_status_t app_logging_init(void)
 
2035
{
 
2036
    /* Redirect log function to ours */
 
2037
 
 
2038
    pj_log_set_log_func( &app_log_writer );
 
2039
 
 
2040
    /* If output log file is desired, create the file: */
 
2041
 
 
2042
    if (app.log_filename) {
 
2043
        log_file = fopen(app.log_filename, "wt");
 
2044
        if (log_file == NULL) {
 
2045
            PJ_LOG(1,(THIS_FILE, "Unable to open log file %s", 
 
2046
                      app.log_filename));   
 
2047
            return -1;
 
2048
        }
 
2049
    }
 
2050
 
 
2051
    return PJ_SUCCESS;
 
2052
}
 
2053
 
 
2054
 
 
2055
void app_logging_shutdown(void)
 
2056
{
 
2057
    /* Close logging file, if any: */
 
2058
 
 
2059
    if (log_file) {
 
2060
        fclose(log_file);
 
2061
        log_file = NULL;
 
2062
    }
 
2063
}
 
2064
 
 
2065
 
 
2066
/*
 
2067
 * main()
 
2068
 */
 
2069
int main(int argc, char *argv[])
 
2070
{
 
2071
    unsigned i;
 
2072
    pj_status_t status;
 
2073
 
 
2074
    /* Must init PJLIB first */
 
2075
    status = pj_init();
 
2076
    if (status != PJ_SUCCESS)
 
2077
        return 1;
 
2078
 
 
2079
    /* Get command line options */
 
2080
    status = init_options(argc, argv);
 
2081
    if (status != PJ_SUCCESS)
 
2082
        return 1;
 
2083
 
 
2084
    /* Verify options: */
 
2085
 
 
2086
    /* Auto-quit can not be specified for UAS */
 
2087
    if (app.auto_quit && app.uri_to_call.slen == 0) {
 
2088
        printf("Error: --auto-quit option only valid for outgoing "
 
2089
               "mode (UAC) only\n");
 
2090
        return 1;
 
2091
    }
 
2092
 
 
2093
    /* Init logging */
 
2094
    status = app_logging_init();
 
2095
    if (status != PJ_SUCCESS)
 
2096
        return 1;
 
2097
 
 
2098
    /* Init SIP etc */
 
2099
    status = init_sip();
 
2100
    if (status != PJ_SUCCESS) {
 
2101
        app_perror(THIS_FILE, "Initialization has failed", status);
 
2102
        destroy_sip();
 
2103
        return 1;
 
2104
    }
 
2105
 
 
2106
    /* Register module to log incoming/outgoing messages */
 
2107
    pjsip_endpt_register_module(app.sip_endpt, &msg_logger);
 
2108
 
 
2109
    /* Init media */
 
2110
    status = init_media();
 
2111
    if (status != PJ_SUCCESS) {
 
2112
        app_perror(THIS_FILE, "Media initialization failed", status);
 
2113
        destroy_sip();
 
2114
        return 1;
 
2115
    }
 
2116
 
 
2117
    /* Start worker threads */
 
2118
#if PJ_HAS_THREADS
 
2119
    for (i=0; i<app.thread_count; ++i) {
 
2120
        pj_thread_create( app.pool, "app", &sip_worker_thread, NULL,
 
2121
                          0, 0, &app.sip_thread[i]);
 
2122
    }
 
2123
#endif
 
2124
 
 
2125
    /* If URL is specified, then make call immediately */
 
2126
    if (app.uri_to_call.slen) {
 
2127
        unsigned i;
 
2128
 
 
2129
        PJ_LOG(3,(THIS_FILE, "Making %d calls to %s..", app.max_calls,
 
2130
                  app.uri_to_call.ptr));
 
2131
 
 
2132
        for (i=0; i<app.max_calls; ++i) {
 
2133
            status = make_call(&app.uri_to_call);
 
2134
            if (status != PJ_SUCCESS) {
 
2135
                app_perror(THIS_FILE, "Error making call", status);
 
2136
                break;
 
2137
            }
 
2138
            pj_thread_sleep(app.call_gap);
 
2139
        }
 
2140
 
 
2141
        if (app.auto_quit) {
 
2142
            /* Wait for calls to complete */
 
2143
            while (app.uac_calls < app.max_calls)
 
2144
                pj_thread_sleep(100);
 
2145
            pj_thread_sleep(200);
 
2146
        } else {
 
2147
#if PJ_HAS_THREADS
 
2148
            /* Start user interface loop */
 
2149
            console_main();
 
2150
#endif
 
2151
        }
 
2152
 
 
2153
    } else {
 
2154
 
 
2155
        PJ_LOG(3,(THIS_FILE, "Ready for incoming calls (max=%d)", 
 
2156
                  app.max_calls));
 
2157
 
 
2158
#if PJ_HAS_THREADS
 
2159
        /* Start user interface loop */
 
2160
        console_main();
 
2161
#endif
 
2162
    }
 
2163
 
 
2164
#if !PJ_HAS_THREADS
 
2165
    PJ_LOG(3,(THIS_FILE, "Press Ctrl-C to quit"));
 
2166
    for (;;) {
 
2167
        pj_time_val t = {0, 10};
 
2168
        pjsip_endpt_handle_events(app.sip_endpt, &t);
 
2169
    }
 
2170
#endif
 
2171
    
 
2172
    /* Shutting down... */
 
2173
    destroy_sip();
 
2174
    destroy_media();
 
2175
 
 
2176
    if (app.pool) {
 
2177
        pj_pool_release(app.pool);
 
2178
        app.pool = NULL;
 
2179
        pj_caching_pool_destroy(&app.cp);
 
2180
    }
 
2181
 
 
2182
    app_logging_shutdown();
 
2183
 
 
2184
    /* Shutdown PJLIB */
 
2185
    pj_shutdown();
 
2186
 
 
2187
    return 0;
 
2188
}
 
2189