~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/jbsim.c

  • Committer: Package Import Robot
  • Author(s): Jonathan Riddell
  • Date: 2015-01-07 14:51:16 UTC
  • mfrom: (4.3.5 sid)
  • Revision ID: package-import@ubuntu.com-20150107145116-yxnafinf4lrdvrmx
Tags: 1.4.1-0.1ubuntu1
* Merge with Debian, remaining changes:
 - Drop soprano, nepomuk build-dep
* Drop ubuntu patches, now upstream

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* $Id: jbsim.c 3664 2011-07-19 03:42:28Z nanang $ */
2
 
/* 
3
 
 * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4
 
 *
5
 
 * This program is free software; you can redistribute it and/or modify
6
 
 * it under the terms of the GNU General Public License as published by
7
 
 * the Free Software Foundation; either version 2 of the License, or
8
 
 * (at your option) any later version.
9
 
 *
10
 
 * This program is distributed in the hope that it will be useful,
11
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 
 * GNU General Public License for more details.
14
 
 *
15
 
 * You should have received a copy of the GNU General Public License
16
 
 * along with this program; if not, write to the Free Software
17
 
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
18
 
 */
19
 
 
20
 
/* jbsim:
21
 
 
22
 
    This program emulates various system and network impairment
23
 
    conditions as well as application parameters and apply it to
24
 
    an input WAV file. The output is another WAV file as well as
25
 
    a detailed log file (in CSV format) for troubleshooting.
26
 
 */
27
 
 
28
 
 
29
 
/* Include PJMEDIA and PJLIB */
30
 
#include <pjmedia.h>
31
 
#include <pjmedia-codec.h>
32
 
#include <pjlib.h>
33
 
#include <pjlib-util.h>
34
 
 
35
 
#define THIS_FILE   "jbsim.c"
36
 
 
37
 
/* Timer resolution in ms (must be NONZERO!) */
38
 
#define WALL_CLOCK_TICK     1
39
 
 
40
 
/* Defaults settings */
41
 
#define CODEC           "PCMU"
42
 
#define LOG_FILE        "jbsim.csv"
43
 
#define WAV_REF         "../../tests/pjsua/wavs/input.8.wav"
44
 
#define WAV_OUT         "jbsim.wav"
45
 
#define DURATION        60
46
 
#define DTX             PJ_TRUE
47
 
#define PLC             PJ_TRUE
48
 
#define MIN_LOST_BURST  0
49
 
#define MAX_LOST_BURST  20
50
 
#define LOSS_CORR       0
51
 
#define LOSS_EXTRA      2
52
 
#define SILENT          1
53
 
 
54
 
/*
55
 
   Test setup:
56
 
 
57
 
   Input WAV --> TX Stream --> Loop transport --> RX Stream --> Out WAV
58
 
 */
59
 
 
60
 
/* Stream settings */
61
 
struct stream_cfg
62
 
{
63
 
    const char  *name;          /* for logging purposes */
64
 
    pjmedia_dir  dir;           /* stream direction     */
65
 
    pj_str_t     codec;         /* codec name           */
66
 
    unsigned     ptime;         /* zero for default     */
67
 
    pj_bool_t    dtx;           /* DTX enabled?         */
68
 
    pj_bool_t    plc;           /* PLC enabled?         */
69
 
};
70
 
 
71
 
/* Stream instance. We will instantiate two streams, TX and RX */
72
 
struct stream
73
 
{
74
 
    pj_pool_t           *pool;
75
 
    pjmedia_stream      *strm;
76
 
    pjmedia_port        *port;
77
 
 
78
 
    /*
79
 
     * Running states: 
80
 
     */
81
 
    union {
82
 
        /* TX stream state */
83
 
        struct {
84
 
            pj_time_val next_schedule;  /* Time to send next packet */
85
 
            unsigned    total_tx;       /* # of TX packets so far   */
86
 
            int         total_lost;     /* # of dropped pkts so far */
87
 
            unsigned    cur_lost_burst; /* current # of lost bursts */
88
 
            unsigned    drop_prob;      /* drop probability value   */
89
 
                                        
90
 
        } tx;
91
 
 
92
 
        /* RX stream state */
93
 
        struct {
94
 
            pj_time_val next_schedule;  /* Time to fetch next pkt   */
95
 
        } rx;
96
 
    } state;
97
 
};
98
 
 
99
 
/* 
100
 
 * Logging 
101
 
 */
102
 
 
103
 
/* Events names */
104
 
#define EVENT_LOG       ""
105
 
#define EVENT_TX        "TX/PUT"
106
 
#define EVENT_TX_DROP   "*** LOSS ***"
107
 
#define EVENT_GET_PRE   "GET (pre)"
108
 
#define EVENT_GET_POST  "GET (post)"
109
 
 
110
 
 
111
 
/* Logging entry */
112
 
struct log_entry
113
 
{
114
 
    pj_time_val                  wall_clock;    /* Wall clock time          */
115
 
    const char                  *event;         /* Event name               */
116
 
    pjmedia_jb_state            *jb_state;      /* JB state, optional       */
117
 
    pjmedia_rtcp_stat           *stat;          /* Stream stat, optional    */
118
 
    const char                  *log;           /* Log message, optional    */
119
 
};
120
 
 
121
 
/* Test settings, taken from command line */
122
 
struct test_cfg
123
 
{
124
 
    /* General options */
125
 
    pj_bool_t        silent;            /* Write little to stdout   */
126
 
    const char      *log_file;          /* The output log file      */
127
 
 
128
 
    /* Test settings */
129
 
    pj_str_t         codec;             /* Codec to be used         */
130
 
    unsigned         duration_msec;     /* Test duration            */
131
 
 
132
 
    /* Transmitter setting */
133
 
    const char      *tx_wav_in;         /* Input/reference WAV      */
134
 
    unsigned         tx_ptime;          /* TX stream ptime          */
135
 
    unsigned         tx_min_jitter;     /* Minimum jitter in ms     */
136
 
    unsigned         tx_max_jitter;     /* Max jitter in ms         */
137
 
    unsigned         tx_dtx;            /* DTX enabled?             */
138
 
    unsigned         tx_pct_avg_lost;   /* Average loss in percent  */
139
 
    unsigned         tx_min_lost_burst; /* Min lost burst in #pkt   */
140
 
    unsigned         tx_max_lost_burst; /* Max lost burst in #pkt   */
141
 
    unsigned         tx_pct_loss_corr;  /* Loss correlation in pct  */
142
 
 
143
 
    /* Receiver setting */
144
 
    const char      *rx_wav_out;        /* Output WAV file          */
145
 
    unsigned         rx_ptime;          /* RX stream ptime          */
146
 
    unsigned         rx_snd_burst;      /* RX sound burst           */
147
 
    pj_bool_t        rx_plc;            /* RX PLC enabled?          */
148
 
    int              rx_jb_init;        /* if > 0 will enable prefetch (ms) */
149
 
    int              rx_jb_min_pre;     /* JB minimum prefetch (ms) */
150
 
    int              rx_jb_max_pre;     /* JB maximum prefetch (ms) */
151
 
    int              rx_jb_max;         /* JB maximum size (ms)     */
152
 
};
153
 
 
154
 
