blob: b01b968de691ea3b50f48b33f06d72fb437a9dc5 [file] [log] [blame]
Benny Prijono84126ab2006-02-09 09:30:09 +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 */
Benny Prijono4f9f64e2006-02-27 00:00:30 +000019#include <pjsua-lib/pjsua.h>
Benny Prijono84126ab2006-02-09 09:30:09 +000020#include <pj/log.h>
21
22
23/*
Benny Prijonob0808372006-03-02 21:18:58 +000024 * pjsua_call.c
Benny Prijono84126ab2006-02-09 09:30:09 +000025 *
Benny Prijonob0808372006-03-02 21:18:58 +000026 * Call (INVITE) related stuffs.
Benny Prijono84126ab2006-02-09 09:30:09 +000027 */
28
29#define THIS_FILE "pjsua_inv.c"
30
31
Benny Prijono105217f2006-03-06 16:25:59 +000032#define REFRESH_CALL_TIMER 0x63
33#define HANGUP_CALL_TIMER 0x64
34
35/* Proto */
36static void schedule_call_timer( pjsua_call *call, pj_timer_entry *e,
37 int timer_type, int duration );
38
39/*
40 * Timer callback when UAS needs to send re-INVITE to see if remote
41 * is still there.
42 */
43static void call_on_timer(pj_timer_heap_t *ht, pj_timer_entry *e)
44{
45 pjsua_call *call = e->user_data;
46
47 PJ_UNUSED_ARG(ht);
48
49 if (e->id == REFRESH_CALL_TIMER) {
50
51 /* If call is still not connected, hangup. */
52 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
53 PJ_LOG(3,(THIS_FILE, "Refresh call timer is called when "
54 "invite is still not confirmed. Call %d will "
55 "disconnect.", call->index));
56 pjsua_call_hangup(call->index);
57 } else {
58 PJ_LOG(3,(THIS_FILE, "Refreshing call %d", call->index));
59 schedule_call_timer(call,e,REFRESH_CALL_TIMER,pjsua.uas_refresh);
60 pjsua_call_reinvite(call->index);
61 }
62
63 } else if (e->id == HANGUP_CALL_TIMER) {
64 PJ_LOG(3,(THIS_FILE, "Call %d duration exceeded, disconnecting call",
65 call->index));
66 pjsua_call_hangup(call->index);
67
68 }
69}
70
71/*
72 * Schedule call timer.
73 */
74static void schedule_call_timer( pjsua_call *call, pj_timer_entry *e,
75 int timer_type, int duration )
76{
77 pj_time_val timeout;
78
79 if (duration == 0) {
80 /* Cancel timer. */
81 if (e->id != 0) {
82 pjsip_endpt_cancel_timer(pjsua.endpt, e);
83 e->id = 0;
84 }
85
86 } else {
87 /* Schedule timer. */
88 timeout.sec = duration;
89 timeout.msec = 0;
90
91 e->cb = &call_on_timer;
92 e->id = timer_type;
93 e->user_data = call;
94
95 pjsip_endpt_schedule_timer( pjsua.endpt, e, &timeout);
96 }
97}
98
99
Benny Prijono275fd682006-03-22 11:59:11 +0000100/* Close and reopen socket. */
101static pj_status_t reopen_sock( pj_sock_t *sock)
102{
103 pj_sockaddr_in addr;
104 int addrlen;
105 pj_status_t status;
106
107 addrlen = sizeof(pj_sockaddr_in);
108 status = pj_sock_getsockname(*sock, &addr, &addrlen);
109 if (status != PJ_SUCCESS) {
110 pjsua_perror(THIS_FILE, "Error getting RTP/RTCP socket name", status);
111 return status;
112 }
113
114 pj_sock_close(*sock);
115
116 status = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, sock);
117 if (status != PJ_SUCCESS) {
118 pjsua_perror(THIS_FILE, "Unable to create socket", status);
119 return status;
120 }
121
122 status = pj_sock_bind(*sock, &addr, sizeof(pj_sockaddr_in));
123 if (status != PJ_SUCCESS) {
124 pjsua_perror(THIS_FILE, "Unable to re-bind RTP/RTCP socket", status);
125 return status;
126 }
127
128 return PJ_SUCCESS;
129}
130
131/*
132 * Destroy the call's media
133 */
134static pj_status_t call_destroy_media(int call_index)
135{
136 pjsua_call *call = &pjsua.calls[call_index];
137
138 if (call->conf_slot > 0) {
139 pjmedia_conf_remove_port(pjsua.mconf, call->conf_slot);
140 call->conf_slot = 0;
141 }
142
143 if (call->session) {
144
145 /* Close and reopen RTP socket.
146 * This is necessary to get the socket unregistered from ioqueue,
147 * when IOCompletionPort is used.
148 */
149 reopen_sock(&call->skinfo.rtp_sock);
150
151 /* Close and reopen RTCP socket too. */
152 reopen_sock(&call->skinfo.rtcp_sock);
153
154 /* Must destroy session after socket is closed. */
155 pjmedia_session_destroy(call->session);
156 call->session = NULL;
157
158 }
159
160 PJ_LOG(3,(THIS_FILE, "Media session for call %d is destroyed",
161 call_index));
162
163 return PJ_SUCCESS;
164}
165
166
Benny Prijono84126ab2006-02-09 09:30:09 +0000167/**
168 * Make outgoing call.
169 */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000170pj_status_t pjsua_make_call(int acc_index,
171 const char *cstr_dest_uri,
172 int *p_call_index)
Benny Prijono84126ab2006-02-09 09:30:09 +0000173{
174 pj_str_t dest_uri;
Benny Prijono1c2bf462006-03-05 11:54:02 +0000175 pjsip_dialog *dlg = NULL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000176 pjmedia_sdp_session *offer;
Benny Prijono1c2bf462006-03-05 11:54:02 +0000177 pjsip_inv_session *inv = NULL;
Benny Prijonoa91a0032006-02-26 21:23:45 +0000178 int call_index = -1;
Benny Prijono84126ab2006-02-09 09:30:09 +0000179 pjsip_tx_data *tdata;
180 pj_status_t status;
181
182 /* Convert cstr_dest_uri to dest_uri */
183
184 dest_uri = pj_str((char*)cstr_dest_uri);
185
Benny Prijonoa91a0032006-02-26 21:23:45 +0000186 /* Find free call slot. */
187 for (call_index=0; call_index<pjsua.max_calls; ++call_index) {
188 if (pjsua.calls[call_index].inv == NULL)
Benny Prijonof04ffdd2006-02-21 00:11:18 +0000189 break;
190 }
191
Benny Prijonoa91a0032006-02-26 21:23:45 +0000192 if (call_index == pjsua.max_calls) {
Benny Prijonof04ffdd2006-02-21 00:11:18 +0000193 PJ_LOG(3,(THIS_FILE, "Error: too many calls!"));
194 return PJ_ETOOMANY;
195 }
196
Benny Prijono84126ab2006-02-09 09:30:09 +0000197 /* Create outgoing dialog: */
198
Benny Prijonoa91a0032006-02-26 21:23:45 +0000199 status = pjsip_dlg_create_uac( pjsip_ua_instance(),
200 &pjsua.acc[acc_index].local_uri,
201 &pjsua.acc[acc_index].contact_uri,
202 &dest_uri, &dest_uri,
Benny Prijono84126ab2006-02-09 09:30:09 +0000203 &dlg);
204 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000205 pjsua_perror(THIS_FILE, "Dialog creation failed", status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000206 return status;
207 }
208
209 /* Get media capability from media endpoint: */
210
Benny Prijonoa91a0032006-02-26 21:23:45 +0000211 status = pjmedia_endpt_create_sdp( pjsua.med_endpt, dlg->pool, 1,
212 &pjsua.calls[call_index].skinfo,
Benny Prijonof04ffdd2006-02-21 00:11:18 +0000213 &offer);
Benny Prijono84126ab2006-02-09 09:30:09 +0000214 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000215 pjsua_perror(THIS_FILE, "pjmedia unable to create SDP", status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000216 goto on_error;
217 }
218
219 /* Create the INVITE session: */
220
221 status = pjsip_inv_create_uac( dlg, offer, 0, &inv);
222 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000223 pjsua_perror(THIS_FILE, "Invite session creation failed", status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000224 goto on_error;
225 }
226
227
228 /* Create and associate our data in the session. */
229
Benny Prijonoa91a0032006-02-26 21:23:45 +0000230 pjsua.calls[call_index].inv = inv;
231
232 dlg->mod_data[pjsua.mod.id] = &pjsua.calls[call_index];
233 inv->mod_data[pjsua.mod.id] = &pjsua.calls[call_index];
Benny Prijono84126ab2006-02-09 09:30:09 +0000234
235
236 /* Set dialog Route-Set: */
237
Benny Prijonoa91a0032006-02-26 21:23:45 +0000238 if (!pj_list_empty(&pjsua.acc[acc_index].route_set))
239 pjsip_dlg_set_route_set(dlg, &pjsua.acc[acc_index].route_set);
Benny Prijono84126ab2006-02-09 09:30:09 +0000240
241
242 /* Set credentials: */
243
244 pjsip_auth_clt_set_credentials( &dlg->auth_sess, pjsua.cred_count,
245 pjsua.cred_info);
246
247
248 /* Create initial INVITE: */
249
250 status = pjsip_inv_invite(inv, &tdata);
251 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000252 pjsua_perror(THIS_FILE, "Unable to create initial INVITE request",
253 status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000254 goto on_error;
255 }
256
257
258 /* Send initial INVITE: */
259
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +0000260 status = pjsip_inv_send_msg(inv, tdata);
Benny Prijono84126ab2006-02-09 09:30:09 +0000261 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000262 pjsua_perror(THIS_FILE, "Unable to send initial INVITE request",
263 status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000264 goto on_error;
265 }
266
267
268 /* Done. */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000269
270 ++pjsua.call_cnt;
271
272 if (p_call_index)
273 *p_call_index = call_index;
Benny Prijono84126ab2006-02-09 09:30:09 +0000274
275 return PJ_SUCCESS;
276
277
278on_error:
Benny Prijono1c2bf462006-03-05 11:54:02 +0000279 if (inv != NULL) {
280 pjsip_inv_terminate(inv, PJSIP_SC_OK, PJ_FALSE);
281 } else {
282 pjsip_dlg_terminate(dlg);
283 }
284
Benny Prijonoa91a0032006-02-26 21:23:45 +0000285 if (call_index != -1) {
286 pjsua.calls[call_index].inv = NULL;
287 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000288 return status;
289}
290
291
292/**
293 * Handle incoming INVITE request.
294 */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000295pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata)
Benny Prijono84126ab2006-02-09 09:30:09 +0000296{
297 pjsip_dialog *dlg = pjsip_rdata_get_dlg(rdata);
298 pjsip_transaction *tsx = pjsip_rdata_get_tsx(rdata);
299 pjsip_msg *msg = rdata->msg_info.msg;
Benny Prijono26ff9062006-02-21 23:47:00 +0000300 pjsip_tx_data *response = NULL;
301 unsigned options = 0;
Benny Prijono1c2bf462006-03-05 11:54:02 +0000302 pjsip_inv_session *inv = NULL;
Benny Prijonoa91a0032006-02-26 21:23:45 +0000303 int acc_index;
304 int call_index = -1;
Benny Prijono26ff9062006-02-21 23:47:00 +0000305 pjmedia_sdp_session *answer;
Benny Prijono26ff9062006-02-21 23:47:00 +0000306 pj_status_t status;
Benny Prijono84126ab2006-02-09 09:30:09 +0000307
Benny Prijono26ff9062006-02-21 23:47:00 +0000308 /* Don't want to handle anything but INVITE */
309 if (msg->line.req.method.id != PJSIP_INVITE_METHOD)
310 return PJ_FALSE;
311
312 /* Don't want to handle anything that's already associated with
313 * existing dialog or transaction.
Benny Prijono84126ab2006-02-09 09:30:09 +0000314 */
Benny Prijono26ff9062006-02-21 23:47:00 +0000315 if (dlg || tsx)
316 return PJ_FALSE;
Benny Prijono84126ab2006-02-09 09:30:09 +0000317
Benny Prijono84126ab2006-02-09 09:30:09 +0000318
Benny Prijono26ff9062006-02-21 23:47:00 +0000319 /* Verify that we can handle the request. */
320 status = pjsip_inv_verify_request(rdata, &options, NULL, NULL,
321 pjsua.endpt, &response);
322 if (status != PJ_SUCCESS) {
Benny Prijono84126ab2006-02-09 09:30:09 +0000323
Benny Prijono26ff9062006-02-21 23:47:00 +0000324 /*
325 * No we can't handle the incoming INVITE request.
326 */
Benny Prijono84126ab2006-02-09 09:30:09 +0000327
Benny Prijono26ff9062006-02-21 23:47:00 +0000328 if (response) {
329 pjsip_response_addr res_addr;
Benny Prijono84126ab2006-02-09 09:30:09 +0000330
Benny Prijono26ff9062006-02-21 23:47:00 +0000331 pjsip_get_response_addr(response->pool, rdata, &res_addr);
332 pjsip_endpt_send_response(pjsua.endpt, &res_addr, response,
333 NULL, NULL);
Benny Prijono84126ab2006-02-09 09:30:09 +0000334
335 } else {
Benny Prijono84126ab2006-02-09 09:30:09 +0000336
Benny Prijono26ff9062006-02-21 23:47:00 +0000337 /* Respond with 500 (Internal Server Error) */
338 pjsip_endpt_respond_stateless(pjsua.endpt, rdata, 500, NULL,
339 NULL, NULL);
Benny Prijono84126ab2006-02-09 09:30:09 +0000340 }
341
Benny Prijono26ff9062006-02-21 23:47:00 +0000342 return PJ_TRUE;
343 }
344
345
346 /*
347 * Yes we can handle the incoming INVITE request.
348 */
349
350 /* Find free call slot. */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000351 for (call_index=0; call_index < pjsua.max_calls; ++call_index) {
352 if (pjsua.calls[call_index].inv == NULL)
Benny Prijono26ff9062006-02-21 23:47:00 +0000353 break;
354 }
355
Benny Prijonoa91a0032006-02-26 21:23:45 +0000356 if (call_index == PJSUA_MAX_CALLS) {
Benny Prijono26ff9062006-02-21 23:47:00 +0000357 pjsip_endpt_respond_stateless(pjsua.endpt, rdata,
358 PJSIP_SC_BUSY_HERE, NULL,
359 NULL, NULL);
Benny Prijono84126ab2006-02-09 09:30:09 +0000360 return PJ_TRUE;
361 }
362
Benny Prijono26ff9062006-02-21 23:47:00 +0000363
Benny Prijono26ff9062006-02-21 23:47:00 +0000364 /* Get media capability from media endpoint: */
365
Benny Prijonoa91a0032006-02-26 21:23:45 +0000366 status = pjmedia_endpt_create_sdp( pjsua.med_endpt, rdata->tp_info.pool, 1,
367 &pjsua.calls[call_index].skinfo,
Benny Prijono26ff9062006-02-21 23:47:00 +0000368 &answer );
369 if (status != PJ_SUCCESS) {
370 pjsip_endpt_respond_stateless(pjsua.endpt, rdata, 500, NULL,
371 NULL, NULL);
372
Benny Prijono26ff9062006-02-21 23:47:00 +0000373 return PJ_TRUE;
374 }
375
Benny Prijonoa91a0032006-02-26 21:23:45 +0000376 /* TODO:
377 *
378 * Get which account is most likely to be associated with this incoming
379 * call. We need the account to find which contact URI to put for
380 * the call.
381 */
382 acc_index = 0;
383
Benny Prijono26ff9062006-02-21 23:47:00 +0000384 /* Create dialog: */
385
386 status = pjsip_dlg_create_uas( pjsip_ua_instance(), rdata,
Benny Prijonoa91a0032006-02-26 21:23:45 +0000387 &pjsua.acc[acc_index].contact_uri,
388 &dlg);
Benny Prijono26ff9062006-02-21 23:47:00 +0000389 if (status != PJ_SUCCESS) {
390 pjsip_endpt_respond_stateless(pjsua.endpt, rdata, 500, NULL,
391 NULL, NULL);
392
Benny Prijono26ff9062006-02-21 23:47:00 +0000393 return PJ_TRUE;
394 }
395
396
397 /* Create invite session: */
398
399 status = pjsip_inv_create_uas( dlg, rdata, answer, 0, &inv);
400 if (status != PJ_SUCCESS) {
401
Benny Prijonob0808372006-03-02 21:18:58 +0000402 pjsip_dlg_respond(dlg, rdata, 500, NULL, NULL, NULL);
Benny Prijono1c2bf462006-03-05 11:54:02 +0000403 pjsip_dlg_terminate(dlg);
Benny Prijono26ff9062006-02-21 23:47:00 +0000404 return PJ_TRUE;
405 }
406
407
408 /* Create and attach pjsua data to the dialog: */
409
Benny Prijonoa91a0032006-02-26 21:23:45 +0000410 pjsua.calls[call_index].inv = inv;
Benny Prijono26ff9062006-02-21 23:47:00 +0000411
Benny Prijonoa91a0032006-02-26 21:23:45 +0000412 dlg->mod_data[pjsua.mod.id] = &pjsua.calls[call_index];
413 inv->mod_data[pjsua.mod.id] = &pjsua.calls[call_index];
Benny Prijono26ff9062006-02-21 23:47:00 +0000414
415
Benny Prijono64f851e2006-02-23 13:49:28 +0000416 /* Must answer with some response to initial INVITE.
417 * If auto-answer flag is set, send 200 straight away, otherwise send 100.
418 */
419
420 status = pjsip_inv_initial_answer(inv, rdata,
Benny Prijonoccb03fa2006-03-06 13:35:47 +0000421 (pjsua.auto_answer ? pjsua.auto_answer
422 : 100),
Benny Prijono64f851e2006-02-23 13:49:28 +0000423 NULL, NULL, &response);
Benny Prijono26ff9062006-02-21 23:47:00 +0000424 if (status != PJ_SUCCESS) {
425
Benny Prijonoccb03fa2006-03-06 13:35:47 +0000426 int st_code;
Benny Prijonod2ae29d2006-02-22 15:42:31 +0000427
Benny Prijonoccb03fa2006-03-06 13:35:47 +0000428 pjsua_perror(THIS_FILE, "Unable to send answer to incoming INVITE",
429 status);
430
431 /* If failed to send 2xx response, there's a good chance that it is
432 * because SDP negotiation has failed.
433 */
434 if (pjsua.auto_answer/100 == 2)
435 st_code = PJSIP_SC_UNSUPPORTED_MEDIA_TYPE;
436 else
437 st_code = 500;
438
439 pjsip_dlg_respond(dlg, rdata, st_code, NULL, NULL, NULL);
440 pjsip_inv_terminate(inv, st_code, PJ_FALSE);
441 return PJ_TRUE;
Benny Prijono26ff9062006-02-21 23:47:00 +0000442
443 } else {
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +0000444 status = pjsip_inv_send_msg(inv, response);
Benny Prijonod2ae29d2006-02-22 15:42:31 +0000445 if (status != PJ_SUCCESS)
446 pjsua_perror(THIS_FILE, "Unable to send 100 response", status);
Benny Prijono26ff9062006-02-21 23:47:00 +0000447 }
448
Benny Prijono64f851e2006-02-23 13:49:28 +0000449 if (pjsua.auto_answer < 200) {
450 PJ_LOG(3,(THIS_FILE,
451 "\nIncoming call!!\n"
452 "From: %.*s\n"
453 "To: %.*s\n"
454 "(press 'a' to answer, 'h' to decline)",
455 (int)dlg->remote.info_str.slen,
456 dlg->remote.info_str.ptr,
457 (int)dlg->local.info_str.slen,
458 dlg->local.info_str.ptr));
459 } else {
460 PJ_LOG(3,(THIS_FILE,
461 "Call From:%.*s To:%.*s was answered with %d (%s)",
462 (int)dlg->remote.info_str.slen,
463 dlg->remote.info_str.ptr,
464 (int)dlg->local.info_str.slen,
465 dlg->local.info_str.ptr,
466 pjsua.auto_answer,
467 pjsip_get_status_text(pjsua.auto_answer)->ptr ));
468 }
469
Benny Prijonoa91a0032006-02-26 21:23:45 +0000470 ++pjsua.call_cnt;
471
Benny Prijono105217f2006-03-06 16:25:59 +0000472 /* Schedule timer to refresh. */
473 if (pjsua.uas_refresh > 0) {
474 schedule_call_timer( &pjsua.calls[call_index],
475 &pjsua.calls[call_index].refresh_tm,
476 REFRESH_CALL_TIMER,
477 pjsua.uas_refresh);
478 }
479
480 /* Schedule timer to hangup call. */
481 if (pjsua.uas_duration > 0) {
482 schedule_call_timer( &pjsua.calls[call_index],
483 &pjsua.calls[call_index].hangup_tm,
484 HANGUP_CALL_TIMER,
485 pjsua.uas_duration);
486 }
487
Benny Prijono26ff9062006-02-21 23:47:00 +0000488 /* This INVITE request has been handled. */
489 return PJ_TRUE;
Benny Prijono84126ab2006-02-09 09:30:09 +0000490}
491
492
493/*
494 * This callback receives notification from invite session when the
495 * session state has changed.
496 */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000497static void pjsua_call_on_state_changed(pjsip_inv_session *inv,
498 pjsip_event *e)
Benny Prijono84126ab2006-02-09 09:30:09 +0000499{
Benny Prijonoa91a0032006-02-26 21:23:45 +0000500 pjsua_call *call = inv->dlg->mod_data[pjsua.mod.id];
Benny Prijono26ff9062006-02-21 23:47:00 +0000501
502 /* If this is an outgoing INVITE that was created because of
503 * REFER/transfer, send NOTIFY to transferer.
504 */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000505 if (call && call->xfer_sub && e->type==PJSIP_EVENT_TSX_STATE) {
Benny Prijono26ff9062006-02-21 23:47:00 +0000506 int st_code = -1;
507 pjsip_evsub_state ev_state = PJSIP_EVSUB_STATE_ACTIVE;
508
509
Benny Prijonoa91a0032006-02-26 21:23:45 +0000510 switch (call->inv->state) {
Benny Prijono26ff9062006-02-21 23:47:00 +0000511 case PJSIP_INV_STATE_NULL:
512 case PJSIP_INV_STATE_CALLING:
513 /* Do nothing */
514 break;
515
516 case PJSIP_INV_STATE_EARLY:
517 case PJSIP_INV_STATE_CONNECTING:
518 st_code = e->body.tsx_state.tsx->status_code;
519 ev_state = PJSIP_EVSUB_STATE_ACTIVE;
520 break;
521
522 case PJSIP_INV_STATE_CONFIRMED:
523 /* When state is confirmed, send the final 200/OK and terminate
524 * subscription.
525 */
526 st_code = e->body.tsx_state.tsx->status_code;
527 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
528 break;
529
530 case PJSIP_INV_STATE_DISCONNECTED:
531 st_code = e->body.tsx_state.tsx->status_code;
532 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
533 break;
534 }
535
536 if (st_code != -1) {
537 pjsip_tx_data *tdata;
538 pj_status_t status;
539
Benny Prijonoa91a0032006-02-26 21:23:45 +0000540 status = pjsip_xfer_notify( call->xfer_sub,
Benny Prijono26ff9062006-02-21 23:47:00 +0000541 ev_state, st_code,
542 NULL, &tdata);
543 if (status != PJ_SUCCESS) {
544 pjsua_perror(THIS_FILE, "Unable to create NOTIFY", status);
545 } else {
Benny Prijonoa91a0032006-02-26 21:23:45 +0000546 status = pjsip_xfer_send_request(call->xfer_sub, tdata);
Benny Prijono26ff9062006-02-21 23:47:00 +0000547 if (status != PJ_SUCCESS) {
548 pjsua_perror(THIS_FILE, "Unable to send NOTIFY", status);
549 }
550 }
551 }
552 }
553
Benny Prijono84126ab2006-02-09 09:30:09 +0000554
Benny Prijonob0808372006-03-02 21:18:58 +0000555 pjsua_ui_on_call_state(call->index, e);
Benny Prijonoa91a0032006-02-26 21:23:45 +0000556
557 /* call->inv may be NULL now */
558
Benny Prijono84126ab2006-02-09 09:30:09 +0000559 /* Destroy media session when invite session is disconnected. */
560 if (inv->state == PJSIP_INV_STATE_DISCONNECTED) {
Benny Prijono632ce712006-02-09 14:01:40 +0000561
Benny Prijonoa91a0032006-02-26 21:23:45 +0000562 pj_assert(call != NULL);
Benny Prijono632ce712006-02-09 14:01:40 +0000563
Benny Prijono275fd682006-03-22 11:59:11 +0000564 if (call)
565 call_destroy_media(call->index);
Benny Prijono84126ab2006-02-09 09:30:09 +0000566
Benny Prijono105217f2006-03-06 16:25:59 +0000567 /* Remove timers. */
568 schedule_call_timer(call, &call->refresh_tm, REFRESH_CALL_TIMER, 0);
569 schedule_call_timer(call, &call->hangup_tm, HANGUP_CALL_TIMER, 0);
570
571 /* Free call */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000572 call->inv = NULL;
573 --pjsua.call_cnt;
Benny Prijono84126ab2006-02-09 09:30:09 +0000574 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000575}
576
577
578/*
Benny Prijono26ff9062006-02-21 23:47:00 +0000579 * Callback called by event framework when the xfer subscription state
580 * has changed.
581 */
582static void xfer_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
583{
584
585 PJ_UNUSED_ARG(event);
586
587 /*
588 * We're only interested when subscription is terminated, to
589 * clear the xfer_sub member of the inv_data.
590 */
591 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
Benny Prijonoa91a0032006-02-26 21:23:45 +0000592 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +0000593
Benny Prijonoa91a0032006-02-26 21:23:45 +0000594 call = pjsip_evsub_get_mod_data(sub, pjsua.mod.id);
595 if (!call)
Benny Prijono26ff9062006-02-21 23:47:00 +0000596 return;
597
598 pjsip_evsub_set_mod_data(sub, pjsua.mod.id, NULL);
Benny Prijonoa91a0032006-02-26 21:23:45 +0000599 call->xfer_sub = NULL;
Benny Prijono26ff9062006-02-21 23:47:00 +0000600
601 PJ_LOG(3,(THIS_FILE, "Xfer subscription terminated"));
602 }
603}
604
605
606/*
607 * Follow transfer (REFER) request.
608 */
609static void on_call_transfered( pjsip_inv_session *inv,
610 pjsip_rx_data *rdata )
611{
612 pj_status_t status;
613 pjsip_tx_data *tdata;
Benny Prijonoa91a0032006-02-26 21:23:45 +0000614 pjsua_call *existing_call;
615 int new_call;
Benny Prijono26ff9062006-02-21 23:47:00 +0000616 const pj_str_t str_refer_to = { "Refer-To", 8};
617 pjsip_generic_string_hdr *refer_to;
618 char *uri;
619 struct pjsip_evsub_user xfer_cb;
620 pjsip_evsub *sub;
621
Benny Prijonoa91a0032006-02-26 21:23:45 +0000622 existing_call = inv->dlg->mod_data[pjsua.mod.id];
623
Benny Prijono26ff9062006-02-21 23:47:00 +0000624 /* Find the Refer-To header */
625 refer_to = (pjsip_generic_string_hdr*)
626 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_to, NULL);
627
628 if (refer_to == NULL) {
629 /* Invalid Request.
630 * No Refer-To header!
631 */
632 PJ_LOG(4,(THIS_FILE, "Received REFER without Refer-To header!"));
Benny Prijonob0808372006-03-02 21:18:58 +0000633 pjsip_dlg_respond( inv->dlg, rdata, 400, NULL, NULL, NULL);
Benny Prijono26ff9062006-02-21 23:47:00 +0000634 return;
635 }
636
637 PJ_LOG(3,(THIS_FILE, "Call to %.*s is being transfered to %.*s",
638 (int)inv->dlg->remote.info_str.slen,
639 inv->dlg->remote.info_str.ptr,
640 (int)refer_to->hvalue.slen,
641 refer_to->hvalue.ptr));
642
643 /* Init callback */
644 pj_memset(&xfer_cb, 0, sizeof(xfer_cb));
645 xfer_cb.on_evsub_state = &xfer_on_evsub_state;
646
647 /* Create transferee event subscription */
648 status = pjsip_xfer_create_uas( inv->dlg, &xfer_cb, rdata, &sub);
649 if (status != PJ_SUCCESS) {
650 pjsua_perror(THIS_FILE, "Unable to create xfer uas", status);
Benny Prijonob0808372006-03-02 21:18:58 +0000651 pjsip_dlg_respond( inv->dlg, rdata, 500, NULL, NULL, NULL);
Benny Prijono26ff9062006-02-21 23:47:00 +0000652 return;
653 }
654
655 /* Accept the REFER request, send 200 (OK). */
656 pjsip_xfer_accept(sub, rdata, 200, NULL);
657
658 /* Create initial NOTIFY request */
659 status = pjsip_xfer_notify( sub, PJSIP_EVSUB_STATE_ACTIVE,
660 100, NULL, &tdata);
661 if (status != PJ_SUCCESS) {
662 pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER", status);
663 return;
664 }
665
666 /* Send initial NOTIFY request */
667 status = pjsip_xfer_send_request( sub, tdata);
668 if (status != PJ_SUCCESS) {
669 pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER", status);
670 return;
671 }
672
673 /* We're cheating here.
674 * We need to get a null terminated string from a pj_str_t.
675 * So grab the pointer from the hvalue and NULL terminate it, knowing
676 * that the NULL position will be occupied by a newline.
677 */
678 uri = refer_to->hvalue.ptr;
679 uri[refer_to->hvalue.slen] = '\0';
680
681 /* Now make the outgoing call. */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000682 status = pjsua_make_call(existing_call->acc_index, uri, &new_call);
Benny Prijono26ff9062006-02-21 23:47:00 +0000683 if (status != PJ_SUCCESS) {
684
685 /* Notify xferer about the error */
686 status = pjsip_xfer_notify(sub, PJSIP_EVSUB_STATE_TERMINATED,
687 500, NULL, &tdata);
688 if (status != PJ_SUCCESS) {
689 pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER",
690 status);
691 return;
692 }
693 status = pjsip_xfer_send_request(sub, tdata);
694 if (status != PJ_SUCCESS) {
695 pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER",
696 status);
697 return;
698 }
699 return;
700 }
701
702 /* Put the server subscription in inv_data.
703 * Subsequent state changed in pjsua_inv_on_state_changed() will be
704 * reported back to the server subscription.
705 */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000706 pjsua.calls[new_call].xfer_sub = sub;
Benny Prijono26ff9062006-02-21 23:47:00 +0000707
708 /* Put the invite_data in the subscription. */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000709 pjsip_evsub_set_mod_data(sub, pjsua.mod.id, &pjsua.calls[new_call]);
Benny Prijono26ff9062006-02-21 23:47:00 +0000710}
711
712
713/*
714 * This callback is called when transaction state has changed in INVITE
Benny Prijonob0808372006-03-02 21:18:58 +0000715 * session. We use this to trap:
716 * - incoming REFER request.
717 * - incoming MESSAGE request.
Benny Prijono26ff9062006-02-21 23:47:00 +0000718 */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000719static void pjsua_call_on_tsx_state_changed(pjsip_inv_session *inv,
720 pjsip_transaction *tsx,
721 pjsip_event *e)
Benny Prijono26ff9062006-02-21 23:47:00 +0000722{
Benny Prijonoa91a0032006-02-26 21:23:45 +0000723 pjsua_call *call = inv->dlg->mod_data[pjsua.mod.id];
724
Benny Prijono26ff9062006-02-21 23:47:00 +0000725 if (tsx->role==PJSIP_ROLE_UAS &&
726 tsx->state==PJSIP_TSX_STATE_TRYING &&
727 pjsip_method_cmp(&tsx->method, &pjsip_refer_method)==0)
728 {
729 /*
730 * Incoming REFER request.
731 */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000732 on_call_transfered(call->inv, e->body.tsx_state.src.rdata);
Benny Prijonob0808372006-03-02 21:18:58 +0000733
Benny Prijono26ff9062006-02-21 23:47:00 +0000734 }
Benny Prijonob0808372006-03-02 21:18:58 +0000735 else if (tsx->role==PJSIP_ROLE_UAS &&
736 tsx->state==PJSIP_TSX_STATE_TRYING &&
737 pjsip_method_cmp(&tsx->method, &pjsip_message_method)==0)
738 {
739 /*
740 * Incoming MESSAGE request!
741 */
742 pjsip_rx_data *rdata;
743 pjsip_msg *msg;
744 pjsip_accept_hdr *accept_hdr;
745 pj_status_t status;
746
747 rdata = e->body.tsx_state.src.rdata;
748 msg = rdata->msg_info.msg;
749
750 /* Request MUST have message body, with Content-Type equal to
751 * "text/plain".
752 */
753 if (pjsua_im_accept_pager(rdata, &accept_hdr) == PJ_FALSE) {
754
755 pjsip_hdr hdr_list;
756
757 pj_list_init(&hdr_list);
758 pj_list_push_back(&hdr_list, accept_hdr);
759
760 pjsip_dlg_respond( inv->dlg, rdata, PJSIP_SC_NOT_ACCEPTABLE_HERE,
761 NULL, &hdr_list, NULL );
762 return;
763 }
764
765 /* Respond with 200 first, so that remote doesn't retransmit in case
766 * the UI takes too long to process the message.
767 */
768 status = pjsip_dlg_respond( inv->dlg, rdata, 200, NULL, NULL, NULL);
769
770 /* Process MESSAGE request */
771 pjsua_im_process_pager(call->index, &inv->dlg->remote.info_str,
772 &inv->dlg->local.info_str, rdata);
773 }
774
Benny Prijono26ff9062006-02-21 23:47:00 +0000775}
776
777
778/*
Benny Prijono84126ab2006-02-09 09:30:09 +0000779 * This callback is called by invite session framework when UAC session
780 * has forked.
781 */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000782static void pjsua_call_on_forked( pjsip_inv_session *inv,
783 pjsip_event *e)
Benny Prijono84126ab2006-02-09 09:30:09 +0000784{
785 PJ_UNUSED_ARG(inv);
786 PJ_UNUSED_ARG(e);
787
788 PJ_TODO(HANDLE_FORKED_DIALOG);
789}
790
791
792/*
Benny Prijono26ff9062006-02-21 23:47:00 +0000793 * Create inactive SDP for call hold.
794 */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000795static pj_status_t create_inactive_sdp(pjsua_call *call,
Benny Prijono26ff9062006-02-21 23:47:00 +0000796 pjmedia_sdp_session **p_answer)
797{
798 pj_status_t status;
799 pjmedia_sdp_conn *conn;
800 pjmedia_sdp_attr *attr;
801 pjmedia_sdp_session *sdp;
802
803 /* Create new offer */
804 status = pjmedia_endpt_create_sdp(pjsua.med_endpt, pjsua.pool, 1,
Benny Prijonoa91a0032006-02-26 21:23:45 +0000805 &call->skinfo, &sdp);
Benny Prijono26ff9062006-02-21 23:47:00 +0000806 if (status != PJ_SUCCESS) {
807 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
808 return status;
809 }
810
811 /* Get SDP media connection line */
812 conn = sdp->media[0]->conn;
813 if (!conn)
814 conn = sdp->conn;
815
816 /* Modify address */
817 conn->addr = pj_str("0.0.0.0");
818
819 /* Remove existing directions attributes */
820 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "sendrecv");
821 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "sendonly");
822 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "recvonly");
823 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "inactive");
824
825 /* Add inactive attribute */
826 attr = pjmedia_sdp_attr_create(pjsua.pool, "inactive", NULL);
827 pjmedia_sdp_media_add_attr(sdp->media[0], attr);
828
829 *p_answer = sdp;
830
831 return status;
832}
833
834/*
835 * Called when session received new offer.
836 */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000837static void pjsua_call_on_rx_offer(pjsip_inv_session *inv,
838 const pjmedia_sdp_session *offer)
Benny Prijono26ff9062006-02-21 23:47:00 +0000839{
Benny Prijonoa91a0032006-02-26 21:23:45 +0000840 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +0000841 pjmedia_sdp_conn *conn;
842 pjmedia_sdp_session *answer;
843 pj_bool_t is_remote_active;
844 pj_status_t status;
845
Benny Prijonoa91a0032006-02-26 21:23:45 +0000846 call = inv->dlg->mod_data[pjsua.mod.id];
Benny Prijono26ff9062006-02-21 23:47:00 +0000847
848 /*
849 * See if remote is offering active media (i.e. not on-hold)
850 */
851 is_remote_active = PJ_TRUE;
852
853 conn = offer->media[0]->conn;
854 if (!conn)
855 conn = offer->conn;
856
857 if (pj_strcmp2(&conn->addr, "0.0.0.0")==0 ||
858 pj_strcmp2(&conn->addr, "0")==0)
859 {
860 is_remote_active = PJ_FALSE;
861
862 }
863 else if (pjmedia_sdp_media_find_attr2(offer->media[0], "inactive", NULL))
864 {
865 is_remote_active = PJ_FALSE;
866 }
867
868 PJ_LOG(4,(THIS_FILE, "Received SDP offer, remote media is %s",
869 (is_remote_active ? "active" : "inactive")));
870
871 /* Supply candidate answer */
872 if (is_remote_active) {
Benny Prijonoa91a0032006-02-26 21:23:45 +0000873 status = pjmedia_endpt_create_sdp( pjsua.med_endpt, call->inv->pool, 1,
874 &call->skinfo, &answer);
Benny Prijono26ff9062006-02-21 23:47:00 +0000875 } else {
Benny Prijonoa91a0032006-02-26 21:23:45 +0000876 status = create_inactive_sdp( call, &answer );
Benny Prijono26ff9062006-02-21 23:47:00 +0000877 }
878
879 if (status != PJ_SUCCESS) {
880 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
881 return;
882 }
883
Benny Prijonoa91a0032006-02-26 21:23:45 +0000884 status = pjsip_inv_set_sdp_answer(call->inv, answer);
Benny Prijono26ff9062006-02-21 23:47:00 +0000885 if (status != PJ_SUCCESS) {
886 pjsua_perror(THIS_FILE, "Unable to set answer", status);
887 return;
888 }
889
890}
891
Benny Prijono1c2bf462006-03-05 11:54:02 +0000892/* Disconnect call */
893static void call_disconnect(pjsip_inv_session *inv,
894 int st_code)
895{
896 pjsip_tx_data *tdata;
897 pj_status_t status;
898
899 status = pjsip_inv_end_session(inv, st_code, NULL, &tdata);
900 if (status == PJ_SUCCESS)
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +0000901 status = pjsip_inv_send_msg(inv, tdata);
Benny Prijono1c2bf462006-03-05 11:54:02 +0000902
903 if (status != PJ_SUCCESS) {
904 pjsua_perror(THIS_FILE, "Unable to disconnect call", status);
905 }
906}
Benny Prijono26ff9062006-02-21 23:47:00 +0000907
908/*
Benny Prijono84126ab2006-02-09 09:30:09 +0000909 * Callback to be called when SDP offer/answer negotiation has just completed
910 * in the session. This function will start/update media if negotiation
911 * has succeeded.
912 */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000913static void pjsua_call_on_media_update(pjsip_inv_session *inv,
914 pj_status_t status)
Benny Prijono84126ab2006-02-09 09:30:09 +0000915{
Benny Prijonoa91a0032006-02-26 21:23:45 +0000916 pjsua_call *call;
Benny Prijono84126ab2006-02-09 09:30:09 +0000917 const pjmedia_sdp_session *local_sdp;
918 const pjmedia_sdp_session *remote_sdp;
Benny Prijono26ff9062006-02-21 23:47:00 +0000919 pjmedia_port *media_port;
920 pj_str_t port_name;
921 char tmp[PJSIP_MAX_URL_SIZE];
Benny Prijono84126ab2006-02-09 09:30:09 +0000922
Benny Prijonoa91a0032006-02-26 21:23:45 +0000923 call = inv->dlg->mod_data[pjsua.mod.id];
924
Benny Prijono84126ab2006-02-09 09:30:09 +0000925 if (status != PJ_SUCCESS) {
926
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000927 pjsua_perror(THIS_FILE, "SDP negotiation has failed", status);
Benny Prijono1c2bf462006-03-05 11:54:02 +0000928
Benny Prijonoccb03fa2006-03-06 13:35:47 +0000929 /* Disconnect call if we're not in the middle of initializing an
930 * UAS dialog and if this is not a re-INVITE
931 */
932 if (inv->state != PJSIP_INV_STATE_NULL &&
933 inv->state != PJSIP_INV_STATE_CONFIRMED)
934 {
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +0000935 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
Benny Prijono1c2bf462006-03-05 11:54:02 +0000936 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000937 return;
938
939 }
940
941 /* Destroy existing media session, if any. */
942
Benny Prijono275fd682006-03-22 11:59:11 +0000943 if (call)
944 call_destroy_media(call->index);
Benny Prijono84126ab2006-02-09 09:30:09 +0000945
946 /* Get local and remote SDP */
947
Benny Prijonoa91a0032006-02-26 21:23:45 +0000948 status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &local_sdp);
Benny Prijono84126ab2006-02-09 09:30:09 +0000949 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000950 pjsua_perror(THIS_FILE,
951 "Unable to retrieve currently active local SDP",
952 status);
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +0000953 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
Benny Prijono84126ab2006-02-09 09:30:09 +0000954 return;
955 }
956
957
Benny Prijonoa91a0032006-02-26 21:23:45 +0000958 status = pjmedia_sdp_neg_get_active_remote(call->inv->neg, &remote_sdp);
Benny Prijono84126ab2006-02-09 09:30:09 +0000959 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000960 pjsua_perror(THIS_FILE,
961 "Unable to retrieve currently active remote SDP",
962 status);
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +0000963 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
Benny Prijono84126ab2006-02-09 09:30:09 +0000964 return;
965 }
966
Benny Prijono84126ab2006-02-09 09:30:09 +0000967 /* Create new media session.
968 * The media session is active immediately.
969 */
Benny Prijono26ff9062006-02-21 23:47:00 +0000970 if (pjsua.null_audio)
971 return;
972
973 status = pjmedia_session_create( pjsua.med_endpt, 1,
Benny Prijonoa91a0032006-02-26 21:23:45 +0000974 &call->skinfo,
Benny Prijono26ff9062006-02-21 23:47:00 +0000975 local_sdp, remote_sdp,
Benny Prijonoa91a0032006-02-26 21:23:45 +0000976 call,
977 &call->session );
Benny Prijono26ff9062006-02-21 23:47:00 +0000978 if (status != PJ_SUCCESS) {
979 pjsua_perror(THIS_FILE, "Unable to create media session",
980 status);
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +0000981 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
Benny Prijono26ff9062006-02-21 23:47:00 +0000982 return;
Benny Prijono84126ab2006-02-09 09:30:09 +0000983 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000984
985
986 /* Get the port interface of the first stream in the session.
987 * We need the port interface to add to the conference bridge.
988 */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000989 pjmedia_session_get_port(call->session, 0, &media_port);
Benny Prijono26ff9062006-02-21 23:47:00 +0000990
991
992 /*
993 * Add the call to conference bridge.
994 */
995 port_name.ptr = tmp;
996 port_name.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI,
Benny Prijonoa91a0032006-02-26 21:23:45 +0000997 call->inv->dlg->remote.info->uri,
Benny Prijono26ff9062006-02-21 23:47:00 +0000998 tmp, sizeof(tmp));
999 if (port_name.slen < 1) {
1000 port_name = pj_str("call");
1001 }
Benny Prijonoa91a0032006-02-26 21:23:45 +00001002 status = pjmedia_conf_add_port( pjsua.mconf, call->inv->pool,
Benny Prijono26ff9062006-02-21 23:47:00 +00001003 media_port,
1004 &port_name,
Benny Prijonoa91a0032006-02-26 21:23:45 +00001005 &call->conf_slot);
Benny Prijono26ff9062006-02-21 23:47:00 +00001006 if (status != PJ_SUCCESS) {
1007 pjsua_perror(THIS_FILE, "Unable to create conference slot",
1008 status);
Benny Prijono275fd682006-03-22 11:59:11 +00001009 call_destroy_media(call->index);
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +00001010 //call_disconnect(inv, PJSIP_SC_INTERNAL_SERVER_ERROR);
Benny Prijono26ff9062006-02-21 23:47:00 +00001011 return;
1012 }
1013
Benny Prijono64f851e2006-02-23 13:49:28 +00001014 /* If auto-play is configured, connect the call to the file player
1015 * port
Benny Prijono26ff9062006-02-21 23:47:00 +00001016 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00001017 if (pjsua.auto_play && pjsua.wav_file &&
1018 call->inv->role == PJSIP_ROLE_UAS)
1019 {
Benny Prijono64f851e2006-02-23 13:49:28 +00001020
1021 pjmedia_conf_connect_port( pjsua.mconf, pjsua.wav_slot,
Benny Prijonob100d692006-03-17 00:16:01 +00001022 call->conf_slot, 0);
Benny Prijonoa91a0032006-02-26 21:23:45 +00001023
1024 } else if (pjsua.auto_loop && call->inv->role == PJSIP_ROLE_UAS) {
1025
1026 pjmedia_conf_connect_port( pjsua.mconf, call->conf_slot,
Benny Prijonob100d692006-03-17 00:16:01 +00001027 call->conf_slot, 0);
Benny Prijonoa91a0032006-02-26 21:23:45 +00001028
1029 } else if (pjsua.auto_conf) {
1030
1031 int i;
1032
Benny Prijonob100d692006-03-17 00:16:01 +00001033 pjmedia_conf_connect_port( pjsua.mconf, 0, call->conf_slot, 0);
1034 pjmedia_conf_connect_port( pjsua.mconf, call->conf_slot, 0, 0);
Benny Prijonoa91a0032006-02-26 21:23:45 +00001035
1036 for (i=0; i < pjsua.max_calls; ++i) {
1037
1038 if (!pjsua.calls[i].session)
1039 continue;
1040
1041 pjmedia_conf_connect_port( pjsua.mconf, call->conf_slot,
Benny Prijonob100d692006-03-17 00:16:01 +00001042 pjsua.calls[i].conf_slot, 0);
Benny Prijonoa91a0032006-02-26 21:23:45 +00001043 pjmedia_conf_connect_port( pjsua.mconf, pjsua.calls[i].conf_slot,
Benny Prijonob100d692006-03-17 00:16:01 +00001044 call->conf_slot, 0);
Benny Prijonoa91a0032006-02-26 21:23:45 +00001045 }
Benny Prijono64f851e2006-02-23 13:49:28 +00001046
1047 } else {
1048
1049 /* Connect new call to the sound device port (port zero) in the
1050 * main conference bridge.
1051 */
Benny Prijonob100d692006-03-17 00:16:01 +00001052 pjmedia_conf_connect_port( pjsua.mconf, 0, call->conf_slot, 0);
1053 pjmedia_conf_connect_port( pjsua.mconf, call->conf_slot, 0, 0);
Benny Prijono64f851e2006-02-23 13:49:28 +00001054 }
1055
Benny Prijono26ff9062006-02-21 23:47:00 +00001056
1057 /* Done. */
1058 {
1059 struct pjmedia_session_info sess_info;
1060 char info[80];
1061 int info_len = 0;
1062 unsigned i;
1063
Benny Prijonoa91a0032006-02-26 21:23:45 +00001064 pjmedia_session_get_info(call->session, &sess_info);
Benny Prijono26ff9062006-02-21 23:47:00 +00001065 for (i=0; i<sess_info.stream_cnt; ++i) {
1066 int len;
1067 const char *dir;
1068 pjmedia_stream_info *strm_info = &sess_info.stream_info[i];
1069
1070 switch (strm_info->dir) {
1071 case PJMEDIA_DIR_NONE:
1072 dir = "inactive";
1073 break;
1074 case PJMEDIA_DIR_ENCODING:
1075 dir = "sendonly";
1076 break;
1077 case PJMEDIA_DIR_DECODING:
1078 dir = "recvonly";
1079 break;
1080 case PJMEDIA_DIR_ENCODING_DECODING:
1081 dir = "sendrecv";
1082 break;
1083 default:
1084 dir = "unknown";
1085 break;
1086 }
1087 len = pj_ansi_sprintf( info+info_len,
1088 ", stream #%d: %.*s (%s)", i,
1089 (int)strm_info->fmt.encoding_name.slen,
Benny Prijonoab7399b2006-02-27 00:40:31 +00001090 strm_info->fmt.encoding_name.ptr,
Benny Prijono26ff9062006-02-21 23:47:00 +00001091 dir);
1092 if (len > 0)
1093 info_len += len;
1094 }
1095 PJ_LOG(3,(THIS_FILE,"Media started%s", info));
1096 }
1097}
1098
1099
1100/*
1101 * Hangup call.
1102 */
Benny Prijono1c2bf462006-03-05 11:54:02 +00001103void pjsua_call_hangup(int call_index)
Benny Prijono26ff9062006-02-21 23:47:00 +00001104{
Benny Prijonoa91a0032006-02-26 21:23:45 +00001105 pjsua_call *call;
Benny Prijono1c2bf462006-03-05 11:54:02 +00001106 int code;
Benny Prijono26ff9062006-02-21 23:47:00 +00001107 pj_status_t status;
1108 pjsip_tx_data *tdata;
1109
Benny Prijonoa91a0032006-02-26 21:23:45 +00001110
1111 call = &pjsua.calls[call_index];
1112
1113 if (!call->inv) {
1114 PJ_LOG(3,(THIS_FILE,"Call has been disconnected"));
1115 return;
1116 }
1117
Benny Prijono1c2bf462006-03-05 11:54:02 +00001118 if (call->inv->state == PJSIP_INV_STATE_CONFIRMED)
1119 code = PJSIP_SC_OK;
1120 else if (call->inv->role == PJSIP_ROLE_UAS)
1121 code = PJSIP_SC_DECLINE;
1122 else
1123 code = PJSIP_SC_REQUEST_TERMINATED;
1124
Benny Prijonoa91a0032006-02-26 21:23:45 +00001125 status = pjsip_inv_end_session(call->inv, code, NULL, &tdata);
Benny Prijono26ff9062006-02-21 23:47:00 +00001126 if (status != PJ_SUCCESS) {
1127 pjsua_perror(THIS_FILE,
1128 "Failed to create end session message",
1129 status);
1130 return;
1131 }
1132
Benny Prijonofccab712006-02-22 22:23:22 +00001133 /* pjsip_inv_end_session may return PJ_SUCCESS with NULL
1134 * as p_tdata when INVITE transaction has not been answered
1135 * with any provisional responses.
1136 */
1137 if (tdata == NULL)
1138 return;
1139
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +00001140 status = pjsip_inv_send_msg(call->inv, tdata);
Benny Prijono26ff9062006-02-21 23:47:00 +00001141 if (status != PJ_SUCCESS) {
1142 pjsua_perror(THIS_FILE,
1143 "Failed to send end session message",
1144 status);
1145 return;
1146 }
1147}
1148
1149
1150/*
1151 * Put call on-Hold.
1152 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00001153void pjsua_call_set_hold(int call_index)
Benny Prijono26ff9062006-02-21 23:47:00 +00001154{
1155 pjmedia_sdp_session *sdp;
Benny Prijonoa91a0032006-02-26 21:23:45 +00001156 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +00001157 pjsip_tx_data *tdata;
1158 pj_status_t status;
1159
Benny Prijonoa91a0032006-02-26 21:23:45 +00001160 call = &pjsua.calls[call_index];
1161
1162 if (!call->inv) {
1163 PJ_LOG(3,(THIS_FILE,"Call has been disconnected"));
1164 return;
1165 }
1166
1167 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
Benny Prijono26ff9062006-02-21 23:47:00 +00001168 PJ_LOG(3,(THIS_FILE, "Can not hold call that is not confirmed"));
1169 return;
1170 }
1171
Benny Prijonoa91a0032006-02-26 21:23:45 +00001172 status = create_inactive_sdp(call, &sdp);
Benny Prijono26ff9062006-02-21 23:47:00 +00001173 if (status != PJ_SUCCESS)
1174 return;
1175
1176 /* Send re-INVITE with new offer */
Benny Prijonoa91a0032006-02-26 21:23:45 +00001177 status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
Benny Prijono26ff9062006-02-21 23:47:00 +00001178 if (status != PJ_SUCCESS) {
1179 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
1180 return;
1181 }
1182
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +00001183 status = pjsip_inv_send_msg( call->inv, tdata);
Benny Prijono26ff9062006-02-21 23:47:00 +00001184 if (status != PJ_SUCCESS) {
1185 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
1186 return;
1187 }
1188}
1189
1190
1191/*
1192 * re-INVITE.
1193 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00001194void pjsua_call_reinvite(int call_index)
Benny Prijono26ff9062006-02-21 23:47:00 +00001195{
1196 pjmedia_sdp_session *sdp;
1197 pjsip_tx_data *tdata;
Benny Prijonoa91a0032006-02-26 21:23:45 +00001198 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +00001199 pj_status_t status;
1200
Benny Prijonoa91a0032006-02-26 21:23:45 +00001201 call = &pjsua.calls[call_index];
Benny Prijono26ff9062006-02-21 23:47:00 +00001202
Benny Prijonoa91a0032006-02-26 21:23:45 +00001203 if (!call->inv) {
1204 PJ_LOG(3,(THIS_FILE,"Call has been disconnected"));
1205 return;
1206 }
1207
1208
1209 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
Benny Prijono26ff9062006-02-21 23:47:00 +00001210 PJ_LOG(3,(THIS_FILE, "Can not re-INVITE call that is not confirmed"));
1211 return;
1212 }
1213
1214 /* Create SDP */
Benny Prijonoa91a0032006-02-26 21:23:45 +00001215 status = pjmedia_endpt_create_sdp( pjsua.med_endpt, call->inv->pool, 1,
1216 &call->skinfo, &sdp);
Benny Prijono26ff9062006-02-21 23:47:00 +00001217 if (status != PJ_SUCCESS) {
Benny Prijonoa91a0032006-02-26 21:23:45 +00001218 pjsua_perror(THIS_FILE, "Unable to get SDP from media endpoint",
1219 status);
Benny Prijono26ff9062006-02-21 23:47:00 +00001220 return;
1221 }
1222
1223 /* Send re-INVITE with new offer */
Benny Prijonoa91a0032006-02-26 21:23:45 +00001224 status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
Benny Prijono26ff9062006-02-21 23:47:00 +00001225 if (status != PJ_SUCCESS) {
1226 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
1227 return;
1228 }
1229
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +00001230 status = pjsip_inv_send_msg( call->inv, tdata);
Benny Prijono26ff9062006-02-21 23:47:00 +00001231 if (status != PJ_SUCCESS) {
1232 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
1233 return;
1234 }
1235}
1236
1237
1238/*
1239 * Transfer call.
1240 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00001241void pjsua_call_xfer(int call_index, const char *dest)
Benny Prijono26ff9062006-02-21 23:47:00 +00001242{
1243 pjsip_evsub *sub;
1244 pjsip_tx_data *tdata;
Benny Prijonoa91a0032006-02-26 21:23:45 +00001245 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +00001246 pj_str_t tmp;
1247 pj_status_t status;
Benny Prijonoa91a0032006-02-26 21:23:45 +00001248
Benny Prijono26ff9062006-02-21 23:47:00 +00001249
Benny Prijonoa91a0032006-02-26 21:23:45 +00001250 call = &pjsua.calls[call_index];
1251
1252 if (!call->inv) {
1253 PJ_LOG(3,(THIS_FILE,"Call has been disconnected"));
1254 return;
1255 }
1256
Benny Prijono26ff9062006-02-21 23:47:00 +00001257 /* Create xfer client subscription.
1258 * We're not interested in knowing the transfer result, so we
1259 * put NULL as the callback.
1260 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00001261 status = pjsip_xfer_create_uac(call->inv->dlg, NULL, &sub);
Benny Prijono26ff9062006-02-21 23:47:00 +00001262 if (status != PJ_SUCCESS) {
1263 pjsua_perror(THIS_FILE, "Unable to create xfer", status);
1264 return;
1265 }
1266
1267 /*
1268 * Create REFER request.
1269 */
1270 status = pjsip_xfer_initiate(sub, pj_cstr(&tmp, dest), &tdata);
1271 if (status != PJ_SUCCESS) {
1272 pjsua_perror(THIS_FILE, "Unable to create REFER request", status);
1273 return;
1274 }
1275
1276 /* Send. */
1277 status = pjsip_xfer_send_request(sub, tdata);
1278 if (status != PJ_SUCCESS) {
1279 pjsua_perror(THIS_FILE, "Unable to send REFER request", status);
1280 return;
1281 }
1282
1283 /* For simplicity (that's what this program is intended to be!),
1284 * leave the original invite session as it is. More advanced application
1285 * may want to hold the INVITE, or terminate the invite, or whatever.
1286 */
Benny Prijono84126ab2006-02-09 09:30:09 +00001287}
Benny Prijono834aee32006-02-19 01:38:06 +00001288
1289
Benny Prijonob0808372006-03-02 21:18:58 +00001290/**
1291 * Send instant messaging inside INVITE session.
1292 */
1293void pjsua_call_send_im(int call_index, const char *str)
1294{
1295 pjsua_call *call;
1296 const pj_str_t mime_text = pj_str("text");
1297 const pj_str_t mime_plain = pj_str("plain");
1298 pj_str_t text;
1299 pjsip_tx_data *tdata;
1300 pj_status_t status;
1301
1302 call = &pjsua.calls[call_index];
1303
1304 if (!call->inv) {
1305 PJ_LOG(3,(THIS_FILE,"Call has been disconnected"));
1306 return;
1307 }
1308
1309 /* Lock dialog. */
1310 pjsip_dlg_inc_lock(call->inv->dlg);
1311
1312 /* Create request message. */
1313 status = pjsip_dlg_create_request( call->inv->dlg, &pjsip_message_method,
1314 -1, &tdata);
1315 if (status != PJ_SUCCESS) {
1316 pjsua_perror(THIS_FILE, "Unable to create MESSAGE request", status);
1317 goto on_return;
1318 }
1319
1320 /* Add accept header. */
1321 pjsip_msg_add_hdr( tdata->msg,
1322 (pjsip_hdr*)pjsua_im_create_accept(tdata->pool));
1323
1324 /* Create "text/plain" message body. */
1325 tdata->msg->body = pjsip_msg_body_create( tdata->pool, &mime_text,
1326 &mime_plain,
1327 pj_cstr(&text, str));
1328 if (tdata->msg->body == NULL) {
1329 pjsua_perror(THIS_FILE, "Unable to create msg body", PJ_ENOMEM);
1330 pjsip_tx_data_dec_ref(tdata);
1331 goto on_return;
1332 }
1333
1334 /* Send the request. */
1335 status = pjsip_dlg_send_request( call->inv->dlg, tdata, NULL);
1336 if (status != PJ_SUCCESS) {
1337 pjsua_perror(THIS_FILE, "Unable to send MESSAGE request", status);
1338 goto on_return;
1339 }
1340
1341on_return:
1342 pjsip_dlg_dec_lock(call->inv->dlg);
1343}
1344
1345
1346/**
1347 * Send IM typing indication inside INVITE session.
1348 */
1349void pjsua_call_typing(int call_index, pj_bool_t is_typing)
1350{
1351 pjsua_call *call;
1352 pjsip_tx_data *tdata;
1353 pj_status_t status;
1354
1355 call = &pjsua.calls[call_index];
1356
1357 if (!call->inv) {
1358 PJ_LOG(3,(THIS_FILE,"Call has been disconnected"));
1359 return;
1360 }
1361
1362 /* Lock dialog. */
1363 pjsip_dlg_inc_lock(call->inv->dlg);
1364
1365 /* Create request message. */
1366 status = pjsip_dlg_create_request( call->inv->dlg, &pjsip_message_method,
1367 -1, &tdata);
1368 if (status != PJ_SUCCESS) {
1369 pjsua_perror(THIS_FILE, "Unable to create MESSAGE request", status);
1370 goto on_return;
1371 }
1372
1373 /* Create "application/im-iscomposing+xml" msg body. */
1374 tdata->msg->body = pjsip_iscomposing_create_body(tdata->pool, is_typing,
1375 NULL, NULL, -1);
1376
1377 /* Send the request. */
1378 status = pjsip_dlg_send_request( call->inv->dlg, tdata, NULL);
1379 if (status != PJ_SUCCESS) {
1380 pjsua_perror(THIS_FILE, "Unable to send MESSAGE request", status);
1381 goto on_return;
1382 }
1383
1384on_return:
1385 pjsip_dlg_dec_lock(call->inv->dlg);}
1386
1387
Benny Prijono834aee32006-02-19 01:38:06 +00001388/*
1389 * Terminate all calls.
1390 */
Benny Prijono1c2bf462006-03-05 11:54:02 +00001391void pjsua_call_hangup_all(void)
Benny Prijono834aee32006-02-19 01:38:06 +00001392{
Benny Prijonoa91a0032006-02-26 21:23:45 +00001393 int i;
Benny Prijono834aee32006-02-19 01:38:06 +00001394
Benny Prijonoa91a0032006-02-26 21:23:45 +00001395 for (i=0; i<pjsua.max_calls; ++i) {
Benny Prijono834aee32006-02-19 01:38:06 +00001396 pjsip_tx_data *tdata;
Benny Prijono1c2bf462006-03-05 11:54:02 +00001397 int st_code;
Benny Prijonoa91a0032006-02-26 21:23:45 +00001398 pjsua_call *call;
Benny Prijono834aee32006-02-19 01:38:06 +00001399
Benny Prijonoa91a0032006-02-26 21:23:45 +00001400 if (pjsua.calls[i].inv == NULL)
1401 continue;
Benny Prijono834aee32006-02-19 01:38:06 +00001402
Benny Prijonoa91a0032006-02-26 21:23:45 +00001403 call = &pjsua.calls[i];
1404
Benny Prijono1c2bf462006-03-05 11:54:02 +00001405 if (call->inv->state == PJSIP_INV_STATE_CONFIRMED) {
1406 st_code = 200;
1407 } else {
1408 st_code = PJSIP_SC_GONE;
1409 }
1410
1411 if (pjsip_inv_end_session(call->inv, st_code, NULL, &tdata)==0) {
Benny Prijonofccab712006-02-22 22:23:22 +00001412 if (tdata)
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +00001413 pjsip_inv_send_msg(call->inv, tdata);
Benny Prijonofccab712006-02-22 22:23:22 +00001414 }
Benny Prijono834aee32006-02-19 01:38:06 +00001415 }
1416}
1417
Benny Prijono26ff9062006-02-21 23:47:00 +00001418
Benny Prijonoa91a0032006-02-26 21:23:45 +00001419pj_status_t pjsua_call_init(void)
1420{
1421 /* Initialize invite session callback. */
1422 pjsip_inv_callback inv_cb;
1423 pj_status_t status;
1424
1425 pj_memset(&inv_cb, 0, sizeof(inv_cb));
1426 inv_cb.on_state_changed = &pjsua_call_on_state_changed;
1427 inv_cb.on_new_session = &pjsua_call_on_forked;
1428 inv_cb.on_media_update = &pjsua_call_on_media_update;
1429 inv_cb.on_rx_offer = &pjsua_call_on_rx_offer;
1430 inv_cb.on_tsx_state_changed = &pjsua_call_on_tsx_state_changed;
1431
1432
1433 /* Initialize invite session module: */
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +00001434 status = pjsip_inv_usage_init(pjsua.endpt, &inv_cb);
Benny Prijonoa91a0032006-02-26 21:23:45 +00001435
1436 return status;
1437}