blob: d8b551fe1e99a9fead44d69b9814a4be13fbe007 [file] [log] [blame]
Benny Prijono6107a002006-03-17 18:01:27 +00001
2/* Include all PJSIP core headers. */
3#include <pjsip.h>
4
5/* Include all PJMEDIA headers. */
6#include <pjmedia.h>
7
8/* Include all PJMEDIA-CODEC headers. */
9#include <pjmedia-codec.h>
10
11/* Include all PJSIP-UA headers */
12#include <pjsip_ua.h>
13
14/* Include all PJSIP-SIMPLE headers */
15#include <pjsip_simple.h>
16
17/* Include all PJLIB-UTIL headers. */
18#include <pjlib-util.h>
19
20/* Include all PJLIB headers. */
21#include <pjlib.h>
22
23
24#define THIS_FILE "simpleua.c"
25
26
27/*
28 * Static variables.
29 */
30static pj_bool_t g_complete;
31
32/* Global endpoint instance. */
33static pjsip_endpoint *g_endpt;
34
35/* Global caching pool factory. */
36static pj_caching_pool cp;
37
38/* Global media endpoint. */
39static pjmedia_endpt *g_med_endpt;
40static pjmedia_sock_info g_med_skinfo;
41
42/* Call variables. */
43static pjsip_inv_session *g_inv;
44static pjmedia_session *g_med_session;
45static pjmedia_snd_port *g_snd_player;
46static pjmedia_snd_port *g_snd_rec;
47
48
49/*
50 * Prototypes.
51 */
52static void call_on_media_update( pjsip_inv_session *inv,
53 pj_status_t status);
54static void call_on_state_changed( pjsip_inv_session *inv,
55 pjsip_event *e);
56static void call_on_forked(pjsip_inv_session *inv, pjsip_event *e);
57static pj_bool_t on_rx_request( pjsip_rx_data *rdata );
58
59
60/* Module to receive incoming requests (e.g. INVITE). */
61static pjsip_module mod_simpleua =
62{
63 NULL, NULL, /* prev, next. */
64 { "mod-simpleua", 12 }, /* Name. */
65 -1, /* Id */
66 PJSIP_MOD_PRIORITY_APPLICATION, /* Priority */
67 NULL, /* load() */
68 NULL, /* start() */
69 NULL, /* stop() */
70 NULL, /* unload() */
71 &on_rx_request, /* on_rx_request() */
72 NULL, /* on_rx_response() */
73 NULL, /* on_tx_request. */
74 NULL, /* on_tx_response() */
75 NULL, /* on_tsx_state() */
76};
77
78
79/*
80 * Show error.
81 */
82static int app_perror( const char *sender, const char *title,
83 pj_status_t status)
84{
85 char errmsg[PJ_ERR_MSG_SIZE];
86
87 pj_strerror(status, errmsg, sizeof(errmsg));
88
89 PJ_LOG(1,(sender, "%s: %s [code=%d]", title, errmsg, status));
90 return 1;
91}
92
93
94/*
95 * main()
96 */
97int main(int argc, char *argv[])
98{
99 pj_status_t status;
100
101 /* Init PJLIB */
102 status = pj_init();
103 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
104
105 /* Init PJLIB-UTIL: */
106 status = pjlib_util_init();
107 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
108
109
110 /* Init memory pool: */
111
112 /* Init caching pool. */
113 pj_caching_pool_init(&cp, &pj_pool_factory_default_policy, 0);
114
115 /* Create global endpoint: */
116 {
117 const pj_str_t *hostname;
118 const char *endpt_name;
119
120 /* Endpoint MUST be assigned a globally unique name.
121 * The name will be used as the hostname in Warning header.
122 */
123
124 /* For this implementation, we'll use hostname for simplicity */
125 hostname = pj_gethostname();
126 endpt_name = hostname->ptr;
127
128 /* Create the endpoint: */
129
130 status = pjsip_endpt_create(&cp.factory, endpt_name,
131 &g_endpt);
132 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
133 }
134
135 /*
136 * Add UDP transport.
137 */
138 {
139 pj_sockaddr_in addr;
140
141 addr.sin_family = PJ_AF_INET;
142 addr.sin_addr.s_addr = 0;
143 addr.sin_port = pj_htons(5060);
144
145 status = pjsip_udp_transport_start( g_endpt, &addr, NULL, 1, NULL);
146 if (status != PJ_SUCCESS) {
147 app_perror(THIS_FILE, "Unable to start UDP transport", status);
148 return 1;
149 }
150 }
151
152
153 /*
154 * Init transaction layer.
155 */
156 status = pjsip_tsx_layer_init_module(g_endpt);
157 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
158
159 /*
160 * Initialize UA layer module:
161 */
162 status = pjsip_ua_init_module( g_endpt, NULL );
163 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
164
165 /*
166 * Init invite session module.
167 */
168 {
169 pjsip_inv_callback inv_cb;
170
171 /* Init the callback for INVITE session: */
172 pj_memset(&inv_cb, 0, sizeof(inv_cb));
173 inv_cb.on_state_changed = &call_on_state_changed;
174 inv_cb.on_new_session = &call_on_forked;
175 inv_cb.on_media_update = &call_on_media_update;
176
177 /* Initialize invite session module: */
178 status = pjsip_inv_usage_init(g_endpt, &inv_cb);
179 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
180 }
181
182
183 /*
184 * Register module to receive incoming requests.
185 */
186 status = pjsip_endpt_register_module( g_endpt, &mod_simpleua);
187 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
188
189
190 /*
191 * Init media endpoint:
192 */
193 status = pjmedia_endpt_create(&cp.factory, &g_med_endpt);
194 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
195
196 /*
197 * Add PCMA/PCMU codec to the media endpoint.
198 */
199 status = pjmedia_codec_g711_init(g_med_endpt);
200 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
201
202 /*
203 * Initialize RTP socket info for the media.
204 */
205 status = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &g_med_skinfo.rtp_sock);
206 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
207
208 pj_sockaddr_in_init( &g_med_skinfo.rtp_addr_name,
209 pjsip_endpt_name(g_endpt), 4000);
210
211 status = pj_sock_bind(g_med_skinfo.rtp_sock, &g_med_skinfo.rtp_addr_name,
212 sizeof(pj_sockaddr_in));
213 if (status != PJ_SUCCESS) {
214 app_perror( THIS_FILE,
215 "Unable to bind RTP socket",
216 status);
217 return 1;
218 }
219
220
221 /* For simplicity, ignore RTCP socket. */
222 g_med_skinfo.rtcp_sock = PJ_INVALID_SOCKET;
223 g_med_skinfo.rtcp_addr_name = g_med_skinfo.rtp_addr_name;
224
225
226 /*
227 * If URL is specified, then make call immediately.
228 */
229 if (argc > 1) {
230 char temp[80];
231 pj_str_t dst_uri = pj_str(argv[1]);
232 pj_str_t local_uri;
233 pjsip_dialog *dlg;
234 pjmedia_sdp_session *local_sdp;
235 pjsip_tx_data *tdata;
236
237 pj_ansi_sprintf(temp, "sip:simpleuac@%s", pjsip_endpt_name(g_endpt)->ptr);
238 local_uri = pj_str(temp);
239
240 /* Create UAC dialog */
241 status = pjsip_dlg_create_uac( pjsip_ua_instance(),
242 &local_uri, /* local URI */
243 NULL, /* local Contact */
244 &dst_uri, /* remote URI */
245 &dst_uri, /* remote target */
246 &dlg); /* dialog */
247 if (status != PJ_SUCCESS) {
248 app_perror(THIS_FILE, "Unable to create UAC dialog", status);
249 return 1;
250 }
251
252 /* Get media capability from media endpoint: */
253 status = pjmedia_endpt_create_sdp( g_med_endpt, dlg->pool, 1,
254 &g_med_skinfo, &local_sdp);
255 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
256
257
258 /* Create the INVITE session: */
259 status = pjsip_inv_create_uac( dlg, local_sdp, 0, &g_inv);
260 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
261
262
263 /* Create initial INVITE request: */
264 status = pjsip_inv_invite(g_inv, &tdata);
265 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
266
267 /* Send initial INVITE request: */
268 status = pjsip_inv_send_msg(g_inv, tdata);
269 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
270
271 } else {
272 PJ_LOG(3,(THIS_FILE, "Ready to accept incoming calls..."));
273 }
274
275
276 /* Loop until one call is completed */
277 for (;!g_complete;) {
278 pj_time_val timeout = {0, 10};
279 pjsip_endpt_handle_events(g_endpt, &timeout);
280 }
281
282 return 0;
283}
284
285
286
287/*
288 * Callback when INVITE session state has changed.
289 */
290static void call_on_state_changed( pjsip_inv_session *inv,
291 pjsip_event *e)
292{
293 if (inv->state == PJSIP_INV_STATE_DISCONNECTED) {
294
295 PJ_LOG(3,(THIS_FILE, "Call DISCONNECTED [reason=%d (%s)]",
296 inv->cause,
297 pjsip_get_status_text(inv->cause)->ptr));
298
299 PJ_LOG(3,(THIS_FILE, "One call completed, application quitting..."));
300 g_complete = 1;
301
302 } else {
303
304 PJ_LOG(3,(THIS_FILE, "Call state changed to %s",
305 pjsip_inv_state_name(inv->state)));
306
307 }
308}
309
310/*
311 * Callback when media negotiation has completed.
312 */
313static void call_on_media_update( pjsip_inv_session *inv,
314 pj_status_t status)
315{
316 const pjmedia_sdp_session *local_sdp;
317 const pjmedia_sdp_session *remote_sdp;
318 pjmedia_port *media_port;
319
320 if (status != PJ_SUCCESS) {
321
322 app_perror(THIS_FILE, "SDP negotiation has failed", status);
323
324 /* Here we should disconnect call if we're not in the middle
325 * of initializing an UAS dialog and if this is not a re-INVITE.
326 */
327 return;
328 }
329
330 /* Get local and remote SDP */
331
332 status = pjmedia_sdp_neg_get_active_local(inv->neg, &local_sdp);
333
334 status = pjmedia_sdp_neg_get_active_remote(inv->neg, &remote_sdp);
335
336 /* Create new media session.
337 * The media session is active immediately.
338 */
339 status = pjmedia_session_create( g_med_endpt, 1,
340 &g_med_skinfo,
341 local_sdp, remote_sdp,
342 NULL, &g_med_session );
343 if (status != PJ_SUCCESS) {
344 app_perror( THIS_FILE, "Unable to create media session", status);
345 return;
346 }
347
348 /* Get the port interface of the first stream in the session. */
349 pjmedia_session_get_port(g_med_session, 0, &media_port);
350
351 /* Create a sound Player device and connect the media port to the
352 * sound device.
353 */
354 status = pjmedia_snd_port_create_player(
355 inv->pool, /* pool */
356 -1, /* sound dev id */
357 media_port->info.sample_rate, /* clock rate */
358 media_port->info.channel_count, /* channel count */
359 media_port->info.samples_per_frame, /* samples per frame*/
360 media_port->info.bits_per_sample, /* bits per sample */
361 0, /* options */
362 &g_snd_player);
363 if (status != PJ_SUCCESS) {
364 app_perror( THIS_FILE, "Unable to create sound player", status);
365 PJ_LOG(3,(THIS_FILE, "%d %d %d %d",
366 media_port->info.sample_rate, /* clock rate */
367 media_port->info.channel_count, /* channel count */
368 media_port->info.samples_per_frame, /* samples per frame*/
369 media_port->info.bits_per_sample /* bits per sample */
370 ));
371 return;
372 }
373
374 status = pjmedia_snd_port_connect(g_snd_player, media_port);
375
376
377 /* Create a sound recorder device and connect the media port to the
378 * sound device.
379 */
380 status = pjmedia_snd_port_create_rec(
381 inv->pool, /* pool */
382 -1, /* sound dev id */
383 media_port->info.sample_rate, /* clock rate */
384 media_port->info.channel_count, /* channel count */
385 media_port->info.samples_per_frame, /* samples per frame*/
386 media_port->info.bits_per_sample, /* bits per sample */
387 0, /* options */
388 &g_snd_rec);
389 if (status != PJ_SUCCESS) {
390 app_perror( THIS_FILE, "Unable to create sound recorder", status);
391 return;
392 }
393
394 status = pjmedia_snd_port_connect(g_snd_rec, media_port);
395
396 /* Done with media. */
397}
398
399
400/*
401 * This callback is called when dialog has forked
402 */
403static void call_on_forked(pjsip_inv_session *inv, pjsip_event *e)
404{
405}
406
407/*
408 * Callback when incoming requests outside any transactions and any
409 * dialogs are received
410 */
411static pj_bool_t on_rx_request( pjsip_rx_data *rdata )
412{
413 pjsip_dialog *dlg;
414 pjmedia_sdp_session *local_sdp;
415 pjsip_tx_data *tdata;
416 unsigned options = 0;
417 pj_status_t status;
418
419 /*
420 * Respond (statelessly) any non-INVITE requests with 500
421 */
422 if (rdata->msg_info.msg->line.req.method.id != PJSIP_INVITE_METHOD) {
423
424 pj_str_t reason = pj_str("Simple UA unable to handle this request");
425
426 pjsip_endpt_respond_stateless( g_endpt, rdata,
427 500, &reason,
428 NULL, NULL);
429 return PJ_TRUE;
430 }
431
432 /*
433 * Reject INVITE if we already have an INVITE session in progress.
434 */
435 if (g_inv) {
436
437 pj_str_t reason = pj_str("Another call is in progress");
438
439 pjsip_endpt_respond_stateless( g_endpt, rdata,
440 500, &reason,
441 NULL, NULL);
442 return PJ_TRUE;
443
444 }
445
446 /* Verify that we can handle the request. */
447 status = pjsip_inv_verify_request(rdata, &options, NULL, NULL,
448 g_endpt, NULL);
449 if (status != PJ_SUCCESS) {
450
451 pj_str_t reason = pj_str("Sorry Simple UA can not handle this INVITE");
452
453 pjsip_endpt_respond_stateless( g_endpt, rdata,
454 500, &reason,
455 NULL, NULL);
456 return PJ_TRUE;
457 }
458
459 /*
460 * Create UAS dialog.
461 */
462 status = pjsip_dlg_create_uas( pjsip_ua_instance(),
463 rdata,
464 NULL, /* contact */
465 &dlg);
466 if (status != PJ_SUCCESS) {
467 pjsip_endpt_respond_stateless(g_endpt, rdata, 500, NULL,
468 NULL, NULL);
469 return PJ_TRUE;
470 }
471
472 /*
473 * Get media capability from media endpoint:
474 */
475
476 status = pjmedia_endpt_create_sdp( g_med_endpt, rdata->tp_info.pool, 1,
477 &g_med_skinfo,
478 &local_sdp);
479 PJ_ASSERT_RETURN(status == PJ_SUCCESS, PJ_TRUE);
480
481 /*
482 * Create invite session:
483 */
484 status = pjsip_inv_create_uas( dlg, rdata, local_sdp, 0, &g_inv);
485 PJ_ASSERT_RETURN(status == PJ_SUCCESS, PJ_TRUE);
486
487 /*
488 * Initially send 180 response.
489 */
490 status = pjsip_inv_initial_answer(g_inv, rdata,
491 180,
492 NULL, NULL, &tdata);
493 PJ_ASSERT_RETURN(status == PJ_SUCCESS, PJ_TRUE);
494
495 /*
496 * Send the 180 response.
497 */
498 status = pjsip_inv_send_msg(g_inv, tdata);
499 PJ_ASSERT_RETURN(status == PJ_SUCCESS, PJ_TRUE);
500
501 /*
502 * Now send 200 response.
503 */
504 status = pjsip_inv_answer( g_inv,
505 200, NULL, /* st_code and st_text */
506 NULL, /* SDP already specified */
507 &tdata);
508 PJ_ASSERT_RETURN(status == PJ_SUCCESS, PJ_TRUE);
509
510 /*
511 * Send the 200 response.
512 */
513 status = pjsip_inv_send_msg(g_inv, tdata);
514 PJ_ASSERT_RETURN(status == PJ_SUCCESS, PJ_TRUE);
515
516 return PJ_TRUE;
517}
518
519