/*
155
 
 * Global var
156
 
 */
157
 
struct global_app
158
 
{
159
 
    pj_caching_pool      cp;
160
 
    pj_pool_t           *pool;
161
 
    pj_int16_t          *framebuf;
162
 
    pjmedia_endpt       *endpt;
163
 
    pjmedia_transport   *loop;
164
 
 
165
 
    pj_oshandle_t        log_fd;
166
 
 
167
 
    struct test_cfg      cfg;
168
 
 
169
 
    struct stream       *tx;
170
 
    pjmedia_port        *tx_wav;
171
 
 
172
 
    struct stream       *rx;
173
 
    pjmedia_port        *rx_wav;
174
 
 
175
 
    pj_time_val          wall_clock;
176
 
};
177
 
 
178
 
static struct global_app g_app;
179
 
 
180
 
 
181
 
#ifndef MAX
182
 
#   define MAX(a,b)     (a<b ? b : a)
183
 
#endif
184
 
 
185
 
#ifndef MIN
186
 
#   define MIN(a,b)     (a<b ? a : b)
187
 
#endif
188
 
 
189
 
/*****************************************************************************
190
 
 * Logging
191
 
 */
192
 
static void write_log(struct log_entry *entry, pj_bool_t to_stdout)
193
 
{
194
 
    /* Format (CSV): */
195
 
    const char *format = "TIME;EVENT;#RX packets;#packets lost;#JB prefetch;#JB size;#JBDISCARD;#JBEMPTY;Log Message";
196
 
    static char log[2000];
197
 
    enum { D = 20 };
198
 
    char s_jbprefetch[D],
199
 
         s_jbsize[D],
200
 
         s_rxpkt[D],
201
 
         s_losspkt[D],
202
 
         s_jbdiscard[D],
203
 
         s_jbempty[D];
204
 
    static pj_bool_t header_written;
205
 
 
206
 
    if (!header_written) {
207
 
        pj_ansi_snprintf(log, sizeof(log),
208
 
                         "%s\n", format);
209
 
        if (g_app.log_fd != NULL) {
210
 
            pj_ssize_t size = strlen(log);
211
 
            pj_file_write(g_app.log_fd, log, &size);
212
 
        }
213
 
        if (to_stdout && !g_app.cfg.silent)
214
 
            printf("%s", log);
215
 
        header_written = PJ_TRUE;
216
 
    }
217
 
 
218
 
    if (entry->jb_state) {
219
 
        sprintf(s_jbprefetch, "%d", entry->jb_state->prefetch);
220
 
        sprintf(s_jbsize, "%d", entry->jb_state->size);
221
 
        sprintf(s_jbdiscard, "%d", entry->jb_state->discard);
222
 
        sprintf(s_jbempty, "%d", entry->jb_state->empty);
223
 
    } else {
224
 
        strcpy(s_jbprefetch, "");
225
 
        strcpy(s_jbsize, "");
226
 
        strcpy(s_jbdiscard, "");
227
 
        strcpy(s_jbempty, "");
228
 
    }
229
 
 
230
 
    if (entry->stat) {
231
 
        sprintf(s_rxpkt, "%d", entry->stat->rx.pkt);
232
 
        sprintf(s_losspkt, "%d", entry->stat->rx.loss);
233
 
    } else {
234
 
        strcpy(s_rxpkt, "");
235
 
        strcpy(s_losspkt, "");
236
 
    }
237
 
 
238
 
    if (entry->log == NULL)
239
 
        entry->log = "";
240
 
 
241
 
    pj_ansi_snprintf(log, sizeof(log),
242
 
                     "'%d.%03d;"            /* time */
243
 
                     "%s;"          /* event */
244
 
                     "%s;"          /* rxpkt */
245
 
                     "%s;"          /* jb prefetch */
246
 
                     "%s;"          /* jbsize */
247
 
                     "%s;"          /* losspkt */
248
 
                     "%s;"          /* jbdiscard */
249
 
                     "%s;"          /* jbempty */
250
 
                     "%s\n"         /* logmsg */,
251
 
 
252
 
                     (int)entry->wall_clock.sec, (int)entry->wall_clock.msec, /* time */
253
 
                     entry->event,
254
 
                     s_rxpkt,
255
 
                     s_losspkt,
256
 
                     s_jbprefetch,
257
 
                     s_jbsize,
258
 
                     s_jbdiscard,
259
 
                     s_jbempty,
260
 
                     entry->log
261
 
                     );
262
 
    if (g_app.log_fd != NULL) {
263
 
        pj_ssize_t size = strlen(log);
264
 
        pj_file_write(g_app.log_fd, log, &size);
265
 
    }
266
 
 
267
 
    if (to_stdout && !g_app.cfg.silent)
268
 
        printf("%s", log);
269
 
}
270
 
 
271
 
static void log_cb(int level, const char *data, int len)
272
 
{
273
 
    struct log_entry entry;
274
 
 
275
 
    /* Write to stdout */
276
 
    pj_log_write(level, data, len);
277
 
    puts("");
278
 
 
279
 
    /* Also add to CSV file */
280
 
    pj_bzero(&entry, sizeof(entry));
281
 
    entry.event = EVENT_LOG;
282
 
    entry.log = data;
283
 
    entry.wall_clock = g_app.wall_clock;
284
 
    write_log(&entry, PJ_FALSE);
285
 
}
286
 
 
287
 
static void jbsim_perror(const char *title, pj_status_t status)
288
 
{
289
 
    char errmsg[PJ_ERR_MSG_SIZE];
290
 
 
291
 
    pj_strerror(status, errmsg, sizeof(errmsg));
292
 
    PJ_LOG(1,(THIS_FILE, "%s: %s", title, errmsg));
293
 
}
294
 
 
295
 
/*****************************************************************************
296
 
 * stream
297
 
 */
298
 
 
299
 
static void stream_destroy(struct stream *stream)
300
 
{
301
 
    if (stream->strm)
302
 
        pjmedia_stream_destroy(stream->strm);
303
 
    if (stream->pool)
304
 
        pj_pool_release(stream->pool);
305
 
}
306
 
 
307
 
static pj_status_t stream_init(const struct stream_cfg *cfg, struct stream **p_stream)
308
 
