blob: 313deee9b39a7e26b6e721059a01fa09a2be8db5 [file] [log] [blame]
Benny Prijono53fde132006-03-17 19:41:19 +00001/* $Id$ */
2/*
Benny Prijonoa771a512007-02-19 01:13:53 +00003 * Copyright (C) 2003-2007 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. */
84static pjmedia_sock_info g_med_skinfo; /* 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 Prijonod8179652008-01-23 20:39:07 +0000304 pjmedia_transport_get_info(g_med_transport, &g_med_skinfo);
Benny Prijonoc1e263f2006-08-09 13:53:59 +0000305
306
Benny Prijono6107a002006-03-17 18:01:27 +0000307 /*
308 * If URL is specified, then make call immediately.
309 */
310 if (argc > 1) {
Benny Prijonoe8df45f2008-03-08 09:26:22 +0000311 pj_sockaddr hostaddr;
312 char hostip[PJ_INET6_ADDRSTRLEN+2];
Benny Prijono6107a002006-03-17 18:01:27 +0000313 char temp[80];
314 pj_str_t dst_uri = pj_str(argv[1]);
315 pj_str_t local_uri;
316 pjsip_dialog *dlg;
317 pjmedia_sdp_session *local_sdp;
318 pjsip_tx_data *tdata;
319
Benny Prijonoe8df45f2008-03-08 09:26:22 +0000320 if (pj_gethostip(AF, &hostaddr) != PJ_SUCCESS) {
321 app_perror(THIS_FILE, "Unable to retrieve local host IP", status);
322 return 1;
323 }
324 pj_sockaddr_print(&hostaddr, hostip, sizeof(hostip), 2);
325
326 pj_ansi_sprintf(temp, "<sip:simpleuac@%s:%d>",
327 hostip, SIP_PORT);
Benny Prijono6107a002006-03-17 18:01:27 +0000328 local_uri = pj_str(temp);
329
330 /* Create UAC dialog */
331 status = pjsip_dlg_create_uac( pjsip_ua_instance(),
332 &local_uri, /* local URI */
Benny Prijonoe8df45f2008-03-08 09:26:22 +0000333 &local_uri, /* local Contact */
Benny Prijono6107a002006-03-17 18:01:27 +0000334 &dst_uri, /* remote URI */
335 &dst_uri, /* remote target */
336 &dlg); /* dialog */
337 if (status != PJ_SUCCESS) {
338 app_perror(THIS_FILE, "Unable to create UAC dialog", status);
339 return 1;
340 }
341
Benny Prijono0a968362006-03-18 12:29:01 +0000342 /* If we expect the outgoing INVITE to be challenged, then we should
343 * put the credentials in the dialog here, with something like this:
344 *
345 {
346 pjsip_cred_info cred[1];
347
348 cred[0].realm = pj_str("sip.server.realm");
349 cred[0].username = pj_str("theuser");
350 cred[0].data_type = PJSIP_CRED_DATA_PLAIN_PASSWD;
351 cred[0].data = pj_str("thepassword");
352
353 pjsip_auth_clt_set_credentials( &dlg->auth_sess, 1, cred);
354 }
355 *
356 */
357
358
Benny Prijonodf912082007-10-30 16:41:45 +0000359 /* Get the SDP body to be put in the outgoing INVITE, by asking
360 * media endpoint to create one for us. The SDP will contain all
361 * codecs that have been registered to it (in this case, only
362 * PCMA and PCMU), plus telephony event.
363 */
364 status = pjmedia_endpt_create_sdp( g_med_endpt, /* the media endpt */
365 dlg->pool, /* pool. */
366 1, /* # of streams */
367 &g_med_skinfo, /* RTP sock info */
368 &local_sdp); /* the SDP result */
369 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
370
371
372
373 /* Create the INVITE session, and pass the SDP returned earlier
374 * as the session's initial capability.
375 */
376 status = pjsip_inv_create_uac( dlg, local_sdp, 0, &g_inv);
377 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
378
Benny Prijono0a968362006-03-18 12:29:01 +0000379 /* If we want the initial INVITE to travel to specific SIP proxies,
380 * then we should put the initial dialog's route set here. The final
381 * route set will be updated once a dialog has been established.
382 * To set the dialog's initial route set, we do it with something
383 * like this:
384 *
385 {
386 pjsip_route_hdr route_set;
387 pjsip_route_hdr *route;
388 const pj_str_t hname = { "Route", 5 };
389 char *uri = "sip:proxy.server;lr";
390
391 pj_list_init(&route_set);
392
393 route = pjsip_parse_hdr( dlg->pool, &hname,
394 uri, strlen(uri),
395 NULL);
396 PJ_ASSERT_RETURN(route != NULL, 1);
397 pj_list_push_back(&route_set, route);
398
399 pjsip_dlg_set_route_set(dlg, &route_set);
400 }
401 *
402 * Note that Route URI SHOULD have an ";lr" parameter!
403 */
404
Benny Prijono53fde132006-03-17 19:41:19 +0000405 /* Create initial INVITE request.
406 * This INVITE request will contain a perfectly good request and
407 * an SDP body as well.
408 */
Benny Prijono6107a002006-03-17 18:01:27 +0000409 status = pjsip_inv_invite(g_inv, &tdata);
410 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
411
Benny Prijono53fde132006-03-17 19:41:19 +0000412
413
414 /* Send initial INVITE request.
415 * From now on, the invite session's state will be reported to us
416 * via the invite session callbacks.
417 */
Benny Prijono6107a002006-03-17 18:01:27 +0000418 status = pjsip_inv_send_msg(g_inv, tdata);
419 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
420
Benny Prijono53fde132006-03-17 19:41:19 +0000421
Benny Prijono6107a002006-03-17 18:01:27 +0000422 } else {
Benny Prijono53fde132006-03-17 19:41:19 +0000423
424 /* No URL to make call to */
425
Benny Prijono6107a002006-03-17 18:01:27 +0000426 PJ_LOG(3,(THIS_FILE, "Ready to accept incoming calls..."));
427 }
428
429
430 /* Loop until one call is completed */
431 for (;!g_complete;) {
432 pj_time_val timeout = {0, 10};
433 pjsip_endpt_handle_events(g_endpt, &timeout);
434 }
435
Benny Prijonobc797312006-03-24 20:44:27 +0000436 /* On exit, dump current memory usage: */
437 dump_pool_usage(THIS_FILE, &cp);
Benny Prijono0a968362006-03-18 12:29:01 +0000438
Benny Prijono6107a002006-03-17 18:01:27 +0000439 return 0;
440}
441
442
443
444/*
445 * Callback when INVITE session state has changed.
Benny Prijono53fde132006-03-17 19:41:19 +0000446 * This callback is registered when the invite session module is initialized.
447 * We mostly want to know when the invite session has been disconnected,
448 * so that we can quit the application.
Benny Prijono6107a002006-03-17 18:01:27 +0000449 */
450static void call_on_state_changed( pjsip_inv_session *inv,
451 pjsip_event *e)
452{
Benny Prijono15953012006-04-27 22:37:08 +0000453 PJ_UNUSED_ARG(e);
454
Benny Prijono6107a002006-03-17 18:01:27 +0000455 if (inv->state == PJSIP_INV_STATE_DISCONNECTED) {
456
457 PJ_LOG(3,(THIS_FILE, "Call DISCONNECTED [reason=%d (%s)]",
458 inv->cause,
459 pjsip_get_status_text(inv->cause)->ptr));
460
461 PJ_LOG(3,(THIS_FILE, "One call completed, application quitting..."));
462 g_complete = 1;
463
464 } else {
465
466 PJ_LOG(3,(THIS_FILE, "Call state changed to %s",
467 pjsip_inv_state_name(inv->state)));
468
469 }
470}
471
Benny Prijono6107a002006-03-17 18:01:27 +0000472
Benny Prijono53fde132006-03-17 19:41:19 +0000473/* This callback is called when dialog has forked. */
Benny Prijono6107a002006-03-17 18:01:27 +0000474static void call_on_forked(pjsip_inv_session *inv, pjsip_event *e)
475{
Benny Prijono53fde132006-03-17 19:41:19 +0000476 /* To be done... */
Benny Prijono15953012006-04-27 22:37:08 +0000477 PJ_UNUSED_ARG(inv);
478 PJ_UNUSED_ARG(e);
Benny Prijono6107a002006-03-17 18:01:27 +0000479}
480
Benny Prijono53fde132006-03-17 19:41:19 +0000481
Benny Prijono6107a002006-03-17 18:01:27 +0000482/*
483 * Callback when incoming requests outside any transactions and any
Benny Prijono53fde132006-03-17 19:41:19 +0000484 * dialogs are received. We're only interested to hande incoming INVITE
485 * request, and we'll reject any other requests with 500 response.
Benny Prijono6107a002006-03-17 18:01:27 +0000486 */
487static pj_bool_t on_rx_request( pjsip_rx_data *rdata )
488{
Benny Prijonoe8df45f2008-03-08 09:26:22 +0000489 pj_sockaddr hostaddr;
490 char temp[80], hostip[PJ_INET6_ADDRSTRLEN];
491 pj_str_t local_uri;
Benny Prijono6107a002006-03-17 18:01:27 +0000492 pjsip_dialog *dlg;
493 pjmedia_sdp_session *local_sdp;
494 pjsip_tx_data *tdata;
495 unsigned options = 0;
496 pj_status_t status;
497
Benny Prijono53fde132006-03-17 19:41:19 +0000498
Benny Prijono6107a002006-03-17 18:01:27 +0000499 /*
500 * Respond (statelessly) any non-INVITE requests with 500
501 */
502 if (rdata->msg_info.msg->line.req.method.id != PJSIP_INVITE_METHOD) {
503
Benny Prijono53932c02007-02-13 18:51:44 +0000504 if (rdata->msg_info.msg->line.req.method.id != PJSIP_ACK_METHOD) {
505 pj_str_t reason = pj_str("Simple UA unable to handle "
506 "this request");
Benny Prijono6107a002006-03-17 18:01:27 +0000507
Benny Prijono53932c02007-02-13 18:51:44 +0000508 pjsip_endpt_respond_stateless( g_endpt, rdata,
509 500, &reason,
510 NULL, NULL);
511 }
Benny Prijono6107a002006-03-17 18:01:27 +0000512 return PJ_TRUE;
513 }
514
Benny Prijono53fde132006-03-17 19:41:19 +0000515
Benny Prijono6107a002006-03-17 18:01:27 +0000516 /*
517 * Reject INVITE if we already have an INVITE session in progress.
518 */
519 if (g_inv) {
520
521 pj_str_t reason = pj_str("Another call is in progress");
522
523 pjsip_endpt_respond_stateless( g_endpt, rdata,
524 500, &reason,
525 NULL, NULL);
526 return PJ_TRUE;
527
528 }
529
530 /* Verify that we can handle the request. */
531 status = pjsip_inv_verify_request(rdata, &options, NULL, NULL,
532 g_endpt, NULL);
533 if (status != PJ_SUCCESS) {
534
535 pj_str_t reason = pj_str("Sorry Simple UA can not handle this INVITE");
536
537 pjsip_endpt_respond_stateless( g_endpt, rdata,
538 500, &reason,
539 NULL, NULL);
540 return PJ_TRUE;
541 }
542
543 /*
Benny Prijonoe8df45f2008-03-08 09:26:22 +0000544 * Generate Contact URI
545 */
546 if (pj_gethostip(AF, &hostaddr) != PJ_SUCCESS) {
547 app_perror(THIS_FILE, "Unable to retrieve local host IP", status);
548 return PJ_TRUE;
549 }
550 pj_sockaddr_print(&hostaddr, hostip, sizeof(hostip), 2);
551
552 pj_ansi_sprintf(temp, "<sip:simpleuas@%s:%d>",
553 hostip, SIP_PORT);
554 local_uri = pj_str(temp);
555
556 /*
Benny Prijono6107a002006-03-17 18:01:27 +0000557 * Create UAS dialog.
558 */
559 status = pjsip_dlg_create_uas( pjsip_ua_instance(),
560 rdata,
Benny Prijonoe8df45f2008-03-08 09:26:22 +0000561 &local_uri, /* contact */
Benny Prijono6107a002006-03-17 18:01:27 +0000562 &dlg);
563 if (status != PJ_SUCCESS) {
564 pjsip_endpt_respond_stateless(g_endpt, rdata, 500, NULL,
565 NULL, NULL);
566 return PJ_TRUE;
567 }
568
569 /*
570 * Get media capability from media endpoint:
571 */
572
573 status = pjmedia_endpt_create_sdp( g_med_endpt, rdata->tp_info.pool, 1,
574 &g_med_skinfo,
575 &local_sdp);
576 PJ_ASSERT_RETURN(status == PJ_SUCCESS, PJ_TRUE);
577
Benny Prijono53fde132006-03-17 19:41:19 +0000578
Benny Prijono6107a002006-03-17 18:01:27 +0000579 /*
Benny Prijono53fde132006-03-17 19:41:19 +0000580 * Create invite session, and pass both the UAS dialog and the SDP
581 * capability to the session.
Benny Prijono6107a002006-03-17 18:01:27 +0000582 */
583 status = pjsip_inv_create_uas( dlg, rdata, local_sdp, 0, &g_inv);
584 PJ_ASSERT_RETURN(status == PJ_SUCCESS, PJ_TRUE);
585
Benny Prijono53fde132006-03-17 19:41:19 +0000586
Benny Prijono6107a002006-03-17 18:01:27 +0000587 /*
588 * Initially send 180 response.
Benny Prijono53fde132006-03-17 19:41:19 +0000589 *
590 * The very first response to an INVITE must be created with
591 * pjsip_inv_initial_answer(). Subsequent responses to the same
592 * transaction MUST use pjsip_inv_answer().
Benny Prijono6107a002006-03-17 18:01:27 +0000593 */
594 status = pjsip_inv_initial_answer(g_inv, rdata,
595 180,
596 NULL, NULL, &tdata);
597 PJ_ASSERT_RETURN(status == PJ_SUCCESS, PJ_TRUE);
598
Benny Prijono53fde132006-03-17 19:41:19 +0000599
600 /* Send the 180 response. */
Benny Prijono6107a002006-03-17 18:01:27 +0000601 status = pjsip_inv_send_msg(g_inv, tdata);
602 PJ_ASSERT_RETURN(status == PJ_SUCCESS, PJ_TRUE);
603
Benny Prijono53fde132006-03-17 19:41:19 +0000604
Benny Prijono6107a002006-03-17 18:01:27 +0000605 /*
Benny Prijono53fde132006-03-17 19:41:19 +0000606 * Now create 200 response.
Benny Prijono6107a002006-03-17 18:01:27 +0000607 */
608 status = pjsip_inv_answer( g_inv,
609 200, NULL, /* st_code and st_text */
610 NULL, /* SDP already specified */
611 &tdata);
612 PJ_ASSERT_RETURN(status == PJ_SUCCESS, PJ_TRUE);
613
614 /*
615 * Send the 200 response.
616 */
617 status = pjsip_inv_send_msg(g_inv, tdata);
618 PJ_ASSERT_RETURN(status == PJ_SUCCESS, PJ_TRUE);
619
Benny Prijono53fde132006-03-17 19:41:19 +0000620
621 /* Done.
622 * When the call is disconnected, it will be reported via the callback.
623 */
624
Benny Prijono6107a002006-03-17 18:01:27 +0000625 return PJ_TRUE;
626}
627
628
Benny Prijono53fde132006-03-17 19:41:19 +0000629
630/*
631 * Callback when SDP negotiation has completed.
632 * We are interested with this callback because we want to start media
633 * as soon as SDP negotiation is completed.
634 */
635static void call_on_media_update( pjsip_inv_session *inv,
636 pj_status_t status)
637{
Benny Prijono8befd9f2006-05-13 22:46:23 +0000638 pjmedia_session_info sess_info;
Benny Prijono53fde132006-03-17 19:41:19 +0000639 const pjmedia_sdp_session *local_sdp;
640 const pjmedia_sdp_session *remote_sdp;
641 pjmedia_port *media_port;
642
643 if (status != PJ_SUCCESS) {
644
645 app_perror(THIS_FILE, "SDP negotiation has failed", status);
646
647 /* Here we should disconnect call if we're not in the middle
648 * of initializing an UAS dialog and if this is not a re-INVITE.
649 */
650 return;
651 }
652
653 /* Get local and remote SDP.
654 * We need both SDPs to create a media session.
655 */
656 status = pjmedia_sdp_neg_get_active_local(inv->neg, &local_sdp);
657
658 status = pjmedia_sdp_neg_get_active_remote(inv->neg, &remote_sdp);
659
660
Benny Prijono8befd9f2006-05-13 22:46:23 +0000661 /* Create session info based on the two SDPs.
662 * We only support one stream per session for now.
663 */
Benny Prijonob04c9e02006-05-17 17:17:39 +0000664 status = pjmedia_session_info_from_sdp(inv->dlg->pool, g_med_endpt,
665 1, &sess_info,
Benny Prijono8befd9f2006-05-13 22:46:23 +0000666 local_sdp, remote_sdp);
667 if (status != PJ_SUCCESS) {
668 app_perror( THIS_FILE, "Unable to create media session", status);
669 return;
670 }
671
672 /* If required, we can also change some settings in the session info,
673 * (such as jitter buffer settings, codec settings, etc) before we
674 * create the session.
675 */
676
Benny Prijono53fde132006-03-17 19:41:19 +0000677 /* Create new media session, passing the two SDPs, and also the
678 * media socket that we created earlier.
679 * The media session is active immediately.
680 */
Benny Prijono8befd9f2006-05-13 22:46:23 +0000681 status = pjmedia_session_create( g_med_endpt, &sess_info,
Benny Prijonob04c9e02006-05-17 17:17:39 +0000682 &g_med_transport, NULL, &g_med_session );
Benny Prijono53fde132006-03-17 19:41:19 +0000683 if (status != PJ_SUCCESS) {
684 app_perror( THIS_FILE, "Unable to create media session", status);
685 return;
686 }
687
688
689 /* Get the media port interface of the first stream in the session.
690 * Media port interface is basicly a struct containing get_frame() and
691 * put_frame() function. With this media port interface, we can attach
692 * the port interface to conference bridge, or directly to a sound
693 * player/recorder device.
694 */
695 pjmedia_session_get_port(g_med_session, 0, &media_port);
696
697
698
699 /* Create a sound Player device and connect the media port to the
700 * sound device.
701 */
702 status = pjmedia_snd_port_create_player(
703 inv->pool, /* pool */
704 -1, /* sound dev id */
Benny Prijono15953012006-04-27 22:37:08 +0000705 media_port->info.clock_rate, /* clock rate */
Benny Prijono53fde132006-03-17 19:41:19 +0000706 media_port->info.channel_count, /* channel count */
707 media_port->info.samples_per_frame, /* samples per frame*/
708 media_port->info.bits_per_sample, /* bits per sample */
709 0, /* options */
710 &g_snd_player);
711 if (status != PJ_SUCCESS) {
712 app_perror( THIS_FILE, "Unable to create sound player", status);
713 PJ_LOG(3,(THIS_FILE, "%d %d %d %d",
Benny Prijono15953012006-04-27 22:37:08 +0000714 media_port->info.clock_rate, /* clock rate */
Benny Prijono53fde132006-03-17 19:41:19 +0000715 media_port->info.channel_count, /* channel count */
716 media_port->info.samples_per_frame, /* samples per frame*/
717 media_port->info.bits_per_sample /* bits per sample */
718 ));
719 return;
720 }
721
722 status = pjmedia_snd_port_connect(g_snd_player, media_port);
723
724
725 /* Create a sound recorder device and connect the media port to the
726 * sound device.
727 */
728 status = pjmedia_snd_port_create_rec(
729 inv->pool, /* pool */
730 -1, /* sound dev id */
Benny Prijono15953012006-04-27 22:37:08 +0000731 media_port->info.clock_rate, /* clock rate */
Benny Prijono53fde132006-03-17 19:41:19 +0000732 media_port->info.channel_count, /* channel count */
733 media_port->info.samples_per_frame, /* samples per frame*/
734 media_port->info.bits_per_sample, /* bits per sample */
735 0, /* options */
736 &g_snd_rec);
737 if (status != PJ_SUCCESS) {
738 app_perror( THIS_FILE, "Unable to create sound recorder", status);
739 return;
740 }
741
742 status = pjmedia_snd_port_connect(g_snd_rec, media_port);
743
744 /* Done with media. */
745}
746
747