blob: 46d6cba01f6b68f4fb955b7436cfffb3126b6563 [file] [log] [blame]
Benny Prijono60b980e2006-04-03 22:41:26 +00001/* $Id$ */
2/*
3 * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
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
Benny Prijonobf13fee2006-04-20 11:13:32 +000021
22
23/* Usage */
24static const char *USAGE =
25" PURPOSE: \n"
26" This program establishes SIP INVITE session and media, and calculate \n"
27" the media quality (packet lost, jitter, rtt, etc.). Unlike normal \n"
28" pjmedia applications, this program bypasses all pjmedia stream \n"
29" framework and transmit encoded RTP packets manually using own thread. \n"
30"\n"
31" USAGE:\n"
32" siprtp [options] => to start in server mode\n"
33" siprtp [options] URL => to start in client mode\n"
34"\n"
35" Program options:\n"
36" --count=N, -c Set number of calls to create (default:1) \n"
Benny Prijono410fbae2006-05-03 18:16:06 +000037" --duration=SEC, -d Set maximum call duration (default:unlimited) \n"
38" --auto-quit, -q Quit when calls have been completed (default:no)\n"
Benny Prijonobf13fee2006-04-20 11:13:32 +000039"\n"
40" Address and ports options:\n"
41" --local-port=PORT,-p Set local SIP port (default: 5060)\n"
42" --rtp-port=PORT, -r Set start of RTP port (default: 4000)\n"
43" --ip-addr=IP, -i Set local IP address to use (otherwise it will\n"
44" try to determine local IP address from hostname)\n"
45"\n"
46" Logging Options:\n"
47" --log-level=N, -l Set log verbosity level (default=5)\n"
48" --app-log-level=N Set app screen log verbosity (default=3)\n"
49" --log-file=FILE Write log to file FILE\n"
Benny Prijonofcb36722006-05-18 18:34:21 +000050" --report-file=FILE Write report to file FILE\n"
Benny Prijonobf13fee2006-04-20 11:13:32 +000051"\n"
Benny Prijono4d7fd202006-05-14 20:57:20 +000052/* Don't support this anymore, because codec is properly examined in
53 pjmedia_session_info_from_sdp() function.
54
Benny Prijonobf13fee2006-04-20 11:13:32 +000055" Codec Options:\n"
56" --a-pt=PT Set audio payload type to PT (default=0)\n"
57" --a-name=NAME Set audio codec name to NAME (default=pcmu)\n"
58" --a-clock=RATE Set audio codec rate to RATE Hz (default=8000Hz)\n"
59" --a-bitrate=BPS Set audio codec bitrate to BPS (default=64000bps)\n"
60" --a-ptime=MS Set audio frame time to MS msec (default=20ms)\n"
Benny Prijono4d7fd202006-05-14 20:57:20 +000061*/
Benny Prijonobf13fee2006-04-20 11:13:32 +000062;
63
64
Benny Prijono60b980e2006-04-03 22:41:26 +000065/* Include all headers. */
66#include <pjsip.h>
67#include <pjmedia.h>
68#include <pjmedia-codec.h>
69#include <pjsip_ua.h>
70#include <pjsip_simple.h>
71#include <pjlib-util.h>
72#include <pjlib.h>
73
74#include <stdlib.h>
75
Benny Prijono9a0eab52006-04-04 19:43:24 +000076
77#if PJ_HAS_HIGH_RES_TIMER==0
78# error "High resolution timer is needed for this sample"
79#endif
80
Benny Prijono60b980e2006-04-03 22:41:26 +000081#define THIS_FILE "siprtp.c"
82#define MAX_CALLS 1024
83#define RTP_START_PORT 44100
84
85
Benny Prijono4adcb912006-04-04 13:12:38 +000086/* Codec descriptor: */
87struct codec
88{
89 unsigned pt;
90 char* name;
91 unsigned clock_rate;
92 unsigned bit_rate;
93 unsigned ptime;
94 char* description;
95};
96
97
Benny Prijono60b980e2006-04-03 22:41:26 +000098/* A bidirectional media stream */
99struct media_stream
100{
101 /* Static: */
102 pj_uint16_t port; /* RTP port (RTCP is +1) */
103
104 /* Current stream info: */
105 pjmedia_stream_info si; /* Current stream info. */
106
107 /* More info: */
108 unsigned clock_rate; /* clock rate */
109 unsigned samples_per_frame; /* samples per frame */
110 unsigned bytes_per_frame; /* frame size. */
111
112 /* Sockets: */
113 pj_sock_t rtp_sock; /* RTP socket. */
114 pj_sock_t rtcp_sock; /* RTCP socket. */
115
116 /* RTP session: */
117 pjmedia_rtp_session out_sess; /* outgoing RTP session */
118 pjmedia_rtp_session in_sess; /* incoming RTP session */
119
120 /* RTCP stats: */
121 pjmedia_rtcp_session rtcp; /* incoming RTCP session. */
Benny Prijono4adcb912006-04-04 13:12:38 +0000122
Benny Prijono60b980e2006-04-03 22:41:26 +0000123 /* Thread: */
124 pj_bool_t thread_quit_flag; /* worker thread quit flag */
125 pj_thread_t *thread; /* RTP/RTCP worker thread */
126};
127
128
129struct call
130{
131 unsigned index;
132 pjsip_inv_session *inv;
133 unsigned media_count;
134 struct media_stream media[2];
Benny Prijono4adcb912006-04-04 13:12:38 +0000135 pj_time_val start_time;
136 pj_time_val response_time;
137 pj_time_val connect_time;
Benny Prijono410fbae2006-05-03 18:16:06 +0000138
139 pj_timer_entry d_timer; /**< Disconnect timer. */
Benny Prijono60b980e2006-04-03 22:41:26 +0000140};
141
142
143static struct app
144{
145 unsigned max_calls;
Benny Prijono410fbae2006-05-03 18:16:06 +0000146 unsigned uac_calls;
147 unsigned duration;
148 pj_bool_t auto_quit;
Benny Prijono60b980e2006-04-03 22:41:26 +0000149 unsigned thread_count;
150 int sip_port;
151 int rtp_start_port;
152 char *local_addr;
153 pj_str_t local_uri;
154 pj_str_t local_contact;
Benny Prijono4adcb912006-04-04 13:12:38 +0000155
156 int app_log_level;
157 int log_level;
158 char *log_filename;
Benny Prijonofcb36722006-05-18 18:34:21 +0000159 char *report_filename;
Benny Prijono4adcb912006-04-04 13:12:38 +0000160
161 struct codec audio_codec;
Benny Prijono60b980e2006-04-03 22:41:26 +0000162
163 pj_str_t uri_to_call;
164
165 pj_caching_pool cp;
166 pj_pool_t *pool;
167
168 pjsip_endpoint *sip_endpt;
169 pj_bool_t thread_quit;
170 pj_thread_t *thread[1];
171
172 pjmedia_endpt *med_endpt;
173 struct call call[MAX_CALLS];
174} app;
175
176
177
178/*
179 * Prototypes:
180 */
181
182/* Callback to be called when SDP negotiation is done in the call: */
183static void call_on_media_update( pjsip_inv_session *inv,
184 pj_status_t status);
185
186/* Callback to be called when invite session's state has changed: */
187static void call_on_state_changed( pjsip_inv_session *inv,
188 pjsip_event *e);
189
190/* Callback to be called when dialog has forked: */
191static void call_on_forked(pjsip_inv_session *inv, pjsip_event *e);
192
193/* Callback to be called to handle incoming requests outside dialogs: */
194static pj_bool_t on_rx_request( pjsip_rx_data *rdata );
195
196/* Worker thread prototype */
197static int worker_thread(void *arg);
198
199/* Create SDP for call */
200static pj_status_t create_sdp( pj_pool_t *pool,
201 struct call *call,
202 pjmedia_sdp_session **p_sdp);
203
Benny Prijono410fbae2006-05-03 18:16:06 +0000204/* Hangup call */
205static void hangup_call(unsigned index);
206
Benny Prijono60b980e2006-04-03 22:41:26 +0000207/* Destroy the call's media */
208static void destroy_call_media(unsigned call_index);
209
210/* Display error */
211static void app_perror(const char *sender, const char *title,
212 pj_status_t status);
213
Benny Prijonod7a13f12006-04-05 19:08:16 +0000214/* Print call */
215static void print_call(int call_index);
Benny Prijono4adcb912006-04-04 13:12:38 +0000216
217
Benny Prijono60b980e2006-04-03 22:41:26 +0000218/* This is a PJSIP module to be registered by application to handle
219 * incoming requests outside any dialogs/transactions. The main purpose
220 * here is to handle incoming INVITE request message, where we will
221 * create a dialog and INVITE session for it.
222 */
223static pjsip_module mod_siprtp =
224{
225 NULL, NULL, /* prev, next. */
226 { "mod-siprtpapp", 13 }, /* Name. */
227 -1, /* Id */
228 PJSIP_MOD_PRIORITY_APPLICATION, /* Priority */
229 NULL, /* load() */
230 NULL, /* start() */
231 NULL, /* stop() */
232 NULL, /* unload() */
233 &on_rx_request, /* on_rx_request() */
234 NULL, /* on_rx_response() */
235 NULL, /* on_tx_request. */
236 NULL, /* on_tx_response() */
237 NULL, /* on_tsx_state() */
238};
239
240
Benny Prijono4adcb912006-04-04 13:12:38 +0000241/* Codec constants */
242struct codec audio_codecs[] =
243{
244 { 0, "pcmu", 8000, 64000, 20, "G.711 ULaw" },
245 { 3, "gsm", 8000, 13200, 20, "GSM" },
246 { 4, "g723", 8000, 6400, 30, "G.723.1" },
247 { 8, "pcma", 8000, 64000, 20, "G.711 ALaw" },
248 { 18, "g729", 8000, 8000, 20, "G.729" },
249};
250
251
Benny Prijono60b980e2006-04-03 22:41:26 +0000252/*
253 * Init SIP stack
254 */
255static pj_status_t init_sip()
256{
Benny Prijono60b980e2006-04-03 22:41:26 +0000257 pj_status_t status;
258
259 /* init PJLIB-UTIL: */
260 status = pjlib_util_init();
261 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
262
263 /* Must create a pool factory before we can allocate any memory. */
264 pj_caching_pool_init(&app.cp, &pj_pool_factory_default_policy, 0);
265
266 /* Create application pool for misc. */
267 app.pool = pj_pool_create(&app.cp.factory, "app", 1000, 1000, NULL);
268
269 /* Create global endpoint: */
270 {
271 const pj_str_t *hostname;
272 const char *endpt_name;
273
274 /* Endpoint MUST be assigned a globally unique name.
275 * The name will be used as the hostname in Warning header.
276 */
277
278 /* For this implementation, we'll use hostname for simplicity */
279 hostname = pj_gethostname();
280 endpt_name = hostname->ptr;
281
282 /* Create the endpoint: */
283
284 status = pjsip_endpt_create(&app.cp.factory, endpt_name,
285 &app.sip_endpt);
286 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
287 }
288
289
290 /* Add UDP transport. */
291 {
292 pj_sockaddr_in addr;
Benny Prijono49ce9a72006-04-05 16:56:19 +0000293 pjsip_host_port addrname;
Benny Prijono60b980e2006-04-03 22:41:26 +0000294
Benny Prijono49ce9a72006-04-05 16:56:19 +0000295 pj_memset(&addr, 0, sizeof(addr));
Benny Prijono60b980e2006-04-03 22:41:26 +0000296 addr.sin_family = PJ_AF_INET;
297 addr.sin_addr.s_addr = 0;
298 addr.sin_port = pj_htons((pj_uint16_t)app.sip_port);
299
Benny Prijono49ce9a72006-04-05 16:56:19 +0000300 if (app.local_addr) {
301 addrname.host = pj_str(app.local_addr);
302 addrname.port = app.sip_port;
303 }
304
305 status = pjsip_udp_transport_start( app.sip_endpt, &addr,
306 (app.local_addr ? &addrname:NULL),
Benny Prijono60b980e2006-04-03 22:41:26 +0000307 1, NULL);
Benny Prijono49ce9a72006-04-05 16:56:19 +0000308 if (status != PJ_SUCCESS) {
309 app_perror(THIS_FILE, "Unable to start UDP transport", status);
Benny Prijono60b980e2006-04-03 22:41:26 +0000310 return status;
Benny Prijono49ce9a72006-04-05 16:56:19 +0000311 }
Benny Prijono60b980e2006-04-03 22:41:26 +0000312 }
313
314 /*
315 * Init transaction layer.
316 * This will create/initialize transaction hash tables etc.
317 */
318 status = pjsip_tsx_layer_init_module(app.sip_endpt);
319 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
320
321 /* Initialize UA layer. */
322 status = pjsip_ua_init_module( app.sip_endpt, NULL );
323 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
324
325 /* Init invite session module. */
326 {
327 pjsip_inv_callback inv_cb;
328
329 /* Init the callback for INVITE session: */
330 pj_memset(&inv_cb, 0, sizeof(inv_cb));
331 inv_cb.on_state_changed = &call_on_state_changed;
332 inv_cb.on_new_session = &call_on_forked;
333 inv_cb.on_media_update = &call_on_media_update;
334
335 /* Initialize invite session module: */
336 status = pjsip_inv_usage_init(app.sip_endpt, &inv_cb);
337 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
338 }
339
340 /* Register our module to receive incoming requests. */
341 status = pjsip_endpt_register_module( app.sip_endpt, &mod_siprtp);
342 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
343
344
Benny Prijono60b980e2006-04-03 22:41:26 +0000345 /* Done */
346 return PJ_SUCCESS;
347}
348
349
350/*
351 * Destroy SIP
352 */
353static void destroy_sip()
354{
355 unsigned i;
356
357 app.thread_quit = 1;
358 for (i=0; i<app.thread_count; ++i) {
359 if (app.thread[i]) {
360 pj_thread_join(app.thread[i]);
361 pj_thread_destroy(app.thread[i]);
362 app.thread[i] = NULL;
363 }
364 }
365
366 if (app.sip_endpt) {
367 pjsip_endpt_destroy(app.sip_endpt);
368 app.sip_endpt = NULL;
369 }
370
371 if (app.pool) {
372 pj_pool_release(app.pool);
373 app.pool = NULL;
374 pj_caching_pool_destroy(&app.cp);
375 }
376}
377
378
379/*
380 * Init media stack.
381 */
382static pj_status_t init_media()
383{
384 pj_ioqueue_t *ioqueue;
385 unsigned i, count;
386 pj_uint16_t rtp_port;
387 pj_str_t temp;
388 pj_sockaddr_in addr;
389 pj_status_t status;
390
391
392 /* Get the ioqueue from the SIP endpoint */
393 ioqueue = pjsip_endpt_get_ioqueue(app.sip_endpt);
394
395
396 /* Initialize media endpoint so that at least error subsystem is properly
397 * initialized.
398 */
399 status = pjmedia_endpt_create(&app.cp.factory, ioqueue, 1,
400 &app.med_endpt);
401 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
402
403
Benny Prijono4d7fd202006-05-14 20:57:20 +0000404 /* Add G711 codec */
405 pjmedia_codec_g711_init(app.med_endpt);
406
Benny Prijono60b980e2006-04-03 22:41:26 +0000407 /* Determine address to bind socket */
408 pj_memset(&addr, 0, sizeof(addr));
409 addr.sin_family = PJ_AF_INET;
410 i = pj_inet_aton(pj_cstr(&temp, app.local_addr), &addr.sin_addr);
411 if (i == 0) {
412 PJ_LOG(3,(THIS_FILE,
413 "Error: invalid local address %s (expecting IP)",
414 app.local_addr));
415 return -1;
416 }
417
Benny Prijono60b980e2006-04-03 22:41:26 +0000418 /* RTP port counter */
419 rtp_port = (pj_uint16_t)(app.rtp_start_port & 0xFFFE);
420
421
422 /* Init media sockets. */
423 for (i=0, count=0; i<app.max_calls; ++i, ++count) {
424
425 int retry;
426
427 app.call[i].index = i;
428
429 /* Repeat binding media socket to next port when fails to bind
430 * to current port number.
431 */
432 retry = 0;
433 do {
434 struct media_stream *m = &app.call[i].media[0];
435
436 ++retry;
437 rtp_port += 2;
438 m->port = rtp_port;
439
440 /* Create and bind RTP socket */
441 status = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0,
442 &m->rtp_sock);
443 if (status != PJ_SUCCESS)
444 goto on_error;
445
446 addr.sin_port = pj_htons(rtp_port);
447 status = pj_sock_bind(m->rtp_sock, &addr, sizeof(addr));
448 if (status != PJ_SUCCESS) {
449 pj_sock_close(m->rtp_sock), m->rtp_sock=0;
450 continue;
451 }
452
453
454 /* Create and bind RTCP socket */
455 status = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0,
456 &m->rtcp_sock);
457 if (status != PJ_SUCCESS)
458 goto on_error;
459
460 addr.sin_port = pj_htons((pj_uint16_t)(rtp_port+1));
461 status = pj_sock_bind(m->rtcp_sock, &addr, sizeof(addr));
462 if (status != PJ_SUCCESS) {
463 pj_sock_close(m->rtp_sock), m->rtp_sock=0;
464 pj_sock_close(m->rtcp_sock), m->rtcp_sock=0;
465 continue;
466 }
467
468 } while (status != PJ_SUCCESS && retry < 100);
469
470 if (status != PJ_SUCCESS)
471 goto on_error;
472
473 }
474
475 /* Done */
476 return PJ_SUCCESS;
477
478on_error:
479 for (i=0; i<count; ++i) {
480 struct media_stream *m = &app.call[i].media[0];
481
482 pj_sock_close(m->rtp_sock), m->rtp_sock=0;
483 pj_sock_close(m->rtcp_sock), m->rtcp_sock=0;
484 }
485
486 return status;
487}
488
489
490/*
491 * Destroy media.
492 */
493static void destroy_media()
494{
495 unsigned i;
496
497 for (i=0; i<app.max_calls; ++i) {
498 struct media_stream *m = &app.call[i].media[0];
499
500 if (m->rtp_sock)
501 pj_sock_close(m->rtp_sock), m->rtp_sock = 0;
502
503 if (m->rtcp_sock)
504 pj_sock_close(m->rtcp_sock), m->rtcp_sock = 0;
505 }
506
507 if (app.med_endpt) {
508 pjmedia_endpt_destroy(app.med_endpt);
509 app.med_endpt = NULL;
510 }
511}
512
513
514/*
515 * Make outgoing call.
516 */
517static pj_status_t make_call(const pj_str_t *dst_uri)
518{
519 unsigned i;
520 struct call *call;
521 pjsip_dialog *dlg;
522 pjmedia_sdp_session *sdp;
523 pjsip_tx_data *tdata;
524 pj_status_t status;
525
526
527 /* Find unused call slot */
528 for (i=0; i<app.max_calls; ++i) {
529 if (app.call[i].inv == NULL)
530 break;
531 }
532
533 if (i == app.max_calls)
534 return PJ_ETOOMANY;
535
536 call = &app.call[i];
537
538 /* Create UAC dialog */
539 status = pjsip_dlg_create_uac( pjsip_ua_instance(),
540 &app.local_uri, /* local URI */
541 &app.local_contact, /* local Contact */
542 dst_uri, /* remote URI */
543 dst_uri, /* remote target */
544 &dlg); /* dialog */
Benny Prijono410fbae2006-05-03 18:16:06 +0000545 if (status != PJ_SUCCESS) {
546 ++app.uac_calls;
Benny Prijono60b980e2006-04-03 22:41:26 +0000547 return status;
Benny Prijono410fbae2006-05-03 18:16:06 +0000548 }
Benny Prijono60b980e2006-04-03 22:41:26 +0000549
550 /* Create SDP */
551 create_sdp( dlg->pool, call, &sdp);
552
553 /* Create the INVITE session. */
554 status = pjsip_inv_create_uac( dlg, sdp, 0, &call->inv);
555 if (status != PJ_SUCCESS) {
556 pjsip_dlg_terminate(dlg);
Benny Prijono410fbae2006-05-03 18:16:06 +0000557 ++app.uac_calls;
Benny Prijono60b980e2006-04-03 22:41:26 +0000558 return status;
559 }
560
561
562 /* Attach call data to invite session */
563 call->inv->mod_data[mod_siprtp.id] = call;
564
Benny Prijono4adcb912006-04-04 13:12:38 +0000565 /* Mark start of call */
566 pj_gettimeofday(&call->start_time);
567
Benny Prijono60b980e2006-04-03 22:41:26 +0000568
569 /* Create initial INVITE request.
570 * This INVITE request will contain a perfectly good request and
571 * an SDP body as well.
572 */
573 status = pjsip_inv_invite(call->inv, &tdata);
574 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
575
576
577 /* Send initial INVITE request.
578 * From now on, the invite session's state will be reported to us
579 * via the invite session callbacks.
580 */
581 status = pjsip_inv_send_msg(call->inv, tdata);
582 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
583
584
585 return PJ_SUCCESS;
586}
587
588
589/*
590 * Receive incoming call
591 */
592static void process_incoming_call(pjsip_rx_data *rdata)
593{
594 unsigned i;
595 struct call *call;
596 pjsip_dialog *dlg;
597 pjmedia_sdp_session *sdp;
598 pjsip_tx_data *tdata;
599 pj_status_t status;
600
601 /* Find free call slot */
602 for (i=0; i<app.max_calls; ++i) {
603 if (app.call[i].inv == NULL)
604 break;
605 }
606
607 if (i == app.max_calls) {
608 const pj_str_t reason = pj_str("Too many calls");
609 pjsip_endpt_respond_stateless( app.sip_endpt, rdata,
610 500, &reason,
611 NULL, NULL);
612 return;
613 }
614
615 call = &app.call[i];
616
617 /* Create UAS dialog */
618 status = pjsip_dlg_create_uas( pjsip_ua_instance(), rdata,
619 &app.local_contact, &dlg);
620 if (status != PJ_SUCCESS) {
621 const pj_str_t reason = pj_str("Unable to create dialog");
622 pjsip_endpt_respond_stateless( app.sip_endpt, rdata,
623 500, &reason,
624 NULL, NULL);
625 return;
626 }
627
628 /* Create SDP */
629 create_sdp( dlg->pool, call, &sdp);
630
631 /* Create UAS invite session */
632 status = pjsip_inv_create_uas( dlg, rdata, sdp, 0, &call->inv);
633 if (status != PJ_SUCCESS) {
Benny Prijono4adcb912006-04-04 13:12:38 +0000634 pjsip_dlg_create_response(dlg, rdata, 500, NULL, &tdata);
635 pjsip_dlg_send_response(dlg, pjsip_rdata_get_tsx(rdata), tdata);
Benny Prijono60b980e2006-04-03 22:41:26 +0000636 return;
637 }
638
Benny Prijono4adcb912006-04-04 13:12:38 +0000639
Benny Prijono60b980e2006-04-03 22:41:26 +0000640 /* Attach call data to invite session */
641 call->inv->mod_data[mod_siprtp.id] = call;
642
Benny Prijono4adcb912006-04-04 13:12:38 +0000643 /* Mark start of call */
644 pj_gettimeofday(&call->start_time);
645
646
647
Benny Prijono60b980e2006-04-03 22:41:26 +0000648 /* Create 200 response .*/
649 status = pjsip_inv_initial_answer(call->inv, rdata, 200,
650 NULL, NULL, &tdata);
Benny Prijono4adcb912006-04-04 13:12:38 +0000651 if (status != PJ_SUCCESS) {
652 status = pjsip_inv_initial_answer(call->inv, rdata,
653 PJSIP_SC_NOT_ACCEPTABLE,
654 NULL, NULL, &tdata);
655 if (status == PJ_SUCCESS)
656 pjsip_inv_send_msg(call->inv, tdata);
657 else
658 pjsip_inv_terminate(call->inv, 500, PJ_FALSE);
659 return;
660 }
661
Benny Prijono60b980e2006-04-03 22:41:26 +0000662
663 /* Send the 200 response. */
664 status = pjsip_inv_send_msg(call->inv, tdata);
665 PJ_ASSERT_ON_FAIL(status == PJ_SUCCESS, return);
666
667
668 /* Done */
669}
670
671
672/* Callback to be called when dialog has forked: */
673static void call_on_forked(pjsip_inv_session *inv, pjsip_event *e)
674{
675 PJ_UNUSED_ARG(inv);
676 PJ_UNUSED_ARG(e);
677
678 PJ_TODO( HANDLE_FORKING );
679}
680
681
682/* Callback to be called to handle incoming requests outside dialogs: */
683static pj_bool_t on_rx_request( pjsip_rx_data *rdata )
684{
Benny Prijono4adcb912006-04-04 13:12:38 +0000685 /* Ignore strandled ACKs (must not send respone */
686 if (rdata->msg_info.msg->line.req.method.id == PJSIP_ACK_METHOD)
687 return PJ_FALSE;
688
Benny Prijono60b980e2006-04-03 22:41:26 +0000689 /* Respond (statelessly) any non-INVITE requests with 500 */
690 if (rdata->msg_info.msg->line.req.method.id != PJSIP_INVITE_METHOD) {
691 pj_str_t reason = pj_str("Unsupported Operation");
692 pjsip_endpt_respond_stateless( app.sip_endpt, rdata,
693 500, &reason,
694 NULL, NULL);
695 return PJ_TRUE;
696 }
697
698 /* Handle incoming INVITE */
699 process_incoming_call(rdata);
700
701 /* Done */
702 return PJ_TRUE;
703}
704
705
Benny Prijono410fbae2006-05-03 18:16:06 +0000706/* Callback timer to disconnect call (limiting call duration) */
707static void timer_disconnect_call( pj_timer_heap_t *timer_heap,
708 struct pj_timer_entry *entry)
709{
710 struct call *call = entry->user_data;
711
712 PJ_UNUSED_ARG(timer_heap);
713
714 entry->id = 0;
715 hangup_call(call->index);
716}
717
718
Benny Prijono60b980e2006-04-03 22:41:26 +0000719/* Callback to be called when invite session's state has changed: */
720static void call_on_state_changed( pjsip_inv_session *inv,
721 pjsip_event *e)
722{
Benny Prijono4adcb912006-04-04 13:12:38 +0000723 struct call *call = inv->mod_data[mod_siprtp.id];
724
Benny Prijono60b980e2006-04-03 22:41:26 +0000725 PJ_UNUSED_ARG(e);
726
Benny Prijono4adcb912006-04-04 13:12:38 +0000727 if (!call)
728 return;
Benny Prijono60b980e2006-04-03 22:41:26 +0000729
Benny Prijono4adcb912006-04-04 13:12:38 +0000730 if (inv->state == PJSIP_INV_STATE_DISCONNECTED) {
731
732 pj_time_val null_time = {0, 0};
Benny Prijono60b980e2006-04-03 22:41:26 +0000733
Benny Prijono410fbae2006-05-03 18:16:06 +0000734 if (call->d_timer.id != 0) {
735 pjsip_endpt_cancel_timer(app.sip_endpt, &call->d_timer);
736 call->d_timer.id = 0;
737 }
738
Benny Prijonod7a13f12006-04-05 19:08:16 +0000739 PJ_LOG(3,(THIS_FILE, "Call #%d disconnected. Reason=%s",
740 call->index,
741 pjsip_get_status_text(inv->cause)->ptr));
742 PJ_LOG(3,(THIS_FILE, "Call #%d statistics:", call->index));
743 print_call(call->index);
744
745
Benny Prijono60b980e2006-04-03 22:41:26 +0000746 call->inv = NULL;
747 inv->mod_data[mod_siprtp.id] = NULL;
748
749 destroy_call_media(call->index);
Benny Prijono4adcb912006-04-04 13:12:38 +0000750
751 call->start_time = null_time;
752 call->response_time = null_time;
753 call->connect_time = null_time;
754
Benny Prijono410fbae2006-05-03 18:16:06 +0000755 ++app.uac_calls;
Benny Prijono4adcb912006-04-04 13:12:38 +0000756
757 } else if (inv->state == PJSIP_INV_STATE_CONFIRMED) {
758
759 pj_time_val t;
760
761 pj_gettimeofday(&call->connect_time);
762 if (call->response_time.sec == 0)
763 call->response_time = call->connect_time;
764
765 t = call->connect_time;
766 PJ_TIME_VAL_SUB(t, call->start_time);
767
768 PJ_LOG(3,(THIS_FILE, "Call #%d connected in %d ms", call->index,
769 PJ_TIME_VAL_MSEC(t)));
770
Benny Prijono410fbae2006-05-03 18:16:06 +0000771 if (app.duration != 0) {
772 call->d_timer.id = 1;
773 call->d_timer.user_data = call;
774 call->d_timer.cb = &timer_disconnect_call;
775
776 t.sec = app.duration;
777 t.msec = 0;
778
779 pjsip_endpt_schedule_timer(app.sip_endpt, &call->d_timer, &t);
780 }
781
Benny Prijono4adcb912006-04-04 13:12:38 +0000782 } else if ( inv->state == PJSIP_INV_STATE_EARLY ||
783 inv->state == PJSIP_INV_STATE_CONNECTING) {
784
785 if (call->response_time.sec == 0)
786 pj_gettimeofday(&call->response_time);
787
Benny Prijono60b980e2006-04-03 22:41:26 +0000788 }
789}
790
791
792/* Utility */
793static void app_perror(const char *sender, const char *title,
794 pj_status_t status)
795{
796 char errmsg[PJ_ERR_MSG_SIZE];
797
798 pj_strerror(status, errmsg, sizeof(errmsg));
799 PJ_LOG(3,(sender, "%s: %s [status=%d]", title, errmsg, status));
800}
801
802
803/* Worker thread */
804static int worker_thread(void *arg)
805{
806 PJ_UNUSED_ARG(arg);
807
808 while (!app.thread_quit) {
809 pj_time_val timeout = {0, 10};
810 pjsip_endpt_handle_events(app.sip_endpt, &timeout);
811 }
812
813 return 0;
814}
815
816
Benny Prijono60b980e2006-04-03 22:41:26 +0000817/* Init application options */
818static pj_status_t init_options(int argc, char *argv[])
819{
820 static char ip_addr[32];
821 static char local_uri[64];
822
Benny Prijono4adcb912006-04-04 13:12:38 +0000823 enum { OPT_START,
824 OPT_APP_LOG_LEVEL, OPT_LOG_FILE,
Benny Prijonofcb36722006-05-18 18:34:21 +0000825 OPT_A_PT, OPT_A_NAME, OPT_A_CLOCK, OPT_A_BITRATE, OPT_A_PTIME,
826 OPT_REPORT_FILE };
Benny Prijono4adcb912006-04-04 13:12:38 +0000827
Benny Prijono60b980e2006-04-03 22:41:26 +0000828 struct pj_getopt_option long_options[] = {
Benny Prijono4adcb912006-04-04 13:12:38 +0000829 { "count", 1, 0, 'c' },
Benny Prijono410fbae2006-05-03 18:16:06 +0000830 { "duration", 1, 0, 'd' },
831 { "auto-quit", 0, 0, 'q' },
Benny Prijono4adcb912006-04-04 13:12:38 +0000832 { "local-port", 1, 0, 'p' },
833 { "rtp-port", 1, 0, 'r' },
834 { "ip-addr", 1, 0, 'i' },
835
836 { "log-level", 1, 0, 'l' },
837 { "app-log-level", 1, 0, OPT_APP_LOG_LEVEL },
838 { "log-file", 1, 0, OPT_LOG_FILE },
Benny Prijono4d7fd202006-05-14 20:57:20 +0000839
Benny Prijonofcb36722006-05-18 18:34:21 +0000840 { "report-file", 1, 0, OPT_REPORT_FILE },
841
Benny Prijono4d7fd202006-05-14 20:57:20 +0000842 /* Don't support this anymore, see comments in USAGE above.
Benny Prijono4adcb912006-04-04 13:12:38 +0000843 { "a-pt", 1, 0, OPT_A_PT },
844 { "a-name", 1, 0, OPT_A_NAME },
845 { "a-clock", 1, 0, OPT_A_CLOCK },
846 { "a-bitrate", 1, 0, OPT_A_BITRATE },
847 { "a-ptime", 1, 0, OPT_A_PTIME },
Benny Prijono4d7fd202006-05-14 20:57:20 +0000848 */
Benny Prijono4adcb912006-04-04 13:12:38 +0000849
Benny Prijono60b980e2006-04-03 22:41:26 +0000850 { NULL, 0, 0, 0 },
851 };
852 int c;
853 int option_index;
854
855 /* Get local IP address for the default IP address */
856 {
857 const pj_str_t *hostname;
858 pj_sockaddr_in tmp_addr;
859 char *addr;
860
861 hostname = pj_gethostname();
862 pj_sockaddr_in_init(&tmp_addr, hostname, 0);
863 addr = pj_inet_ntoa(tmp_addr.sin_addr);
864 pj_ansi_strcpy(ip_addr, addr);
865 }
866
Benny Prijono4adcb912006-04-04 13:12:38 +0000867 /* Init defaults */
Benny Prijono60b980e2006-04-03 22:41:26 +0000868 app.max_calls = 1;
869 app.thread_count = 1;
870 app.sip_port = 5060;
871 app.rtp_start_port = 4000;
872 app.local_addr = ip_addr;
Benny Prijono4adcb912006-04-04 13:12:38 +0000873 app.log_level = 5;
874 app.app_log_level = 3;
875 app.log_filename = NULL;
876
877 /* Default codecs: */
878 app.audio_codec = audio_codecs[0];
Benny Prijono60b980e2006-04-03 22:41:26 +0000879
880 /* Parse options */
881 pj_optind = 0;
Benny Prijono410fbae2006-05-03 18:16:06 +0000882 while((c=pj_getopt_long(argc,argv, "c:d:p:r:i:l:q",
Benny Prijono60b980e2006-04-03 22:41:26 +0000883 long_options, &option_index))!=-1)
884 {
885 switch (c) {
886 case 'c':
887 app.max_calls = atoi(pj_optarg);
888 if (app.max_calls < 0 || app.max_calls > MAX_CALLS) {
889 PJ_LOG(3,(THIS_FILE, "Invalid max calls value %s", pj_optarg));
890 return 1;
891 }
892 break;
Benny Prijono410fbae2006-05-03 18:16:06 +0000893 case 'd':
894 app.duration = atoi(pj_optarg);
895 break;
896 case 'q':
897 app.auto_quit = 1;
898 break;
899
Benny Prijono60b980e2006-04-03 22:41:26 +0000900 case 'p':
901 app.sip_port = atoi(pj_optarg);
902 break;
903 case 'r':
904 app.rtp_start_port = atoi(pj_optarg);
905 break;
906 case 'i':
907 app.local_addr = pj_optarg;
908 break;
Benny Prijono4adcb912006-04-04 13:12:38 +0000909
910 case 'l':
911 app.log_level = atoi(pj_optarg);
912 break;
913 case OPT_APP_LOG_LEVEL:
914 app.app_log_level = atoi(pj_optarg);
915 break;
916 case OPT_LOG_FILE:
917 app.log_filename = pj_optarg;
918 break;
919
920 case OPT_A_PT:
921 app.audio_codec.pt = atoi(pj_optarg);
922 break;
923 case OPT_A_NAME:
924 app.audio_codec.name = pj_optarg;
925 break;
926 case OPT_A_CLOCK:
927 app.audio_codec.clock_rate = atoi(pj_optarg);
928 break;
929 case OPT_A_BITRATE:
930 app.audio_codec.bit_rate = atoi(pj_optarg);
931 break;
932 case OPT_A_PTIME:
933 app.audio_codec.ptime = atoi(pj_optarg);
934 break;
Benny Prijonofcb36722006-05-18 18:34:21 +0000935 case OPT_REPORT_FILE:
936 app.report_filename = pj_optarg;
937 break;
Benny Prijono4adcb912006-04-04 13:12:38 +0000938
Benny Prijono60b980e2006-04-03 22:41:26 +0000939 default:
940 puts(USAGE);
941 return 1;
942 }
943 }
944
945 /* Check if URL is specified */
946 if (pj_optind < argc)
947 app.uri_to_call = pj_str(argv[pj_optind]);
948
949 /* Build local URI and contact */
950 pj_ansi_sprintf( local_uri, "sip:%s:%d", app.local_addr, app.sip_port);
951 app.local_uri = pj_str(local_uri);
952 app.local_contact = app.local_uri;
953
954
955 return PJ_SUCCESS;
956}
957
958
Benny Prijono4adcb912006-04-04 13:12:38 +0000959/*****************************************************************************
Benny Prijono60b980e2006-04-03 22:41:26 +0000960 * MEDIA STUFFS
961 */
962
963/*
964 * Create SDP session for a call.
965 */
966static pj_status_t create_sdp( pj_pool_t *pool,
967 struct call *call,
968 pjmedia_sdp_session **p_sdp)
969{
970 pj_time_val tv;
971 pjmedia_sdp_session *sdp;
972 pjmedia_sdp_media *m;
973 pjmedia_sdp_attr *attr;
974 struct media_stream *audio = &call->media[0];
975
976 PJ_ASSERT_RETURN(pool && p_sdp, PJ_EINVAL);
977
978
979 /* Create and initialize basic SDP session */
980 sdp = pj_pool_zalloc (pool, sizeof(pjmedia_sdp_session));
981
982 pj_gettimeofday(&tv);
983 sdp->origin.user = pj_str("pjsip-siprtp");
984 sdp->origin.version = sdp->origin.id = tv.sec + 2208988800UL;
985 sdp->origin.net_type = pj_str("IN");
986 sdp->origin.addr_type = pj_str("IP4");
987 sdp->origin.addr = *pj_gethostname();
988 sdp->name = pj_str("pjsip");
989
990 /* Since we only support one media stream at present, put the
991 * SDP connection line in the session level.
992 */
993 sdp->conn = pj_pool_zalloc (pool, sizeof(pjmedia_sdp_conn));
994 sdp->conn->net_type = pj_str("IN");
995 sdp->conn->addr_type = pj_str("IP4");
996 sdp->conn->addr = pj_str(app.local_addr);
997
998
999 /* SDP time and attributes. */
1000 sdp->time.start = sdp->time.stop = 0;
1001 sdp->attr_count = 0;
1002
1003 /* Create media stream 0: */
1004
1005 sdp->media_count = 1;
1006 m = pj_pool_zalloc (pool, sizeof(pjmedia_sdp_media));
1007 sdp->media[0] = m;
1008
1009 /* Standard media info: */
1010 m->desc.media = pj_str("audio");
1011 m->desc.port = audio->port;
1012 m->desc.port_count = 1;
1013 m->desc.transport = pj_str("RTP/AVP");
1014
1015 /* Add format and rtpmap for each codec. */
1016 m->desc.fmt_count = 1;
1017 m->attr_count = 0;
1018
1019 {
1020 pjmedia_sdp_rtpmap rtpmap;
1021 pjmedia_sdp_attr *attr;
Benny Prijono4adcb912006-04-04 13:12:38 +00001022 char ptstr[10];
Benny Prijono60b980e2006-04-03 22:41:26 +00001023
Benny Prijono4adcb912006-04-04 13:12:38 +00001024 sprintf(ptstr, "%d", app.audio_codec.pt);
1025 pj_strdup2(pool, &m->desc.fmt[0], ptstr);
1026 rtpmap.pt = m->desc.fmt[0];
1027 rtpmap.clock_rate = app.audio_codec.clock_rate;
1028 rtpmap.enc_name = pj_str(app.audio_codec.name);
Benny Prijono60b980e2006-04-03 22:41:26 +00001029 rtpmap.param.slen = 0;
1030
1031 pjmedia_sdp_rtpmap_to_attr(pool, &rtpmap, &attr);
1032 m->attr[m->attr_count++] = attr;
1033 }
1034
1035 /* Add sendrecv attribute. */
1036 attr = pj_pool_zalloc(pool, sizeof(pjmedia_sdp_attr));
1037 attr->name = pj_str("sendrecv");
1038 m->attr[m->attr_count++] = attr;
1039
1040#if 1
1041 /*
1042 * Add support telephony event
1043 */
Benny Prijono4adcb912006-04-04 13:12:38 +00001044 m->desc.fmt[m->desc.fmt_count++] = pj_str("121");
Benny Prijono60b980e2006-04-03 22:41:26 +00001045 /* Add rtpmap. */
1046 attr = pj_pool_zalloc(pool, sizeof(pjmedia_sdp_attr));
1047 attr->name = pj_str("rtpmap");
Benny Prijono4adcb912006-04-04 13:12:38 +00001048 attr->value = pj_str(":121 telephone-event/8000");
Benny Prijono60b980e2006-04-03 22:41:26 +00001049 m->attr[m->attr_count++] = attr;
1050 /* Add fmtp */
1051 attr = pj_pool_zalloc(pool, sizeof(pjmedia_sdp_attr));
1052 attr->name = pj_str("fmtp");
Benny Prijono4adcb912006-04-04 13:12:38 +00001053 attr->value = pj_str(":121 0-15");
Benny Prijono60b980e2006-04-03 22:41:26 +00001054 m->attr[m->attr_count++] = attr;
1055#endif
1056
1057 /* Done */
1058 *p_sdp = sdp;
1059
1060 return PJ_SUCCESS;
1061}
1062
1063
Benny Prijono4adcb912006-04-04 13:12:38 +00001064/*
1065 * Media thread
1066 *
1067 * This is the thread to send and receive both RTP and RTCP packets.
1068 */
Benny Prijono60b980e2006-04-03 22:41:26 +00001069static int media_thread(void *arg)
1070{
Benny Prijono14b7b662006-04-07 15:01:51 +00001071 enum { RTCP_INTERVAL = 5000, RTCP_RAND = 2000 };
Benny Prijono60b980e2006-04-03 22:41:26 +00001072 struct media_stream *strm = arg;
1073 char packet[1500];
Benny Prijono9a0eab52006-04-04 19:43:24 +00001074 unsigned msec_interval;
1075 pj_timestamp freq, next_rtp, next_rtcp;
Benny Prijono60b980e2006-04-03 22:41:26 +00001076
Benny Prijono9a0eab52006-04-04 19:43:24 +00001077 msec_interval = strm->samples_per_frame * 1000 / strm->clock_rate;
1078 pj_get_timestamp_freq(&freq);
1079
1080 pj_get_timestamp(&next_rtp);
1081 next_rtp.u64 += (freq.u64 * msec_interval / 1000);
Benny Prijono60b980e2006-04-03 22:41:26 +00001082
1083 next_rtcp = next_rtp;
Benny Prijono14b7b662006-04-07 15:01:51 +00001084 next_rtcp.u64 += (freq.u64 * (RTCP_INTERVAL+(pj_rand()%RTCP_RAND)) / 1000);
Benny Prijono60b980e2006-04-03 22:41:26 +00001085
1086
1087 while (!strm->thread_quit_flag) {
1088 pj_fd_set_t set;
Benny Prijono9a0eab52006-04-04 19:43:24 +00001089 pj_timestamp now, lesser;
1090 pj_time_val timeout;
Benny Prijono60b980e2006-04-03 22:41:26 +00001091 int rc;
1092
1093 /* Determine how long to sleep */
Benny Prijono9a0eab52006-04-04 19:43:24 +00001094 if (next_rtp.u64 < next_rtcp.u64)
Benny Prijono60b980e2006-04-03 22:41:26 +00001095 lesser = next_rtp;
1096 else
1097 lesser = next_rtcp;
1098
Benny Prijono9a0eab52006-04-04 19:43:24 +00001099 pj_get_timestamp(&now);
1100 if (lesser.u64 <= now.u64) {
Benny Prijono60b980e2006-04-03 22:41:26 +00001101 timeout.sec = timeout.msec = 0;
Benny Prijono9a0eab52006-04-04 19:43:24 +00001102 //printf("immediate "); fflush(stdout);
1103 } else {
1104 pj_uint64_t tick_delay;
1105 tick_delay = lesser.u64 - now.u64;
1106 timeout.sec = 0;
1107 timeout.msec = (pj_uint32_t)(tick_delay * 1000 / freq.u64);
1108 pj_time_val_normalize(&timeout);
1109
1110 //printf("%d:%03d ", timeout.sec, timeout.msec); fflush(stdout);
Benny Prijono60b980e2006-04-03 22:41:26 +00001111 }
1112
1113 PJ_FD_ZERO(&set);
1114 PJ_FD_SET(strm->rtp_sock, &set);
1115 PJ_FD_SET(strm->rtcp_sock, &set);
1116
1117 rc = pj_sock_select(FD_SETSIZE, &set, NULL, NULL, &timeout);
1118
Benny Prijono9a0eab52006-04-04 19:43:24 +00001119 if (rc > 0 && PJ_FD_ISSET(strm->rtp_sock, &set)) {
Benny Prijono60b980e2006-04-03 22:41:26 +00001120
1121 /*
1122 * Process incoming RTP packet.
1123 */
1124 pj_status_t status;
1125 pj_ssize_t size;
1126 const pjmedia_rtp_hdr *hdr;
1127 const void *payload;
1128 unsigned payload_len;
1129
1130 size = sizeof(packet);
1131 status = pj_sock_recv(strm->rtp_sock, packet, &size, 0);
1132 if (status != PJ_SUCCESS) {
1133 app_perror(THIS_FILE, "RTP recv() error", status);
1134 continue;
1135 }
1136
Benny Prijono4adcb912006-04-04 13:12:38 +00001137
Benny Prijono60b980e2006-04-03 22:41:26 +00001138 /* Decode RTP packet. */
1139 status = pjmedia_rtp_decode_rtp(&strm->in_sess,
1140 packet, size,
1141 &hdr,
1142 &payload, &payload_len);
1143 if (status != PJ_SUCCESS) {
1144 app_perror(THIS_FILE, "RTP decode error", status);
Benny Prijono60b980e2006-04-03 22:41:26 +00001145 continue;
1146 }
1147
1148 /* Update the RTCP session. */
1149 pjmedia_rtcp_rx_rtp(&strm->rtcp, pj_ntohs(hdr->seq),
Benny Prijono69968232006-04-06 19:29:03 +00001150 pj_ntohl(hdr->ts), payload_len);
Benny Prijono60b980e2006-04-03 22:41:26 +00001151
Benny Prijono69968232006-04-06 19:29:03 +00001152 /* Update RTP session */
1153 pjmedia_rtp_session_update(&strm->in_sess, hdr, NULL);
Benny Prijono9a0eab52006-04-04 19:43:24 +00001154 }
1155
1156 if (rc > 0 && PJ_FD_ISSET(strm->rtcp_sock, &set)) {
Benny Prijono60b980e2006-04-03 22:41:26 +00001157
1158 /*
1159 * Process incoming RTCP
1160 */
1161 pj_status_t status;
1162 pj_ssize_t size;
1163
1164 size = sizeof(packet);
1165 status = pj_sock_recv( strm->rtcp_sock, packet, &size, 0);
1166 if (status != PJ_SUCCESS)
1167 app_perror(THIS_FILE, "Error receiving RTCP packet", status);
Benny Prijono69968232006-04-06 19:29:03 +00001168 else
1169 pjmedia_rtcp_rx_rtcp(&strm->rtcp, packet, size);
Benny Prijono60b980e2006-04-03 22:41:26 +00001170 }
1171
1172
Benny Prijono9a0eab52006-04-04 19:43:24 +00001173 pj_get_timestamp(&now);
Benny Prijono60b980e2006-04-03 22:41:26 +00001174
Benny Prijono9a0eab52006-04-04 19:43:24 +00001175 if (next_rtp.u64 <= now.u64) {
Benny Prijono60b980e2006-04-03 22:41:26 +00001176 /*
1177 * Time to send RTP packet.
1178 */
1179 pj_status_t status;
1180 const pjmedia_rtp_hdr *hdr;
1181 pj_ssize_t size;
1182 int hdrlen;
1183
1184 /* Format RTP header */
1185 status = pjmedia_rtp_encode_rtp( &strm->out_sess, strm->si.tx_pt,
1186 0, /* marker bit */
1187 strm->bytes_per_frame,
1188 strm->samples_per_frame,
Benny Prijono8befd9f2006-05-13 22:46:23 +00001189 (const void**)&hdr, &hdrlen);
Benny Prijono60b980e2006-04-03 22:41:26 +00001190 if (status == PJ_SUCCESS) {
1191
1192 /* Copy RTP header to packet */
1193 pj_memcpy(packet, hdr, hdrlen);
1194
1195 /* Zero the payload */
1196 pj_memset(packet+hdrlen, 0, strm->bytes_per_frame);
1197
1198 /* Send RTP packet */
1199 size = hdrlen + strm->bytes_per_frame;
1200 status = pj_sock_sendto( strm->rtp_sock, packet, &size, 0,
1201 &strm->si.rem_addr,
1202 sizeof(strm->si.rem_addr));
1203
1204 if (status != PJ_SUCCESS)
1205 app_perror(THIS_FILE, "Error sending RTP packet", status);
1206
1207 }
1208
1209 /* Update RTCP SR */
1210 pjmedia_rtcp_tx_rtp( &strm->rtcp, (pj_uint16_t)strm->bytes_per_frame);
1211
1212 /* Schedule next send */
Benny Prijono9a0eab52006-04-04 19:43:24 +00001213 next_rtp.u64 += (msec_interval * freq.u64 / 1000);
Benny Prijono60b980e2006-04-03 22:41:26 +00001214 }
1215
1216
Benny Prijono9a0eab52006-04-04 19:43:24 +00001217 if (next_rtcp.u64 <= now.u64) {
Benny Prijono60b980e2006-04-03 22:41:26 +00001218 /*
1219 * Time to send RTCP packet.
1220 */
1221 pjmedia_rtcp_pkt *rtcp_pkt;
1222 int rtcp_len;
1223 pj_sockaddr_in rem_addr;
1224 pj_ssize_t size;
1225 int port;
1226 pj_status_t status;
1227
1228 /* Build RTCP packet */
1229 pjmedia_rtcp_build_rtcp(&strm->rtcp, &rtcp_pkt, &rtcp_len);
1230
1231
1232 /* Calculate address based on RTP address */
1233 rem_addr = strm->si.rem_addr;
1234 port = pj_ntohs(strm->si.rem_addr.sin_port) + 1;
1235 rem_addr.sin_port = pj_htons((pj_uint16_t)port);
1236
1237 /* Send packet */
1238 size = rtcp_len;
1239 status = pj_sock_sendto(strm->rtcp_sock, rtcp_pkt, &size, 0,
1240 &rem_addr, sizeof(rem_addr));
1241 if (status != PJ_SUCCESS) {
1242 app_perror(THIS_FILE, "Error sending RTCP packet", status);
1243 }
1244
Benny Prijono69968232006-04-06 19:29:03 +00001245 /* Schedule next send */
Benny Prijono14b7b662006-04-07 15:01:51 +00001246 next_rtcp.u64 += (freq.u64 * (RTCP_INTERVAL+(pj_rand()%RTCP_RAND)) /
1247 1000);
Benny Prijono60b980e2006-04-03 22:41:26 +00001248 }
Benny Prijono60b980e2006-04-03 22:41:26 +00001249 }
1250
1251 return 0;
1252}
1253
1254
1255/* Callback to be called when SDP negotiation is done in the call: */
1256static void call_on_media_update( pjsip_inv_session *inv,
1257 pj_status_t status)
1258{
1259 struct call *call;
1260 pj_pool_t *pool;
1261 struct media_stream *audio;
Benny Prijono49ce9a72006-04-05 16:56:19 +00001262 const pjmedia_sdp_session *local_sdp, *remote_sdp;
Benny Prijono4adcb912006-04-04 13:12:38 +00001263 struct codec *codec_desc = NULL;
1264 unsigned i;
Benny Prijono60b980e2006-04-03 22:41:26 +00001265
1266 call = inv->mod_data[mod_siprtp.id];
1267 pool = inv->dlg->pool;
1268 audio = &call->media[0];
1269
1270 /* If this is a mid-call media update, then destroy existing media */
1271 if (audio->thread != NULL)
1272 destroy_call_media(call->index);
1273
1274
1275 /* Do nothing if media negotiation has failed */
1276 if (status != PJ_SUCCESS) {
1277 app_perror(THIS_FILE, "SDP negotiation failed", status);
1278 return;
1279 }
1280
1281
1282 /* Capture stream definition from the SDP */
1283 pjmedia_sdp_neg_get_active_local(inv->neg, &local_sdp);
1284 pjmedia_sdp_neg_get_active_remote(inv->neg, &remote_sdp);
1285
1286 status = pjmedia_stream_info_from_sdp(&audio->si, inv->pool, app.med_endpt,
Benny Prijonob04c9e02006-05-17 17:17:39 +00001287 local_sdp, remote_sdp, 0);
Benny Prijono60b980e2006-04-03 22:41:26 +00001288 if (status != PJ_SUCCESS) {
1289 app_perror(THIS_FILE, "Error creating stream info from SDP", status);
1290 return;
1291 }
1292
Benny Prijono4adcb912006-04-04 13:12:38 +00001293 /* Get the remainder of codec information from codec descriptor */
1294 if (audio->si.fmt.pt == app.audio_codec.pt)
1295 codec_desc = &app.audio_codec;
1296 else {
1297 /* Find the codec description in codec array */
1298 for (i=0; i<PJ_ARRAY_SIZE(audio_codecs); ++i) {
1299 if (audio_codecs[i].pt == audio->si.fmt.pt) {
1300 codec_desc = &audio_codecs[i];
1301 break;
1302 }
1303 }
1304
1305 if (codec_desc == NULL) {
1306 PJ_LOG(3, (THIS_FILE, "Error: Invalid codec payload type"));
1307 return;
1308 }
1309 }
Benny Prijono60b980e2006-04-03 22:41:26 +00001310
Benny Prijono15953012006-04-27 22:37:08 +00001311 audio->clock_rate = audio->si.fmt.clock_rate;
Benny Prijono4adcb912006-04-04 13:12:38 +00001312 audio->samples_per_frame = audio->clock_rate * codec_desc->ptime / 1000;
1313 audio->bytes_per_frame = codec_desc->bit_rate * codec_desc->ptime / 1000 / 8;
Benny Prijono60b980e2006-04-03 22:41:26 +00001314
1315
1316 pjmedia_rtp_session_init(&audio->out_sess, audio->si.tx_pt,
Benny Prijono9d8a8732006-04-04 13:39:58 +00001317 pj_rand());
Benny Prijono60b980e2006-04-03 22:41:26 +00001318 pjmedia_rtp_session_init(&audio->in_sess, audio->si.fmt.pt, 0);
Benny Prijono6d7a45f2006-04-24 23:13:00 +00001319 pjmedia_rtcp_init(&audio->rtcp, "rtcp", audio->clock_rate,
Benny Prijono69968232006-04-06 19:29:03 +00001320 audio->samples_per_frame, 0);
Benny Prijono60b980e2006-04-03 22:41:26 +00001321
Benny Prijono4adcb912006-04-04 13:12:38 +00001322
Benny Prijono4adcb912006-04-04 13:12:38 +00001323
Benny Prijono60b980e2006-04-03 22:41:26 +00001324 /* Start media thread. */
1325 audio->thread_quit_flag = 0;
1326 status = pj_thread_create( inv->pool, "media", &media_thread, audio,
1327 0, 0, &audio->thread);
1328 if (status != PJ_SUCCESS) {
1329 app_perror(THIS_FILE, "Error creating media thread", status);
1330 }
1331}
1332
1333
1334
1335/* Destroy call's media */
1336static void destroy_call_media(unsigned call_index)
1337{
1338 struct media_stream *audio = &app.call[call_index].media[0];
1339
1340 if (audio->thread) {
1341 audio->thread_quit_flag = 1;
1342 pj_thread_join(audio->thread);
1343 pj_thread_destroy(audio->thread);
1344 audio->thread = NULL;
1345 audio->thread_quit_flag = 0;
Benny Prijono4adcb912006-04-04 13:12:38 +00001346
1347 /* Flush RTP/RTCP packets */
1348 {
1349 pj_fd_set_t set;
1350 pj_time_val timeout = {0, 0};
1351 char packet[1500];
1352 pj_ssize_t size;
1353 pj_status_t status;
1354 int rc;
1355
1356 do {
1357 PJ_FD_ZERO(&set);
1358 PJ_FD_SET(audio->rtp_sock, &set);
1359 PJ_FD_SET(audio->rtcp_sock, &set);
1360
1361 rc = pj_sock_select(FD_SETSIZE, &set, NULL, NULL, &timeout);
1362 if (rc > 0 && PJ_FD_ISSET(audio->rtp_sock, &set)) {
1363 size = sizeof(packet);
1364 status = pj_sock_recv(audio->rtp_sock, packet, &size, 0);
1365
1366 }
1367 if (rc > 0 && PJ_FD_ISSET(audio->rtcp_sock, &set)) {
1368 size = sizeof(packet);
1369 status = pj_sock_recv(audio->rtcp_sock, packet, &size, 0);
1370 }
1371
1372 } while (rc > 0);
1373 }
Benny Prijono60b980e2006-04-03 22:41:26 +00001374 }
1375}
1376
1377
Benny Prijono4adcb912006-04-04 13:12:38 +00001378/*****************************************************************************
Benny Prijono60b980e2006-04-03 22:41:26 +00001379 * USER INTERFACE STUFFS
1380 */
Benny Prijono16a6b0e2006-05-12 10:20:03 +00001381#include "siprtp_report.c"
Benny Prijono60b980e2006-04-03 22:41:26 +00001382
1383
1384static void list_calls()
1385{
1386 unsigned i;
1387 puts("List all calls:");
1388 for (i=0; i<app.max_calls; ++i) {
1389 if (!app.call[i].inv)
1390 continue;
1391 print_call(i);
1392 }
1393}
1394
1395static void hangup_call(unsigned index)
1396{
1397 pjsip_tx_data *tdata;
1398 pj_status_t status;
1399
1400 if (app.call[index].inv == NULL)
1401 return;
1402
1403 status = pjsip_inv_end_session(app.call[index].inv, 603, NULL, &tdata);
1404 if (status==PJ_SUCCESS && tdata!=NULL)
1405 pjsip_inv_send_msg(app.call[index].inv, tdata);
1406}
1407
1408static void hangup_all_calls()
1409{
1410 unsigned i;
1411 for (i=0; i<app.max_calls; ++i) {
1412 if (!app.call[i].inv)
1413 continue;
1414 hangup_call(i);
1415 }
1416}
1417
1418static pj_bool_t simple_input(const char *title, char *buf, pj_size_t len)
1419{
1420 char *p;
1421
1422 printf("%s (empty to cancel): ", title); fflush(stdout);
1423 fgets(buf, len, stdin);
1424
1425 /* Remove trailing newlines. */
1426 for (p=buf; ; ++p) {
1427 if (*p=='\r' || *p=='\n') *p='\0';
1428 else if (!*p) break;
1429 }
1430
1431 if (!*buf)
1432 return PJ_FALSE;
1433
1434 return PJ_TRUE;
1435}
1436
1437
1438static const char *MENU =
1439"\n"
1440"Enter menu character:\n"
1441" l List all calls\n"
1442" h Hangup a call\n"
1443" H Hangup all calls\n"
1444" q Quit\n"
1445"\n";
1446
1447
1448/* Main screen menu */
1449static void console_main()
1450{
1451 char input1[10];
1452 unsigned i;
1453
Benny Prijono4adcb912006-04-04 13:12:38 +00001454 printf("%s", MENU);
1455
Benny Prijono60b980e2006-04-03 22:41:26 +00001456 for (;;) {
1457 printf(">>> "); fflush(stdout);
1458 fgets(input1, sizeof(input1), stdin);
1459
1460 switch (input1[0]) {
1461 case 'l':
1462 list_calls();
1463 break;
1464
1465 case 'h':
1466 if (!simple_input("Call number to hangup", input1, sizeof(input1)))
1467 break;
1468
1469 i = atoi(input1);
1470 hangup_call(i);
1471 break;
1472
1473 case 'H':
1474 hangup_all_calls();
1475 break;
1476
1477 case 'q':
1478 goto on_exit;
1479
1480 default:
Benny Prijono4adcb912006-04-04 13:12:38 +00001481 puts("Invalid command");
Benny Prijono60b980e2006-04-03 22:41:26 +00001482 printf("%s", MENU);
1483 break;
1484 }
1485
1486 fflush(stdout);
1487 }
1488
1489on_exit:
Benny Prijono4adcb912006-04-04 13:12:38 +00001490 hangup_all_calls();
Benny Prijono60b980e2006-04-03 22:41:26 +00001491}
1492
1493
Benny Prijono4adcb912006-04-04 13:12:38 +00001494/*****************************************************************************
1495 * Below is a simple module to log all incoming and outgoing SIP messages
1496 */
1497
1498
Benny Prijono60b980e2006-04-03 22:41:26 +00001499/* Notification on incoming messages */
Benny Prijono4adcb912006-04-04 13:12:38 +00001500static pj_bool_t logger_on_rx_msg(pjsip_rx_data *rdata)
Benny Prijono60b980e2006-04-03 22:41:26 +00001501{
1502 PJ_LOG(4,(THIS_FILE, "RX %d bytes %s from %s:%d:\n"
1503 "%s\n"
1504 "--end msg--",
1505 rdata->msg_info.len,
1506 pjsip_rx_data_get_info(rdata),
1507 rdata->pkt_info.src_name,
1508 rdata->pkt_info.src_port,
1509 rdata->msg_info.msg_buf));
1510
1511 /* Always return false, otherwise messages will not get processed! */
1512 return PJ_FALSE;
1513}
1514
1515/* Notification on outgoing messages */
Benny Prijono4adcb912006-04-04 13:12:38 +00001516static pj_status_t logger_on_tx_msg(pjsip_tx_data *tdata)
Benny Prijono60b980e2006-04-03 22:41:26 +00001517{
1518
1519 /* Important note:
1520 * tp_info field is only valid after outgoing messages has passed
1521 * transport layer. So don't try to access tp_info when the module
1522 * has lower priority than transport layer.
1523 */
1524
1525 PJ_LOG(4,(THIS_FILE, "TX %d bytes %s to %s:%d:\n"
1526 "%s\n"
1527 "--end msg--",
1528 (tdata->buf.cur - tdata->buf.start),
1529 pjsip_tx_data_get_info(tdata),
1530 tdata->tp_info.dst_name,
1531 tdata->tp_info.dst_port,
1532 tdata->buf.start));
1533
1534 /* Always return success, otherwise message will not get sent! */
1535 return PJ_SUCCESS;
1536}
1537
1538/* The module instance. */
1539static pjsip_module msg_logger =
1540{
1541 NULL, NULL, /* prev, next. */
1542 { "mod-siprtp-log", 14 }, /* Name. */
1543 -1, /* Id */
1544 PJSIP_MOD_PRIORITY_TRANSPORT_LAYER-1,/* Priority */
1545 NULL, /* load() */
1546 NULL, /* start() */
1547 NULL, /* stop() */
1548 NULL, /* unload() */
Benny Prijono4adcb912006-04-04 13:12:38 +00001549 &logger_on_rx_msg, /* on_rx_request() */
1550 &logger_on_rx_msg, /* on_rx_response() */
1551 &logger_on_tx_msg, /* on_tx_request. */
1552 &logger_on_tx_msg, /* on_tx_response() */
Benny Prijono60b980e2006-04-03 22:41:26 +00001553 NULL, /* on_tsx_state() */
1554
1555};
1556
1557
1558
Benny Prijono4adcb912006-04-04 13:12:38 +00001559/*****************************************************************************
1560 * Console application custom logging:
1561 */
1562
1563
1564static FILE *log_file;
1565
1566
1567static void app_log_writer(int level, const char *buffer, int len)
1568{
1569 /* Write to both stdout and file. */
1570
1571 if (level <= app.app_log_level)
1572 pj_log_write(level, buffer, len);
1573
1574 if (log_file) {
1575 fwrite(buffer, len, 1, log_file);
1576 fflush(log_file);
1577 }
1578}
1579
1580
1581pj_status_t app_logging_init(void)
1582{
1583 /* Redirect log function to ours */
1584
1585 pj_log_set_log_func( &app_log_writer );
1586
1587 /* If output log file is desired, create the file: */
1588
1589 if (app.log_filename) {
1590 log_file = fopen(app.log_filename, "wt");
1591 if (log_file == NULL) {
1592 PJ_LOG(1,(THIS_FILE, "Unable to open log file %s",
1593 app.log_filename));
1594 return -1;
1595 }
1596 }
1597
1598 return PJ_SUCCESS;
1599}
1600
1601
1602void app_logging_shutdown(void)
1603{
1604 /* Close logging file, if any: */
1605
1606 if (log_file) {
1607 fclose(log_file);
1608 log_file = NULL;
1609 }
1610}
1611
Benny Prijono60b980e2006-04-03 22:41:26 +00001612
1613/*
1614 * main()
1615 */
1616int main(int argc, char *argv[])
1617{
Benny Prijono4adcb912006-04-04 13:12:38 +00001618 unsigned i;
Benny Prijono60b980e2006-04-03 22:41:26 +00001619 pj_status_t status;
1620
Benny Prijono4adcb912006-04-04 13:12:38 +00001621 /* Must init PJLIB first */
Benny Prijono60b980e2006-04-03 22:41:26 +00001622 status = pj_init();
1623 if (status != PJ_SUCCESS)
1624 return 1;
1625
Benny Prijono4adcb912006-04-04 13:12:38 +00001626 /* Get command line options */
Benny Prijono60b980e2006-04-03 22:41:26 +00001627 status = init_options(argc, argv);
1628 if (status != PJ_SUCCESS)
1629 return 1;
1630
Benny Prijono410fbae2006-05-03 18:16:06 +00001631 /* Verify options: */
1632
1633 /* Auto-quit can not be specified for UAS */
1634 if (app.auto_quit && app.uri_to_call.slen == 0) {
1635 printf("Error: --auto-quit option only valid for outgoing "
1636 "mode (UAC) only\n");
1637 return 1;
1638 }
1639
Benny Prijono4adcb912006-04-04 13:12:38 +00001640 /* Init logging */
1641 status = app_logging_init();
1642 if (status != PJ_SUCCESS)
1643 return 1;
1644
1645 /* Init SIP etc */
Benny Prijono60b980e2006-04-03 22:41:26 +00001646 status = init_sip();
1647 if (status != PJ_SUCCESS) {
1648 app_perror(THIS_FILE, "Initialization has failed", status);
1649 destroy_sip();
1650 return 1;
1651 }
1652
Benny Prijono4adcb912006-04-04 13:12:38 +00001653 /* Register module to log incoming/outgoing messages */
Benny Prijono60b980e2006-04-03 22:41:26 +00001654 pjsip_endpt_register_module(app.sip_endpt, &msg_logger);
1655
Benny Prijono4adcb912006-04-04 13:12:38 +00001656 /* Init media */
Benny Prijono60b980e2006-04-03 22:41:26 +00001657 status = init_media();
1658 if (status != PJ_SUCCESS) {
1659 app_perror(THIS_FILE, "Media initialization failed", status);
1660 destroy_sip();
1661 return 1;
1662 }
1663
Benny Prijono9a0eab52006-04-04 19:43:24 +00001664 /* Start worker threads */
1665 for (i=0; i<app.thread_count; ++i) {
1666 pj_thread_create( app.pool, "app", &worker_thread, NULL,
1667 0, 0, &app.thread[i]);
1668 }
1669
Benny Prijono4adcb912006-04-04 13:12:38 +00001670 /* If URL is specified, then make call immediately */
Benny Prijono60b980e2006-04-03 22:41:26 +00001671 if (app.uri_to_call.slen) {
1672 unsigned i;
1673
Benny Prijono4adcb912006-04-04 13:12:38 +00001674 PJ_LOG(3,(THIS_FILE, "Making %d calls to %s..", app.max_calls,
1675 app.uri_to_call.ptr));
1676
Benny Prijono60b980e2006-04-03 22:41:26 +00001677 for (i=0; i<app.max_calls; ++i) {
1678 status = make_call(&app.uri_to_call);
1679 if (status != PJ_SUCCESS) {
1680 app_perror(THIS_FILE, "Error making call", status);
1681 break;
1682 }
1683 }
Benny Prijono4adcb912006-04-04 13:12:38 +00001684
Benny Prijono410fbae2006-05-03 18:16:06 +00001685 if (app.auto_quit) {
1686 /* Wait for calls to complete */
1687 while (app.uac_calls < app.max_calls)
1688 pj_thread_sleep(100);
1689 pj_thread_sleep(200);
1690 } else {
1691 /* Start user interface loop */
1692 console_main();
1693 }
1694
Benny Prijono4adcb912006-04-04 13:12:38 +00001695 } else {
1696
1697 PJ_LOG(3,(THIS_FILE, "Ready for incoming calls (max=%d)",
1698 app.max_calls));
Benny Prijono4adcb912006-04-04 13:12:38 +00001699
Benny Prijono410fbae2006-05-03 18:16:06 +00001700 /* Start user interface loop */
1701 console_main();
1702
1703 }
Benny Prijono60b980e2006-04-03 22:41:26 +00001704
Benny Prijono4adcb912006-04-04 13:12:38 +00001705
1706 /* Shutting down... */
Benny Prijono60b980e2006-04-03 22:41:26 +00001707 destroy_media();
1708 destroy_sip();
Benny Prijono4adcb912006-04-04 13:12:38 +00001709 app_logging_shutdown();
1710
Benny Prijono60b980e2006-04-03 22:41:26 +00001711
1712 return 0;
1713}
1714