{
309
 
    pj_pool_t *pool = NULL;
310
 
    struct stream *stream = NULL;
311
 
    pjmedia_codec_mgr *cm;
312
 
    unsigned count;
313
 
    const pjmedia_codec_info *ci;
314
 
    pjmedia_stream_info si;
315
 
    pj_status_t status;
316
 
 
317
 
    /* Create instance */
318
 
    pool = pj_pool_create(&g_app.cp.factory, cfg->name, 512, 512, NULL);
319
 
    stream = PJ_POOL_ZALLOC_T(pool, struct stream);
320
 
    stream->pool = pool;
321
 
    
322
 
    /* Create stream info */
323
 
    pj_bzero(&si, sizeof(si));
324
 
    si.type = PJMEDIA_TYPE_AUDIO;
325
 
    si.proto = PJMEDIA_TP_PROTO_RTP_AVP;
326
 
    si.dir = cfg->dir;
327
 
    pj_sockaddr_in_init(&si.rem_addr.ipv4, NULL, 4000); /* dummy */
328
 
    pj_sockaddr_in_init(&si.rem_rtcp.ipv4, NULL, 4001); /* dummy */
329
 
 
330
 
    /* Apply JB settings if this is RX direction */
331
 
    if (cfg->dir == PJMEDIA_DIR_DECODING) {
332
 
        si.jb_init = g_app.cfg.rx_jb_init;
333
 
        si.jb_min_pre = g_app.cfg.rx_jb_min_pre;
334
 
        si.jb_max_pre = g_app.cfg.rx_jb_max_pre;
335
 
        si.jb_max = g_app.cfg.rx_jb_max;
336
 
    }
337
 
 
338
 
    /* Get the codec info and param */
339
 
    cm = pjmedia_endpt_get_codec_mgr(g_app.endpt);
340
 
    count = 1;
341
 
    status = pjmedia_codec_mgr_find_codecs_by_id(cm, &cfg->codec, &count, &ci, NULL);
342
 
    if (status != PJ_SUCCESS) {
343
 
        jbsim_perror("Unable to find codec", status);
344
 
        goto on_error;
345
 
    }
346
 
 
347
 
    pj_memcpy(&si.fmt, ci, sizeof(*ci));
348
 
 
349
 
    si.param = PJ_POOL_ALLOC_T(pool, struct pjmedia_codec_param);
350
 
    status = pjmedia_codec_mgr_get_default_param(cm, &si.fmt, si.param);
351
 
    if (status != PJ_SUCCESS) {
352
 
        jbsim_perror("Unable to get codec defaults", status);
353
 
        goto on_error;
354
 
    }
355
 
 
356
 
    si.tx_pt = si.fmt.pt;
357
 
 
358
 
    /* Apply ptime setting */
359
 
    if (cfg->ptime) {
360
 
        si.param->setting.frm_per_pkt = (pj_uint8_t)
361
 
                                        ((cfg->ptime + si.param->info.frm_ptime - 1) /
362
 
                                         si.param->info.frm_ptime);
363
 
    }
364
 
    /* Apply DTX setting */
365
 
    si.param->setting.vad = cfg->dtx;
366
 
 
367
 
    /* Apply PLC setting */
368
 
    si.param->setting.plc = cfg->plc;
369
 
 
370
 
    /* Create stream */
371
 
    status = pjmedia_stream_create(g_app.endpt, pool, &si, g_app.loop, NULL, &stream->strm);
372
 
    if (status != PJ_SUCCESS) {
373
 
        jbsim_perror("Error creating stream", status);
374
 
        goto on_error;
375
 
    }
376
 
 
377
 
    status = pjmedia_stream_get_port(stream->strm, &stream->port);
378
 
    if (status != PJ_SUCCESS) {
379
 
        jbsim_perror("Error retrieving stream", status);
380
 
        goto on_error;
381
 
    }
382
 
 
383
 
    /* Start stream */
384
 
    status = pjmedia_stream_start(stream->strm);
385
 
    if (status != PJ_SUCCESS) {
386
 
        jbsim_perror("Error starting stream", status);
387
 
        goto on_error;
388
 
    }
389
 
 
390
 
    /* Done */
391
 
    *p_stream = stream;
392
 
    return PJ_SUCCESS;
393
 
 
394
 
on_error:
395
 
    if (stream) {
396
 
        stream_destroy(stream);
397
 
    } else {
398
 
        if (pool)
399
 
            pj_pool_release(pool);
400
 
    }
401
 
    return status;
402
 
}
403
 
 
404
 
 
405
 
/*****************************************************************************
406
 
 * The test session
407
 
 */
408
 
static void test_destroy(void)
409
 
{
410
 
    if (g_app.tx)
411
 
        stream_destroy(g_app.tx);
412
 
    if (g_app.tx_wav)
413
 
        pjmedia_port_destroy(g_app.tx_wav);
414
 
    if (g_app.rx)
415
 
        stream_destroy(g_app.rx);
416
 
    if (g_app.rx_wav)
417
 
        pjmedia_port_destroy(g_app.rx_wav);
418
 
    if (g_app.loop)
419
 
        pjmedia_transport_close(g_app.loop);
420
 
    if (g_app.endpt)
421
 
        pjmedia_endpt_destroy( g_app.endpt );
422
 
    if (g_app.log_fd) {
423
 
        pj_log_set_log_func(&pj_log_write);
424
 
        pj_log_set_decor(pj_log_get_decor() | PJ_LOG_HAS_NEWLINE);
425
 
        pj_file_close(g_app.log_fd);
426
 
        g_app.log_fd = NULL;
427
 
    }
428
 
    if (g_app.pool)
429
 
        pj_pool_release(g_app.pool);
430
 
    pj_caching_pool_destroy( &g_app.cp );
431
 
    pj_shutdown();
432
 
}
433
 
 
434
 
 
435
 
static pj_status_t test_init(void)
436
 
