blob: 9bbde4bfaa042ddea6e590fd81d36d3bcacfdda1 [file] [log] [blame]
Benny Prijono53fde132006-03-17 19:41:19 +00001/* $Id$ */
2/*
Benny Prijono32177c02008-06-20 22:44:47 +00003 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
Benny Prijono53fde132006-03-17 19:41:19 +00004 *
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 */
Benny Prijono6107a002006-03-17 18:01:27 +000019
Benny Prijono53fde132006-03-17 19:41:19 +000020
21/**
22 * simpleua.c
23 *
24 * This is a very simple SIP user agent complete with media. The user
25 * agent should do a proper SDP negotiation and start RTP media once
26 * SDP negotiation has completed.
27 *
28 * This program does not register to SIP server.
29 *
30 * Capabilities to be demonstrated here:
31 * - Basic call
Benny Prijonoe8df45f2008-03-08 09:26:22 +000032 * - Should support IPv6 (not tested)
Benny Prijono53fde132006-03-17 19:41:19 +000033 * - UDP transport at port 5060 (hard coded)
34 * - RTP socket at port 4000 (hard coded)
35 * - proper SDP negotiation
36 * - PCMA/PCMU codec only.
37 * - Audio/media to sound device.
38 *
39 *
40 * Usage:
41 * - To make outgoing call, start simpleua with the URL of remote
42 * destination to contact.
43 * E.g.:
44 * simpleua sip:user@remote
45 *
46 * - Incoming calls will automatically be answered with 180, then 200.
47 *
48 * This program does not disconnect call.
49 *
50 * This program will quit once it has completed a single call.
51 */
52
53/* Include all headers. */
Benny Prijono6107a002006-03-17 18:01:27 +000054#include <pjsip.h>
Benny Prijono6107a002006-03-17 18:01:27 +000055#include <pjmedia.h>
Benny Prijono6107a002006-03-17 18:01:27 +000056#include <pjmedia-codec.h>
Benny Prijono6107a002006-03-17 18:01:27 +000057#include <pjsip_ua.h>
Benny Prijono6107a002006-03-17 18:01:27 +000058#include <pjsip_simple.h>
Benny Prijono6107a002006-03-17 18:01:27 +000059#include <pjlib-util.h>
Benny Prijono6107a002006-03-17 18:01:27 +000060#include <pjlib.h>
61
Benny Prijono53fde132006-03-17 19:41:19 +000062/* For logging purpose. */
Benny Prijono6107a002006-03-17 18:01:27 +000063#define THIS_FILE "simpleua.c"
64
Benny Prijonobc797312006-03-24 20:44:27 +000065#include "util.h"
Benny Prijono6107a002006-03-17 18:01:27 +000066
Benny Prijonoe8df45f2008-03-08 09:26:22 +000067
68/* Settings */
69#define AF pj_AF_INET() /* Change to pj_AF_INET6() for IPv6.
70 * PJ_HAS_IPV6 must be enabled and
71 * your system must support IPv6. */
72#define SIP_PORT 5060 /* Listening SIP port */
73#define RTP_PORT 4000 /* RTP port */
74
Benny Prijono6107a002006-03-17 18:01:27 +000075/*
76 * Static variables.
77 */
Benny Prijono6107a002006-03-17 18:01:27 +000078
Benny Prijono53fde132006-03-17 19:41:19 +000079static pj_bool_t g_complete; /* Quit flag. */
80static pjsip_endpoint *g_endpt; /* SIP endpoint. */
81static pj_caching_pool cp; /* Global pool factory. */
Benny Prijono6107a002006-03-17 18:01:27 +000082
Benny Prijono53fde132006-03-17 19:41:19 +000083static pjmedia_endpt *g_med_endpt; /* Media endpoint. */
Benny Prijonoe1a5a852008-03-11 21:38:05 +000084static pjmedia_transport_info g_med_tpinfo; /* Socket info for media */
Benny Prijonob04c9e02006-05-17 17:17:39 +000085static pjmedia_transport *g_med_transport;/* Media stream transport */
Benny Prijono6107a002006-03-17 18:01:27 +000086
Benny Prijono53fde132006-03-17 19:41:19 +000087/* Call variables: */
88static pjsip_inv_session *g_inv; /* Current invite session. */
89static pjmedia_session *g_med_session; /* Call's media session. */
90static pjmedia_snd_port *g_snd_player; /* Call's sound player */
91static pjmedia_snd_port *g_snd_rec; /* Call's sound recorder. */
Benny Prijono6107a002006-03-17 18:01:27 +000092
93
94/*
Benny Prijono53fde132006-03-17 19:41:19 +000095 * Prototypes:
Benny Prijono6107a002006-03-17 18:01:27 +000096 */
Benny Prijono53fde132006-03-17 19:41:19 +000097
98/* Callback to be called when SDP negotiation is done in the call: */
Benny Prijono6107a002006-03-17 18:01:27 +000099static void call_on_media_update( pjsip_inv_session *inv,
100 pj_status_t status);
Benny Prijono53fde132006-03-17 19:41:19 +0000101
102/* Callback to be called when invite session's state has changed: */
Benny Prijono6107a002006-03-17 18:01:27 +0000103static void call_on_state_changed( pjsip_inv_session *inv,
104 pjsip_event *e);
Benny Prijono53fde132006-03-17 19:41:19 +0000105
106/* Callback to be called when dialog has forked: */
Benny Prijono6107a002006-03-17 18:01:27 +0000107static void call_on_forked(pjsip_inv_session *inv, pjsip_event *e);
Benny Prijono53fde132006-03-17 19:41:19 +0000108
109/* Callback to be called to handle incoming requests outside dialogs: */
Benny Prijono6107a002006-03-17 18:01:27 +0000110static pj_bool_t on_rx_request( pjsip_rx_data *rdata );
111
112
Benny Prijono53fde132006-03-17 19:41:19 +0000113
114
115/* This is a PJSIP module to be registered by application to handle
116 * incoming requests outside any dialogs/transactions. The main purpose
117 * here is to handle incoming INVITE request message, where we will
118 * create a dialog and INVITE session for it.
119 */
Benny Prijono6107a002006-03-17 18:01:27 +0000120static pjsip_module mod_simpleua =
121{
122 NULL, NULL, /* prev, next. */
123 { "mod-simpleua", 12 }, /* Name. */
124 -1, /* Id */
125 PJSIP_MOD_PRIORITY_APPLICATION, /* Priority */
126 NULL, /* load() */
127 NULL, /* start() */
128 NULL, /* stop() */
129 NULL, /* unload() */
130 &on_rx_request, /* on_rx_request() */
131 NULL, /* on_rx_response() */
132 NULL, /* on_tx_request. */
133 NULL, /* on_tx_response() */
134 NULL, /* on_tsx_state() */
135};
136
137
Benny Prijono6107a002006-03-17 18:01:27 +0000138
139/*
140 * main()
Benny Prijono53fde132006-03-17 19:41:19 +0000141 *
142 * If called with argument, treat argument as SIP URL to be called.
143 * Otherwise wait for incoming calls.
Benny Prijono6107a002006-03-17 18:01:27 +0000144 */
145int main(int argc, char *argv[])
146{
147 pj_status_t status;
148
Benny Prijono53fde132006-03-17 19:41:19 +0000149 /* Must init PJLIB first: */
Benny Prijono6107a002006-03-17 18:01:27 +0000150 status = pj_init();
151 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
152
Benny Prijono53fde132006-03-17 19:41:19 +0000153
154 /* Then init PJLIB-UTIL: */
Benny Prijono6107a002006-03-17 18:01:27 +0000155 status = pjlib_util_init();
156 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
157
158
Benny Prijono53fde132006-03-17 19:41:19 +0000159 /* Must create a pool factory before we can allocate any memory. */
Benny Prijono6107a002006-03-17 18:01:27 +0000160 pj_caching_pool_init(&cp, &pj_pool_factory_default_policy, 0);
161
Benny Prijono53fde132006-03-17 19:41:19 +0000162
Benny Prijono6107a002006-03-17 18:01:27 +0000163 /* Create global endpoint: */
164 {
165 const pj_str_t *hostname;
166 const char *endpt_name;
167
168 /* Endpoint MUST be assigned a globally unique name.
169 * The name will be used as the hostname in Warning header.
170 */
171
172 /* For this implementation, we'll use hostname for simplicity */
173 hostname = pj_gethostname();
174 endpt_name = hostname->ptr;
175
176 /* Create the endpoint: */
177
178 status = pjsip_endpt_create(&cp.factory, endpt_name,
179 &g_endpt);
180 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
181 }
182
Benny Prijono53fde132006-03-17 19:41:19 +0000183
Benny Prijono6107a002006-03-17 18:01:27 +0000184 /*
Benny Prijono53fde132006-03-17 19:41:19 +0000185 * Add UDP transport, with hard-coded port
186 * Alternatively, application can use pjsip_udp_transport_attach() to
187 * start UDP transport, if it already has an UDP socket (e.g. after it
188 * resolves the address with STUN).
Benny Prijono6107a002006-03-17 18:01:27 +0000189 */
190 {
Benny Prijonoe8df45f2008-03-08 09:26:22 +0000191 pj_sockaddr addr;
Benny Prijono6107a002006-03-17 18:01:27 +0000192
Benny Prijonoe8df45f2008-03-08 09:26:22 +0000193 pj_sockaddr_init(AF, &addr, NULL, (pj_uint16_t)SIP_PORT);
194
195 if (AF == pj_AF_INET()) {
196 status = pjsip_udp_transport_start( g_endpt, &addr.ipv4, NULL,
197 1, NULL);
198 } else if (AF == pj_AF_INET6()) {
199 status = pjsip_udp_transport_start6(g_endpt, &addr.ipv6, NULL,
200 1, NULL);
201 } else {
202 status = PJ_EAFNOTSUP;
203 }
Benny Prijono6107a002006-03-17 18:01:27 +0000204
Benny Prijono6107a002006-03-17 18:01:27 +0000205 if (status != PJ_SUCCESS) {
206 app_perror(THIS_FILE, "Unable to start UDP transport", status);
207 return 1;
208 }
209 }
210
211
212 /*
Benny Prijono53fde132006-03-17 19:41:19 +0000213 * Init transaction layer.
214 * This will create/initialize transaction hash tables etc.
Benny Prijono6107a002006-03-17 18:01:27 +0000215 */
216 status = pjsip_tsx_layer_init_module(g_endpt);
217 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
218
Benny Prijono53fde132006-03-17 19:41:19 +0000219
Benny Prijono6107a002006-03-17 18:01:27 +0000220 /*
Benny Prijono53fde132006-03-17 19:41:19 +0000221 * Initialize UA layer module.
222 * This will create/initialize dialog hash tables etc.
Benny Prijono6107a002006-03-17 18:01:27 +0000223 */
224 status = pjsip_ua_init_module( g_endpt, NULL );
225 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
226
Benny Prijono53fde132006-03-17 19:41:19 +0000227
228 /*
Benny Prijono6107a002006-03-17 18:01:27 +0000229 * Init invite session module.
Benny Prijono53fde132006-03-17 19:41:19 +0000230 * The invite session module initialization takes additional argument,
231 * i.e. a structure containing callbacks to be called on specific
232 * occurence of events.
233 *
234 * The on_state_changed and on_new_session callbacks are mandatory.
235 * Application must supply the callback function.
236 *
237 * We use on_media_update() callback in this application to start
238 * media transmission.
Benny Prijono6107a002006-03-17 18:01:27 +0000239 */
240 {
241 pjsip_inv_callback inv_cb;
242
243 /* Init the callback for INVITE session: */
Benny Prijonoac623b32006-07-03 15:19:31 +0000244 pj_bzero(&inv_cb, sizeof(inv_cb));
Benny Prijono6107a002006-03-17 18:01:27 +0000245 inv_cb.on_state_changed = &call_on_state_changed;
246 inv_cb.on_new_session = &call_on_forked;
247 inv_cb.on_media_update = &call_on_media_update;
248
249 /* Initialize invite session module: */
250 status = pjsip_inv_usage_init(g_endpt, &inv_cb);
251 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
252 }
253
Benny Prijonodf912082007-10-30 16:41:45 +0000254 /* Initialize 100rel support */
255 status = pjsip_100rel_init_module(g_endpt);
256 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
Benny Prijono6107a002006-03-17 18:01:27 +0000257
258 /*
Benny Prijono53fde132006-03-17 19:41:19 +0000259 * Register our module to receive incoming requests.
Benny Prijono6107a002006-03-17 18:01:27 +0000260 */
261 status = pjsip_endpt_register_module( g_endpt, &mod_simpleua);
262 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
263
264
265 /*
Benny Prijono53fde132006-03-17 19:41:19 +0000266 * Initialize media endpoint.
267 * This will implicitly initialize PJMEDIA too.
Benny Prijono6107a002006-03-17 18:01:27 +0000268 */
Benny Prijono54826a32007-03-30 18:57:01 +0000269#if PJ_HAS_THREADS
Benny Prijono275fd682006-03-22 11:59:11 +0000270 status = pjmedia_endpt_create(&cp.factory, NULL, 1, &g_med_endpt);
Benny Prijono54826a32007-03-30 18:57:01 +0000271#else
272 status = pjmedia_endpt_create(&cp.factory,
273 pjsip_endpt_get_ioqueue(g_endpt),
274 0, &g_med_endpt);
275#endif
Benny Prijono6107a002006-03-17 18:01:27 +0000276 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
277
278 /*
279 * Add PCMA/PCMU codec to the media endpoint.
280 */
Benny Prijonofc24e692007-01-27 18:31:51 +0000281#if defined(PJMEDIA_HAS_G711_CODEC) && PJMEDIA_HAS_G711_CODEC!=0
Benny Prijono6107a002006-03-17 18:01:27 +0000282 status = pjmedia_codec_g711_init(g_med_endpt);
283 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
Benny Prijonofc24e692007-01-27 18:31:51 +0000284#endif
Benny Prijono6107a002006-03-17 18:01:27 +0000285
Benny Prijonoc1e263f2006-08-09 13:53:59 +0000286
Benny Prijono6107a002006-03-17 18:01:27 +0000287 /*
Benny Prijonoc1e263f2006-08-09 13:53:59 +0000288 * Create media transport used to send/receive RTP/RTCP socket.
289 * One media transport is needed for each call. Application may
290 * opt to re-use the same media transport for subsequent calls.
Benny Prijono6107a002006-03-17 18:01:27 +0000291 */
Benny Prijonoe8df45f2008-03-08 09:26:22 +0000292 status = pjmedia_transport_udp_create3(g_med_endpt, AF, NULL, NULL,
293 RTP_PORT, 0, &g_med_transport);
Benny Prijonob04c9e02006-05-17 17:17:39 +0000294 if (status != PJ_SUCCESS) {
295 app_perror(THIS_FILE, "Unable to create media transport", status);
296 return 1;
297 }
298
Benny Prijonoc1e263f2006-08-09 13:53:59 +0000299 /*
300 * Get socket info (address, port) of the media transport. We will
301 * need this info to create SDP (i.e. the address and port info in
302 * the SDP).
303 */
Benny Prijono734fc2d2008-03-17 16:05:35 +0000304 pjmedia_transport_info_init(&g_med_tpinfo);
Benny Prijonoe1a5a852008-03-11 21:38:05 +0000305 pjmedia_transport_get_info(g_med_transport, &g_med_tpinfo);
Benny Prijonoc1e263f2006-08-09 13:53:59 +0000306
307
Benny Prijono6107a002006-03-17 18:01:27 +0000308 /*
309 * If URL is specified, then make call immediately.
310 */
311 if (argc > 1) {
Benny Prijonoe8df45f2008-03-08 09:26:22 +0000312 pj_sockaddr hostaddr;
313 char hostip[PJ_INET6_ADDRSTRLEN+2];
Benny Prijono6107a002006-03-17 18:01:27 +0000314 char temp[80];
315 pj_str_t dst_uri = pj_str(argv[1]);
316 pj_str_t local_uri;
317 pjsip_dialog *dlg;
318 pjmedia_sdp_session *local_sdp;
319 pjsip_tx_data *tdata;
320
Benny Prijonoe8df45f2008-03-08 09:26:22 +0000321 if (pj_gethostip(AF, &hostaddr) != PJ_SUCCESS) {
322 app_perror(THIS_FILE, "Unable to retrieve local host IP", status);
323 return 1;
324 }
325 pj_sockaddr_print(&hostaddr, hostip, sizeof(hostip), 2);
326
327 pj_ansi_sprintf(temp, "<sip:simpleuac@%s:%d>",
328 hostip, SIP_PORT);
Benny Prijono6107a002006-03-17 18:01:27 +0000329 local_uri = pj_str(temp);
330
331 /* Create UAC dialog */
332 status = pjsip_dlg_create_uac( pjsip_ua_instance(),
333 &local_uri, /* local URI */
Benny Prijonoe8df45f2008-03-08 09:26:22 +0000334 &local_uri, /* local Contact */
Benny Prijono6107a002006-03-17 18:01:27 +0000335 &dst_uri, /* remote URI */
336 &dst_uri, /* remote target */
337 &dlg); /* dialog */
338 if (status != PJ_SUCCESS) {
339 app_perror(THIS_FILE, "Unable to create UAC dialog", status);
340 return 1;
341 }
342
Benny Prijono0a968362006-03-18 12:29:01 +0000343 /* If we expect the outgoing INVITE to be challenged, then we should
344 * put the credentials in the dialog here, with something like this:
345 *
346 {
347 pjsip_cred_info cred[1];
348
349 cred[0].realm = pj_str("sip.server.realm");
350 cred[0].username = pj_str("theuser");
351 cred[0].data_type = PJSIP_CRED_DATA_PLAIN_PASSWD;
352 cred[0].data = pj_str("thepassword");
353
354 pjsip_auth_clt_set_credentials( &dlg->auth_sess, 1, cred);
355 }
356 *
357 */
358
359
Benny Prijonodf912082007-10-30 16:41:45 +0000360 /* Get the SDP body to be put in the outgoing INVITE, by asking
361 * media endpoint to create one for us. The SDP will contain all
362 * codecs that have been registered to it (in this case, only
363 * PCMA and PCMU), plus telephony event.
364 */
365 status = pjmedia_endpt_create_sdp( g_med_endpt, /* the media endpt */
366 dlg->pool, /* pool. */
367 1, /* # of streams */
Benny Prijonoe1a5a852008-03-11 21:38:05 +0000368 &g_med_tpinfo.sock_info,
369 /* RTP sock info */
Benny Prijonodf912082007-10-30 16:41:45 +0000370 &local_sdp); /* the SDP result */
371 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
372
373
374
375 /* Create the INVITE session, and pass the SDP returned earlier
376 * as the session's initial capability.
377 */
378 status = pjsip_inv_create_uac( dlg, local_sdp, 0, &g_inv);
379 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
380
Benny Prijono0a968362006-03-18 12:29:01 +0000381 /* If we want the initial INVITE to travel to specific SIP proxies,
382 * then we should put the initial dialog's route set here. The final
383 * route set will be updated once a dialog has been established.
384 * To set the dialog's initial route set, we do it with something
385 * like this:
386 *
387 {
388 pjsip_route_hdr route_set;
389 pjsip_route_hdr *route;
390 const pj_str_t hname = { "Route", 5 };
391 char *uri = "sip:proxy.server;lr";
392
393 pj_list_init(&route_set);
394
395 route = pjsip_parse_hdr( dlg->pool, &hname,
396 uri, strlen(uri),
397 NULL);
398 PJ_ASSERT_RETURN(route != NULL, 1);
399 pj_list_push_back(&route_set, route);
400
401 pjsip_dlg_set_route_set(dlg, &route_set);
402 }
403 *
404 * Note that Route URI SHOULD have an ";lr" parameter!
405 */
406
Benny Prijono53fde132006-03-17 19:41:19 +0000407 /* Create initial INVITE request.
408 * This INVITE request will contain a perfectly good request and
409 * an SDP body as well.
410 */
Benny Prijono6107a002006-03-17 18:01:27 +0000411 status = pjsip_inv_invite(g_inv, &tdata);
412 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
413
Benny Prijono53fde132006-03-17 19:41:19 +0000414
415
416 /* Send initial INVITE request.
417 * From now on, the invite session's state will be reported to us
418 * via the invite session callbacks.
419 */
Benny Prijono6107a002006-03-17 18:01:27 +0000420 status = pjsip_inv_send_msg(g_inv, tdata);
421 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
422
Benny Prijono53fde132006-03-17 19:41:19 +0000423
Benny Prijono6107a002006-03-17 18:01:27 +0000424 } else {
Benny Prijono53fde132006-03-17 19:41:19 +0000425
426 /* No URL to make call to */
427
Benny Prijono6107a002006-03-17 18:01:27 +0000428 PJ_LOG(3,(THIS_FILE, "Ready to accept incoming calls..."));
429 }
430
431
432 /* Loop until one call is completed */
433 for (;!g_complete;) {
434 pj_time_val timeout = {0, 10};
435 pjsip_endpt_handle_events(g_endpt, &timeout);
436 }
437
Benny Prijonobc797312006-03-24 20:44:27 +0000438 /* On exit, dump current memory usage: */
439 dump_pool_usage(THIS_FILE, &cp);
Benny Prijono0a968362006-03-18 12:29:01 +0000440
Benny Prijono6107a002006-03-17 18:01:27 +0000441 return 0;
442}
443
444
445
446/*
447 * Callback when INVITE session state has changed.
Benny Prijono53fde132006-03-17 19:41:19 +0000448 * This callback is registered when the invite session module is initialized.
449 * We mostly want to know when the invite session has been disconnected,
450 * so that we can quit the application.
Benny Prijono6107a002006-03-17 18:01:27 +0000451 */
452static void call_on_state_changed( pjsip_inv_session *inv,
453 pjsip_event *e)
454{
Benny Prijono15953012006-04-27 22:37:08 +0000455 PJ_UNUSED_ARG(e);
456
Benny Prijono6107a002006-03-17 18:01:27 +0000457 if (inv->state == PJSIP_INV_STATE_DISCONNECTED) {
458
459 PJ_LOG(3,(THIS_FILE, "Call DISCONNECTED [reason=%d (%s)]",
460 inv->cause,
461 pjsip_get_status_text(inv->cause)->ptr));
462
463 PJ_LOG(3,(THIS_FILE, "One call completed, application quitting..."));
464 g_complete = 1;
465
466 } else {
467
468 PJ_LOG(3,(THIS_FILE, "Call state changed to %s",
469 pjsip_inv_state_name(inv->state)));
470
471 }
472}
473
Benny Prijono6107a002006-03-17 18:01:27 +0000474
Benny Prijono53fde132006-03-17 19:41:19 +0000475/* This callback is called when dialog has forked. */
Benny Prijono6107a002006-03-17 18:01:27 +0000476static void call_on_forked(pjsip_inv_session *inv, pjsip_event *e)
477{
Benny Prijono53fde132006-03-17 19:41:19 +0000478 /* To be done... */
Benny Prijono15953012006-04-27 22:37:08 +0000479 PJ_UNUSED_ARG(inv);
480 PJ_UNUSED_ARG(e);
Benny Prijono6107a002006-03-17 18:01:27 +0000481}
482
Benny Prijono53fde132006-03-17 19:41:19 +0000483
Benny Prijono6107a002006-03-17 18:01:27 +0000484/*
485 * Callback when incoming requests outside any transactions and any
Benny Prijono53fde132006-03-17 19:41:19 +0000486 * dialogs are received. We're only interested to hande incoming INVITE
487 * request, and we'll reject any other requests with 500 response.
Benny Prijono6107a002006-03-17 18:01:27 +0000488 */
489static pj_bool_t on_rx_request( pjsip_rx_data *rdata )
490{
Benny Prijonoe8df45f2008-03-08 09:26:22 +0000491 pj_sockaddr hostaddr;
492 char temp[80], hostip[PJ_INET6_ADDRSTRLEN];
493 pj_str_t local_uri;
Benny Prijono6107a002006-03-17 18:01:27 +0000494 pjsip_dialog *dlg;
495 pjmedia_sdp_session *local_sdp;
496 pjsip_tx_data *tdata;
497 unsigned options = 0;
498 pj_status_t status;
499
Benny Prijono53fde132006-03-17 19:41:19 +0000500
Benny Prijono6107a002006-03-17 18:01:27 +0000501 /*
502 * Respond (statelessly) any non-INVITE requests with 500
503 */
504 if (rdata->msg_info.msg->line.req.method.id != PJSIP_INVITE_METHOD) {
505
Benny Prijono53932c02007-02-13 18:51:44 +0000506 if (rdata->msg_info.msg->line.req.method.id != PJSIP_ACK_METHOD) {
507 pj_str_t reason = pj_str("Simple UA unable to handle "
508 "this request");
Benny Prijono6107a002006-03-17 18:01:27 +0000509
Benny Prijono53932c02007-02-13 18:51:44 +0000510 pjsip_endpt_respond_stateless( g_endpt, rdata,
511 500, &reason,
512 NULL, NULL);
513 }
Benny Prijono6107a002006-03-17 18:01:27 +0000514 return PJ_TRUE;
515 }
516
Benny Prijono53fde132006-03-17 19:41:19 +0000517
Benny Prijono6107a002006-03-17 18:01:27 +0000518 /*
519 * Reject INVITE if we already have an INVITE session in progress.
520 */
521 if (g_inv) {
522
523 pj_str_t reason = pj_str("Another call is in progress");
524
525 pjsip_endpt_respond_stateless( g_endpt, rdata,
526 500, &reason,
527 NULL, NULL);
528 return PJ_TRUE;
529
530 }
531
532 /* Verify that we can handle the request. */
533 status = pjsip_inv_verify_request(rdata, &options, NULL, NULL,
534 g_endpt, NULL);
535 if (status != PJ_SUCCESS) {
536
537 pj_str_t reason = pj_str("Sorry Simple UA can not handle this INVITE");
538
539 pjsip_endpt_respond_stateless( g_endpt, rdata,
540 500, &reason,
541 NULL, NULL);
542 return PJ_TRUE;
543 }
544
545 /*
Benny Prijonoe8df45f2008-03-08 09:26:22 +0000546 * Generate Contact URI
547 */
548 if (pj_gethostip(AF, &hostaddr) != PJ_SUCCESS) {
549 app_perror(THIS_FILE, "Unable to retrieve local host IP", status);
550 return PJ_TRUE;
551 }
552 pj_sockaddr_print(&hostaddr, hostip, sizeof(hostip), 2);
553
554 pj_ansi_sprintf(temp, "<sip:simpleuas@%s:%d>",
555 hostip, SIP_PORT);
556 local_uri = pj_str(temp);
557
558 /*
Benny Prijono6107a002006-03-17 18:01:27 +0000559 * Create UAS dialog.
560 */
561 status = pjsip_dlg_create_uas( pjsip_ua_instance(),
562 rdata,
Benny Prijonoe8df45f2008-03-08 09:26:22 +0000563 &local_uri, /* contact */
Benny Prijono6107a002006-03-17 18:01:27 +0000564 &dlg);
565 if (status != PJ_SUCCESS) {
566 pjsip_endpt_respond_stateless(g_endpt, rdata, 500, NULL,
567 NULL, NULL);
568 return PJ_TRUE;
569 }
570
571 /*
572 * Get media capability from media endpoint:
573 */
574
575 status = pjmedia_endpt_create_sdp( g_med_endpt, rdata->tp_info.pool, 1,
Benny Prijonoe1a5a852008-03-11 21:38:05 +0000576 &g_med_tpinfo.sock_info,
Benny Prijono6107a002006-03-17 18:01:27 +0000577 &local_sdp);
578 PJ_ASSERT_RETURN(status == PJ_SUCCESS, PJ_TRUE);
579
Benny Prijono53fde132006-03-17 19:41:19 +0000580
Benny Prijono6107a002006-03-17 18:01:27 +0000581 /*
Benny Prijono53fde132006-03-17 19:41:19 +0000582 * Create invite session, and pass both the UAS dialog and the SDP
583 * capability to the session.
Benny Prijono6107a002006-03-17 18:01:27 +0000584 */
585 status = pjsip_inv_create_uas( dlg, rdata, local_sdp, 0, &g_inv);
586 PJ_ASSERT_RETURN(status == PJ_SUCCESS, PJ_TRUE);
587
Benny Prijono53fde132006-03-17 19:41:19 +0000588
Benny Prijono6107a002006-03-17 18:01:27 +0000589 /*
590 * Initially send 180 response.
Benny Prijono53fde132006-03-17 19:41:19 +0000591 *
592 * The very first response to an INVITE must be created with
593 * pjsip_inv_initial_answer(). Subsequent responses to the same
594 * transaction MUST use pjsip_inv_answer().
Benny Prijono6107a002006-03-17 18:01:27 +0000595 */
596 status = pjsip_inv_initial_answer(g_inv, rdata,
597 180,
598 NULL, NULL, &tdata);
599 PJ_ASSERT_RETURN(status == PJ_SUCCESS, PJ_TRUE);
600
Benny Prijono53fde132006-03-17 19:41:19 +0000601
602 /* Send the 180 response. */
Benny Prijono6107a002006-03-17 18:01:27 +0000603 status = pjsip_inv_send_msg(g_inv, tdata);
604 PJ_ASSERT_RETURN(status == PJ_SUCCESS, PJ_TRUE);
605
Benny Prijono53fde132006-03-17 19:41:19 +0000606
Benny Prijono6107a002006-03-17 18:01:27 +0000607 /*
Benny Prijono53fde132006-03-17 19:41:19 +0000608 * Now create 200 response.
Benny Prijono6107a002006-03-17 18:01:27 +0000609 */
610 status = pjsip_inv_answer( g_inv,
611 200, NULL, /* st_code and st_text */
612 NULL, /* SDP already specified */
613 &tdata);
614 PJ_ASSERT_RETURN(status == PJ_SUCCESS, PJ_TRUE);
615
616 /*
617 * Send the 200 response.
618 */
619 status = pjsip_inv_send_msg(g_inv, tdata);
620 PJ_ASSERT_RETURN(status == PJ_SUCCESS, PJ_TRUE);
621
Benny Prijono53fde132006-03-17 19:41:19 +0000622
623 /* Done.
624 * When the call is disconnected, it will be reported via the callback.
625 */
626
Benny Prijono6107a002006-03-17 18:01:27 +0000627 return PJ_TRUE;
628}
629
630
Benny Prijono53fde132006-03-17 19:41:19 +0000631
632/*
633 * Callback when SDP negotiation has completed.
634 * We are interested with this callback because we want to start media
635 * as soon as SDP negotiation is completed.
636 */
637static void call_on_media_update( pjsip_inv_session *inv,
638 pj_status_t status)
639{
Benny Prijono8befd9f2006-05-13 22:46:23 +0000640 pjmedia_session_info sess_info;
Benny Prijono53fde132006-03-17 19:41:19 +0000641 const pjmedia_sdp_session *local_sdp;
642 const pjmedia_sdp_session *remote_sdp;
643 pjmedia_port *media_port;
644
645 if (status != PJ_SUCCESS) {
646
647 app_perror(THIS_FILE, "SDP negotiation has failed", status);
648
649 /* Here we should disconnect call if we're not in the middle
650 * of initializing an UAS dialog and if this is not a re-INVITE.
651 */
652 return;
653 }
654
655 /* Get local and remote SDP.
656 * We need both SDPs to create a media session.
657 */
658 status = pjmedia_sdp_neg_get_active_local(inv->neg, &local_sdp);
659
660 status = pjmedia_sdp_neg_get_active_remote(inv->neg, &remote_sdp);
661
662
Benny Prijono8befd9f2006-05-13 22:46:23 +0000663 /* Create session info based on the two SDPs.
664 * We only support one stream per session for now.
665 */
Benny Prijonob04c9e02006-05-17 17:17:39 +0000666 status = pjmedia_session_info_from_sdp(inv->dlg->pool, g_med_endpt,
667 1, &sess_info,
Benny Prijono8befd9f2006-05-13 22:46:23 +0000668 local_sdp, remote_sdp);
669 if (status != PJ_SUCCESS) {
670 app_perror( THIS_FILE, "Unable to create media session", status);
671 return;
672 }
673
674 /* If required, we can also change some settings in the session info,
675 * (such as jitter buffer settings, codec settings, etc) before we
676 * create the session.
677 */
678
Benny Prijono53fde132006-03-17 19:41:19 +0000679 /* Create new media session, passing the two SDPs, and also the
680 * media socket that we created earlier.
681 * The media session is active immediately.
682 */
Benny Prijono8befd9f2006-05-13 22:46:23 +0000683 status = pjmedia_session_create( g_med_endpt, &sess_info,
Benny Prijonob04c9e02006-05-17 17:17:39 +0000684 &g_med_transport, NULL, &g_med_session );
Benny Prijono53fde132006-03-17 19:41:19 +0000685 if (status != PJ_SUCCESS) {
686 app_perror( THIS_FILE, "Unable to create media session", status);
687 return;
688 }
689
690
691 /* Get the media port interface of the first stream in the session.
692 * Media port interface is basicly a struct containing get_frame() and
693 * put_frame() function. With this media port interface, we can attach
694 * the port interface to conference bridge, or directly to a sound
695 * player/recorder device.
696 */
697 pjmedia_session_get_port(g_med_session, 0, &media_port);
698
699
700
701 /* Create a sound Player device and connect the media port to the
702 * sound device.
703 */
704 status = pjmedia_snd_port_create_player(
705 inv->pool, /* pool */
706 -1, /* sound dev id */
Benny Prijono15953012006-04-27 22:37:08 +0000707 media_port->info.clock_rate, /* clock rate */
Benny Prijono53fde132006-03-17 19:41:19 +0000708 media_port->info.channel_count, /* channel count */
709 media_port->info.samples_per_frame, /* samples per frame*/
710 media_port->info.bits_per_sample, /* bits per sample */
711 0, /* options */
712 &g_snd_player);
713 if (status != PJ_SUCCESS) {
714 app_perror( THIS_FILE, "Unable to create sound player", status);
715 PJ_LOG(3,(THIS_FILE, "%d %d %d %d",
Benny Prijono15953012006-04-27 22:37:08 +0000716 media_port->info.clock_rate, /* clock rate */
Benny Prijono53fde132006-03-17 19:41:19 +0000717 media_port->info.channel_count, /* channel count */
718 media_port->info.samples_per_frame, /* samples per frame*/
719 media_port->info.bits_per_sample /* bits per sample */
720 ));
721 return;
722 }
723
724 status = pjmedia_snd_port_connect(g_snd_player, media_port);
725
726
727 /* Create a sound recorder device and connect the media port to the
728 * sound device.
729 */
730 status = pjmedia_snd_port_create_rec(
731 inv->pool, /* pool */
732 -1, /* sound dev id */
Benny Prijono15953012006-04-27 22:37:08 +0000733 media_port->info.clock_rate, /* clock rate */
Benny Prijono53fde132006-03-17 19:41:19 +0000734 media_port->info.channel_count, /* channel count */
735 media_port->info.samples_per_frame, /* samples per frame*/
736 media_port->info.bits_per_sample, /* bits per sample */
737 0, /* options */
738 &g_snd_rec);
739 if (status != PJ_SUCCESS) {
740 app_perror( THIS_FILE, "Unable to create sound recorder", status);
741 return;
742 }
743
744 status = pjmedia_snd_port_connect(g_snd_rec, media_port);
745
746 /* Done with media. */
747}
748
749