blob: 4ffe01238aacec368cc1e7a96d8df432992e5718 [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
Benny Prijono1ec70b32006-06-20 15:39:07 +000023
Benny Prijonobf13fee2006-04-20 11:13:32 +000024/* Usage */
25static const char *USAGE =
26" PURPOSE: \n"
27" This program establishes SIP INVITE session and media, and calculate \n"
28" the media quality (packet lost, jitter, rtt, etc.). Unlike normal \n"
29" pjmedia applications, this program bypasses all pjmedia stream \n"
30" framework and transmit encoded RTP packets manually using own thread. \n"
31"\n"
32" USAGE:\n"
33" siprtp [options] => to start in server mode\n"
34" siprtp [options] URL => to start in client mode\n"
35"\n"
36" Program options:\n"
37" --count=N, -c Set number of calls to create (default:1) \n"
Benny Prijono410fbae2006-05-03 18:16:06 +000038" --duration=SEC, -d Set maximum call duration (default:unlimited) \n"
39" --auto-quit, -q Quit when calls have been completed (default:no)\n"
Benny Prijonobf13fee2006-04-20 11:13:32 +000040"\n"
41" Address and ports options:\n"
42" --local-port=PORT,-p Set local SIP port (default: 5060)\n"
43" --rtp-port=PORT, -r Set start of RTP port (default: 4000)\n"
44" --ip-addr=IP, -i Set local IP address to use (otherwise it will\n"
45" try to determine local IP address from hostname)\n"
46"\n"
47" Logging Options:\n"
48" --log-level=N, -l Set log verbosity level (default=5)\n"
49" --app-log-level=N Set app screen log verbosity (default=3)\n"
50" --log-file=FILE Write log to file FILE\n"
Benny Prijonofcb36722006-05-18 18:34:21 +000051" --report-file=FILE Write report to file FILE\n"
Benny Prijonobf13fee2006-04-20 11:13:32 +000052"\n"
Benny Prijono4d7fd202006-05-14 20:57:20 +000053/* Don't support this anymore, because codec is properly examined in
54 pjmedia_session_info_from_sdp() function.
55
Benny Prijonobf13fee2006-04-20 11:13:32 +000056" Codec Options:\n"
57" --a-pt=PT Set audio payload type to PT (default=0)\n"
58" --a-name=NAME Set audio codec name to NAME (default=pcmu)\n"
59" --a-clock=RATE Set audio codec rate to RATE Hz (default=8000Hz)\n"
60" --a-bitrate=BPS Set audio codec bitrate to BPS (default=64000bps)\n"
61" --a-ptime=MS Set audio frame time to MS msec (default=20ms)\n"
Benny Prijono4d7fd202006-05-14 20:57:20 +000062*/
Benny Prijonobf13fee2006-04-20 11:13:32 +000063;
64
65
Benny Prijono60b980e2006-04-03 22:41:26 +000066/* Include all headers. */
67#include <pjsip.h>
68#include <pjmedia.h>
69#include <pjmedia-codec.h>
70#include <pjsip_ua.h>
71#include <pjsip_simple.h>
72#include <pjlib-util.h>
73#include <pjlib.h>
74
75#include <stdlib.h>
76
Benny Prijono9a0eab52006-04-04 19:43:24 +000077
78#if PJ_HAS_HIGH_RES_TIMER==0
79# error "High resolution timer is needed for this sample"
80#endif
81
Benny Prijono60b980e2006-04-03 22:41:26 +000082#define THIS_FILE "siprtp.c"
83#define MAX_CALLS 1024
Benny Prijono6647d822006-05-20 13:01:07 +000084#define RTP_START_PORT 4000
Benny Prijono60b980e2006-04-03 22:41:26 +000085
86
Benny Prijono4adcb912006-04-04 13:12:38 +000087/* Codec descriptor: */
88struct codec
89{
90 unsigned pt;
91 char* name;
92 unsigned clock_rate;
93 unsigned bit_rate;
94 unsigned ptime;
95 char* description;
96};
97
98
Benny Prijono60b980e2006-04-03 22:41:26 +000099/* A bidirectional media stream */
100struct media_stream
101{
102 /* Static: */
103 pj_uint16_t port; /* RTP port (RTCP is +1) */
104
105 /* Current stream info: */
106 pjmedia_stream_info si; /* Current stream info. */
107
108 /* More info: */
109 unsigned clock_rate; /* clock rate */
110 unsigned samples_per_frame; /* samples per frame */
111 unsigned bytes_per_frame; /* frame size. */
112
113 /* Sockets: */
114 pj_sock_t rtp_sock; /* RTP socket. */
115 pj_sock_t rtcp_sock; /* RTCP socket. */
116
117 /* RTP session: */
118 pjmedia_rtp_session out_sess; /* outgoing RTP session */
119 pjmedia_rtp_session in_sess; /* incoming RTP session */
120
121 /* RTCP stats: */
122 pjmedia_rtcp_session rtcp; /* incoming RTCP session. */
Benny Prijono4adcb912006-04-04 13:12:38 +0000123
Benny Prijono60b980e2006-04-03 22:41:26 +0000124 /* Thread: */
125 pj_bool_t thread_quit_flag; /* worker thread quit flag */
126 pj_thread_t *thread; /* RTP/RTCP worker thread */
127};
128
129
130struct call
131{
132 unsigned index;
133 pjsip_inv_session *inv;
134 unsigned media_count;
135 struct media_stream media[2];
Benny Prijono4adcb912006-04-04 13:12:38 +0000136 pj_time_val start_time;
137 pj_time_val response_time;
138 pj_time_val connect_time;
Benny Prijono410fbae2006-05-03 18:16:06 +0000139
140 pj_timer_entry d_timer; /**< Disconnect timer. */
Benny Prijono60b980e2006-04-03 22:41:26 +0000141};
142
143
144static struct app
145{
146 unsigned max_calls;
Benny Prijono410fbae2006-05-03 18:16:06 +0000147 unsigned uac_calls;
148 unsigned duration;
149 pj_bool_t auto_quit;
Benny Prijono60b980e2006-04-03 22:41:26 +0000150 unsigned thread_count;
151 int sip_port;
152 int rtp_start_port;
153 char *local_addr;
154 pj_str_t local_uri;
155 pj_str_t local_contact;
Benny Prijono4adcb912006-04-04 13:12:38 +0000156
157 int app_log_level;
158 int log_level;
159 char *log_filename;
Benny Prijonofcb36722006-05-18 18:34:21 +0000160 char *report_filename;
Benny Prijono4adcb912006-04-04 13:12:38 +0000161
162 struct codec audio_codec;
Benny Prijono60b980e2006-04-03 22:41:26 +0000163
164 pj_str_t uri_to_call;
165
166 pj_caching_pool cp;
167 pj_pool_t *pool;
168
169 pjsip_endpoint *sip_endpt;
170 pj_bool_t thread_quit;
171 pj_thread_t *thread[1];
172
173 pjmedia_endpt *med_endpt;
174 struct call call[MAX_CALLS];
175} app;
176
177
178
179/*
180 * Prototypes:
181 */
182
183/* Callback to be called when SDP negotiation is done in the call: */
184static void call_on_media_update( pjsip_inv_session *inv,
185 pj_status_t status);
186
187/* Callback to be called when invite session's state has changed: */
188static void call_on_state_changed( pjsip_inv_session *inv,
189 pjsip_event *e);
190
191/* Callback to be called when dialog has forked: */
192static void call_on_forked(pjsip_inv_session *inv, pjsip_event *e);
193
194/* Callback to be called to handle incoming requests outside dialogs: */
195static pj_bool_t on_rx_request( pjsip_rx_data *rdata );
196
197/* Worker thread prototype */
198static int worker_thread(void *arg);
199
200/* Create SDP for call */
201static pj_status_t create_sdp( pj_pool_t *pool,
202 struct call *call,
203 pjmedia_sdp_session **p_sdp);
204
Benny Prijono410fbae2006-05-03 18:16:06 +0000205/* Hangup call */
206static void hangup_call(unsigned index);
207
Benny Prijono60b980e2006-04-03 22:41:26 +0000208/* Destroy the call's media */
209static void destroy_call_media(unsigned call_index);
210
211/* Display error */
212static void app_perror(const char *sender, const char *title,
213 pj_status_t status);
214
Benny Prijonod7a13f12006-04-05 19:08:16 +0000215/* Print call */
216static void print_call(int call_index);
Benny Prijono4adcb912006-04-04 13:12:38 +0000217
218
Benny Prijono60b980e2006-04-03 22:41:26 +0000219/* This is a PJSIP module to be registered by application to handle
220 * incoming requests outside any dialogs/transactions. The main purpose
221 * here is to handle incoming INVITE request message, where we will
222 * create a dialog and INVITE session for it.
223 */
224static pjsip_module mod_siprtp =
225{
226 NULL, NULL, /* prev, next. */
227 { "mod-siprtpapp", 13 }, /* Name. */
228 -1, /* Id */
229 PJSIP_MOD_PRIORITY_APPLICATION, /* Priority */
230 NULL, /* load() */
231 NULL, /* start() */
232 NULL, /* stop() */
233 NULL, /* unload() */
234 &on_rx_request, /* on_rx_request() */
235 NULL, /* on_rx_response() */
236 NULL, /* on_tx_request. */
237 NULL, /* on_tx_response() */
238 NULL, /* on_tsx_state() */
239};
240
241
Benny Prijono4adcb912006-04-04 13:12:38 +0000242/* Codec constants */
243struct codec audio_codecs[] =
244{
Benny Prijono97f2a372006-06-07 21:21:57 +0000245 { 0, "PCMU", 8000, 64000, 20, "G.711 ULaw" },
246 { 3, "GSM", 8000, 13200, 20, "GSM" },
247 { 4, "G723", 8000, 6400, 30, "G.723.1" },
248 { 8, "PCMA", 8000, 64000, 20, "G.711 ALaw" },
249 { 18, "G729", 8000, 8000, 20, "G.729" },
Benny Prijono4adcb912006-04-04 13:12:38 +0000250};
251
252
Benny Prijono60b980e2006-04-03 22:41:26 +0000253/*
254 * Init SIP stack
255 */
256static pj_status_t init_sip()
257{
Benny Prijono60b980e2006-04-03 22:41:26 +0000258 pj_status_t status;
259
260 /* init PJLIB-UTIL: */
261 status = pjlib_util_init();
262 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
263
264 /* Must create a pool factory before we can allocate any memory. */
265 pj_caching_pool_init(&app.cp, &pj_pool_factory_default_policy, 0);
266
267 /* Create application pool for misc. */
268 app.pool = pj_pool_create(&app.cp.factory, "app", 1000, 1000, NULL);
269
270 /* Create global endpoint: */
271 {
272 const pj_str_t *hostname;
273 const char *endpt_name;
274
275 /* Endpoint MUST be assigned a globally unique name.
276 * The name will be used as the hostname in Warning header.
277 */
278
279 /* For this implementation, we'll use hostname for simplicity */
280 hostname = pj_gethostname();
281 endpt_name = hostname->ptr;
282
283 /* Create the endpoint: */
284
285 status = pjsip_endpt_create(&app.cp.factory, endpt_name,
286 &app.sip_endpt);
287 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
288 }
289
290
291 /* Add UDP transport. */
292 {
293 pj_sockaddr_in addr;
Benny Prijono49ce9a72006-04-05 16:56:19 +0000294 pjsip_host_port addrname;
Benny Prijono60b980e2006-04-03 22:41:26 +0000295
Benny Prijono49ce9a72006-04-05 16:56:19 +0000296 pj_memset(&addr, 0, sizeof(addr));
Benny Prijono60b980e2006-04-03 22:41:26 +0000297 addr.sin_family = PJ_AF_INET;
298 addr.sin_addr.s_addr = 0;
299 addr.sin_port = pj_htons((pj_uint16_t)app.sip_port);
300
Benny Prijono49ce9a72006-04-05 16:56:19 +0000301 if (app.local_addr) {
302 addrname.host = pj_str(app.local_addr);
303 addrname.port = app.sip_port;
304 }
305
306 status = pjsip_udp_transport_start( app.sip_endpt, &addr,
307 (app.local_addr ? &addrname:NULL),
Benny Prijono60b980e2006-04-03 22:41:26 +0000308 1, NULL);
Benny Prijono49ce9a72006-04-05 16:56:19 +0000309 if (status != PJ_SUCCESS) {
310 app_perror(THIS_FILE, "Unable to start UDP transport", status);
Benny Prijono60b980e2006-04-03 22:41:26 +0000311 return status;
Benny Prijono49ce9a72006-04-05 16:56:19 +0000312 }
Benny Prijono60b980e2006-04-03 22:41:26 +0000313 }
314
315 /*
316 * Init transaction layer.
317 * This will create/initialize transaction hash tables etc.
318 */
319 status = pjsip_tsx_layer_init_module(app.sip_endpt);
320 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
321
322 /* Initialize UA layer. */
323 status = pjsip_ua_init_module( app.sip_endpt, NULL );
324 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
325
326 /* Init invite session module. */
327 {
328 pjsip_inv_callback inv_cb;
329
330 /* Init the callback for INVITE session: */
331 pj_memset(&inv_cb, 0, sizeof(inv_cb));
332 inv_cb.on_state_changed = &call_on_state_changed;
333 inv_cb.on_new_session = &call_on_forked;
334 inv_cb.on_media_update = &call_on_media_update;
335
336 /* Initialize invite session module: */
337 status = pjsip_inv_usage_init(app.sip_endpt, &inv_cb);
338 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
339 }
340
341 /* Register our module to receive incoming requests. */
342 status = pjsip_endpt_register_module( app.sip_endpt, &mod_siprtp);
343 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
344
345
Benny Prijono60b980e2006-04-03 22:41:26 +0000346 /* Done */
347 return PJ_SUCCESS;
348}
349
350
351/*
352 * Destroy SIP
353 */
354static void destroy_sip()
355{
356 unsigned i;
357
358 app.thread_quit = 1;
359 for (i=0; i<app.thread_count; ++i) {
360 if (app.thread[i]) {
361 pj_thread_join(app.thread[i]);
362 pj_thread_destroy(app.thread[i]);
363 app.thread[i] = NULL;
364 }
365 }
366
367 if (app.sip_endpt) {
368 pjsip_endpt_destroy(app.sip_endpt);
369 app.sip_endpt = NULL;
370 }
Benny Prijono60b980e2006-04-03 22:41:26 +0000371}
372
373
374/*
375 * Init media stack.
376 */
377static pj_status_t init_media()
378{
379 pj_ioqueue_t *ioqueue;
380 unsigned i, count;
381 pj_uint16_t rtp_port;
382 pj_str_t temp;
383 pj_sockaddr_in addr;
384 pj_status_t status;
385
386
387 /* Get the ioqueue from the SIP endpoint */
388 ioqueue = pjsip_endpt_get_ioqueue(app.sip_endpt);
389
390
391 /* Initialize media endpoint so that at least error subsystem is properly
392 * initialized.
393 */
394 status = pjmedia_endpt_create(&app.cp.factory, ioqueue, 1,
395 &app.med_endpt);
396 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
397
398
Benny Prijono4d7fd202006-05-14 20:57:20 +0000399 /* Add G711 codec */
400 pjmedia_codec_g711_init(app.med_endpt);
401
Benny Prijono60b980e2006-04-03 22:41:26 +0000402 /* Determine address to bind socket */
403 pj_memset(&addr, 0, sizeof(addr));
404 addr.sin_family = PJ_AF_INET;
405 i = pj_inet_aton(pj_cstr(&temp, app.local_addr), &addr.sin_addr);
406 if (i == 0) {
407 PJ_LOG(3,(THIS_FILE,
408 "Error: invalid local address %s (expecting IP)",
409 app.local_addr));
410 return -1;
411 }
412
Benny Prijono60b980e2006-04-03 22:41:26 +0000413 /* RTP port counter */
414 rtp_port = (pj_uint16_t)(app.rtp_start_port & 0xFFFE);
415
416
417 /* Init media sockets. */
418 for (i=0, count=0; i<app.max_calls; ++i, ++count) {
419
420 int retry;
421
422 app.call[i].index = i;
423
424 /* Repeat binding media socket to next port when fails to bind
425 * to current port number.
426 */
427 retry = 0;
Benny Prijono6647d822006-05-20 13:01:07 +0000428 status = -1;
429 for (retry=0; status!=PJ_SUCCESS && retry<100; ++retry,rtp_port+=2) {
Benny Prijono60b980e2006-04-03 22:41:26 +0000430 struct media_stream *m = &app.call[i].media[0];
431
Benny Prijono60b980e2006-04-03 22:41:26 +0000432 m->port = rtp_port;
433
434 /* Create and bind RTP socket */
435 status = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0,
436 &m->rtp_sock);
437 if (status != PJ_SUCCESS)
438 goto on_error;
439
440 addr.sin_port = pj_htons(rtp_port);
441 status = pj_sock_bind(m->rtp_sock, &addr, sizeof(addr));
442 if (status != PJ_SUCCESS) {
443 pj_sock_close(m->rtp_sock), m->rtp_sock=0;
444 continue;
445 }
446
447
448 /* Create and bind RTCP socket */
449 status = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0,
450 &m->rtcp_sock);
451 if (status != PJ_SUCCESS)
452 goto on_error;
453
454 addr.sin_port = pj_htons((pj_uint16_t)(rtp_port+1));
455 status = pj_sock_bind(m->rtcp_sock, &addr, sizeof(addr));
456 if (status != PJ_SUCCESS) {
457 pj_sock_close(m->rtp_sock), m->rtp_sock=0;
458 pj_sock_close(m->rtcp_sock), m->rtcp_sock=0;
459 continue;
460 }
461
Benny Prijono6647d822006-05-20 13:01:07 +0000462 }
Benny Prijono60b980e2006-04-03 22:41:26 +0000463
464 if (status != PJ_SUCCESS)
465 goto on_error;
466
467 }
468
469 /* Done */
470 return PJ_SUCCESS;
471
472on_error:
473 for (i=0; i<count; ++i) {
474 struct media_stream *m = &app.call[i].media[0];
475
476 pj_sock_close(m->rtp_sock), m->rtp_sock=0;
477 pj_sock_close(m->rtcp_sock), m->rtcp_sock=0;
478 }
479
480 return status;
481}
482
483
484/*
485 * Destroy media.
486 */
487static void destroy_media()
488{
489 unsigned i;
490
491 for (i=0; i<app.max_calls; ++i) {
492 struct media_stream *m = &app.call[i].media[0];
493
494 if (m->rtp_sock)
495 pj_sock_close(m->rtp_sock), m->rtp_sock = 0;
496
497 if (m->rtcp_sock)
498 pj_sock_close(m->rtcp_sock), m->rtcp_sock = 0;
499 }
500
501 if (app.med_endpt) {
502 pjmedia_endpt_destroy(app.med_endpt);
503 app.med_endpt = NULL;
504 }
505}
506
507
508/*
509 * Make outgoing call.
510 */
511static pj_status_t make_call(const pj_str_t *dst_uri)
512{
513 unsigned i;
514 struct call *call;
515 pjsip_dialog *dlg;
516 pjmedia_sdp_session *sdp;
517 pjsip_tx_data *tdata;
518 pj_status_t status;
519
520
521 /* Find unused call slot */
522 for (i=0; i<app.max_calls; ++i) {
523 if (app.call[i].inv == NULL)
524 break;
525 }
526
527 if (i == app.max_calls)
528 return PJ_ETOOMANY;
529
530 call = &app.call[i];
531
532 /* Create UAC dialog */
533 status = pjsip_dlg_create_uac( pjsip_ua_instance(),
534 &app.local_uri, /* local URI */
535 &app.local_contact, /* local Contact */
536 dst_uri, /* remote URI */
537 dst_uri, /* remote target */
538 &dlg); /* dialog */
Benny Prijono410fbae2006-05-03 18:16:06 +0000539 if (status != PJ_SUCCESS) {
540 ++app.uac_calls;
Benny Prijono60b980e2006-04-03 22:41:26 +0000541 return status;
Benny Prijono410fbae2006-05-03 18:16:06 +0000542 }
Benny Prijono60b980e2006-04-03 22:41:26 +0000543
544 /* Create SDP */
545 create_sdp( dlg->pool, call, &sdp);
546
547 /* Create the INVITE session. */
548 status = pjsip_inv_create_uac( dlg, sdp, 0, &call->inv);
549 if (status != PJ_SUCCESS) {
550 pjsip_dlg_terminate(dlg);
Benny Prijono410fbae2006-05-03 18:16:06 +0000551 ++app.uac_calls;
Benny Prijono60b980e2006-04-03 22:41:26 +0000552 return status;
553 }
554
555
556 /* Attach call data to invite session */
557 call->inv->mod_data[mod_siprtp.id] = call;
558
Benny Prijono4adcb912006-04-04 13:12:38 +0000559 /* Mark start of call */
560 pj_gettimeofday(&call->start_time);
561
Benny Prijono60b980e2006-04-03 22:41:26 +0000562
563 /* Create initial INVITE request.
564 * This INVITE request will contain a perfectly good request and
565 * an SDP body as well.
566 */
567 status = pjsip_inv_invite(call->inv, &tdata);
568 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
569
570
571 /* Send initial INVITE request.
572 * From now on, the invite session's state will be reported to us
573 * via the invite session callbacks.
574 */
575 status = pjsip_inv_send_msg(call->inv, tdata);
576 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
577
578
579 return PJ_SUCCESS;
580}
581
582
583/*
584 * Receive incoming call
585 */
586static void process_incoming_call(pjsip_rx_data *rdata)
587{
588 unsigned i;
589 struct call *call;
590 pjsip_dialog *dlg;
591 pjmedia_sdp_session *sdp;
592 pjsip_tx_data *tdata;
593 pj_status_t status;
594
595 /* Find free call slot */
596 for (i=0; i<app.max_calls; ++i) {
597 if (app.call[i].inv == NULL)
598 break;
599 }
600
601 if (i == app.max_calls) {
602 const pj_str_t reason = pj_str("Too many calls");
603 pjsip_endpt_respond_stateless( app.sip_endpt, rdata,
604 500, &reason,
605 NULL, NULL);
606 return;
607 }
608
609 call = &app.call[i];
610
611 /* Create UAS dialog */
612 status = pjsip_dlg_create_uas( pjsip_ua_instance(), rdata,
613 &app.local_contact, &dlg);
614 if (status != PJ_SUCCESS) {
615 const pj_str_t reason = pj_str("Unable to create dialog");
616 pjsip_endpt_respond_stateless( app.sip_endpt, rdata,
617 500, &reason,
618 NULL, NULL);
619 return;
620 }
621
622 /* Create SDP */
623 create_sdp( dlg->pool, call, &sdp);
624
625 /* Create UAS invite session */
626 status = pjsip_inv_create_uas( dlg, rdata, sdp, 0, &call->inv);
627 if (status != PJ_SUCCESS) {
Benny Prijono4adcb912006-04-04 13:12:38 +0000628 pjsip_dlg_create_response(dlg, rdata, 500, NULL, &tdata);
629 pjsip_dlg_send_response(dlg, pjsip_rdata_get_tsx(rdata), tdata);
Benny Prijono60b980e2006-04-03 22:41:26 +0000630 return;
631 }
632
Benny Prijono4adcb912006-04-04 13:12:38 +0000633
Benny Prijono60b980e2006-04-03 22:41:26 +0000634 /* Attach call data to invite session */
635 call->inv->mod_data[mod_siprtp.id] = call;
636
Benny Prijono4adcb912006-04-04 13:12:38 +0000637 /* Mark start of call */
638 pj_gettimeofday(&call->start_time);
639
640
641
Benny Prijono60b980e2006-04-03 22:41:26 +0000642 /* Create 200 response .*/
643 status = pjsip_inv_initial_answer(call->inv, rdata, 200,
644 NULL, NULL, &tdata);
Benny Prijono4adcb912006-04-04 13:12:38 +0000645 if (status != PJ_SUCCESS) {
646 status = pjsip_inv_initial_answer(call->inv, rdata,
647 PJSIP_SC_NOT_ACCEPTABLE,
648 NULL, NULL, &tdata);
649 if (status == PJ_SUCCESS)
650 pjsip_inv_send_msg(call->inv, tdata);
651 else
652 pjsip_inv_terminate(call->inv, 500, PJ_FALSE);
653 return;
654 }
655
Benny Prijono60b980e2006-04-03 22:41:26 +0000656
657 /* Send the 200 response. */
658 status = pjsip_inv_send_msg(call->inv, tdata);
659 PJ_ASSERT_ON_FAIL(status == PJ_SUCCESS, return);
660
661
662 /* Done */
663}
664
665
666/* Callback to be called when dialog has forked: */
667static void call_on_forked(pjsip_inv_session *inv, pjsip_event *e)
668{
669 PJ_UNUSED_ARG(inv);
670 PJ_UNUSED_ARG(e);
671
672 PJ_TODO( HANDLE_FORKING );
673}
674
675
676/* Callback to be called to handle incoming requests outside dialogs: */
677static pj_bool_t on_rx_request( pjsip_rx_data *rdata )
678{
Benny Prijono4adcb912006-04-04 13:12:38 +0000679 /* Ignore strandled ACKs (must not send respone */
680 if (rdata->msg_info.msg->line.req.method.id == PJSIP_ACK_METHOD)
681 return PJ_FALSE;
682
Benny Prijono60b980e2006-04-03 22:41:26 +0000683 /* Respond (statelessly) any non-INVITE requests with 500 */
684 if (rdata->msg_info.msg->line.req.method.id != PJSIP_INVITE_METHOD) {
685 pj_str_t reason = pj_str("Unsupported Operation");
686 pjsip_endpt_respond_stateless( app.sip_endpt, rdata,
687 500, &reason,
688 NULL, NULL);
689 return PJ_TRUE;
690 }
691
692 /* Handle incoming INVITE */
693 process_incoming_call(rdata);
694
695 /* Done */
696 return PJ_TRUE;
697}
698
699
Benny Prijono410fbae2006-05-03 18:16:06 +0000700/* Callback timer to disconnect call (limiting call duration) */
701static void timer_disconnect_call( pj_timer_heap_t *timer_heap,
702 struct pj_timer_entry *entry)
703{
704 struct call *call = entry->user_data;
705
706 PJ_UNUSED_ARG(timer_heap);
707
708 entry->id = 0;
709 hangup_call(call->index);
710}
711
712
Benny Prijono60b980e2006-04-03 22:41:26 +0000713/* Callback to be called when invite session's state has changed: */
714static void call_on_state_changed( pjsip_inv_session *inv,
715 pjsip_event *e)
716{
Benny Prijono4adcb912006-04-04 13:12:38 +0000717 struct call *call = inv->mod_data[mod_siprtp.id];
718
Benny Prijono60b980e2006-04-03 22:41:26 +0000719 PJ_UNUSED_ARG(e);
720
Benny Prijono4adcb912006-04-04 13:12:38 +0000721 if (!call)
722 return;
Benny Prijono60b980e2006-04-03 22:41:26 +0000723
Benny Prijono4adcb912006-04-04 13:12:38 +0000724 if (inv->state == PJSIP_INV_STATE_DISCONNECTED) {
725
726 pj_time_val null_time = {0, 0};
Benny Prijono60b980e2006-04-03 22:41:26 +0000727
Benny Prijono410fbae2006-05-03 18:16:06 +0000728 if (call->d_timer.id != 0) {
729 pjsip_endpt_cancel_timer(app.sip_endpt, &call->d_timer);
730 call->d_timer.id = 0;
731 }
732
Benny Prijonod7a13f12006-04-05 19:08:16 +0000733 PJ_LOG(3,(THIS_FILE, "Call #%d disconnected. Reason=%s",
734 call->index,
735 pjsip_get_status_text(inv->cause)->ptr));
736 PJ_LOG(3,(THIS_FILE, "Call #%d statistics:", call->index));
737 print_call(call->index);
738
739
Benny Prijono60b980e2006-04-03 22:41:26 +0000740 call->inv = NULL;
741 inv->mod_data[mod_siprtp.id] = NULL;
742
743 destroy_call_media(call->index);
Benny Prijono4adcb912006-04-04 13:12:38 +0000744
745 call->start_time = null_time;
746 call->response_time = null_time;
747 call->connect_time = null_time;
748
Benny Prijono410fbae2006-05-03 18:16:06 +0000749 ++app.uac_calls;
Benny Prijono4adcb912006-04-04 13:12:38 +0000750
751 } else if (inv->state == PJSIP_INV_STATE_CONFIRMED) {
752
753 pj_time_val t;
754
755 pj_gettimeofday(&call->connect_time);
756 if (call->response_time.sec == 0)
757 call->response_time = call->connect_time;
758
759 t = call->connect_time;
760 PJ_TIME_VAL_SUB(t, call->start_time);
761
762 PJ_LOG(3,(THIS_FILE, "Call #%d connected in %d ms", call->index,
763 PJ_TIME_VAL_MSEC(t)));
764
Benny Prijono410fbae2006-05-03 18:16:06 +0000765 if (app.duration != 0) {
766 call->d_timer.id = 1;
767 call->d_timer.user_data = call;
768 call->d_timer.cb = &timer_disconnect_call;
769
770 t.sec = app.duration;
771 t.msec = 0;
772
773 pjsip_endpt_schedule_timer(app.sip_endpt, &call->d_timer, &t);
774 }
775
Benny Prijono4adcb912006-04-04 13:12:38 +0000776 } else if ( inv->state == PJSIP_INV_STATE_EARLY ||
777 inv->state == PJSIP_INV_STATE_CONNECTING) {
778
779 if (call->response_time.sec == 0)
780 pj_gettimeofday(&call->response_time);
781
Benny Prijono60b980e2006-04-03 22:41:26 +0000782 }
783}
784
785
786/* Utility */
787static void app_perror(const char *sender, const char *title,
788 pj_status_t status)
789{
790 char errmsg[PJ_ERR_MSG_SIZE];
791
792 pj_strerror(status, errmsg, sizeof(errmsg));
793 PJ_LOG(3,(sender, "%s: %s [status=%d]", title, errmsg, status));
794}
795
796
797/* Worker thread */
798static int worker_thread(void *arg)
799{
800 PJ_UNUSED_ARG(arg);
801
802 while (!app.thread_quit) {
803 pj_time_val timeout = {0, 10};
804 pjsip_endpt_handle_events(app.sip_endpt, &timeout);
805 }
806
807 return 0;
808}
809
810
Benny Prijono60b980e2006-04-03 22:41:26 +0000811/* Init application options */
812static pj_status_t init_options(int argc, char *argv[])
813{
814 static char ip_addr[32];
815 static char local_uri[64];
816
Benny Prijono4adcb912006-04-04 13:12:38 +0000817 enum { OPT_START,
818 OPT_APP_LOG_LEVEL, OPT_LOG_FILE,
Benny Prijonofcb36722006-05-18 18:34:21 +0000819 OPT_A_PT, OPT_A_NAME, OPT_A_CLOCK, OPT_A_BITRATE, OPT_A_PTIME,
820 OPT_REPORT_FILE };
Benny Prijono4adcb912006-04-04 13:12:38 +0000821
Benny Prijono60b980e2006-04-03 22:41:26 +0000822 struct pj_getopt_option long_options[] = {
Benny Prijono4adcb912006-04-04 13:12:38 +0000823 { "count", 1, 0, 'c' },
Benny Prijono410fbae2006-05-03 18:16:06 +0000824 { "duration", 1, 0, 'd' },
825 { "auto-quit", 0, 0, 'q' },
Benny Prijono4adcb912006-04-04 13:12:38 +0000826 { "local-port", 1, 0, 'p' },
827 { "rtp-port", 1, 0, 'r' },
828 { "ip-addr", 1, 0, 'i' },
829
830 { "log-level", 1, 0, 'l' },
831 { "app-log-level", 1, 0, OPT_APP_LOG_LEVEL },
832 { "log-file", 1, 0, OPT_LOG_FILE },
Benny Prijono4d7fd202006-05-14 20:57:20 +0000833
Benny Prijonofcb36722006-05-18 18:34:21 +0000834 { "report-file", 1, 0, OPT_REPORT_FILE },
835
Benny Prijono4d7fd202006-05-14 20:57:20 +0000836 /* Don't support this anymore, see comments in USAGE above.
Benny Prijono4adcb912006-04-04 13:12:38 +0000837 { "a-pt", 1, 0, OPT_A_PT },
838 { "a-name", 1, 0, OPT_A_NAME },
839 { "a-clock", 1, 0, OPT_A_CLOCK },
840 { "a-bitrate", 1, 0, OPT_A_BITRATE },
841 { "a-ptime", 1, 0, OPT_A_PTIME },
Benny Prijono4d7fd202006-05-14 20:57:20 +0000842 */
Benny Prijono4adcb912006-04-04 13:12:38 +0000843
Benny Prijono60b980e2006-04-03 22:41:26 +0000844 { NULL, 0, 0, 0 },
845 };
846 int c;
847 int option_index;
848
849 /* Get local IP address for the default IP address */
850 {
851 const pj_str_t *hostname;
852 pj_sockaddr_in tmp_addr;
853 char *addr;
854
855 hostname = pj_gethostname();
856 pj_sockaddr_in_init(&tmp_addr, hostname, 0);
857 addr = pj_inet_ntoa(tmp_addr.sin_addr);
858 pj_ansi_strcpy(ip_addr, addr);
859 }
860
Benny Prijono4adcb912006-04-04 13:12:38 +0000861 /* Init defaults */
Benny Prijono60b980e2006-04-03 22:41:26 +0000862 app.max_calls = 1;
863 app.thread_count = 1;
864 app.sip_port = 5060;
Benny Prijono6647d822006-05-20 13:01:07 +0000865 app.rtp_start_port = RTP_START_PORT;
Benny Prijono60b980e2006-04-03 22:41:26 +0000866 app.local_addr = ip_addr;
Benny Prijono4adcb912006-04-04 13:12:38 +0000867 app.log_level = 5;
868 app.app_log_level = 3;
869 app.log_filename = NULL;
870
871 /* Default codecs: */
872 app.audio_codec = audio_codecs[0];
Benny Prijono60b980e2006-04-03 22:41:26 +0000873
874 /* Parse options */
875 pj_optind = 0;
Benny Prijono410fbae2006-05-03 18:16:06 +0000876 while((c=pj_getopt_long(argc,argv, "c:d:p:r:i:l:q",
Benny Prijono60b980e2006-04-03 22:41:26 +0000877 long_options, &option_index))!=-1)
878 {
879 switch (c) {
880 case 'c':
881 app.max_calls = atoi(pj_optarg);
882 if (app.max_calls < 0 || app.max_calls > MAX_CALLS) {
883 PJ_LOG(3,(THIS_FILE, "Invalid max calls value %s", pj_optarg));
884 return 1;
885 }
886 break;
Benny Prijono410fbae2006-05-03 18:16:06 +0000887 case 'd':
888 app.duration = atoi(pj_optarg);
889 break;
890 case 'q':
891 app.auto_quit = 1;
892 break;
893
Benny Prijono60b980e2006-04-03 22:41:26 +0000894 case 'p':
895 app.sip_port = atoi(pj_optarg);
896 break;
897 case 'r':
898 app.rtp_start_port = atoi(pj_optarg);
899 break;
900 case 'i':
901 app.local_addr = pj_optarg;
902 break;
Benny Prijono4adcb912006-04-04 13:12:38 +0000903
904 case 'l':
905 app.log_level = atoi(pj_optarg);
906 break;
907 case OPT_APP_LOG_LEVEL:
908 app.app_log_level = atoi(pj_optarg);
909 break;
910 case OPT_LOG_FILE:
911 app.log_filename = pj_optarg;
912 break;
913
914 case OPT_A_PT:
915 app.audio_codec.pt = atoi(pj_optarg);
916 break;
917 case OPT_A_NAME:
918 app.audio_codec.name = pj_optarg;
919 break;
920 case OPT_A_CLOCK:
921 app.audio_codec.clock_rate = atoi(pj_optarg);
922 break;
923 case OPT_A_BITRATE:
924 app.audio_codec.bit_rate = atoi(pj_optarg);
925 break;
926 case OPT_A_PTIME:
927 app.audio_codec.ptime = atoi(pj_optarg);
928 break;
Benny Prijonofcb36722006-05-18 18:34:21 +0000929 case OPT_REPORT_FILE:
930 app.report_filename = pj_optarg;
931 break;
Benny Prijono4adcb912006-04-04 13:12:38 +0000932
Benny Prijono60b980e2006-04-03 22:41:26 +0000933 default:
934 puts(USAGE);
935 return 1;
936 }
937 }
938
939 /* Check if URL is specified */
940 if (pj_optind < argc)
941 app.uri_to_call = pj_str(argv[pj_optind]);
942
943 /* Build local URI and contact */
944 pj_ansi_sprintf( local_uri, "sip:%s:%d", app.local_addr, app.sip_port);
945 app.local_uri = pj_str(local_uri);
946 app.local_contact = app.local_uri;
947
948
949 return PJ_SUCCESS;
950}
951
952
Benny Prijono4adcb912006-04-04 13:12:38 +0000953/*****************************************************************************
Benny Prijono60b980e2006-04-03 22:41:26 +0000954 * MEDIA STUFFS
955 */
956
957/*
958 * Create SDP session for a call.
959 */
960static pj_status_t create_sdp( pj_pool_t *pool,
961 struct call *call,
962 pjmedia_sdp_session **p_sdp)
963{
964 pj_time_val tv;
965 pjmedia_sdp_session *sdp;
966 pjmedia_sdp_media *m;
967 pjmedia_sdp_attr *attr;
968 struct media_stream *audio = &call->media[0];
969
970 PJ_ASSERT_RETURN(pool && p_sdp, PJ_EINVAL);
971
972
973 /* Create and initialize basic SDP session */
974 sdp = pj_pool_zalloc (pool, sizeof(pjmedia_sdp_session));
975
976 pj_gettimeofday(&tv);
977 sdp->origin.user = pj_str("pjsip-siprtp");
978 sdp->origin.version = sdp->origin.id = tv.sec + 2208988800UL;
979 sdp->origin.net_type = pj_str("IN");
980 sdp->origin.addr_type = pj_str("IP4");
981 sdp->origin.addr = *pj_gethostname();
982 sdp->name = pj_str("pjsip");
983
984 /* Since we only support one media stream at present, put the
985 * SDP connection line in the session level.
986 */
987 sdp->conn = pj_pool_zalloc (pool, sizeof(pjmedia_sdp_conn));
988 sdp->conn->net_type = pj_str("IN");
989 sdp->conn->addr_type = pj_str("IP4");
990 sdp->conn->addr = pj_str(app.local_addr);
991
992
993 /* SDP time and attributes. */
994 sdp->time.start = sdp->time.stop = 0;
995 sdp->attr_count = 0;
996
997 /* Create media stream 0: */
998
999 sdp->media_count = 1;
1000 m = pj_pool_zalloc (pool, sizeof(pjmedia_sdp_media));
1001 sdp->media[0] = m;
1002
1003 /* Standard media info: */
1004 m->desc.media = pj_str("audio");
1005 m->desc.port = audio->port;
1006 m->desc.port_count = 1;
1007 m->desc.transport = pj_str("RTP/AVP");
1008
1009 /* Add format and rtpmap for each codec. */
1010 m->desc.fmt_count = 1;
1011 m->attr_count = 0;
1012
1013 {
1014 pjmedia_sdp_rtpmap rtpmap;
1015 pjmedia_sdp_attr *attr;
Benny Prijono4adcb912006-04-04 13:12:38 +00001016 char ptstr[10];
Benny Prijono60b980e2006-04-03 22:41:26 +00001017
Benny Prijono4adcb912006-04-04 13:12:38 +00001018 sprintf(ptstr, "%d", app.audio_codec.pt);
1019 pj_strdup2(pool, &m->desc.fmt[0], ptstr);
1020 rtpmap.pt = m->desc.fmt[0];
1021 rtpmap.clock_rate = app.audio_codec.clock_rate;
1022 rtpmap.enc_name = pj_str(app.audio_codec.name);
Benny Prijono60b980e2006-04-03 22:41:26 +00001023 rtpmap.param.slen = 0;
1024
1025 pjmedia_sdp_rtpmap_to_attr(pool, &rtpmap, &attr);
1026 m->attr[m->attr_count++] = attr;
1027 }
1028
1029 /* Add sendrecv attribute. */
1030 attr = pj_pool_zalloc(pool, sizeof(pjmedia_sdp_attr));
1031 attr->name = pj_str("sendrecv");
1032 m->attr[m->attr_count++] = attr;
1033
1034#if 1
1035 /*
1036 * Add support telephony event
1037 */
Benny Prijono4adcb912006-04-04 13:12:38 +00001038 m->desc.fmt[m->desc.fmt_count++] = pj_str("121");
Benny Prijono60b980e2006-04-03 22:41:26 +00001039 /* Add rtpmap. */
1040 attr = pj_pool_zalloc(pool, sizeof(pjmedia_sdp_attr));
1041 attr->name = pj_str("rtpmap");
Benny Prijono4adcb912006-04-04 13:12:38 +00001042 attr->value = pj_str(":121 telephone-event/8000");
Benny Prijono60b980e2006-04-03 22:41:26 +00001043 m->attr[m->attr_count++] = attr;
1044 /* Add fmtp */
1045 attr = pj_pool_zalloc(pool, sizeof(pjmedia_sdp_attr));
1046 attr->name = pj_str("fmtp");
Benny Prijono4adcb912006-04-04 13:12:38 +00001047 attr->value = pj_str(":121 0-15");
Benny Prijono60b980e2006-04-03 22:41:26 +00001048 m->attr[m->attr_count++] = attr;
1049#endif
1050
1051 /* Done */
1052 *p_sdp = sdp;
1053
1054 return PJ_SUCCESS;
1055}
1056
1057
Benny Prijono6647d822006-05-20 13:01:07 +00001058#if defined(PJ_WIN32) && PJ_WIN32 != 0
1059#include <windows.h>
1060static void boost_priority(void)
1061{
1062 SetPriorityClass( GetCurrentProcess(), REALTIME_PRIORITY_CLASS);
1063 SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
1064}
1065
1066#else
1067# define boost_priority()
1068#endif
1069
Benny Prijono4adcb912006-04-04 13:12:38 +00001070/*
1071 * Media thread
1072 *
1073 * This is the thread to send and receive both RTP and RTCP packets.
1074 */
Benny Prijono60b980e2006-04-03 22:41:26 +00001075static int media_thread(void *arg)
1076{
Benny Prijono14b7b662006-04-07 15:01:51 +00001077 enum { RTCP_INTERVAL = 5000, RTCP_RAND = 2000 };
Benny Prijono60b980e2006-04-03 22:41:26 +00001078 struct media_stream *strm = arg;
1079 char packet[1500];
Benny Prijono9a0eab52006-04-04 19:43:24 +00001080 unsigned msec_interval;
1081 pj_timestamp freq, next_rtp, next_rtcp;
Benny Prijono60b980e2006-04-03 22:41:26 +00001082
Benny Prijono6647d822006-05-20 13:01:07 +00001083
1084 /* Boost thread priority if necessary */
1085 boost_priority();
1086
1087
Benny Prijono9a0eab52006-04-04 19:43:24 +00001088 msec_interval = strm->samples_per_frame * 1000 / strm->clock_rate;
1089 pj_get_timestamp_freq(&freq);
1090
1091 pj_get_timestamp(&next_rtp);
1092 next_rtp.u64 += (freq.u64 * msec_interval / 1000);
Benny Prijono60b980e2006-04-03 22:41:26 +00001093
1094 next_rtcp = next_rtp;
Benny Prijono14b7b662006-04-07 15:01:51 +00001095 next_rtcp.u64 += (freq.u64 * (RTCP_INTERVAL+(pj_rand()%RTCP_RAND)) / 1000);
Benny Prijono60b980e2006-04-03 22:41:26 +00001096
1097
1098 while (!strm->thread_quit_flag) {
1099 pj_fd_set_t set;
Benny Prijono9a0eab52006-04-04 19:43:24 +00001100 pj_timestamp now, lesser;
1101 pj_time_val timeout;
Benny Prijono60b980e2006-04-03 22:41:26 +00001102 int rc;
1103
1104 /* Determine how long to sleep */
Benny Prijono9a0eab52006-04-04 19:43:24 +00001105 if (next_rtp.u64 < next_rtcp.u64)
Benny Prijono60b980e2006-04-03 22:41:26 +00001106 lesser = next_rtp;
1107 else
1108 lesser = next_rtcp;
1109
Benny Prijono9a0eab52006-04-04 19:43:24 +00001110 pj_get_timestamp(&now);
1111 if (lesser.u64 <= now.u64) {
Benny Prijono60b980e2006-04-03 22:41:26 +00001112 timeout.sec = timeout.msec = 0;
Benny Prijono9a0eab52006-04-04 19:43:24 +00001113 //printf("immediate "); fflush(stdout);
1114 } else {
1115 pj_uint64_t tick_delay;
1116 tick_delay = lesser.u64 - now.u64;
1117 timeout.sec = 0;
1118 timeout.msec = (pj_uint32_t)(tick_delay * 1000 / freq.u64);
1119 pj_time_val_normalize(&timeout);
1120
1121 //printf("%d:%03d ", timeout.sec, timeout.msec); fflush(stdout);
Benny Prijono60b980e2006-04-03 22:41:26 +00001122 }
1123
1124 PJ_FD_ZERO(&set);
1125 PJ_FD_SET(strm->rtp_sock, &set);
1126 PJ_FD_SET(strm->rtcp_sock, &set);
1127
1128 rc = pj_sock_select(FD_SETSIZE, &set, NULL, NULL, &timeout);
1129
Benny Prijono6647d822006-05-20 13:01:07 +00001130 if (rc < 0) {
1131 pj_thread_sleep(10);
1132 continue;
1133 }
1134
Benny Prijono9a0eab52006-04-04 19:43:24 +00001135 if (rc > 0 && PJ_FD_ISSET(strm->rtp_sock, &set)) {
Benny Prijono60b980e2006-04-03 22:41:26 +00001136
1137 /*
1138 * Process incoming RTP packet.
1139 */
1140 pj_status_t status;
1141 pj_ssize_t size;
1142 const pjmedia_rtp_hdr *hdr;
1143 const void *payload;
1144 unsigned payload_len;
1145
1146 size = sizeof(packet);
1147 status = pj_sock_recv(strm->rtp_sock, packet, &size, 0);
1148 if (status != PJ_SUCCESS) {
1149 app_perror(THIS_FILE, "RTP recv() error", status);
Benny Prijono6647d822006-05-20 13:01:07 +00001150 pj_thread_sleep(10);
Benny Prijono60b980e2006-04-03 22:41:26 +00001151 continue;
1152 }
1153
Benny Prijono4adcb912006-04-04 13:12:38 +00001154
Benny Prijono60b980e2006-04-03 22:41:26 +00001155 /* Decode RTP packet. */
1156 status = pjmedia_rtp_decode_rtp(&strm->in_sess,
1157 packet, size,
1158 &hdr,
1159 &payload, &payload_len);
1160 if (status != PJ_SUCCESS) {
1161 app_perror(THIS_FILE, "RTP decode error", status);
Benny Prijono60b980e2006-04-03 22:41:26 +00001162 continue;
1163 }
1164
1165 /* Update the RTCP session. */
1166 pjmedia_rtcp_rx_rtp(&strm->rtcp, pj_ntohs(hdr->seq),
Benny Prijono69968232006-04-06 19:29:03 +00001167 pj_ntohl(hdr->ts), payload_len);
Benny Prijono60b980e2006-04-03 22:41:26 +00001168
Benny Prijono69968232006-04-06 19:29:03 +00001169 /* Update RTP session */
1170 pjmedia_rtp_session_update(&strm->in_sess, hdr, NULL);
Benny Prijono9a0eab52006-04-04 19:43:24 +00001171 }
1172
1173 if (rc > 0 && PJ_FD_ISSET(strm->rtcp_sock, &set)) {
Benny Prijono60b980e2006-04-03 22:41:26 +00001174
1175 /*
1176 * Process incoming RTCP
1177 */
1178 pj_status_t status;
1179 pj_ssize_t size;
1180
1181 size = sizeof(packet);
1182 status = pj_sock_recv( strm->rtcp_sock, packet, &size, 0);
Benny Prijono6647d822006-05-20 13:01:07 +00001183 if (status != PJ_SUCCESS) {
Benny Prijono60b980e2006-04-03 22:41:26 +00001184 app_perror(THIS_FILE, "Error receiving RTCP packet", status);
Benny Prijono6647d822006-05-20 13:01:07 +00001185 pj_thread_sleep(10);
1186 } else
Benny Prijono69968232006-04-06 19:29:03 +00001187 pjmedia_rtcp_rx_rtcp(&strm->rtcp, packet, size);
Benny Prijono60b980e2006-04-03 22:41:26 +00001188 }
1189
1190
Benny Prijono9a0eab52006-04-04 19:43:24 +00001191 pj_get_timestamp(&now);
Benny Prijono60b980e2006-04-03 22:41:26 +00001192
Benny Prijono9a0eab52006-04-04 19:43:24 +00001193 if (next_rtp.u64 <= now.u64) {
Benny Prijono60b980e2006-04-03 22:41:26 +00001194 /*
1195 * Time to send RTP packet.
1196 */
1197 pj_status_t status;
1198 const pjmedia_rtp_hdr *hdr;
1199 pj_ssize_t size;
1200 int hdrlen;
1201
1202 /* Format RTP header */
1203 status = pjmedia_rtp_encode_rtp( &strm->out_sess, strm->si.tx_pt,
1204 0, /* marker bit */
1205 strm->bytes_per_frame,
1206 strm->samples_per_frame,
Benny Prijono8befd9f2006-05-13 22:46:23 +00001207 (const void**)&hdr, &hdrlen);
Benny Prijono60b980e2006-04-03 22:41:26 +00001208 if (status == PJ_SUCCESS) {
1209
1210 /* Copy RTP header to packet */
1211 pj_memcpy(packet, hdr, hdrlen);
1212
1213 /* Zero the payload */
1214 pj_memset(packet+hdrlen, 0, strm->bytes_per_frame);
1215
1216 /* Send RTP packet */
1217 size = hdrlen + strm->bytes_per_frame;
1218 status = pj_sock_sendto( strm->rtp_sock, packet, &size, 0,
1219 &strm->si.rem_addr,
1220 sizeof(strm->si.rem_addr));
1221
1222 if (status != PJ_SUCCESS)
1223 app_perror(THIS_FILE, "Error sending RTP packet", status);
1224
1225 }
1226
1227 /* Update RTCP SR */
1228 pjmedia_rtcp_tx_rtp( &strm->rtcp, (pj_uint16_t)strm->bytes_per_frame);
1229
1230 /* Schedule next send */
Benny Prijono9a0eab52006-04-04 19:43:24 +00001231 next_rtp.u64 += (msec_interval * freq.u64 / 1000);
Benny Prijono60b980e2006-04-03 22:41:26 +00001232 }
1233
1234
Benny Prijono9a0eab52006-04-04 19:43:24 +00001235 if (next_rtcp.u64 <= now.u64) {
Benny Prijono60b980e2006-04-03 22:41:26 +00001236 /*
1237 * Time to send RTCP packet.
1238 */
1239 pjmedia_rtcp_pkt *rtcp_pkt;
1240 int rtcp_len;
1241 pj_sockaddr_in rem_addr;
1242 pj_ssize_t size;
1243 int port;
1244 pj_status_t status;
1245
1246 /* Build RTCP packet */
1247 pjmedia_rtcp_build_rtcp(&strm->rtcp, &rtcp_pkt, &rtcp_len);
1248
1249
1250 /* Calculate address based on RTP address */
1251 rem_addr = strm->si.rem_addr;
1252 port = pj_ntohs(strm->si.rem_addr.sin_port) + 1;
1253 rem_addr.sin_port = pj_htons((pj_uint16_t)port);
1254
1255 /* Send packet */
1256 size = rtcp_len;
1257 status = pj_sock_sendto(strm->rtcp_sock, rtcp_pkt, &size, 0,
1258 &rem_addr, sizeof(rem_addr));
1259 if (status != PJ_SUCCESS) {
1260 app_perror(THIS_FILE, "Error sending RTCP packet", status);
1261 }
1262
Benny Prijono69968232006-04-06 19:29:03 +00001263 /* Schedule next send */
Benny Prijono14b7b662006-04-07 15:01:51 +00001264 next_rtcp.u64 += (freq.u64 * (RTCP_INTERVAL+(pj_rand()%RTCP_RAND)) /
1265 1000);
Benny Prijono60b980e2006-04-03 22:41:26 +00001266 }
Benny Prijono60b980e2006-04-03 22:41:26 +00001267 }
1268
1269 return 0;
1270}
1271
1272
1273/* Callback to be called when SDP negotiation is done in the call: */
1274static void call_on_media_update( pjsip_inv_session *inv,
1275 pj_status_t status)
1276{
1277 struct call *call;
1278 pj_pool_t *pool;
1279 struct media_stream *audio;
Benny Prijono49ce9a72006-04-05 16:56:19 +00001280 const pjmedia_sdp_session *local_sdp, *remote_sdp;
Benny Prijono4adcb912006-04-04 13:12:38 +00001281 struct codec *codec_desc = NULL;
1282 unsigned i;
Benny Prijono60b980e2006-04-03 22:41:26 +00001283
1284 call = inv->mod_data[mod_siprtp.id];
1285 pool = inv->dlg->pool;
1286 audio = &call->media[0];
1287
1288 /* If this is a mid-call media update, then destroy existing media */
1289 if (audio->thread != NULL)
1290 destroy_call_media(call->index);
1291
1292
1293 /* Do nothing if media negotiation has failed */
1294 if (status != PJ_SUCCESS) {
1295 app_perror(THIS_FILE, "SDP negotiation failed", status);
1296 return;
1297 }
1298
1299
1300 /* Capture stream definition from the SDP */
1301 pjmedia_sdp_neg_get_active_local(inv->neg, &local_sdp);
1302 pjmedia_sdp_neg_get_active_remote(inv->neg, &remote_sdp);
1303
1304 status = pjmedia_stream_info_from_sdp(&audio->si, inv->pool, app.med_endpt,
Benny Prijonob04c9e02006-05-17 17:17:39 +00001305 local_sdp, remote_sdp, 0);
Benny Prijono60b980e2006-04-03 22:41:26 +00001306 if (status != PJ_SUCCESS) {
1307 app_perror(THIS_FILE, "Error creating stream info from SDP", status);
1308 return;
1309 }
1310
Benny Prijono4adcb912006-04-04 13:12:38 +00001311 /* Get the remainder of codec information from codec descriptor */
1312 if (audio->si.fmt.pt == app.audio_codec.pt)
1313 codec_desc = &app.audio_codec;
1314 else {
1315 /* Find the codec description in codec array */
1316 for (i=0; i<PJ_ARRAY_SIZE(audio_codecs); ++i) {
1317 if (audio_codecs[i].pt == audio->si.fmt.pt) {
1318 codec_desc = &audio_codecs[i];
1319 break;
1320 }
1321 }
1322
1323 if (codec_desc == NULL) {
1324 PJ_LOG(3, (THIS_FILE, "Error: Invalid codec payload type"));
1325 return;
1326 }
1327 }
Benny Prijono60b980e2006-04-03 22:41:26 +00001328
Benny Prijono15953012006-04-27 22:37:08 +00001329 audio->clock_rate = audio->si.fmt.clock_rate;
Benny Prijono4adcb912006-04-04 13:12:38 +00001330 audio->samples_per_frame = audio->clock_rate * codec_desc->ptime / 1000;
1331 audio->bytes_per_frame = codec_desc->bit_rate * codec_desc->ptime / 1000 / 8;
Benny Prijono60b980e2006-04-03 22:41:26 +00001332
1333
1334 pjmedia_rtp_session_init(&audio->out_sess, audio->si.tx_pt,
Benny Prijono9d8a8732006-04-04 13:39:58 +00001335 pj_rand());
Benny Prijono60b980e2006-04-03 22:41:26 +00001336 pjmedia_rtp_session_init(&audio->in_sess, audio->si.fmt.pt, 0);
Benny Prijono6d7a45f2006-04-24 23:13:00 +00001337 pjmedia_rtcp_init(&audio->rtcp, "rtcp", audio->clock_rate,
Benny Prijono69968232006-04-06 19:29:03 +00001338 audio->samples_per_frame, 0);
Benny Prijono60b980e2006-04-03 22:41:26 +00001339
Benny Prijono4adcb912006-04-04 13:12:38 +00001340
Benny Prijono4adcb912006-04-04 13:12:38 +00001341
Benny Prijono60b980e2006-04-03 22:41:26 +00001342 /* Start media thread. */
1343 audio->thread_quit_flag = 0;
1344 status = pj_thread_create( inv->pool, "media", &media_thread, audio,
1345 0, 0, &audio->thread);
1346 if (status != PJ_SUCCESS) {
1347 app_perror(THIS_FILE, "Error creating media thread", status);
1348 }
1349}
1350
1351
1352
1353/* Destroy call's media */
1354static void destroy_call_media(unsigned call_index)
1355{
1356 struct media_stream *audio = &app.call[call_index].media[0];
1357
1358 if (audio->thread) {
1359 audio->thread_quit_flag = 1;
1360 pj_thread_join(audio->thread);
1361 pj_thread_destroy(audio->thread);
1362 audio->thread = NULL;
1363 audio->thread_quit_flag = 0;
Benny Prijono4adcb912006-04-04 13:12:38 +00001364
1365 /* Flush RTP/RTCP packets */
1366 {
1367 pj_fd_set_t set;
1368 pj_time_val timeout = {0, 0};
1369 char packet[1500];
1370 pj_ssize_t size;
1371 pj_status_t status;
1372 int rc;
1373
1374 do {
1375 PJ_FD_ZERO(&set);
1376 PJ_FD_SET(audio->rtp_sock, &set);
1377 PJ_FD_SET(audio->rtcp_sock, &set);
1378
1379 rc = pj_sock_select(FD_SETSIZE, &set, NULL, NULL, &timeout);
1380 if (rc > 0 && PJ_FD_ISSET(audio->rtp_sock, &set)) {
1381 size = sizeof(packet);
1382 status = pj_sock_recv(audio->rtp_sock, packet, &size, 0);
1383
1384 }
1385 if (rc > 0 && PJ_FD_ISSET(audio->rtcp_sock, &set)) {
1386 size = sizeof(packet);
1387 status = pj_sock_recv(audio->rtcp_sock, packet, &size, 0);
1388 }
1389
1390 } while (rc > 0);
1391 }
Benny Prijono60b980e2006-04-03 22:41:26 +00001392 }
1393}
1394
1395
Benny Prijono4adcb912006-04-04 13:12:38 +00001396/*****************************************************************************
Benny Prijono60b980e2006-04-03 22:41:26 +00001397 * USER INTERFACE STUFFS
1398 */
Benny Prijono16a6b0e2006-05-12 10:20:03 +00001399#include "siprtp_report.c"
Benny Prijono60b980e2006-04-03 22:41:26 +00001400
1401
1402static void list_calls()
1403{
1404 unsigned i;
1405 puts("List all calls:");
1406 for (i=0; i<app.max_calls; ++i) {
1407 if (!app.call[i].inv)
1408 continue;
1409 print_call(i);
1410 }
1411}
1412
1413static void hangup_call(unsigned index)
1414{
1415 pjsip_tx_data *tdata;
1416 pj_status_t status;
1417
1418 if (app.call[index].inv == NULL)
1419 return;
1420
1421 status = pjsip_inv_end_session(app.call[index].inv, 603, NULL, &tdata);
1422 if (status==PJ_SUCCESS && tdata!=NULL)
1423 pjsip_inv_send_msg(app.call[index].inv, tdata);
1424}
1425
1426static void hangup_all_calls()
1427{
1428 unsigned i;
1429 for (i=0; i<app.max_calls; ++i) {
1430 if (!app.call[i].inv)
1431 continue;
1432 hangup_call(i);
1433 }
1434}
1435
1436static pj_bool_t simple_input(const char *title, char *buf, pj_size_t len)
1437{
1438 char *p;
1439
1440 printf("%s (empty to cancel): ", title); fflush(stdout);
1441 fgets(buf, len, stdin);
1442
1443 /* Remove trailing newlines. */
1444 for (p=buf; ; ++p) {
1445 if (*p=='\r' || *p=='\n') *p='\0';
1446 else if (!*p) break;
1447 }
1448
1449 if (!*buf)
1450 return PJ_FALSE;
1451
1452 return PJ_TRUE;
1453}
1454
1455
1456static const char *MENU =
1457"\n"
1458"Enter menu character:\n"
1459" l List all calls\n"
1460" h Hangup a call\n"
1461" H Hangup all calls\n"
1462" q Quit\n"
1463"\n";
1464
1465
1466/* Main screen menu */
1467static void console_main()
1468{
1469 char input1[10];
1470 unsigned i;
1471
Benny Prijono4adcb912006-04-04 13:12:38 +00001472 printf("%s", MENU);
1473
Benny Prijono60b980e2006-04-03 22:41:26 +00001474 for (;;) {
1475 printf(">>> "); fflush(stdout);
1476 fgets(input1, sizeof(input1), stdin);
1477
1478 switch (input1[0]) {
1479 case 'l':
1480 list_calls();
1481 break;
1482
1483 case 'h':
1484 if (!simple_input("Call number to hangup", input1, sizeof(input1)))
1485 break;
1486
1487 i = atoi(input1);
1488 hangup_call(i);
1489 break;
1490
1491 case 'H':
1492 hangup_all_calls();
1493 break;
1494
1495 case 'q':
1496 goto on_exit;
1497
1498 default:
Benny Prijono4adcb912006-04-04 13:12:38 +00001499 puts("Invalid command");
Benny Prijono60b980e2006-04-03 22:41:26 +00001500 printf("%s", MENU);
1501 break;
1502 }
1503
1504 fflush(stdout);
1505 }
1506
1507on_exit:
Benny Prijono4adcb912006-04-04 13:12:38 +00001508 hangup_all_calls();
Benny Prijono60b980e2006-04-03 22:41:26 +00001509}
1510
1511
Benny Prijono4adcb912006-04-04 13:12:38 +00001512/*****************************************************************************
1513 * Below is a simple module to log all incoming and outgoing SIP messages
1514 */
1515
1516
Benny Prijono60b980e2006-04-03 22:41:26 +00001517/* Notification on incoming messages */
Benny Prijono4adcb912006-04-04 13:12:38 +00001518static pj_bool_t logger_on_rx_msg(pjsip_rx_data *rdata)
Benny Prijono60b980e2006-04-03 22:41:26 +00001519{
1520 PJ_LOG(4,(THIS_FILE, "RX %d bytes %s from %s:%d:\n"
1521 "%s\n"
1522 "--end msg--",
1523 rdata->msg_info.len,
1524 pjsip_rx_data_get_info(rdata),
1525 rdata->pkt_info.src_name,
1526 rdata->pkt_info.src_port,
1527 rdata->msg_info.msg_buf));
1528
1529 /* Always return false, otherwise messages will not get processed! */
1530 return PJ_FALSE;
1531}
1532
1533/* Notification on outgoing messages */
Benny Prijono4adcb912006-04-04 13:12:38 +00001534static pj_status_t logger_on_tx_msg(pjsip_tx_data *tdata)
Benny Prijono60b980e2006-04-03 22:41:26 +00001535{
1536
1537 /* Important note:
1538 * tp_info field is only valid after outgoing messages has passed
1539 * transport layer. So don't try to access tp_info when the module
1540 * has lower priority than transport layer.
1541 */
1542
1543 PJ_LOG(4,(THIS_FILE, "TX %d bytes %s to %s:%d:\n"
1544 "%s\n"
1545 "--end msg--",
1546 (tdata->buf.cur - tdata->buf.start),
1547 pjsip_tx_data_get_info(tdata),
1548 tdata->tp_info.dst_name,
1549 tdata->tp_info.dst_port,
1550 tdata->buf.start));
1551
1552 /* Always return success, otherwise message will not get sent! */
1553 return PJ_SUCCESS;
1554}
1555
1556/* The module instance. */
1557static pjsip_module msg_logger =
1558{
1559 NULL, NULL, /* prev, next. */
1560 { "mod-siprtp-log", 14 }, /* Name. */
1561 -1, /* Id */
1562 PJSIP_MOD_PRIORITY_TRANSPORT_LAYER-1,/* Priority */
1563 NULL, /* load() */
1564 NULL, /* start() */
1565 NULL, /* stop() */
1566 NULL, /* unload() */
Benny Prijono4adcb912006-04-04 13:12:38 +00001567 &logger_on_rx_msg, /* on_rx_request() */
1568 &logger_on_rx_msg, /* on_rx_response() */
1569 &logger_on_tx_msg, /* on_tx_request. */
1570 &logger_on_tx_msg, /* on_tx_response() */
Benny Prijono60b980e2006-04-03 22:41:26 +00001571 NULL, /* on_tsx_state() */
1572
1573};
1574
1575
1576
Benny Prijono4adcb912006-04-04 13:12:38 +00001577/*****************************************************************************
1578 * Console application custom logging:
1579 */
1580
1581
1582static FILE *log_file;
1583
1584
1585static void app_log_writer(int level, const char *buffer, int len)
1586{
1587 /* Write to both stdout and file. */
1588
1589 if (level <= app.app_log_level)
1590 pj_log_write(level, buffer, len);
1591
1592 if (log_file) {
1593 fwrite(buffer, len, 1, log_file);
1594 fflush(log_file);
1595 }
1596}
1597
1598
1599pj_status_t app_logging_init(void)
1600{
1601 /* Redirect log function to ours */
1602
1603 pj_log_set_log_func( &app_log_writer );
1604
1605 /* If output log file is desired, create the file: */
1606
1607 if (app.log_filename) {
1608 log_file = fopen(app.log_filename, "wt");
1609 if (log_file == NULL) {
1610 PJ_LOG(1,(THIS_FILE, "Unable to open log file %s",
1611 app.log_filename));
1612 return -1;
1613 }
1614 }
1615
1616 return PJ_SUCCESS;
1617}
1618
1619
1620void app_logging_shutdown(void)
1621{
1622 /* Close logging file, if any: */
1623
1624 if (log_file) {
1625 fclose(log_file);
1626 log_file = NULL;
1627 }
1628}
1629
Benny Prijono60b980e2006-04-03 22:41:26 +00001630
1631/*
1632 * main()
1633 */
1634int main(int argc, char *argv[])
1635{
Benny Prijono4adcb912006-04-04 13:12:38 +00001636 unsigned i;
Benny Prijono60b980e2006-04-03 22:41:26 +00001637 pj_status_t status;
1638
Benny Prijono4adcb912006-04-04 13:12:38 +00001639 /* Must init PJLIB first */
Benny Prijono60b980e2006-04-03 22:41:26 +00001640 status = pj_init();
1641 if (status != PJ_SUCCESS)
1642 return 1;
1643
Benny Prijono4adcb912006-04-04 13:12:38 +00001644 /* Get command line options */
Benny Prijono60b980e2006-04-03 22:41:26 +00001645 status = init_options(argc, argv);
1646 if (status != PJ_SUCCESS)
1647 return 1;
1648
Benny Prijono410fbae2006-05-03 18:16:06 +00001649 /* Verify options: */
1650
1651 /* Auto-quit can not be specified for UAS */
1652 if (app.auto_quit && app.uri_to_call.slen == 0) {
1653 printf("Error: --auto-quit option only valid for outgoing "
1654 "mode (UAC) only\n");
1655 return 1;
1656 }
1657
Benny Prijono4adcb912006-04-04 13:12:38 +00001658 /* Init logging */
1659 status = app_logging_init();
1660 if (status != PJ_SUCCESS)
1661 return 1;
1662
1663 /* Init SIP etc */
Benny Prijono60b980e2006-04-03 22:41:26 +00001664 status = init_sip();
1665 if (status != PJ_SUCCESS) {
1666 app_perror(THIS_FILE, "Initialization has failed", status);
1667 destroy_sip();
1668 return 1;
1669 }
1670
Benny Prijono4adcb912006-04-04 13:12:38 +00001671 /* Register module to log incoming/outgoing messages */
Benny Prijono60b980e2006-04-03 22:41:26 +00001672 pjsip_endpt_register_module(app.sip_endpt, &msg_logger);
1673
Benny Prijono4adcb912006-04-04 13:12:38 +00001674 /* Init media */
Benny Prijono60b980e2006-04-03 22:41:26 +00001675 status = init_media();
1676 if (status != PJ_SUCCESS) {
1677 app_perror(THIS_FILE, "Media initialization failed", status);
1678 destroy_sip();
1679 return 1;
1680 }
1681
Benny Prijono9a0eab52006-04-04 19:43:24 +00001682 /* Start worker threads */
1683 for (i=0; i<app.thread_count; ++i) {
1684 pj_thread_create( app.pool, "app", &worker_thread, NULL,
1685 0, 0, &app.thread[i]);
1686 }
1687
Benny Prijono4adcb912006-04-04 13:12:38 +00001688 /* If URL is specified, then make call immediately */
Benny Prijono60b980e2006-04-03 22:41:26 +00001689 if (app.uri_to_call.slen) {
1690 unsigned i;
1691
Benny Prijono4adcb912006-04-04 13:12:38 +00001692 PJ_LOG(3,(THIS_FILE, "Making %d calls to %s..", app.max_calls,
1693 app.uri_to_call.ptr));
1694
Benny Prijono60b980e2006-04-03 22:41:26 +00001695 for (i=0; i<app.max_calls; ++i) {
1696 status = make_call(&app.uri_to_call);
1697 if (status != PJ_SUCCESS) {
1698 app_perror(THIS_FILE, "Error making call", status);
1699 break;
1700 }
1701 }
Benny Prijono4adcb912006-04-04 13:12:38 +00001702
Benny Prijono410fbae2006-05-03 18:16:06 +00001703 if (app.auto_quit) {
1704 /* Wait for calls to complete */
1705 while (app.uac_calls < app.max_calls)
1706 pj_thread_sleep(100);
1707 pj_thread_sleep(200);
1708 } else {
1709 /* Start user interface loop */
1710 console_main();
1711 }
1712
Benny Prijono4adcb912006-04-04 13:12:38 +00001713 } else {
1714
1715 PJ_LOG(3,(THIS_FILE, "Ready for incoming calls (max=%d)",
1716 app.max_calls));
Benny Prijono4adcb912006-04-04 13:12:38 +00001717
Benny Prijono410fbae2006-05-03 18:16:06 +00001718 /* Start user interface loop */
1719 console_main();
1720
1721 }
Benny Prijono60b980e2006-04-03 22:41:26 +00001722
Benny Prijono4adcb912006-04-04 13:12:38 +00001723
1724 /* Shutting down... */
Benny Prijono60b980e2006-04-03 22:41:26 +00001725 destroy_sip();
Benny Prijono6647d822006-05-20 13:01:07 +00001726 destroy_media();
1727
1728 if (app.pool) {
1729 pj_pool_release(app.pool);
1730 app.pool = NULL;
1731 pj_caching_pool_destroy(&app.cp);
1732 }
1733
Benny Prijono4adcb912006-04-04 13:12:38 +00001734 app_logging_shutdown();
1735
Benny Prijono60b980e2006-04-03 22:41:26 +00001736
1737 return 0;
1738}
1739