{
437
 
    struct stream_cfg strm_cfg;
438
 
    pj_status_t status;
439
 
 
440
 
    /* Must init PJLIB first: */
441
 
    status = pj_init();
442
 
    PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
443
 
 
444
 
    /* Must create a pool factory before we can allocate any memory. */
445
 
    pj_caching_pool_init(&g_app.cp, &pj_pool_factory_default_policy, 0);
446
 
 
447
 
    /* Pool */
448
 
    g_app.pool = pj_pool_create(&g_app.cp.factory, "g_app", 512, 512, NULL);
449
 
 
450
 
    /* Log file */
451
 
    if (g_app.cfg.log_file) {
452
 
        status = pj_file_open(g_app.pool, g_app.cfg.log_file, 
453
 
                              PJ_O_WRONLY,
454
 
                              &g_app.log_fd);
455
 
        if (status != PJ_SUCCESS) {
456
 
            jbsim_perror("Error writing output file", status);
457
 
            goto on_error;
458
 
        }
459
 
 
460
 
        pj_log_set_decor(PJ_LOG_HAS_SENDER | PJ_LOG_HAS_COLOR | PJ_LOG_HAS_LEVEL_TEXT);
461
 
        pj_log_set_log_func(&log_cb);
462
 
    }
463
 
 
464
 
    /* 
465
 
     * Initialize media endpoint.
466
 
     * This will implicitly initialize PJMEDIA too.
467
 
     */
468
 
    status = pjmedia_endpt_create(&g_app.cp.factory, NULL, 0, &g_app.endpt);
469
 
    if (status != PJ_SUCCESS) {
470
 
        jbsim_perror("Error creating media endpoint", status);
471
 
        goto on_error;
472
 
    }
473
 
 
474
 
    /* Register codecs */
475
 
    pjmedia_codec_register_audio_codecs(g_app.endpt, NULL);
476
 
 
477
 
    /* Create the loop transport */
478
 
    status = pjmedia_transport_loop_create(g_app.endpt, &g_app.loop);
479
 
    if (status != PJ_SUCCESS) {
480
 
        jbsim_perror("Error creating loop transport", status);
481
 
        goto on_error;
482
 
    }
483
 
 
484
 
    /* Create transmitter stream */
485
 
    pj_bzero(&strm_cfg, sizeof(strm_cfg));
486
 
    strm_cfg.name = "tx";
487
 
    strm_cfg.dir = PJMEDIA_DIR_ENCODING;
488
 
    strm_cfg.codec = g_app.cfg.codec;
489
 
    strm_cfg.ptime = g_app.cfg.tx_ptime;
490
 
    strm_cfg.dtx = g_app.cfg.tx_dtx;
491
 
    strm_cfg.plc = PJ_TRUE;
492
 
    status = stream_init(&strm_cfg, &g_app.tx);
493
 
    if (status != PJ_SUCCESS) 
494
 
        goto on_error;
495
 
 
496
 
    /* Create transmitter WAV */
497
 
    status = pjmedia_wav_player_port_create(g_app.pool, 
498
 
                                            g_app.cfg.tx_wav_in,
499
 
                                            g_app.cfg.tx_ptime,
500
 
                                            0,
501
 
                                            0,
502
 
                                            &g_app.tx_wav);
503
 
    if (status != PJ_SUCCESS) {
504
 
        jbsim_perror("Error reading input WAV file", status);
505
 
        goto on_error;
506
 
    }
507
 
 
508
 
    /* Make sure stream and WAV parameters match */
509
 
    if (PJMEDIA_PIA_SRATE(&g_app.tx_wav->info) != PJMEDIA_PIA_SRATE(&g_app.tx->port->info) ||
510
 
        PJMEDIA_PIA_CCNT(&g_app.tx_wav->info) != PJMEDIA_PIA_CCNT(&g_app.tx->port->info))
511
 
    {
512
 
        jbsim_perror("Error: Input WAV file has different clock rate "
513
 
                     "or number of channels than the codec", PJ_SUCCESS);
514
 
        goto on_error;
515
 
    }
516
 
 
517
 
 
518
 
    /* Create receiver */
519
 
    pj_bzero(&strm_cfg, sizeof(strm_cfg));
520
 
    strm_cfg.name = "rx";
521
 
    strm_cfg.dir = PJMEDIA_DIR_DECODING;
522
 
    strm_cfg.codec = g_app.cfg.codec;
523
 
    strm_cfg.ptime = g_app.cfg.rx_ptime;
524
 
    strm_cfg.dtx = PJ_TRUE;
525
 
    strm_cfg.plc = g_app.cfg.rx_plc;
526
 
    status = stream_init(&strm_cfg, &g_app.rx);
527
 
    if (status != PJ_SUCCESS) 
528
 
        goto on_error;
529
 
 
530
 
    /* Create receiver WAV */
531
 
    status = pjmedia_wav_writer_port_create(g_app.pool, 
532
 
                                            g_app.cfg.rx_wav_out,
533
 
                                            PJMEDIA_PIA_SRATE(&g_app.rx->port->info),
534
 
                                            PJMEDIA_PIA_CCNT(&g_app.rx->port->info),
535
 
                                            PJMEDIA_PIA_SPF(&g_app.rx->port->info),
536
 
                                            PJMEDIA_PIA_BITS(&g_app.rx->port->info),
537
 
                                            0,
538
 
                                            0,
539
 
                                            &g_app.rx_wav);
540
 
    if (status != PJ_SUCCESS) {
541
 
        jbsim_perror("Error creating output WAV file", status);
542
 
        goto on_error;
543
 
    }
544
 
 
545
 
 
546
 
    /* Frame buffer */
547
 
    g_app.framebuf = (pj_int16_t*)
548
 
                     pj_pool_alloc(g_app.pool,
549
 
                                   MAX(PJMEDIA_PIA_SPF(&g_app.rx->port->info),
550
 
                                       PJMEDIA_PIA_SPF(&g_app.tx->port->info)) * sizeof(pj_int16_t));
551
 
 
552
 
 
553
 
    /* Set the receiver in the loop transport */
554
 
    pjmedia_transport_loop_disable_rx(g_app.loop, g_app.tx->strm, PJ_TRUE);
555
 
 
556
 
    /* Done */
557
 
    return PJ_SUCCESS;
558
 
 
559
 
on_error:
560
 
    test_destroy();
561
 
    return status;
562
 
}
563
 
 
564
 
static void run_one_frame(pjmedia_port *src, pjmedia_port *dst,
565
 
                          pj_bool_t *has_frame)
566
 
{
567
 
    pjmedia_frame frame;
568
 
    pj_status_t status;
569
 
 
570
 
    pj_bzero(&frame, sizeof(frame));
571
 
    frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
572
 
    frame.buf = g_app.framebuf;
573
 
    frame.size = PJMEDIA_PIA_SPF(&dst->info) * 2;
574
 
    
575
 
    status = pjmedia_port_get_frame(src, &frame);
576
 
    pj_assert(status == PJ_SUCCESS);
577
 
 
578
 
    if (status!= PJ_SUCCESS || frame.type != PJMEDIA_FRAME_TYPE_AUDIO) {
579
 
        frame.buf = g_app.framebuf;
580
 
        pjmedia_zero_samples(g_app.framebuf, PJMEDIA_PIA_SPF(&src->info));
581
 
        frame.size = PJMEDIA_PIA_SPF(&src->info) * 2;
582
 
        if (has_frame)
583
 
            *has_frame = PJ_FALSE;
584
 
    } else {
585
 
        if (has_frame)
586
 
            *has_frame = PJ_TRUE;
587
 
    }
588
 
 
589
 
 
590
 
    status = pjmedia_port_put_frame(dst, &frame);
591
 
    pj_assert(status == PJ_SUCCESS);
592
 
}
593
 
 
594
 
 
595
 
/* This is the transmission "tick".
596
 
 * This function is called periodically every "tick" milliseconds, and
597
 
 * it will determine whether to transmit packet(s) (or to drop it).
598
 
 */
599
 
static void tx_tick(const pj_time_val *t)
600
 
