blob: e2891abcd10c51fe40b12c7d137b60f98bf53e89 [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
Benny Prijono6647d822006-05-20 13:01:07 +000083#define RTP_START_PORT 4000
Benny Prijono60b980e2006-04-03 22:41:26 +000084
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 }
Benny Prijono60b980e2006-04-03 22:41:26 +0000370}
371
372
373/*
374 * Init media stack.
375 */
376static pj_status_t init_media()
377{
378 pj_ioqueue_t *ioqueue;
379 unsigned i, count;
380 pj_uint16_t rtp_port;
381 pj_str_t temp;
382 pj_sockaddr_in addr;
383 pj_status_t status;
384
385
386 /* Get the ioqueue from the SIP endpoint */
387 ioqueue = pjsip_endpt_get_ioqueue(app.sip_endpt);
388
389
390 /* Initialize media endpoint so that at least error subsystem is properly
391 * initialized.
392 */
393 status = pjmedia_endpt_create(&app.cp.factory, ioqueue, 1,
394 &app.med_endpt);
395 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
396
397
Benny Prijono4d7fd202006-05-14 20:57:20 +0000398 /* Add G711 codec */
399 pjmedia_codec_g711_init(app.med_endpt);
400
Benny Prijono60b980e2006-04-03 22:41:26 +0000401 /* Determine address to bind socket */
402 pj_memset(&addr, 0, sizeof(addr));
403 addr.sin_family = PJ_AF_INET;
404 i = pj_inet_aton(pj_cstr(&temp, app.local_addr), &addr.sin_addr);
405 if (i == 0) {
406 PJ_LOG(3,(THIS_FILE,
407 "Error: invalid local address %s (expecting IP)",
408 app.local_addr));
409 return -1;
410 }
411
Benny Prijono60b980e2006-04-03 22:41:26 +0000412 /* RTP port counter */
413 rtp_port = (pj_uint16_t)(app.rtp_start_port & 0xFFFE);
414
415
416 /* Init media sockets. */
417 for (i=0, count=0; i<app.max_calls; ++i, ++count) {
418
419 int retry;
420
421 app.call[i].index = i;
422
423 /* Repeat binding media socket to next port when fails to bind
424 * to current port number.
425 */
426 retry = 0;
Benny Prijono6647d822006-05-20 13:01:07 +0000427 status = -1;
428 for (retry=0; status!=PJ_SUCCESS && retry<100; ++retry,rtp_port+=2) {
Benny Prijono60b980e2006-04-03 22:41:26 +0000429 struct media_stream *m = &app.call[i].media[0];
430
Benny Prijono60b980e2006-04-03 22:41:26 +0000431 m->port = rtp_port;
432
433 /* Create and bind RTP socket */
434 status = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0,
435 &m->rtp_sock);
436 if (status != PJ_SUCCESS)
437 goto on_error;
438
439 addr.sin_port = pj_htons(rtp_port);
440 status = pj_sock_bind(m->rtp_sock, &addr, sizeof(addr));
441 if (status != PJ_SUCCESS) {
442 pj_sock_close(m->rtp_sock), m->rtp_sock=0;
443 continue;
444 }
445
446
447 /* Create and bind RTCP socket */
448 status = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0,
449 &m->rtcp_sock);
450 if (status != PJ_SUCCESS)
451 goto on_error;
452
453 addr.sin_port = pj_htons((pj_uint16_t)(rtp_port+1));
454 status = pj_sock_bind(m->rtcp_sock, &addr, sizeof(addr));
455 if (status != PJ_SUCCESS) {
456 pj_sock_close(m->rtp_sock), m->rtp_sock=0;
457 pj_sock_close(m->rtcp_sock), m->rtcp_sock=0;
458 continue;
459 }
460
Benny Prijono6647d822006-05-20 13:01:07 +0000461 }
Benny Prijono60b980e2006-04-03 22:41:26 +0000462
463 if (status != PJ_SUCCESS)
464 goto on_error;
465
466 }
467
468 /* Done */
469 return PJ_SUCCESS;
470
471on_error:
472 for (i=0; i<count; ++i) {
473 struct media_stream *m = &app.call[i].media[0];
474
475 pj_sock_close(m->rtp_sock), m->rtp_sock=0;
476 pj_sock_close(m->rtcp_sock), m->rtcp_sock=0;
477 }
478
479 return status;
480}
481
482
483/*
484 * Destroy media.
485 */
486static void destroy_media()
487{
488 unsigned i;
489
490 for (i=0; i<app.max_calls; ++i) {
491 struct media_stream *m = &app.call[i].media[0];
492
493 if (m->rtp_sock)
494 pj_sock_close(m->rtp_sock), m->rtp_sock = 0;
495
496 if (m->rtcp_sock)
497 pj_sock_close(m->rtcp_sock), m->rtcp_sock = 0;
498 }
499
500 if (app.med_endpt) {
501 pjmedia_endpt_destroy(app.med_endpt);
502 app.med_endpt = NULL;
503 }
504}
505
506
507/*
508 * Make outgoing call.
509 */
510static pj_status_t make_call(const pj_str_t *dst_uri)
511{
512 unsigned i;
513 struct call *call;
514 pjsip_dialog *dlg;
515 pjmedia_sdp_session *sdp;
516 pjsip_tx_data *tdata;
517 pj_status_t status;
518
519
520 /* Find unused call slot */
521 for (i=0; i<app.max_calls; ++i) {
522 if (app.call[i].inv == NULL)
523 break;
524 }
525
526 if (i == app.max_calls)
527 return PJ_ETOOMANY;
528
529 call = &app.call[i];
530
531 /* Create UAC dialog */
532 status = pjsip_dlg_create_uac( pjsip_ua_instance(),
533 &app.local_uri, /* local URI */
534 &app.local_contact, /* local Contact */
535 dst_uri, /* remote URI */
536 dst_uri, /* remote target */
537 &dlg); /* dialog */
Benny Prijono410fbae2006-05-03 18:16:06 +0000538 if (status != PJ_SUCCESS) {
539 ++app.uac_calls;
Benny Prijono60b980e2006-04-03 22:41:26 +0000540 return status;
Benny Prijono410fbae2006-05-03 18:16:06 +0000541 }
Benny Prijono60b980e2006-04-03 22:41:26 +0000542
543 /* Create SDP */
544 create_sdp( dlg->pool, call, &sdp);
545
546 /* Create the INVITE session. */
547 status = pjsip_inv_create_uac( dlg, sdp, 0, &call->inv);
548 if (status != PJ_SUCCESS) {
549 pjsip_dlg_terminate(dlg);
Benny Prijono410fbae2006-05-03 18:16:06 +0000550 ++app.uac_calls;
Benny Prijono60b980e2006-04-03 22:41:26 +0000551 return status;
552 }
553
554
555 /* Attach call data to invite session */
556 call->inv->mod_data[mod_siprtp.id] = call;
557
Benny Prijono4adcb912006-04-04 13:12:38 +0000558 /* Mark start of call */
559 pj_gettimeofday(&call->start_time);
560
Benny Prijono60b980e2006-04-03 22:41:26 +0000561
562 /* Create initial INVITE request.
563 * This INVITE request will contain a perfectly good request and
564 * an SDP body as well.
565 */
566 status = pjsip_inv_invite(call->inv, &tdata);
567 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
568
569
570 /* Send initial INVITE request.
571 * From now on, the invite session's state will be reported to us
572 * via the invite session callbacks.
573 */
574 status = pjsip_inv_send_msg(call->inv, tdata);
575 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
576
577
578 return PJ_SUCCESS;
579}
580
581
582/*
583 * Receive incoming call
584 */
585static void process_incoming_call(pjsip_rx_data *rdata)
586{
587 unsigned i;
588 struct call *call;
589 pjsip_dialog *dlg;
590 pjmedia_sdp_session *sdp;
591 pjsip_tx_data *tdata;
592 pj_status_t status;
593
594 /* Find free call slot */
595 for (i=0; i<app.max_calls; ++i) {
596 if (app.call[i].inv == NULL)
597 break;
598 }
599
600 if (i == app.max_calls) {
601 const pj_str_t reason = pj_str("Too many calls");
602 pjsip_endpt_respond_stateless( app.sip_endpt, rdata,
603 500, &reason,
604 NULL, NULL);
605 return;
606 }
607
608 call = &app.call[i];
609
610 /* Create UAS dialog */
611 status = pjsip_dlg_create_uas( pjsip_ua_instance(), rdata,
612 &app.local_contact, &dlg);
613 if (status != PJ_SUCCESS) {
614 const pj_str_t reason = pj_str("Unable to create dialog");
615 pjsip_endpt_respond_stateless( app.sip_endpt, rdata,
616 500, &reason,
617 NULL, NULL);
618 return;
619 }
620
621 /* Create SDP */
622 create_sdp( dlg->pool, call, &sdp);
623
624 /* Create UAS invite session */
625 status = pjsip_inv_create_uas( dlg, rdata, sdp, 0, &call->inv);
626 if (status != PJ_SUCCESS) {
Benny Prijono4adcb912006-04-04 13:12:38 +0000627 pjsip_dlg_create_response(dlg, rdata, 500, NULL, &tdata);
628 pjsip_dlg_send_response(dlg, pjsip_rdata_get_tsx(rdata), tdata);
Benny Prijono60b980e2006-04-03 22:41:26 +0000629 return;
630 }
631
Benny Prijono4adcb912006-04-04 13:12:38 +0000632
Benny Prijono60b980e2006-04-03 22:41:26 +0000633 /* Attach call data to invite session */
634 call->inv->mod_data[mod_siprtp.id] = call;
635
Benny Prijono4adcb912006-04-04 13:12:38 +0000636 /* Mark start of call */
637 pj_gettimeofday(&call->start_time);
638
639
640
Benny Prijono60b980e2006-04-03 22:41:26 +0000641 /* Create 200 response .*/
642 status = pjsip_inv_initial_answer(call->inv, rdata, 200,
643 NULL, NULL, &tdata);
Benny Prijono4adcb912006-04-04 13:12:38 +0000644 if (status != PJ_SUCCESS) {
645 status = pjsip_inv_initial_answer(call->inv, rdata,
646 PJSIP_SC_NOT_ACCEPTABLE,
647 NULL, NULL, &tdata);
648 if (status == PJ_SUCCESS)
649 pjsip_inv_send_msg(call->inv, tdata);
650 else
651 pjsip_inv_terminate(call->inv, 500, PJ_FALSE);
652 return;
653 }
654
Benny Prijono60b980e2006-04-03 22:41:26 +0000655
656 /* Send the 200 response. */
657 status = pjsip_inv_send_msg(call->inv, tdata);
658 PJ_ASSERT_ON_FAIL(status == PJ_SUCCESS, return);
659
660
661 /* Done */
662}
663
664
665/* Callback to be called when dialog has forked: */
666static void call_on_forked(pjsip_inv_session *inv, pjsip_event *e)
667{
668 PJ_UNUSED_ARG(inv);
669 PJ_UNUSED_ARG(e);
670
671 PJ_TODO( HANDLE_FORKING );
672}
673
674
675/* Callback to be called to handle incoming requests outside dialogs: */
676static pj_bool_t on_rx_request( pjsip_rx_data *rdata )
677{
Benny Prijono4adcb912006-04-04 13:12:38 +0000678 /* Ignore strandled ACKs (must not send respone */
679 if (rdata->msg_info.msg->line.req.method.id == PJSIP_ACK_METHOD)
680 return PJ_FALSE;
681
Benny Prijono60b980e2006-04-03 22:41:26 +0000682 /* Respond (statelessly) any non-INVITE requests with 500 */
683 if (rdata->msg_info.msg->line.req.method.id != PJSIP_INVITE_METHOD) {
684 pj_str_t reason = pj_str("Unsupported Operation");
685 pjsip_endpt_respond_stateless( app.sip_endpt, rdata,
686 500, &reason,
687 NULL, NULL);
688 return PJ_TRUE;
689 }
690
691 /* Handle incoming INVITE */
692 process_incoming_call(rdata);
693
694 /* Done */
695 return PJ_TRUE;
696}
697
698
Benny Prijono410fbae2006-05-03 18:16:06 +0000699/* Callback timer to disconnect call (limiting call duration) */
700static void timer_disconnect_call( pj_timer_heap_t *timer_heap,
701 struct pj_timer_entry *entry)
702{
703 struct call *call = entry->user_data;
704
705 PJ_UNUSED_ARG(timer_heap);
706
707 entry->id = 0;
708 hangup_call(call->index);
709}
710
711
Benny Prijono60b980e2006-04-03 22:41:26 +0000712/* Callback to be called when invite session's state has changed: */
713static void call_on_state_changed( pjsip_inv_session *inv,
714 pjsip_event *e)
715{
Benny Prijono4adcb912006-04-04 13:12:38 +0000716 struct call *call = inv->mod_data[mod_siprtp.id];
717
Benny Prijono60b980e2006-04-03 22:41:26 +0000718 PJ_UNUSED_ARG(e);
719
Benny Prijono4adcb912006-04-04 13:12:38 +0000720 if (!call)
721 return;
Benny Prijono60b980e2006-04-03 22:41:26 +0000722
Benny Prijono4adcb912006-04-04 13:12:38 +0000723 if (inv->state == PJSIP_INV_STATE_DISCONNECTED) {
724
725 pj_time_val null_time = {0, 0};
Benny Prijono60b980e2006-04-03 22:41:26 +0000726
Benny Prijono410fbae2006-05-03 18:16:06 +0000727 if (call->d_timer.id != 0) {
728 pjsip_endpt_cancel_timer(app.sip_endpt, &call->d_timer);
729 call->d_timer.id = 0;
730 }
731
Benny Prijonod7a13f12006-04-05 19:08:16 +0000732 PJ_LOG(3,(THIS_FILE, "Call #%d disconnected. Reason=%s",
733 call->index,
734 pjsip_get_status_text(inv->cause)->ptr));
735 PJ_LOG(3,(THIS_FILE, "Call #%d statistics:", call->index));
736 print_call(call->index);
737
738
Benny Prijono60b980e2006-04-03 22:41:26 +0000739 call->inv = NULL;
740 inv->mod_data[mod_siprtp.id] = NULL;
741
742 destroy_call_media(call->index);
Benny Prijono4adcb912006-04-04 13:12:38 +0000743
744 call->start_time = null_time;
745 call->response_time = null_time;
746 call->connect_time = null_time;
747
Benny Prijono410fbae2006-05-03 18:16:06 +0000748 ++app.uac_calls;
Benny Prijono4adcb912006-04-04 13:12:38 +0000749
750 } else if (inv->state == PJSIP_INV_STATE_CONFIRMED) {
751
752 pj_time_val t;
753
754 pj_gettimeofday(&call->connect_time);
755 if (call->response_time.sec == 0)
756 call->response_time = call->connect_time;
757
758 t = call->connect_time;
759 PJ_TIME_VAL_SUB(t, call->start_time);
760
761 PJ_LOG(3,(THIS_FILE, "Call #%d connected in %d ms", call->index,
762 PJ_TIME_VAL_MSEC(t)));
763
Benny Prijono410fbae2006-05-03 18:16:06 +0000764 if (app.duration != 0) {
765 call->d_timer.id = 1;
766 call->d_timer.user_data = call;
767 call->d_timer.cb = &timer_disconnect_call;
768
769 t.sec = app.duration;
770 t.msec = 0;
771
772 pjsip_endpt_schedule_timer(app.sip_endpt, &call->d_timer, &t);
773 }
774
Benny Prijono4adcb912006-04-04 13:12:38 +0000775 } else if ( inv->state == PJSIP_INV_STATE_EARLY ||
776 inv->state == PJSIP_INV_STATE_CONNECTING) {
777
778 if (call->response_time.sec == 0)
779 pj_gettimeofday(&call->response_time);
780
Benny Prijono60b980e2006-04-03 22:41:26 +0000781 }
782}
783
784
785/* Utility */
786static void app_perror(const char *sender, const char *title,
787 pj_status_t status)
788{
789 char errmsg[PJ_ERR_MSG_SIZE];
790
791 pj_strerror(status, errmsg, sizeof(errmsg));
792 PJ_LOG(3,(sender, "%s: %s [status=%d]", title, errmsg, status));
793}
794
795
796/* Worker thread */
797static int worker_thread(void *arg)
798{
799 PJ_UNUSED_ARG(arg);
800
801 while (!app.thread_quit) {
802 pj_time_val timeout = {0, 10};
803 pjsip_endpt_handle_events(app.sip_endpt, &timeout);
804 }
805
806 return 0;
807}
808
809
Benny Prijono60b980e2006-04-03 22:41:26 +0000810/* Init application options */
811static pj_status_t init_options(int argc, char *argv[])
812{
813 static char ip_addr[32];
814 static char local_uri[64];
815
Benny Prijono4adcb912006-04-04 13:12:38 +0000816 enum { OPT_START,
817 OPT_APP_LOG_LEVEL, OPT_LOG_FILE,
Benny Prijonofcb36722006-05-18 18:34:21 +0000818 OPT_A_PT, OPT_A_NAME, OPT_A_CLOCK, OPT_A_BITRATE, OPT_A_PTIME,
819 OPT_REPORT_FILE };
Benny Prijono4adcb912006-04-04 13:12:38 +0000820
Benny Prijono60b980e2006-04-03 22:41:26 +0000821 struct pj_getopt_option long_options[] = {
Benny Prijono4adcb912006-04-04 13:12:38 +0000822 { "count", 1, 0, 'c' },
Benny Prijono410fbae2006-05-03 18:16:06 +0000823 { "duration", 1, 0, 'd' },
824 { "auto-quit", 0, 0, 'q' },
Benny Prijono4adcb912006-04-04 13:12:38 +0000825 { "local-port", 1, 0, 'p' },
826 { "rtp-port", 1, 0, 'r' },
827 { "ip-addr", 1, 0, 'i' },
828
829 { "log-level", 1, 0, 'l' },
830 { "app-log-level", 1, 0, OPT_APP_LOG_LEVEL },
831 { "log-file", 1, 0, OPT_LOG_FILE },
Benny Prijono4d7fd202006-05-14 20:57:20 +0000832
Benny Prijonofcb36722006-05-18 18:34:21 +0000833 { "report-file", 1, 0, OPT_REPORT_FILE },
834
Benny Prijono4d7fd202006-05-14 20:57:20 +0000835 /* Don't support this anymore, see comments in USAGE above.
Benny Prijono4adcb912006-04-04 13:12:38 +0000836 { "a-pt", 1, 0, OPT_A_PT },
837 { "a-name", 1, 0, OPT_A_NAME },
838 { "a-clock", 1, 0, OPT_A_CLOCK },
839 { "a-bitrate", 1, 0, OPT_A_BITRATE },
840 { "a-ptime", 1, 0, OPT_A_PTIME },
Benny Prijono4d7fd202006-05-14 20:57:20 +0000841 */
Benny Prijono4adcb912006-04-04 13:12:38 +0000842
Benny Prijono60b980e2006-04-03 22:41:26 +0000843 { NULL, 0, 0, 0 },
844 };
845 int c;
846 int option_index;
847
848 /* Get local IP address for the default IP address */
849 {
850 const pj_str_t *hostname;
851 pj_sockaddr_in tmp_addr;
852 char *addr;
853
854 hostname = pj_gethostname();
855 pj_sockaddr_in_init(&tmp_addr, hostname, 0);
856 addr = pj_inet_ntoa(tmp_addr.sin_addr);
857 pj_ansi_strcpy(ip_addr, addr);
858 }
859
Benny Prijono4adcb912006-04-04 13:12:38 +0000860 /* Init defaults */
Benny Prijono60b980e2006-04-03 22:41:26 +0000861 app.max_calls = 1;
862 app.thread_count = 1;
863 app.sip_port = 5060;
Benny Prijono6647d822006-05-20 13:01:07 +0000864 app.rtp_start_port = RTP_START_PORT;
Benny Prijono60b980e2006-04-03 22:41:26 +0000865 app.local_addr = ip_addr;
Benny Prijono4adcb912006-04-04 13:12:38 +0000866 app.log_level = 5;
867 app.app_log_level = 3;
868 app.log_filename = NULL;
869
870 /* Default codecs: */
871 app.audio_codec = audio_codecs[0];
Benny Prijono60b980e2006-04-03 22:41:26 +0000872
873 /* Parse options */
874 pj_optind = 0;
Benny Prijono410fbae2006-05-03 18:16:06 +0000875 while((c=pj_getopt_long(argc,argv, "c:d:p:r:i:l:q",
Benny Prijono60b980e2006-04-03 22:41:26 +0000876 long_options, &option_index))!=-1)
877 {
878 switch (c) {
879 case 'c':
880 app.max_calls = atoi(pj_optarg);
881 if (app.max_calls < 0 || app.max_calls > MAX_CALLS) {
882 PJ_LOG(3,(THIS_FILE, "Invalid max calls value %s", pj_optarg));
883 return 1;
884 }
885 break;
Benny Prijono410fbae2006-05-03 18:16:06 +0000886 case 'd':
887 app.duration = atoi(pj_optarg);
888 break;
889 case 'q':
890 app.auto_quit = 1;
891 break;
892
Benny Prijono60b980e2006-04-03 22:41:26 +0000893 case 'p':
894 app.sip_port = atoi(pj_optarg);
895 break;
896 case 'r':
897 app.rtp_start_port = atoi(pj_optarg);
898 break;
899 case 'i':
900 app.local_addr = pj_optarg;
901 break;
Benny Prijono4adcb912006-04-04 13:12:38 +0000902
903 case 'l':
904 app.log_level = atoi(pj_optarg);
905 break;
906 case OPT_APP_LOG_LEVEL:
907 app.app_log_level = atoi(pj_optarg);
908 break;
909 case OPT_LOG_FILE:
910 app.log_filename = pj_optarg;
911 break;
912
913 case OPT_A_PT:
914 app.audio_codec.pt = atoi(pj_optarg);
915 break;
916 case OPT_A_NAME:
917 app.audio_codec.name = pj_optarg;
918 break;
919 case OPT_A_CLOCK:
920 app.audio_codec.clock_rate = atoi(pj_optarg);
921 break;
922 case OPT_A_BITRATE:
923 app.audio_codec.bit_rate = atoi(pj_optarg);
924 break;
925 case OPT_A_PTIME:
926 app.audio_codec.ptime = atoi(pj_optarg);
927 break;
Benny Prijonofcb36722006-05-18 18:34:21 +0000928 case OPT_REPORT_FILE:
929 app.report_filename = pj_optarg;
930 break;
Benny Prijono4adcb912006-04-04 13:12:38 +0000931
Benny Prijono60b980e2006-04-03 22:41:26 +0000932 default:
933 puts(USAGE);
934 return 1;
935 }
936 }
937
938 /* Check if URL is specified */
939 if (pj_optind < argc)
940 app.uri_to_call = pj_str(argv[pj_optind]);
941
942 /* Build local URI and contact */
943 pj_ansi_sprintf( local_uri, "sip:%s:%d", app.local_addr, app.sip_port);
944 app.local_uri = pj_str(local_uri);
945 app.local_contact = app.local_uri;
946
947
948 return PJ_SUCCESS;
949}
950
951
Benny Prijono4adcb912006-04-04 13:12:38 +0000952/*****************************************************************************
Benny Prijono60b980e2006-04-03 22:41:26 +0000953 * MEDIA STUFFS
954 */
955
956/*
957 * Create SDP session for a call.
958 */
959static pj_status_t create_sdp( pj_pool_t *pool,
960 struct call *call,
961 pjmedia_sdp_session **p_sdp)
962{
963 pj_time_val tv;
964 pjmedia_sdp_session *sdp;
965 pjmedia_sdp_media *m;
966 pjmedia_sdp_attr *attr;
967 struct media_stream *audio = &call->media[0];
968
969 PJ_ASSERT_RETURN(pool && p_sdp, PJ_EINVAL);
970
971
972 /* Create and initialize basic SDP session */
973 sdp = pj_pool_zalloc (pool, sizeof(pjmedia_sdp_session));
974
975 pj_gettimeofday(&tv);
976 sdp->origin.user = pj_str("pjsip-siprtp");
977 sdp->origin.version = sdp->origin.id = tv.sec + 2208988800UL;
978 sdp->origin.net_type = pj_str("IN");
979 sdp->origin.addr_type = pj_str("IP4");
980 sdp->origin.addr = *pj_gethostname();
981 sdp->name = pj_str("pjsip");
982
983 /* Since we only support one media stream at present, put the
984 * SDP connection line in the session level.
985 */
986 sdp->conn = pj_pool_zalloc (pool, sizeof(pjmedia_sdp_conn));
987 sdp->conn->net_type = pj_str("IN");
988 sdp->conn->addr_type = pj_str("IP4");
989 sdp->conn->addr = pj_str(app.local_addr);
990
991
992 /* SDP time and attributes. */
993 sdp->time.start = sdp->time.stop = 0;
994 sdp->attr_count = 0;
995
996 /* Create media stream 0: */
997
998 sdp->media_count = 1;
999 m = pj_pool_zalloc (pool, sizeof(pjmedia_sdp_media));
1000 sdp->media[0] = m;
1001
1002 /* Standard media info: */
1003 m->desc.media = pj_str("audio");
1004 m->desc.port = audio->port;
1005 m->desc.port_count = 1;
1006 m->desc.transport = pj_str("RTP/AVP");
1007
1008 /* Add format and rtpmap for each codec. */
1009 m->desc.fmt_count = 1;
1010 m->attr_count = 0;
1011
1012 {
1013 pjmedia_sdp_rtpmap rtpmap;
1014 pjmedia_sdp_attr *attr;
Benny Prijono4adcb912006-04-04 13:12:38 +00001015 char ptstr[10];
Benny Prijono60b980e2006-04-03 22:41:26 +00001016
Benny Prijono4adcb912006-04-04 13:12:38 +00001017 sprintf(ptstr, "%d", app.audio_codec.pt);
1018 pj_strdup2(pool, &m->desc.fmt[0], ptstr);
1019 rtpmap.pt = m->desc.fmt[0];
1020 rtpmap.clock_rate = app.audio_codec.clock_rate;
1021 rtpmap.enc_name = pj_str(app.audio_codec.name);
Benny Prijono60b980e2006-04-03 22:41:26 +00001022 rtpmap.param.slen = 0;
1023
1024 pjmedia_sdp_rtpmap_to_attr(pool, &rtpmap, &attr);
1025 m->attr[m->attr_count++] = attr;
1026 }
1027
1028 /* Add sendrecv attribute. */
1029 attr = pj_pool_zalloc(pool, sizeof(pjmedia_sdp_attr));
1030 attr->name = pj_str("sendrecv");
1031 m->attr[m->attr_count++] = attr;
1032
1033#if 1
1034 /*
1035 * Add support telephony event
1036 */
Benny Prijono4adcb912006-04-04 13:12:38 +00001037 m->desc.fmt[m->desc.fmt_count++] = pj_str("121");
Benny Prijono60b980e2006-04-03 22:41:26 +00001038 /* Add rtpmap. */
1039 attr = pj_pool_zalloc(pool, sizeof(pjmedia_sdp_attr));
1040 attr->name = pj_str("rtpmap");
Benny Prijono4adcb912006-04-04 13:12:38 +00001041 attr->value = pj_str(":121 telephone-event/8000");
Benny Prijono60b980e2006-04-03 22:41:26 +00001042 m->attr[m->attr_count++] = attr;
1043 /* Add fmtp */
1044 attr = pj_pool_zalloc(pool, sizeof(pjmedia_sdp_attr));
1045 attr->name = pj_str("fmtp");
Benny Prijono4adcb912006-04-04 13:12:38 +00001046 attr->value = pj_str(":121 0-15");
Benny Prijono60b980e2006-04-03 22:41:26 +00001047 m->attr[m->attr_count++] = attr;
1048#endif
1049
1050 /* Done */
1051 *p_sdp = sdp;
1052
1053 return PJ_SUCCESS;
1054}
1055
1056
Benny Prijono6647d822006-05-20 13:01:07 +00001057#if defined(PJ_WIN32) && PJ_WIN32 != 0
1058#include <windows.h>
1059static void boost_priority(void)
1060{
1061 SetPriorityClass( GetCurrentProcess(), REALTIME_PRIORITY_CLASS);
1062 SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
1063}
1064
1065#else
1066# define boost_priority()
1067#endif
1068
Benny Prijono4adcb912006-04-04 13:12:38 +00001069/*
1070 * Media thread
1071 *
1072 * This is the thread to send and receive both RTP and RTCP packets.
1073 */
Benny Prijono60b980e2006-04-03 22:41:26 +00001074static int media_thread(void *arg)
1075{
Benny Prijono14b7b662006-04-07 15:01:51 +00001076 enum { RTCP_INTERVAL = 5000, RTCP_RAND = 2000 };
Benny Prijono60b980e2006-04-03 22:41:26 +00001077 struct media_stream *strm = arg;
1078 char packet[1500];
Benny Prijono9a0eab52006-04-04 19:43:24 +00001079 unsigned msec_interval;
1080 pj_timestamp freq, next_rtp, next_rtcp;
Benny Prijono60b980e2006-04-03 22:41:26 +00001081
Benny Prijono6647d822006-05-20 13:01:07 +00001082
1083 /* Boost thread priority if necessary */
1084 boost_priority();
1085
1086
Benny Prijono9a0eab52006-04-04 19:43:24 +00001087 msec_interval = strm->samples_per_frame * 1000 / strm->clock_rate;
1088 pj_get_timestamp_freq(&freq);
1089
1090 pj_get_timestamp(&next_rtp);
1091 next_rtp.u64 += (freq.u64 * msec_interval / 1000);
Benny Prijono60b980e2006-04-03 22:41:26 +00001092
1093 next_rtcp = next_rtp;
Benny Prijono14b7b662006-04-07 15:01:51 +00001094 next_rtcp.u64 += (freq.u64 * (RTCP_INTERVAL+(pj_rand()%RTCP_RAND)) / 1000);
Benny Prijono60b980e2006-04-03 22:41:26 +00001095
1096
1097 while (!strm->thread_quit_flag) {
1098 pj_fd_set_t set;
Benny Prijono9a0eab52006-04-04 19:43:24 +00001099 pj_timestamp now, lesser;
1100 pj_time_val timeout;
Benny Prijono60b980e2006-04-03 22:41:26 +00001101 int rc;
1102
1103 /* Determine how long to sleep */
Benny Prijono9a0eab52006-04-04 19:43:24 +00001104 if (next_rtp.u64 < next_rtcp.u64)
Benny Prijono60b980e2006-04-03 22:41:26 +00001105 lesser = next_rtp;
1106 else
1107 lesser = next_rtcp;
1108
Benny Prijono9a0eab52006-04-04 19:43:24 +00001109 pj_get_timestamp(&now);
1110 if (lesser.u64 <= now.u64) {
Benny Prijono60b980e2006-04-03 22:41:26 +00001111 timeout.sec = timeout.msec = 0;
Benny Prijono9a0eab52006-04-04 19:43:24 +00001112 //printf("immediate "); fflush(stdout);
1113 } else {
1114 pj_uint64_t tick_delay;
1115 tick_delay = lesser.u64 - now.u64;
1116 timeout.sec = 0;
1117 timeout.msec = (pj_uint32_t)(tick_delay * 1000 / freq.u64);
1118 pj_time_val_normalize(&timeout);
1119
1120 //printf("%d:%03d ", timeout.sec, timeout.msec); fflush(stdout);
Benny Prijono60b980e2006-04-03 22:41:26 +00001121 }
1122
1123 PJ_FD_ZERO(&set);
1124 PJ_FD_SET(strm->rtp_sock, &set);
1125 PJ_FD_SET(strm->rtcp_sock, &set);
1126
1127 rc = pj_sock_select(FD_SETSIZE, &set, NULL, NULL, &timeout);
1128
Benny Prijono6647d822006-05-20 13:01:07 +00001129 if (rc < 0) {
1130 pj_thread_sleep(10);
1131 continue;
1132 }
1133
Benny Prijono9a0eab52006-04-04 19:43:24 +00001134 if (rc > 0 && PJ_FD_ISSET(strm->rtp_sock, &set)) {
Benny Prijono60b980e2006-04-03 22:41:26 +00001135
1136 /*
1137 * Process incoming RTP packet.
1138 */
1139 pj_status_t status;
1140 pj_ssize_t size;
1141 const pjmedia_rtp_hdr *hdr;
1142 const void *payload;
1143 unsigned payload_len;
1144
1145 size = sizeof(packet);
1146 status = pj_sock_recv(strm->rtp_sock, packet, &size, 0);
1147 if (status != PJ_SUCCESS) {
1148 app_perror(THIS_FILE, "RTP recv() error", status);
Benny Prijono6647d822006-05-20 13:01:07 +00001149 pj_thread_sleep(10);
Benny Prijono60b980e2006-04-03 22:41:26 +00001150 continue;
1151 }
1152
Benny Prijono4adcb912006-04-04 13:12:38 +00001153
Benny Prijono60b980e2006-04-03 22:41:26 +00001154 /* Decode RTP packet. */
1155 status = pjmedia_rtp_decode_rtp(&strm->in_sess,
1156 packet, size,
1157 &hdr,
1158 &payload, &payload_len);
1159 if (status != PJ_SUCCESS) {
1160 app_perror(THIS_FILE, "RTP decode error", status);
Benny Prijono60b980e2006-04-03 22:41:26 +00001161 continue;
1162 }
1163
1164 /* Update the RTCP session. */
1165 pjmedia_rtcp_rx_rtp(&strm->rtcp, pj_ntohs(hdr->seq),
Benny Prijono69968232006-04-06 19:29:03 +00001166 pj_ntohl(hdr->ts), payload_len);
Benny Prijono60b980e2006-04-03 22:41:26 +00001167
Benny Prijono69968232006-04-06 19:29:03 +00001168 /* Update RTP session */
1169 pjmedia_rtp_session_update(&strm->in_sess, hdr, NULL);
Benny Prijono9a0eab52006-04-04 19:43:24 +00001170 }
1171
1172 if (rc > 0 && PJ_FD_ISSET(strm->rtcp_sock, &set)) {
Benny Prijono60b980e2006-04-03 22:41:26 +00001173
1174 /*
1175 * Process incoming RTCP
1176 */
1177 pj_status_t status;
1178 pj_ssize_t size;
1179
1180 size = sizeof(packet);
1181 status = pj_sock_recv( strm->rtcp_sock, packet, &size, 0);
Benny Prijono6647d822006-05-20 13:01:07 +00001182 if (status != PJ_SUCCESS) {
Benny Prijono60b980e2006-04-03 22:41:26 +00001183 app_perror(THIS_FILE, "Error receiving RTCP packet", status);
Benny Prijono6647d822006-05-20 13:01:07 +00001184 pj_thread_sleep(10);
1185 } else
Benny Prijono69968232006-04-06 19:29:03 +00001186 pjmedia_rtcp_rx_rtcp(&strm->rtcp, packet, size);
Benny Prijono60b980e2006-04-03 22:41:26 +00001187 }
1188
1189
Benny Prijono9a0eab52006-04-04 19:43:24 +00001190 pj_get_timestamp(&now);
Benny Prijono60b980e2006-04-03 22:41:26 +00001191
Benny Prijono9a0eab52006-04-04 19:43:24 +00001192 if (next_rtp.u64 <= now.u64) {
Benny Prijono60b980e2006-04-03 22:41:26 +00001193 /*
1194 * Time to send RTP packet.
1195 */
1196 pj_status_t status;
1197 const pjmedia_rtp_hdr *hdr;
1198 pj_ssize_t size;
1199 int hdrlen;
1200
1201 /* Format RTP header */
1202 status = pjmedia_rtp_encode_rtp( &strm->out_sess, strm->si.tx_pt,
1203 0, /* marker bit */
1204 strm->bytes_per_frame,
1205 strm->samples_per_frame,
Benny Prijono8befd9f2006-05-13 22:46:23 +00001206 (const void**)&hdr, &hdrlen);
Benny Prijono60b980e2006-04-03 22:41:26 +00001207 if (status == PJ_SUCCESS) {
1208
1209 /* Copy RTP header to packet */
1210 pj_memcpy(packet, hdr, hdrlen);
1211
1212 /* Zero the payload */
1213 pj_memset(packet+hdrlen, 0, strm->bytes_per_frame);
1214
1215 /* Send RTP packet */
1216 size = hdrlen + strm->bytes_per_frame;
1217 status = pj_sock_sendto( strm->rtp_sock, packet, &size, 0,
1218 &strm->si.rem_addr,
1219 sizeof(strm->si.rem_addr));
1220
1221 if (status != PJ_SUCCESS)
1222 app_perror(THIS_FILE, "Error sending RTP packet", status);
1223
1224 }
1225
1226 /* Update RTCP SR */
1227 pjmedia_rtcp_tx_rtp( &strm->rtcp, (pj_uint16_t)strm->bytes_per_frame);
1228
1229 /* Schedule next send */
Benny Prijono9a0eab52006-04-04 19:43:24 +00001230 next_rtp.u64 += (msec_interval * freq.u64 / 1000);
Benny Prijono60b980e2006-04-03 22:41:26 +00001231 }
1232
1233
Benny Prijono9a0eab52006-04-04 19:43:24 +00001234 if (next_rtcp.u64 <= now.u64) {
Benny Prijono60b980e2006-04-03 22:41:26 +00001235 /*
1236 * Time to send RTCP packet.
1237 */
1238 pjmedia_rtcp_pkt *rtcp_pkt;
1239 int rtcp_len;
1240 pj_sockaddr_in rem_addr;
1241 pj_ssize_t size;
1242 int port;
1243 pj_status_t status;
1244
1245 /* Build RTCP packet */
1246 pjmedia_rtcp_build_rtcp(&strm->rtcp, &rtcp_pkt, &rtcp_len);
1247
1248
1249 /* Calculate address based on RTP address */
1250 rem_addr = strm->si.rem_addr;
1251 port = pj_ntohs(strm->si.rem_addr.sin_port) + 1;
1252 rem_addr.sin_port = pj_htons((pj_uint16_t)port);
1253
1254 /* Send packet */
1255 size = rtcp_len;
1256 status = pj_sock_sendto(strm->rtcp_sock, rtcp_pkt, &size, 0,
1257 &rem_addr, sizeof(rem_addr));
1258 if (status != PJ_SUCCESS) {
1259 app_perror(THIS_FILE, "Error sending RTCP packet", status);
1260 }
1261
Benny Prijono69968232006-04-06 19:29:03 +00001262 /* Schedule next send */
Benny Prijono14b7b662006-04-07 15:01:51 +00001263 next_rtcp.u64 += (freq.u64 * (RTCP_INTERVAL+(pj_rand()%RTCP_RAND)) /
1264 1000);
Benny Prijono60b980e2006-04-03 22:41:26 +00001265 }
Benny Prijono60b980e2006-04-03 22:41:26 +00001266 }
1267
1268 return 0;
1269}
1270
1271
1272/* Callback to be called when SDP negotiation is done in the call: */
1273static void call_on_media_update( pjsip_inv_session *inv,
1274 pj_status_t status)
1275{
1276 struct call *call;
1277 pj_pool_t *pool;
1278 struct media_stream *audio;
Benny Prijono49ce9a72006-04-05 16:56:19 +00001279 const pjmedia_sdp_session *local_sdp, *remote_sdp;
Benny Prijono4adcb912006-04-04 13:12:38 +00001280 struct codec *codec_desc = NULL;
1281 unsigned i;
Benny Prijono60b980e2006-04-03 22:41:26 +00001282
1283 call = inv->mod_data[mod_siprtp.id];
1284 pool = inv->dlg->pool;
1285 audio = &call->media[0];
1286
1287 /* If this is a mid-call media update, then destroy existing media */
1288 if (audio->thread != NULL)
1289 destroy_call_media(call->index);
1290
1291
1292 /* Do nothing if media negotiation has failed */
1293 if (status != PJ_SUCCESS) {
1294 app_perror(THIS_FILE, "SDP negotiation failed", status);
1295 return;
1296 }
1297
1298
1299 /* Capture stream definition from the SDP */
1300 pjmedia_sdp_neg_get_active_local(inv->neg, &local_sdp);
1301 pjmedia_sdp_neg_get_active_remote(inv->neg, &remote_sdp);
1302
1303 status = pjmedia_stream_info_from_sdp(&audio->si, inv->pool, app.med_endpt,
Benny Prijonob04c9e02006-05-17 17:17:39 +00001304 local_sdp, remote_sdp, 0);
Benny Prijono60b980e2006-04-03 22:41:26 +00001305 if (status != PJ_SUCCESS) {
1306 app_perror(THIS_FILE, "Error creating stream info from SDP", status);
1307 return;
1308 }
1309
Benny Prijono4adcb912006-04-04 13:12:38 +00001310 /* Get the remainder of codec information from codec descriptor */
1311 if (audio->si.fmt.pt == app.audio_codec.pt)
1312 codec_desc = &app.audio_codec;
1313 else {
1314 /* Find the codec description in codec array */
1315 for (i=0; i<PJ_ARRAY_SIZE(audio_codecs); ++i) {
1316 if (audio_codecs[i].pt == audio->si.fmt.pt) {
1317 codec_desc = &audio_codecs[i];
1318 break;
1319 }
1320 }
1321
1322 if (codec_desc == NULL) {
1323 PJ_LOG(3, (THIS_FILE, "Error: Invalid codec payload type"));
1324 return;
1325 }
1326 }
Benny Prijono60b980e2006-04-03 22:41:26 +00001327
Benny Prijono15953012006-04-27 22:37:08 +00001328 audio->clock_rate = audio->si.fmt.clock_rate;
Benny Prijono4adcb912006-04-04 13:12:38 +00001329 audio->samples_per_frame = audio->clock_rate * codec_desc->ptime / 1000;
1330 audio->bytes_per_frame = codec_desc->bit_rate * codec_desc->ptime / 1000 / 8;
Benny Prijono60b980e2006-04-03 22:41:26 +00001331
1332
1333 pjmedia_rtp_session_init(&audio->out_sess, audio->si.tx_pt,
Benny Prijono9d8a8732006-04-04 13:39:58 +00001334 pj_rand());
Benny Prijono60b980e2006-04-03 22:41:26 +00001335 pjmedia_rtp_session_init(&audio->in_sess, audio->si.fmt.pt, 0);
Benny Prijono6d7a45f2006-04-24 23:13:00 +00001336 pjmedia_rtcp_init(&audio->rtcp, "rtcp", audio->clock_rate,
Benny Prijono69968232006-04-06 19:29:03 +00001337 audio->samples_per_frame, 0);
Benny Prijono60b980e2006-04-03 22:41:26 +00001338
Benny Prijono4adcb912006-04-04 13:12:38 +00001339
Benny Prijono4adcb912006-04-04 13:12:38 +00001340
Benny Prijono60b980e2006-04-03 22:41:26 +00001341 /* Start media thread. */
1342 audio->thread_quit_flag = 0;
1343 status = pj_thread_create( inv->pool, "media", &media_thread, audio,
1344 0, 0, &audio->thread);
1345 if (status != PJ_SUCCESS) {
1346 app_perror(THIS_FILE, "Error creating media thread", status);
1347 }
1348}
1349
1350
1351
1352/* Destroy call's media */
1353static void destroy_call_media(unsigned call_index)
1354{
1355 struct media_stream *audio = &app.call[call_index].media[0];
1356
1357 if (audio->thread) {
1358 audio->thread_quit_flag = 1;
1359 pj_thread_join(audio->thread);
1360 pj_thread_destroy(audio->thread);
1361 audio->thread = NULL;
1362 audio->thread_quit_flag = 0;
Benny Prijono4adcb912006-04-04 13:12:38 +00001363
1364 /* Flush RTP/RTCP packets */
1365 {
1366 pj_fd_set_t set;
1367 pj_time_val timeout = {0, 0};
1368 char packet[1500];
1369 pj_ssize_t size;
1370 pj_status_t status;
1371 int rc;
1372
1373 do {
1374 PJ_FD_ZERO(&set);
1375 PJ_FD_SET(audio->rtp_sock, &set);
1376 PJ_FD_SET(audio->rtcp_sock, &set);
1377
1378 rc = pj_sock_select(FD_SETSIZE, &set, NULL, NULL, &timeout);
1379 if (rc > 0 && PJ_FD_ISSET(audio->rtp_sock, &set)) {
1380 size = sizeof(packet);
1381 status = pj_sock_recv(audio->rtp_sock, packet, &size, 0);
1382
1383 }
1384 if (rc > 0 && PJ_FD_ISSET(audio->rtcp_sock, &set)) {
1385 size = sizeof(packet);
1386 status = pj_sock_recv(audio->rtcp_sock, packet, &size, 0);
1387 }
1388
1389 } while (rc > 0);
1390 }
Benny Prijono60b980e2006-04-03 22:41:26 +00001391 }
1392}
1393
1394
Benny Prijono4adcb912006-04-04 13:12:38 +00001395/*****************************************************************************
Benny Prijono60b980e2006-04-03 22:41:26 +00001396 * USER INTERFACE STUFFS
1397 */
Benny Prijono16a6b0e2006-05-12 10:20:03 +00001398#include "siprtp_report.c"
Benny Prijono60b980e2006-04-03 22:41:26 +00001399
1400
1401static void list_calls()
1402{
1403 unsigned i;
1404 puts("List all calls:");
1405 for (i=0; i<app.max_calls; ++i) {
1406 if (!app.call[i].inv)
1407 continue;
1408 print_call(i);
1409 }
1410}
1411
1412static void hangup_call(unsigned index)
1413{
1414 pjsip_tx_data *tdata;
1415 pj_status_t status;
1416
1417 if (app.call[index].inv == NULL)
1418 return;
1419
1420 status = pjsip_inv_end_session(app.call[index].inv, 603, NULL, &tdata);
1421 if (status==PJ_SUCCESS && tdata!=NULL)
1422 pjsip_inv_send_msg(app.call[index].inv, tdata);
1423}
1424
1425static void hangup_all_calls()
1426{
1427 unsigned i;
1428 for (i=0; i<app.max_calls; ++i) {
1429 if (!app.call[i].inv)
1430 continue;
1431 hangup_call(i);
1432 }
1433}
1434
1435static pj_bool_t simple_input(const char *title, char *buf, pj_size_t len)
1436{
1437 char *p;
1438
1439 printf("%s (empty to cancel): ", title); fflush(stdout);
1440 fgets(buf, len, stdin);
1441
1442 /* Remove trailing newlines. */
1443 for (p=buf; ; ++p) {
1444 if (*p=='\r' || *p=='\n') *p='\0';
1445 else if (!*p) break;
1446 }
1447
1448 if (!*buf)
1449 return PJ_FALSE;
1450
1451 return PJ_TRUE;
1452}
1453
1454
1455static const char *MENU =
1456"\n"
1457"Enter menu character:\n"
1458" l List all calls\n"
1459" h Hangup a call\n"
1460" H Hangup all calls\n"
1461" q Quit\n"
1462"\n";
1463
1464
1465/* Main screen menu */
1466static void console_main()
1467{
1468 char input1[10];
1469 unsigned i;
1470
Benny Prijono4adcb912006-04-04 13:12:38 +00001471 printf("%s", MENU);
1472
Benny Prijono60b980e2006-04-03 22:41:26 +00001473 for (;;) {
1474 printf(">>> "); fflush(stdout);
1475 fgets(input1, sizeof(input1), stdin);
1476
1477 switch (input1[0]) {
1478 case 'l':
1479 list_calls();
1480 break;
1481
1482 case 'h':
1483 if (!simple_input("Call number to hangup", input1, sizeof(input1)))
1484 break;
1485
1486 i = atoi(input1);
1487 hangup_call(i);
1488 break;
1489
1490 case 'H':
1491 hangup_all_calls();
1492 break;
1493
1494 case 'q':
1495 goto on_exit;
1496
1497 default:
Benny Prijono4adcb912006-04-04 13:12:38 +00001498 puts("Invalid command");
Benny Prijono60b980e2006-04-03 22:41:26 +00001499 printf("%s", MENU);
1500 break;
1501 }
1502
1503 fflush(stdout);
1504 }
1505
1506on_exit:
Benny Prijono4adcb912006-04-04 13:12:38 +00001507 hangup_all_calls();
Benny Prijono60b980e2006-04-03 22:41:26 +00001508}
1509
1510
Benny Prijono4adcb912006-04-04 13:12:38 +00001511/*****************************************************************************
1512 * Below is a simple module to log all incoming and outgoing SIP messages
1513 */
1514
1515
Benny Prijono60b980e2006-04-03 22:41:26 +00001516/* Notification on incoming messages */
Benny Prijono4adcb912006-04-04 13:12:38 +00001517static pj_bool_t logger_on_rx_msg(pjsip_rx_data *rdata)
Benny Prijono60b980e2006-04-03 22:41:26 +00001518{
1519 PJ_LOG(4,(THIS_FILE, "RX %d bytes %s from %s:%d:\n"
1520 "%s\n"
1521 "--end msg--",
1522 rdata->msg_info.len,
1523 pjsip_rx_data_get_info(rdata),
1524 rdata->pkt_info.src_name,
1525 rdata->pkt_info.src_port,
1526 rdata->msg_info.msg_buf));
1527
1528 /* Always return false, otherwise messages will not get processed! */
1529 return PJ_FALSE;
1530}
1531
1532/* Notification on outgoing messages */
Benny Prijono4adcb912006-04-04 13:12:38 +00001533static pj_status_t logger_on_tx_msg(pjsip_tx_data *tdata)
Benny Prijono60b980e2006-04-03 22:41:26 +00001534{
1535
1536 /* Important note:
1537 * tp_info field is only valid after outgoing messages has passed
1538 * transport layer. So don't try to access tp_info when the module
1539 * has lower priority than transport layer.
1540 */
1541
1542 PJ_LOG(4,(THIS_FILE, "TX %d bytes %s to %s:%d:\n"
1543 "%s\n"
1544 "--end msg--",
1545 (tdata->buf.cur - tdata->buf.start),
1546 pjsip_tx_data_get_info(tdata),
1547 tdata->tp_info.dst_name,
1548 tdata->tp_info.dst_port,
1549 tdata->buf.start));
1550
1551 /* Always return success, otherwise message will not get sent! */
1552 return PJ_SUCCESS;
1553}
1554
1555/* The module instance. */
1556static pjsip_module msg_logger =
1557{
1558 NULL, NULL, /* prev, next. */
1559 { "mod-siprtp-log", 14 }, /* Name. */
1560 -1, /* Id */
1561 PJSIP_MOD_PRIORITY_TRANSPORT_LAYER-1,/* Priority */
1562 NULL, /* load() */
1563 NULL, /* start() */
1564 NULL, /* stop() */
1565 NULL, /* unload() */
Benny Prijono4adcb912006-04-04 13:12:38 +00001566 &logger_on_rx_msg, /* on_rx_request() */
1567 &logger_on_rx_msg, /* on_rx_response() */
1568 &logger_on_tx_msg, /* on_tx_request. */
1569 &logger_on_tx_msg, /* on_tx_response() */
Benny Prijono60b980e2006-04-03 22:41:26 +00001570 NULL, /* on_tsx_state() */
1571
1572};
1573
1574
1575
Benny Prijono4adcb912006-04-04 13:12:38 +00001576/*****************************************************************************
1577 * Console application custom logging:
1578 */
1579
1580
1581static FILE *log_file;
1582
1583
1584static void app_log_writer(int level, const char *buffer, int len)
1585{
1586 /* Write to both stdout and file. */
1587
1588 if (level <= app.app_log_level)
1589 pj_log_write(level, buffer, len);
1590
1591 if (log_file) {
1592 fwrite(buffer, len, 1, log_file);
1593 fflush(log_file);
1594 }
1595}
1596
1597
1598pj_status_t app_logging_init(void)
1599{
1600 /* Redirect log function to ours */
1601
1602 pj_log_set_log_func( &app_log_writer );
1603
1604 /* If output log file is desired, create the file: */
1605
1606 if (app.log_filename) {
1607 log_file = fopen(app.log_filename, "wt");
1608 if (log_file == NULL) {
1609 PJ_LOG(1,(THIS_FILE, "Unable to open log file %s",
1610 app.log_filename));
1611 return -1;
1612 }
1613 }
1614
1615 return PJ_SUCCESS;
1616}
1617
1618
1619void app_logging_shutdown(void)
1620{
1621 /* Close logging file, if any: */
1622
1623 if (log_file) {
1624 fclose(log_file);
1625 log_file = NULL;
1626 }
1627}
1628
Benny Prijono60b980e2006-04-03 22:41:26 +00001629
1630/*
1631 * main()
1632 */
1633int main(int argc, char *argv[])
1634{
Benny Prijono4adcb912006-04-04 13:12:38 +00001635 unsigned i;
Benny Prijono60b980e2006-04-03 22:41:26 +00001636 pj_status_t status;
1637
Benny Prijono4adcb912006-04-04 13:12:38 +00001638 /* Must init PJLIB first */
Benny Prijono60b980e2006-04-03 22:41:26 +00001639 status = pj_init();
1640 if (status != PJ_SUCCESS)
1641 return 1;
1642
Benny Prijono4adcb912006-04-04 13:12:38 +00001643 /* Get command line options */
Benny Prijono60b980e2006-04-03 22:41:26 +00001644 status = init_options(argc, argv);
1645 if (status != PJ_SUCCESS)
1646 return 1;
1647
Benny Prijono410fbae2006-05-03 18:16:06 +00001648 /* Verify options: */
1649
1650 /* Auto-quit can not be specified for UAS */
1651 if (app.auto_quit && app.uri_to_call.slen == 0) {
1652 printf("Error: --auto-quit option only valid for outgoing "
1653 "mode (UAC) only\n");
1654 return 1;
1655 }
1656
Benny Prijono4adcb912006-04-04 13:12:38 +00001657 /* Init logging */
1658 status = app_logging_init();
1659 if (status != PJ_SUCCESS)
1660 return 1;
1661
1662 /* Init SIP etc */
Benny Prijono60b980e2006-04-03 22:41:26 +00001663 status = init_sip();
1664 if (status != PJ_SUCCESS) {
1665 app_perror(THIS_FILE, "Initialization has failed", status);
1666 destroy_sip();
1667 return 1;
1668 }
1669
Benny Prijono4adcb912006-04-04 13:12:38 +00001670 /* Register module to log incoming/outgoing messages */
Benny Prijono60b980e2006-04-03 22:41:26 +00001671 pjsip_endpt_register_module(app.sip_endpt, &msg_logger);
1672
Benny Prijono4adcb912006-04-04 13:12:38 +00001673 /* Init media */
Benny Prijono60b980e2006-04-03 22:41:26 +00001674 status = init_media();
1675 if (status != PJ_SUCCESS) {
1676 app_perror(THIS_FILE, "Media initialization failed", status);
1677 destroy_sip();
1678 return 1;
1679 }
1680
Benny Prijono9a0eab52006-04-04 19:43:24 +00001681 /* Start worker threads */
1682 for (i=0; i<app.thread_count; ++i) {
1683 pj_thread_create( app.pool, "app", &worker_thread, NULL,
1684 0, 0, &app.thread[i]);
1685 }
1686
Benny Prijono4adcb912006-04-04 13:12:38 +00001687 /* If URL is specified, then make call immediately */
Benny Prijono60b980e2006-04-03 22:41:26 +00001688 if (app.uri_to_call.slen) {
1689 unsigned i;
1690
Benny Prijono4adcb912006-04-04 13:12:38 +00001691 PJ_LOG(3,(THIS_FILE, "Making %d calls to %s..", app.max_calls,
1692 app.uri_to_call.ptr));
1693
Benny Prijono60b980e2006-04-03 22:41:26 +00001694 for (i=0; i<app.max_calls; ++i) {
1695 status = make_call(&app.uri_to_call);
1696 if (status != PJ_SUCCESS) {
1697 app_perror(THIS_FILE, "Error making call", status);
1698 break;
1699 }
1700 }
Benny Prijono4adcb912006-04-04 13:12:38 +00001701
Benny Prijono410fbae2006-05-03 18:16:06 +00001702 if (app.auto_quit) {
1703 /* Wait for calls to complete */
1704 while (app.uac_calls < app.max_calls)
1705 pj_thread_sleep(100);
1706 pj_thread_sleep(200);
1707 } else {
1708 /* Start user interface loop */
1709 console_main();
1710 }
1711
Benny Prijono4adcb912006-04-04 13:12:38 +00001712 } else {
1713
1714 PJ_LOG(3,(THIS_FILE, "Ready for incoming calls (max=%d)",
1715 app.max_calls));
Benny Prijono4adcb912006-04-04 13:12:38 +00001716
Benny Prijono410fbae2006-05-03 18:16:06 +00001717 /* Start user interface loop */
1718 console_main();
1719
1720 }
Benny Prijono60b980e2006-04-03 22:41:26 +00001721
Benny Prijono4adcb912006-04-04 13:12:38 +00001722
1723 /* Shutting down... */
Benny Prijono60b980e2006-04-03 22:41:26 +00001724 destroy_sip();
Benny Prijono6647d822006-05-20 13:01:07 +00001725 destroy_media();
1726
1727 if (app.pool) {
1728 pj_pool_release(app.pool);
1729 app.pool = NULL;
1730 pj_caching_pool_destroy(&app.cp);
1731 }
1732
Benny Prijono4adcb912006-04-04 13:12:38 +00001733 app_logging_shutdown();
1734
Benny Prijono60b980e2006-04-03 22:41:26 +00001735
1736 return 0;
1737}
1738