{
601
 
    struct stream *strm = g_app.tx;
602
 
    static char log_msg[120];
603
 
    pjmedia_port *port = g_app.tx->port;
604
 
    long pkt_interval; 
605
 
 
606
 
    /* packet interval, without jitter */
607
 
    pkt_interval = PJMEDIA_PIA_SPF(&port->info) * 1000 /
608
 
                   PJMEDIA_PIA_SRATE(&port->info);
609
 
 
610
 
    while (PJ_TIME_VAL_GTE(*t, strm->state.tx.next_schedule)) {
611
 
        struct log_entry entry;
612
 
        pj_bool_t drop_this_pkt = PJ_FALSE;
613
 
        int jitter;
614
 
 
615
 
        /* Init log entry */
616
 
        pj_bzero(&entry, sizeof(entry));
617
 
        entry.wall_clock = *t;
618
 
 
619
 
        /* 
620
 
         * Determine whether to drop this packet 
621
 
         */
622
 
        if (strm->state.tx.cur_lost_burst) {
623
 
            /* We are currently dropping packet */
624
 
 
625
 
            /* Make it comply to minimum lost burst */
626
 
            if (strm->state.tx.cur_lost_burst < g_app.cfg.tx_min_lost_burst) {
627
 
                drop_this_pkt = PJ_TRUE;
628
 
            }
629
 
 
630
 
            /* Correlate the next packet loss */
631
 
            if (!drop_this_pkt && 
632
 
                strm->state.tx.cur_lost_burst < g_app.cfg.tx_max_lost_burst &&
633
 
                MAX(strm->state.tx.total_lost-LOSS_EXTRA,0) * 100 / MAX(strm->state.tx.total_tx,1) < g_app.cfg.tx_pct_avg_lost
634
 
               ) 
635
 
            {
636
 
                strm->state.tx.drop_prob = ((g_app.cfg.tx_pct_loss_corr * strm->state.tx.drop_prob) +
637
 
                                             ((100-g_app.cfg.tx_pct_loss_corr) * (pj_rand()%100))
638
 
                                           ) / 100;
639
 
                if (strm->state.tx.drop_prob >= 100)
640
 
                    strm->state.tx.drop_prob = 99;
641
 
 
642
 
                if (strm->state.tx.drop_prob >= 100 - g_app.cfg.tx_pct_avg_lost)
643
 
                    drop_this_pkt = PJ_TRUE;
644
 
            }
645
 
        }
646
 
 
647
 
        /* If we're not dropping packet then use randomly distributed loss */
648
 
        if (!drop_this_pkt &&
649
 
            MAX(strm->state.tx.total_lost-LOSS_EXTRA,0) * 100 / MAX(strm->state.tx.total_tx,1) < g_app.cfg.tx_pct_avg_lost)
650
 
        {
651
 
            strm->state.tx.drop_prob = pj_rand() % 100;
652
 
 
653
 
            if (strm->state.tx.drop_prob >= 100 - g_app.cfg.tx_pct_avg_lost)
654
 
                drop_this_pkt = PJ_TRUE;
655
 
        }
656
 
 
657
 
        if (drop_this_pkt) {
658
 
            /* Drop the frame */
659
 
            pjmedia_transport_simulate_lost(g_app.loop, PJMEDIA_DIR_ENCODING, 100);
660
 
            run_one_frame(g_app.tx_wav, g_app.tx->port, NULL);
661
 
            pjmedia_transport_simulate_lost(g_app.loop, PJMEDIA_DIR_ENCODING, 0);
662
 
 
663
 
            entry.event = EVENT_TX_DROP;
664
 
            entry.log = "** This packet was lost **";
665
 
 
666
 
            ++strm->state.tx.total_lost;
667
 
            ++strm->state.tx.cur_lost_burst;
668
 
 
669
 
        } else {
670
 
            pjmedia_rtcp_stat stat;
671
 
            pjmedia_jb_state jstate;
672
 
            unsigned last_discard;
673
 
 
674
 
            pjmedia_stream_get_stat_jbuf(g_app.rx->strm, &jstate);
675
 
            last_discard = jstate.discard;
676
 
 
677
 
            run_one_frame(g_app.tx_wav, g_app.tx->port, NULL);
678
 
 
679
 
            pjmedia_stream_get_stat(g_app.rx->strm, &stat);
680
 
            pjmedia_stream_get_stat_jbuf(g_app.rx->strm, &jstate);
681
 
 
682
 
            entry.event = EVENT_TX;
683
 
            entry.jb_state = &jstate;
684
 
            entry.stat = &stat;
685
 
            entry.log = log_msg;
686
 
 
687
 
            if (jstate.discard > last_discard)
688
 
                strcat(log_msg, "** Note: packet was discarded by jitter buffer **");
689
 
 
690
 
            strm->state.tx.cur_lost_burst = 0;
691
 
        }
692
 
 
693
 
        write_log(&entry, PJ_TRUE);
694
 
 
695
 
        ++strm->state.tx.total_tx;
696
 
 
697
 
        /* Calculate next schedule */
698
 
        strm->state.tx.next_schedule.sec = 0;
699
 
        strm->state.tx.next_schedule.msec = (strm->state.tx.total_tx + 1) * pkt_interval;
700
 
 
701
 
        /* Apply jitter */
702
 
        if (g_app.cfg.tx_max_jitter || g_app.cfg.tx_min_jitter) {
703
 
 
704
 
            if (g_app.cfg.tx_max_jitter == g_app.cfg.tx_min_jitter) {
705
 
                /* Fixed jitter */
706
 
                switch (pj_rand() % 3) {
707
 
                case 0:
708
 
                    jitter = 0 - g_app.cfg.tx_min_jitter;
709
 
                    break;
710
 
                case 2:
711
 
                    jitter = g_app.cfg.tx_min_jitter;
712
 
                    break;
713
 
                default:
714
 
                    jitter = 0;
715
 
                    break;
716
 
                }
717
 
            } else {
718
 
                int jitter_range;
719
 
                jitter_range = (g_app.cfg.tx_max_jitter-g_app.cfg.tx_min_jitter)*2;
720
 
                jitter = pj_rand() % jitter_range;
721
 
                if (jitter < jitter_range/2) {
722
 
                    jitter = 0 - g_app.cfg.tx_min_jitter - (jitter/2);
723
 
                } else {
724
 
                    jitter = g_app.cfg.tx_min_jitter + (jitter/2);
725
 
                }
726
 
            }
727
 
 
728
 
        } else {
729
 
            jitter = 0;
730
 
        }
731
 
 
732
 
        pj_time_val_normalize(&strm->state.tx.next_schedule);
733
 
 
734
 
        sprintf(log_msg, "** Packet #%u tick is at %d.%03d, %d ms jitter applied **", 
735
 
                strm->state.tx.total_tx+1,
736
 
                (int)strm->state.tx.next_schedule.sec, (int)strm->state.tx.next_schedule.msec,
737
 
                jitter);
738
 
 
739
 
        strm->state.tx.next_schedule.msec += jitter;
740
 
        pj_time_val_normalize(&strm->state.tx.next_schedule);
741
 
 
742
 
    } /* while */
743
 
}
744
 
 
745
 
 
746
 
/* This is the RX "tick".
747
 
 * This function is called periodically every "tick" milliseconds, and
748
 
 * it will determine whether to call get_frame() from the RX stream.
749
 
 */
750
 
static void rx_tick(const pj_time_val *t)
751
 
{
752
 
    struct stream *strm = g_app.rx;
753
 
    pjmedia_port *port = g_app.rx->port;
754
 
    long pkt_interval;
755
 
 
756
 
    pkt_interval = PJMEDIA_PIA_SPF(&port->info) * 1000 /
757
 
                   PJMEDIA_PIA_SRATE(&port->info) *
758
 
                   g_app.cfg.rx_snd_burst;
759
 
 
760
 
    if (PJ_TIME_VAL_GTE(*t, strm->state.rx.next_schedule)) {
761
 
        unsigned i;
762
 
        for (i=0; i<g_app.cfg.rx_snd_burst; ++i) {
763
 
            struct log_entry entry;
764
 
            pjmedia_rtcp_stat stat;
765
 
            pjmedia_jb_state jstate;
766
 
            pj_bool_t has_frame;
767
 
            char msg[120];
768
 
            unsigned last_empty;
769
 
 
770
 
            pjmedia_stream_get_stat(g_app.rx->strm, &stat);
771
 
            pjmedia_stream_get_stat_jbuf(g_app.rx->strm, &jstate);
772
 
            last_empty = jstate.empty;
773
 
 
774
 
            /* Pre GET event */
775
 
            pj_bzero(&entry, sizeof(entry));
776
 
            entry.event = EVENT_GET_PRE;
777
 
            entry.wall_clock = *t;
778
 
            entry.stat = &stat;
779
 
            entry.jb_state = &jstate;
780
 
 
781
 
            write_log(&entry, PJ_TRUE);
782
 
 
783
 
            /* GET */
784
 
            run_one_frame(g_app.rx->port, g_app.rx_wav, &has_frame);
785
 
 
786
 
            /* Post GET event */
787
 
            pjmedia_stream_get_stat(g_app.rx->strm, &stat);
788
 
            pjmedia_stream_get_stat_jbuf(g_app.rx->strm, &jstate);
789
 
 
790
 
            pj_bzero(&entry, sizeof(entry));
791
 
            entry.event = EVENT_GET_POST;
792
 
            entry.wall_clock = *t;
793
 
            entry.stat = &stat;
794
 
            entry.jb_state = &jstate;
795
 
 
796
 
            msg[0] = '\0';
797
 
            entry.log = msg;
798
 
 
799
 
            if (jstate.empty > last_empty)
800
 
                strcat(msg, "** JBUF was empty **");
801
 
            if (!has_frame)
802
 
                strcat(msg, "** NULL frame was returned **");
803
 
 
804
 
            write_log(&entry, PJ_TRUE);
805
 
 
806
 
        }
807
 
 
808
 
 
809
 
        strm->state.rx.next_schedule.msec += pkt_interval;
810
 
        pj_time_val_normalize(&strm->state.rx.next_schedule);
811
 
    }
812
 
            
813
 
}
814
 
 
815
 
static void test_loop(long duration)
816
 
{
817
 
    g_app.wall_clock.sec = 0;
818
 
    g_app.wall_clock.msec = 0;
819
 
 
820
 
    while (PJ_TIME_VAL_MSEC(g_app.wall_clock) <= duration) {
821
 
 
822
 
        /* Run TX tick */
823
 
        tx_tick(&g_app.wall_clock);
824
 
 
825
 
        /* Run RX tick */
826
 
        rx_tick(&g_app.wall_clock);
827
 
 
828
 
        /* Increment tick */
829
 
        g_app.wall_clock.msec += WALL_CLOCK_TICK;
830
 
        pj_time_val_normalize(&g_app.wall_clock);
831
 
    }
832
 
}
833
 
 
834
 
 
835
 
/*****************************************************************************
836
 
 * usage()
837
 
 */
838
 
enum {
839
 
    OPT_CODEC       = 'c',
840
 
    OPT_INPUT       = 'i',
841
 
    OPT_OUTPUT      = 'o',
842
 
    OPT_DURATION    = 'd',
843
 
    OPT_LOG_FILE    = 'l',
844
 
    OPT_LOSS        = 'x',
845
 
    OPT_MIN_JITTER  = 'j',
846
 
    OPT_MAX_JITTER  = 'J',
847
 
    OPT_SND_BURST   = 'b',
848
 
    OPT_TX_PTIME    = 't',
849
 
    OPT_RX_PTIME    = 'r',
850
 
    OPT_NO_VAD      = 'U',
851
 
    OPT_NO_PLC      = 'p',
852
 
    OPT_JB_PREFETCH = 'P', 
853
 
    OPT_JB_MIN_PRE  = 'm',
854
 
    OPT_JB_MAX_PRE  = 'M',
855
 
    OPT_JB_MAX      = 'X',
856
 
    OPT_HELP        = 'h',
857
 
    OPT_MIN_LOST_BURST = 1,
858
 
    OPT_MAX_LOST_BURST,
859
 
    OPT_LOSS_CORR,
860
 
};
861
 
 
862
 
 
863
 
static void usage(void)
864
 
{
865
 
    printf("jbsim - System and network impairments simulator\n");
866
 
    printf("Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com)\n");
867
 
    printf("\n");
868
 
    printf("This program emulates various system and network impairment\n");
869
 
    printf("conditions as well as application parameters and apply it to\n");
870
 
    printf("an input WAV file. The output is another WAV file as well as\n");
871
 
    printf("a detailed log file (in CSV format) for troubleshooting.\n");
872
 
    printf("\n");
873
 
    printf("Usage:\n");
874
 
    printf(" jbsim [OPTIONS]\n");
875
 
    printf("\n");
876
 
    printf("General OPTIONS:\n");
877
 
    printf("  --codec, -%c NAME       Set the audio codec\n", OPT_CODEC);
878
 
    printf("                         Default: %s\n", CODEC);
879
 
    printf("  --input, -%c FILE       Set WAV reference file to FILE\n", OPT_INPUT);
880
 
    printf("                         Default: " WAV_REF "\n");
881
 
    printf("  --output, -%c FILE      Set WAV output file to FILE\n", OPT_OUTPUT);
882
 
    printf("                         Default: " WAV_OUT "\n");
883
 
    printf("  --duration, -%c SEC     Set test duration to SEC seconds\n", OPT_DURATION);
884
 
    printf("                         Default: %d\n", DURATION);
885
 
    printf("  --log-file, -%c FILE    Save simulation log file to FILE\n", OPT_LOG_FILE);
886
 
    printf("                         Note: FILE will be in CSV format with semicolon separator\n");
887
 
    printf("                         Default: %s\n", LOG_FILE);
888
 
    printf("  --help, -h             Display this screen\n");
889
 
    printf("\n");
890
 
    printf("Simulation OPTIONS:\n");
891
 
    printf("  --loss, -%c PCT         Set packet average loss to PCT percent\n", OPT_LOSS);
892
 
    printf("                         Default: 0\n");
893
 
    printf("  --loss-corr PCT        Set the loss correlation to PCT percent. Default: 0\n");
894
 
    printf("  --min-lost-burst N     Set minimum packet lost burst (default:%d)\n", MIN_LOST_BURST);
895
 
    printf("  --max-lost-burst N     Set maximum packet lost burst (default:%d)\n", MAX_LOST_BURST);
896
 
    printf("  --min-jitter, -%c MSEC  Set minimum network jitter to MSEC\n", OPT_MIN_JITTER);
897
 
    printf("                         Default: 0\n");
898
 
    printf("  --max-jitter, -%c MSEC  Set maximum network jitter to MSEC\n", OPT_MAX_JITTER);
899
 
    printf("                         Default: 0\n");
900
 
    printf("  --snd-burst, -%c VAL    Set RX sound burst value to VAL frames.\n", OPT_SND_BURST);
901
 
    printf("                         Default: 1\n");
902
 
    printf("  --tx-ptime, -%c MSEC    Set transmitter ptime to MSEC\n", OPT_TX_PTIME);
903
 
    printf("                         Default: 0 (not set, use default)\n");
904
 
    printf("  --rx-ptime, -%c MSEC    Set receiver ptime to MSEC\n", OPT_RX_PTIME);
905
 
    printf("                         Default: 0 (not set, use default)\n");
906
 
    printf("  --no-vad, -%c           Disable VAD/DTX in transmitter\n", OPT_NO_VAD);
907
 
    printf("  --no-plc, -%c           Disable PLC in receiver\n", OPT_NO_PLC);
908
 
    printf("  --jb-prefetch, -%c      Enable prefetch bufferring in jitter buffer\n", OPT_JB_PREFETCH);
909
 
    printf("  --jb-min-pre, -%c MSEC  Jitter buffer minimum prefetch delay in msec\n", OPT_JB_MIN_PRE);
910
 
    printf("  --jb-max-pre, -%c MSEC  Jitter buffer maximum prefetch delay in msec\n", OPT_JB_MAX_PRE);
911
 
    printf("  --jb-max, -%c MSEC      Set maximum delay that can be accomodated by the\n", OPT_JB_MAX);
912
 
    printf("                         jitter buffer msec.\n");
913
 
}
914
 
 
915
 
 
916
 
static int init_options(int argc, char *argv[])
917
 
{
918
 
    struct pj_getopt_option long_options[] = {
919
 
        { "codec",          1, 0, OPT_CODEC },
920
 
        { "input",          1, 0, OPT_INPUT },
921
 
        { "output",         1, 0, OPT_OUTPUT },
922
 
        { "duration",       1, 0, OPT_DURATION },
923
 
        { "log-file",       1, 0, OPT_LOG_FILE},
924
 
        { "loss",           1, 0, OPT_LOSS },
925
 
        { "min-lost-burst", 1, 0, OPT_MIN_LOST_BURST},
926
 
        { "max-lost-burst", 1, 0, OPT_MAX_LOST_BURST},
927
 
        { "loss-corr",      1, 0, OPT_LOSS_CORR},
928
 
        { "min-jitter",     1, 0, OPT_MIN_JITTER },
929
 
        { "max-jitter",     1, 0, OPT_MAX_JITTER },
930
 
        { "snd-burst",      1, 0, OPT_SND_BURST },
931
 
        { "tx-ptime",       1, 0, OPT_TX_PTIME },
932
 
        { "rx-ptime",       1, 0, OPT_RX_PTIME },
933
 
        { "no-vad",         0, 0, OPT_NO_VAD },
934
 
        { "no-plc",         0, 0, OPT_NO_PLC },
935
 
        { "jb-prefetch",    0, 0, OPT_JB_PREFETCH },
936
 
        { "jb-min-pre",     1, 0, OPT_JB_MIN_PRE },
937
 
        { "jb-max-pre",     1, 0, OPT_JB_MAX_PRE },
938
 
        { "jb-max",         1, 0, OPT_JB_MAX },
939
 
        { "help",           0, 0, OPT_HELP},
940
 
        { NULL, 0, 0, 0 },
941
 
    };
942
 
    int c;
943
 
    int option_index;
944
 
    char format[128];
945
 
 
946
 
    /* Init default config */
947
 
    g_app.cfg.codec = pj_str(CODEC);
948
 
    g_app.cfg.duration_msec = DURATION * 1000;
949
 
    g_app.cfg.silent = SILENT;
950
 
    g_app.cfg.log_file = LOG_FILE;
951
 
    g_app.cfg.tx_wav_in = WAV_REF;
952
 
    g_app.cfg.tx_ptime = 0;
953
 
    g_app.cfg.tx_min_jitter = 0;
954
 
    g_app.cfg.tx_max_jitter = 0;
955
 
    g_app.cfg.tx_dtx = DTX;
956
 
    g_app.cfg.tx_pct_avg_lost = 0;
957
 
    g_app.cfg.tx_min_lost_burst = MIN_LOST_BURST;
958
 
    g_app.cfg.tx_max_lost_burst = MAX_LOST_BURST;
959
 
    g_app.cfg.tx_pct_loss_corr = LOSS_CORR;
960
 
 
961
 
    g_app.cfg.rx_wav_out = WAV_OUT;
962
 
    g_app.cfg.rx_ptime = 0;
963
 
    g_app.cfg.rx_plc = PLC;
964
 
    g_app.cfg.rx_snd_burst = 1;
965
 
    g_app.cfg.rx_jb_init = -1;
966
 
    g_app.cfg.rx_jb_min_pre = -1;
967
 
    g_app.cfg.rx_jb_max_pre = -1;
968
 
    g_app.cfg.rx_jb_max = -1;
969
 
 
970
 
    /* Build format */
971
 
    format[0] = '\0';
972
 
    for (c=0; c<PJ_ARRAY_SIZE(long_options)-1; ++c) {
973
 
        if (long_options[c].has_arg) {
974
 
            char cmd[10];
975
 
            pj_ansi_snprintf(cmd, sizeof(cmd), "%c:", long_options[c].val);
976
 
            pj_ansi_strcat(format, cmd);
977
 
        }
978
 
    }
979
 
    for (c=0; c<PJ_ARRAY_SIZE(long_options)-1; ++c) {
980
 
        if (long_options[c].has_arg == 0) {
981
 
            char cmd[10];
982
 
            pj_ansi_snprintf(cmd, sizeof(cmd), "%c", long_options[c].val);
983
 
            pj_ansi_strcat(format, cmd);
984
 
        }
985
 
    }
986
 
 
987
 
    /* Parse options */
988
 
    pj_optind = 0;
989
 
    while((c=pj_getopt_long(argc,argv, format, 
990
 
                            long_options, &option_index))!=-1) 
991
 
    {
992
 
        switch (c) {
993
 
        case OPT_CODEC:
994
 
            g_app.cfg.codec = pj_str(pj_optarg);
995
 
            break;
996
 
        case OPT_INPUT:
997
 
            g_app.cfg.tx_wav_in = pj_optarg;
998
 
            break;
999
 
        case OPT_OUTPUT:
1000
 
            g_app.cfg.rx_wav_out = pj_optarg;
1001
 
            break;
1002
 
        case OPT_DURATION:
1003
 
            g_app.cfg.duration_msec = atoi(pj_optarg) * 1000;
1004
 
            break;
1005
 
        case OPT_LOG_FILE:
1006
 
            g_app.cfg.log_file = pj_optarg;
1007
 
            break;
1008
 
        case OPT_LOSS:
1009
 
            g_app.cfg.tx_pct_avg_lost = atoi(pj_optarg);
1010
 
            if (g_app.cfg.tx_pct_avg_lost > 100) {
1011
 
                puts("Error: Invalid loss value?");
1012
 
                return 1;
1013
 
            }
1014
 
            break;
1015
 
        case OPT_MIN_LOST_BURST:
1016
 
            g_app.cfg.tx_min_lost_burst = atoi(pj_optarg);
1017
 
            break;
1018
 
        case OPT_MAX_LOST_BURST:
1019
 
            g_app.cfg.tx_max_lost_burst = atoi(pj_optarg);
1020
 
            break;
1021
 
        case OPT_LOSS_CORR:
1022
 
            g_app.cfg.tx_pct_loss_corr = atoi(pj_optarg);
1023
 
            if (g_app.cfg.tx_pct_avg_lost > 100) {
1024
 
                puts("Error: Loss correlation is in percentage, value is not valid?");
1025
 
                return 1;
1026
 
            }
1027
 
            break;
1028
 
        case OPT_MIN_JITTER:
1029
 
            g_app.cfg.tx_min_jitter = atoi(pj_optarg);
1030
 
            break;
1031
 
        case OPT_MAX_JITTER:
1032
 
            g_app.cfg.tx_max_jitter = atoi(pj_optarg);
1033
 
            break;
1034
 
        case OPT_SND_BURST:
1035
 
            g_app.cfg.rx_snd_burst = atoi(pj_optarg);
1036
 
            break;
1037
 
        case OPT_TX_PTIME:
1038
 
            g_app.cfg.tx_ptime = atoi(pj_optarg);
1039
 
            break;
1040
 
        case OPT_RX_PTIME:
1041
 
            g_app.cfg.rx_ptime = atoi(pj_optarg);
1042
 
            break;
1043
 
        case OPT_NO_VAD:
1044
 
            g_app.cfg.tx_dtx = PJ_FALSE;
1045
 
            break;
1046
 
        case OPT_NO_PLC:
1047
 
            g_app.cfg.rx_plc = PJ_FALSE;
1048
 
            break;
1049
 
        case OPT_JB_PREFETCH:
1050
 
            g_app.cfg.rx_jb_init = 1;
1051
 
            break;
1052
 
        case OPT_JB_MIN_PRE:
1053
 
            g_app.cfg.rx_jb_min_pre = atoi(pj_optarg);
1054
 
            break;
1055
 
        case OPT_JB_MAX_PRE:
1056
 
            g_app.cfg.rx_jb_max_pre = atoi(pj_optarg);
1057
 
            break;
1058
 
        case OPT_JB_MAX:
1059
 
            g_app.cfg.rx_jb_max = atoi(pj_optarg);
1060
 
            break;
1061
 
        case OPT_HELP:
1062
 
            usage();
1063
 
            return 1;
1064
 
        default:
1065
 
            usage();
1066
 
            return 1;
1067
 
        }
1068
 
    }
1069
 
 
1070
 
    /* Check for orphaned params */
1071
 
    if (pj_optind < argc) {
1072
 
        usage();
1073
 
        return 1;
1074
 
    }
1075
 
 
1076
 
    /* Normalize options */
1077
 
    if (g_app.cfg.rx_jb_init < g_app.cfg.rx_jb_min_pre)
1078
 
        g_app.cfg.rx_jb_init = g_app.cfg.rx_jb_min_pre;
1079
 
    else if (g_app.cfg.rx_jb_init > g_app.cfg.rx_jb_max_pre)
1080
 
        g_app.cfg.rx_jb_init = g_app.cfg.rx_jb_max_pre;
1081
 
 
1082
 
    if (g_app.cfg.tx_max_jitter < g_app.cfg.tx_min_jitter)
1083
 
        g_app.cfg.tx_max_jitter = g_app.cfg.tx_min_jitter;
1084
 
    return 0;
1085
 
}
1086
 
 
1087
 
/*****************************************************************************
1088
 
 * main()
1089
 
 */
1090
 
int main(int argc, char *argv[])
1091
 
{
1092
 
    pj_status_t status;
1093
 
 
1094
 
    if (init_options(argc, argv) != 0)
1095
 
        return 1;
1096
 
 
1097
 
 
1098
 
    /* Init */
1099
 
    status = test_init();
1100
 
    if (status != PJ_SUCCESS)
1101
 
        return 1;
1102
 
 
1103
 
    /* Print parameters */
1104
 
    PJ_LOG(3,(THIS_FILE, "Starting simulation. Parameters: "));
1105
 
    PJ_LOG(3,(THIS_FILE, "  Codec=%.*s, tx_ptime=%d, rx_ptime=%d",
1106
 
              (int)g_app.cfg.codec.slen,
1107
 
              g_app.cfg.codec.ptr,
1108
 
              g_app.cfg.tx_ptime,
1109
 
              g_app.cfg.rx_ptime));
1110
 
    PJ_LOG(3,(THIS_FILE, " Loss avg=%d%%, min_burst=%d, max_burst=%d",
1111
 
              g_app.cfg.tx_pct_avg_lost,
1112
 
              g_app.cfg.tx_min_lost_burst,
1113
 
              g_app.cfg.tx_max_lost_burst));
1114
 
    PJ_LOG(3,(THIS_FILE, " TX jitter min=%dms, max=%dms",
1115
 
              g_app.cfg.tx_min_jitter, 
1116
 
              g_app.cfg.tx_max_jitter));
1117
 
    PJ_LOG(3,(THIS_FILE, " RX jb init:%dms, min_pre=%dms, max_pre=%dms, max=%dms",
1118
 
              g_app.cfg.rx_jb_init,
1119
 
              g_app.cfg.rx_jb_min_pre,
1120
 
              g_app.cfg.rx_jb_max_pre,
1121
 
              g_app.cfg.rx_jb_max));
1122
 
    PJ_LOG(3,(THIS_FILE, " RX sound burst:%d frames",
1123
 
              g_app.cfg.rx_snd_burst));
1124
 
    PJ_LOG(3,(THIS_FILE, " DTX=%d, PLC=%d",
1125
 
              g_app.cfg.tx_dtx, g_app.cfg.rx_plc));
1126
 
 
1127
 
    /* Run test loop */
1128
 
    test_loop(g_app.cfg.duration_msec);
1129
 
 
1130
 
    /* Print statistics */
1131
 
    PJ_LOG(3,(THIS_FILE, "Simulation done"));
1132
 
    PJ_LOG(3,(THIS_FILE, " TX packets=%u, dropped=%u/%5.1f%%",
1133
 
              g_app.tx->state.tx.total_tx,
1134
 
              g_app.tx->state.tx.total_lost,
1135
 
              (float)(g_app.tx->state.tx.total_lost * 100.0 / g_app.tx->state.tx.total_tx)));
1136
 
 
1137
 
    /* Done */
1138
 
    test_destroy();
1139
 
 
1140
 
    return 0;
1141
 
}