blob: d6013a137a943f54ec8a4d2d355b0186e56d8335 [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>
Benny Prijonodc39fe82006-05-26 12:17:46 +000021#include "pjsua_imp.h"
Benny Prijono84126ab2006-02-09 09:30:09 +000022
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
Benny Prijono8befd9f2006-05-13 22:46:23 +000029#define THIS_FILE "pjsua_call.c"
Benny Prijono84126ab2006-02-09 09:30:09 +000030
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));
Benny Prijonodc39fe82006-05-26 12:17:46 +000059 schedule_call_timer(call,e,REFRESH_CALL_TIMER,
60 pjsua.config.uas_refresh);
Benny Prijono105217f2006-03-06 16:25:59 +000061 pjsua_call_reinvite(call->index);
62 }
63
64 } else if (e->id == HANGUP_CALL_TIMER) {
65 PJ_LOG(3,(THIS_FILE, "Call %d duration exceeded, disconnecting call",
66 call->index));
67 pjsua_call_hangup(call->index);
68
69 }
70}
71
72/*
73 * Schedule call timer.
74 */
75static void schedule_call_timer( pjsua_call *call, pj_timer_entry *e,
76 int timer_type, int duration )
77{
78 pj_time_val timeout;
79
80 if (duration == 0) {
81 /* Cancel timer. */
82 if (e->id != 0) {
83 pjsip_endpt_cancel_timer(pjsua.endpt, e);
84 e->id = 0;
85 }
86
87 } else {
88 /* Schedule timer. */
89 timeout.sec = duration;
90 timeout.msec = 0;
91
92 e->cb = &call_on_timer;
93 e->id = timer_type;
94 e->user_data = call;
95
96 pjsip_endpt_schedule_timer( pjsua.endpt, e, &timeout);
97 }
98}
99
100
Benny Prijono275fd682006-03-22 11:59:11 +0000101/*
102 * Destroy the call's media
103 */
104static pj_status_t call_destroy_media(int call_index)
105{
106 pjsua_call *call = &pjsua.calls[call_index];
107
108 if (call->conf_slot > 0) {
109 pjmedia_conf_remove_port(pjsua.mconf, call->conf_slot);
110 call->conf_slot = 0;
111 }
112
113 if (call->session) {
Benny Prijono46ecff82006-03-30 16:46:36 +0000114 /* Destroy session (this will also close RTP/RTCP sockets). */
115 pjmedia_session_destroy(call->session);
Benny Prijono275fd682006-03-22 11:59:11 +0000116 call->session = NULL;
117
Benny Prijono8befd9f2006-05-13 22:46:23 +0000118 PJ_LOG(3,(THIS_FILE, "Media session for call %d is destroyed",
119 call_index));
Benny Prijono275fd682006-03-22 11:59:11 +0000120
Benny Prijono8befd9f2006-05-13 22:46:23 +0000121 }
Benny Prijono275fd682006-03-22 11:59:11 +0000122
123 return PJ_SUCCESS;
124}
125
126
Benny Prijono84126ab2006-02-09 09:30:09 +0000127/**
128 * Make outgoing call.
129 */
Benny Prijonodc39fe82006-05-26 12:17:46 +0000130PJ_DEF(pj_status_t) pjsua_make_call(int acc_index,
131 const char *cstr_dest_uri,
132 int *p_call_index)
Benny Prijono84126ab2006-02-09 09:30:09 +0000133{
134 pj_str_t dest_uri;
Benny Prijono1c2bf462006-03-05 11:54:02 +0000135 pjsip_dialog *dlg = NULL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000136 pjmedia_sdp_session *offer;
Benny Prijono1c2bf462006-03-05 11:54:02 +0000137 pjsip_inv_session *inv = NULL;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000138 unsigned call_index;
Benny Prijono84126ab2006-02-09 09:30:09 +0000139 pjsip_tx_data *tdata;
140 pj_status_t status;
141
142 /* Convert cstr_dest_uri to dest_uri */
143
144 dest_uri = pj_str((char*)cstr_dest_uri);
145
Benny Prijonoa91a0032006-02-26 21:23:45 +0000146 /* Find free call slot. */
Benny Prijonodc39fe82006-05-26 12:17:46 +0000147 for (call_index=0; call_index<pjsua.config.max_calls; ++call_index) {
Benny Prijonoa91a0032006-02-26 21:23:45 +0000148 if (pjsua.calls[call_index].inv == NULL)
Benny Prijonof04ffdd2006-02-21 00:11:18 +0000149 break;
150 }
151
Benny Prijonodc39fe82006-05-26 12:17:46 +0000152 if (call_index == pjsua.config.max_calls) {
Benny Prijonof04ffdd2006-02-21 00:11:18 +0000153 PJ_LOG(3,(THIS_FILE, "Error: too many calls!"));
154 return PJ_ETOOMANY;
155 }
156
Benny Prijonoe21e7842006-04-09 16:46:05 +0000157 /* Mark call start time. */
158 pj_gettimeofday(&pjsua.calls[call_index].start_time);
Benny Prijono84126ab2006-02-09 09:30:09 +0000159
Benny Prijonoe21e7842006-04-09 16:46:05 +0000160 /* Reset first response time */
161 pjsua.calls[call_index].res_time.sec = 0;
162
163 /* Create outgoing dialog: */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000164 status = pjsip_dlg_create_uac( pjsip_ua_instance(),
Benny Prijonodc39fe82006-05-26 12:17:46 +0000165 &pjsua.config.acc_config[acc_index].id,
166 &pjsua.config.acc_config[acc_index].contact,
Benny Prijonoa91a0032006-02-26 21:23:45 +0000167 &dest_uri, &dest_uri,
Benny Prijono84126ab2006-02-09 09:30:09 +0000168 &dlg);
169 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000170 pjsua_perror(THIS_FILE, "Dialog creation failed", status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000171 return status;
172 }
173
174 /* Get media capability from media endpoint: */
175
Benny Prijonoa91a0032006-02-26 21:23:45 +0000176 status = pjmedia_endpt_create_sdp( pjsua.med_endpt, dlg->pool, 1,
177 &pjsua.calls[call_index].skinfo,
Benny Prijonof04ffdd2006-02-21 00:11:18 +0000178 &offer);
Benny Prijono84126ab2006-02-09 09:30:09 +0000179 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000180 pjsua_perror(THIS_FILE, "pjmedia unable to create SDP", status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000181 goto on_error;
182 }
183
184 /* Create the INVITE session: */
185
186 status = pjsip_inv_create_uac( dlg, offer, 0, &inv);
187 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000188 pjsua_perror(THIS_FILE, "Invite session creation failed", status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000189 goto on_error;
190 }
191
192
193 /* Create and associate our data in the session. */
194
Benny Prijonoa91a0032006-02-26 21:23:45 +0000195 pjsua.calls[call_index].inv = inv;
196
197 dlg->mod_data[pjsua.mod.id] = &pjsua.calls[call_index];
198 inv->mod_data[pjsua.mod.id] = &pjsua.calls[call_index];
Benny Prijono84126ab2006-02-09 09:30:09 +0000199
200
201 /* Set dialog Route-Set: */
202
Benny Prijonoa91a0032006-02-26 21:23:45 +0000203 if (!pj_list_empty(&pjsua.acc[acc_index].route_set))
204 pjsip_dlg_set_route_set(dlg, &pjsua.acc[acc_index].route_set);
Benny Prijono84126ab2006-02-09 09:30:09 +0000205
206
207 /* Set credentials: */
Benny Prijonodc39fe82006-05-26 12:17:46 +0000208 if (pjsua.config.acc_config[acc_index].cred_count) {
209 pjsua_acc_config *acc_cfg = &pjsua.config.acc_config[acc_index];
210 pjsip_auth_clt_set_credentials( &dlg->auth_sess,
211 acc_cfg->cred_count,
212 acc_cfg->cred_info);
213 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000214
215
216 /* Create initial INVITE: */
217
218 status = pjsip_inv_invite(inv, &tdata);
219 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000220 pjsua_perror(THIS_FILE, "Unable to create initial INVITE request",
221 status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000222 goto on_error;
223 }
224
225
226 /* Send initial INVITE: */
227
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +0000228 status = pjsip_inv_send_msg(inv, tdata);
Benny Prijono84126ab2006-02-09 09:30:09 +0000229 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000230 pjsua_perror(THIS_FILE, "Unable to send initial INVITE request",
231 status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000232
233 /* Upon failure to send first request, both dialog and invite
234 * session would have been cleared.
235 */
236 inv = NULL;
237 dlg = NULL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000238 goto on_error;
239 }
240
241
242 /* Done. */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000243
244 ++pjsua.call_cnt;
245
246 if (p_call_index)
247 *p_call_index = call_index;
Benny Prijono84126ab2006-02-09 09:30:09 +0000248
249 return PJ_SUCCESS;
250
251
252on_error:
Benny Prijono1c2bf462006-03-05 11:54:02 +0000253 if (inv != NULL) {
254 pjsip_inv_terminate(inv, PJSIP_SC_OK, PJ_FALSE);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000255 } else if (dlg) {
Benny Prijono1c2bf462006-03-05 11:54:02 +0000256 pjsip_dlg_terminate(dlg);
257 }
258
Benny Prijonoa91a0032006-02-26 21:23:45 +0000259 if (call_index != -1) {
260 pjsua.calls[call_index].inv = NULL;
261 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000262 return status;
263}
264
265
266/**
Benny Prijonodc39fe82006-05-26 12:17:46 +0000267 * Answer call.
268 */
269PJ_DEF(void) pjsua_call_answer(int call_index, int code)
270{
271 pjsip_tx_data *tdata;
272 pj_status_t status;
273
274 PJ_ASSERT_ON_FAIL(call_index >= 0 &&
275 call_index < (int)pjsua.config.max_calls,
276 return);
277
278 if (pjsua.calls[call_index].inv == NULL) {
279 PJ_LOG(3,(THIS_FILE, "Call %d already disconnected"));
280 return;
281 }
282
283 status = pjsip_inv_answer(pjsua.calls[call_index].inv,
284 code, NULL, NULL, &tdata);
285 if (status == PJ_SUCCESS)
286 status = pjsip_inv_send_msg(pjsua.calls[call_index].inv,
287 tdata);
288
289 if (status != PJ_SUCCESS)
290 pjsua_perror(THIS_FILE, "Unable to create/send response",
291 status);
292
293}
294
295
296/**
Benny Prijono84126ab2006-02-09 09:30:09 +0000297 * Handle incoming INVITE request.
298 */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000299pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata)
Benny Prijono84126ab2006-02-09 09:30:09 +0000300{
301 pjsip_dialog *dlg = pjsip_rdata_get_dlg(rdata);
302 pjsip_transaction *tsx = pjsip_rdata_get_tsx(rdata);
303 pjsip_msg *msg = rdata->msg_info.msg;
Benny Prijono26ff9062006-02-21 23:47:00 +0000304 pjsip_tx_data *response = NULL;
305 unsigned options = 0;
Benny Prijono1c2bf462006-03-05 11:54:02 +0000306 pjsip_inv_session *inv = NULL;
Benny Prijonoa91a0032006-02-26 21:23:45 +0000307 int acc_index;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000308 unsigned call_index;
Benny Prijono26ff9062006-02-21 23:47:00 +0000309 pjmedia_sdp_session *answer;
Benny Prijono26ff9062006-02-21 23:47:00 +0000310 pj_status_t status;
Benny Prijono84126ab2006-02-09 09:30:09 +0000311
Benny Prijono26ff9062006-02-21 23:47:00 +0000312 /* Don't want to handle anything but INVITE */
313 if (msg->line.req.method.id != PJSIP_INVITE_METHOD)
314 return PJ_FALSE;
315
316 /* Don't want to handle anything that's already associated with
317 * existing dialog or transaction.
Benny Prijono84126ab2006-02-09 09:30:09 +0000318 */
Benny Prijono26ff9062006-02-21 23:47:00 +0000319 if (dlg || tsx)
320 return PJ_FALSE;
Benny Prijono84126ab2006-02-09 09:30:09 +0000321
Benny Prijono84126ab2006-02-09 09:30:09 +0000322
Benny Prijono26ff9062006-02-21 23:47:00 +0000323 /* Verify that we can handle the request. */
324 status = pjsip_inv_verify_request(rdata, &options, NULL, NULL,
325 pjsua.endpt, &response);
326 if (status != PJ_SUCCESS) {
Benny Prijono84126ab2006-02-09 09:30:09 +0000327
Benny Prijono26ff9062006-02-21 23:47:00 +0000328 /*
329 * No we can't handle the incoming INVITE request.
330 */
Benny Prijono84126ab2006-02-09 09:30:09 +0000331
Benny Prijono26ff9062006-02-21 23:47:00 +0000332 if (response) {
333 pjsip_response_addr res_addr;
Benny Prijono84126ab2006-02-09 09:30:09 +0000334
Benny Prijono26ff9062006-02-21 23:47:00 +0000335 pjsip_get_response_addr(response->pool, rdata, &res_addr);
336 pjsip_endpt_send_response(pjsua.endpt, &res_addr, response,
337 NULL, NULL);
Benny Prijono84126ab2006-02-09 09:30:09 +0000338
339 } else {
Benny Prijono84126ab2006-02-09 09:30:09 +0000340
Benny Prijono26ff9062006-02-21 23:47:00 +0000341 /* Respond with 500 (Internal Server Error) */
342 pjsip_endpt_respond_stateless(pjsua.endpt, rdata, 500, NULL,
343 NULL, NULL);
Benny Prijono84126ab2006-02-09 09:30:09 +0000344 }
345
Benny Prijono26ff9062006-02-21 23:47:00 +0000346 return PJ_TRUE;
347 }
348
349
350 /*
351 * Yes we can handle the incoming INVITE request.
352 */
353
354 /* Find free call slot. */
Benny Prijonodc39fe82006-05-26 12:17:46 +0000355 for (call_index=0; call_index < pjsua.config.max_calls; ++call_index) {
Benny Prijonoa91a0032006-02-26 21:23:45 +0000356 if (pjsua.calls[call_index].inv == NULL)
Benny Prijono26ff9062006-02-21 23:47:00 +0000357 break;
358 }
359
Benny Prijonoa91a0032006-02-26 21:23:45 +0000360 if (call_index == PJSUA_MAX_CALLS) {
Benny Prijono26ff9062006-02-21 23:47:00 +0000361 pjsip_endpt_respond_stateless(pjsua.endpt, rdata,
362 PJSIP_SC_BUSY_HERE, NULL,
363 NULL, NULL);
Benny Prijono84126ab2006-02-09 09:30:09 +0000364 return PJ_TRUE;
365 }
366
Benny Prijonoe21e7842006-04-09 16:46:05 +0000367 /* Mark call start time. */
368 pj_gettimeofday(&pjsua.calls[call_index].start_time);
369
370 /* Reset first response time */
371 pjsua.calls[call_index].res_time.sec = 0;
Benny Prijono26ff9062006-02-21 23:47:00 +0000372
Benny Prijono26ff9062006-02-21 23:47:00 +0000373 /* Get media capability from media endpoint: */
374
Benny Prijonoa91a0032006-02-26 21:23:45 +0000375 status = pjmedia_endpt_create_sdp( pjsua.med_endpt, rdata->tp_info.pool, 1,
376 &pjsua.calls[call_index].skinfo,
Benny Prijono26ff9062006-02-21 23:47:00 +0000377 &answer );
378 if (status != PJ_SUCCESS) {
379 pjsip_endpt_respond_stateless(pjsua.endpt, rdata, 500, NULL,
380 NULL, NULL);
381
Benny Prijono26ff9062006-02-21 23:47:00 +0000382 return PJ_TRUE;
383 }
384
Benny Prijonoa91a0032006-02-26 21:23:45 +0000385 /* TODO:
386 *
387 * Get which account is most likely to be associated with this incoming
388 * call. We need the account to find which contact URI to put for
389 * the call.
390 */
391 acc_index = 0;
392
Benny Prijono26ff9062006-02-21 23:47:00 +0000393 /* Create dialog: */
394
395 status = pjsip_dlg_create_uas( pjsip_ua_instance(), rdata,
Benny Prijonodc39fe82006-05-26 12:17:46 +0000396 &pjsua.config.acc_config[acc_index].contact,
Benny Prijonoa91a0032006-02-26 21:23:45 +0000397 &dlg);
Benny Prijono26ff9062006-02-21 23:47:00 +0000398 if (status != PJ_SUCCESS) {
399 pjsip_endpt_respond_stateless(pjsua.endpt, rdata, 500, NULL,
400 NULL, NULL);
401
Benny Prijono26ff9062006-02-21 23:47:00 +0000402 return PJ_TRUE;
403 }
404
405
406 /* Create invite session: */
407
408 status = pjsip_inv_create_uas( dlg, rdata, answer, 0, &inv);
409 if (status != PJ_SUCCESS) {
410
Benny Prijonob0808372006-03-02 21:18:58 +0000411 pjsip_dlg_respond(dlg, rdata, 500, NULL, NULL, NULL);
Benny Prijono1c2bf462006-03-05 11:54:02 +0000412 pjsip_dlg_terminate(dlg);
Benny Prijono26ff9062006-02-21 23:47:00 +0000413 return PJ_TRUE;
414 }
415
416
417 /* Create and attach pjsua data to the dialog: */
418
Benny Prijonoa91a0032006-02-26 21:23:45 +0000419 pjsua.calls[call_index].inv = inv;
Benny Prijono26ff9062006-02-21 23:47:00 +0000420
Benny Prijonoa91a0032006-02-26 21:23:45 +0000421 dlg->mod_data[pjsua.mod.id] = &pjsua.calls[call_index];
422 inv->mod_data[pjsua.mod.id] = &pjsua.calls[call_index];
Benny Prijono26ff9062006-02-21 23:47:00 +0000423
424
Benny Prijono64f851e2006-02-23 13:49:28 +0000425 /* Must answer with some response to initial INVITE.
426 * If auto-answer flag is set, send 200 straight away, otherwise send 100.
427 */
428
429 status = pjsip_inv_initial_answer(inv, rdata,
Benny Prijonodc39fe82006-05-26 12:17:46 +0000430 (pjsua.config.auto_answer ?
431 pjsua.config.auto_answer : 100),
Benny Prijono64f851e2006-02-23 13:49:28 +0000432 NULL, NULL, &response);
Benny Prijono26ff9062006-02-21 23:47:00 +0000433 if (status != PJ_SUCCESS) {
434
Benny Prijonoccb03fa2006-03-06 13:35:47 +0000435 int st_code;
Benny Prijonod2ae29d2006-02-22 15:42:31 +0000436
Benny Prijonoccb03fa2006-03-06 13:35:47 +0000437 pjsua_perror(THIS_FILE, "Unable to send answer to incoming INVITE",
438 status);
439
440 /* If failed to send 2xx response, there's a good chance that it is
441 * because SDP negotiation has failed.
442 */
Benny Prijonodc39fe82006-05-26 12:17:46 +0000443 if (pjsua.config.auto_answer/100 == 2)
Benny Prijonoccb03fa2006-03-06 13:35:47 +0000444 st_code = PJSIP_SC_UNSUPPORTED_MEDIA_TYPE;
445 else
446 st_code = 500;
447
448 pjsip_dlg_respond(dlg, rdata, st_code, NULL, NULL, NULL);
449 pjsip_inv_terminate(inv, st_code, PJ_FALSE);
450 return PJ_TRUE;
Benny Prijono26ff9062006-02-21 23:47:00 +0000451
452 } else {
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +0000453 status = pjsip_inv_send_msg(inv, response);
Benny Prijonod2ae29d2006-02-22 15:42:31 +0000454 if (status != PJ_SUCCESS)
455 pjsua_perror(THIS_FILE, "Unable to send 100 response", status);
Benny Prijono26ff9062006-02-21 23:47:00 +0000456 }
457
Benny Prijonodc39fe82006-05-26 12:17:46 +0000458 if (pjsua.config.auto_answer < 200) {
Benny Prijono64f851e2006-02-23 13:49:28 +0000459 PJ_LOG(3,(THIS_FILE,
460 "\nIncoming call!!\n"
461 "From: %.*s\n"
462 "To: %.*s\n"
463 "(press 'a' to answer, 'h' to decline)",
464 (int)dlg->remote.info_str.slen,
465 dlg->remote.info_str.ptr,
466 (int)dlg->local.info_str.slen,
467 dlg->local.info_str.ptr));
468 } else {
469 PJ_LOG(3,(THIS_FILE,
470 "Call From:%.*s To:%.*s was answered with %d (%s)",
471 (int)dlg->remote.info_str.slen,
472 dlg->remote.info_str.ptr,
473 (int)dlg->local.info_str.slen,
474 dlg->local.info_str.ptr,
Benny Prijonodc39fe82006-05-26 12:17:46 +0000475 pjsua.config.auto_answer,
476 pjsip_get_status_text(pjsua.config.auto_answer)->ptr ));
Benny Prijono64f851e2006-02-23 13:49:28 +0000477 }
478
Benny Prijonoa91a0032006-02-26 21:23:45 +0000479 ++pjsua.call_cnt;
480
Benny Prijono105217f2006-03-06 16:25:59 +0000481 /* Schedule timer to refresh. */
Benny Prijonodc39fe82006-05-26 12:17:46 +0000482 if (pjsua.config.uas_refresh > 0) {
Benny Prijono105217f2006-03-06 16:25:59 +0000483 schedule_call_timer( &pjsua.calls[call_index],
484 &pjsua.calls[call_index].refresh_tm,
485 REFRESH_CALL_TIMER,
Benny Prijonodc39fe82006-05-26 12:17:46 +0000486 pjsua.config.uas_refresh);
Benny Prijono105217f2006-03-06 16:25:59 +0000487 }
488
489 /* Schedule timer to hangup call. */
Benny Prijonodc39fe82006-05-26 12:17:46 +0000490 if (pjsua.config.uas_duration > 0) {
Benny Prijono105217f2006-03-06 16:25:59 +0000491 schedule_call_timer( &pjsua.calls[call_index],
492 &pjsua.calls[call_index].hangup_tm,
493 HANGUP_CALL_TIMER,
Benny Prijonodc39fe82006-05-26 12:17:46 +0000494 pjsua.config.uas_duration);
Benny Prijono105217f2006-03-06 16:25:59 +0000495 }
496
Benny Prijono26ff9062006-02-21 23:47:00 +0000497 /* This INVITE request has been handled. */
498 return PJ_TRUE;
Benny Prijono84126ab2006-02-09 09:30:09 +0000499}
500
501
502/*
503 * This callback receives notification from invite session when the
504 * session state has changed.
505 */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000506static void pjsua_call_on_state_changed(pjsip_inv_session *inv,
507 pjsip_event *e)
Benny Prijono84126ab2006-02-09 09:30:09 +0000508{
Benny Prijonoa91a0032006-02-26 21:23:45 +0000509 pjsua_call *call = inv->dlg->mod_data[pjsua.mod.id];
Benny Prijono26ff9062006-02-21 23:47:00 +0000510
Benny Prijonoe21e7842006-04-09 16:46:05 +0000511 if (!call)
512 return;
513
514 /* Get call times */
515 switch (inv->state) {
516 case PJSIP_INV_STATE_EARLY:
517 case PJSIP_INV_STATE_CONNECTING:
518 if (call->res_time.sec == 0)
519 pj_gettimeofday(&call->res_time);
520 break;
521 case PJSIP_INV_STATE_CONFIRMED:
522 pj_gettimeofday(&call->conn_time);
523 break;
524 case PJSIP_INV_STATE_DISCONNECTED:
525 pj_gettimeofday(&call->dis_time);
526 break;
Benny Prijono8befd9f2006-05-13 22:46:23 +0000527 default:
528 /* Nothing to do. Just to keep gcc from complaining about
529 * unused enums.
530 */
531 break;
Benny Prijonoe21e7842006-04-09 16:46:05 +0000532 }
533
Benny Prijono26ff9062006-02-21 23:47:00 +0000534 /* If this is an outgoing INVITE that was created because of
535 * REFER/transfer, send NOTIFY to transferer.
536 */
Benny Prijonoe21e7842006-04-09 16:46:05 +0000537 if (call->xfer_sub && e->type==PJSIP_EVENT_TSX_STATE) {
Benny Prijono26ff9062006-02-21 23:47:00 +0000538 int st_code = -1;
539 pjsip_evsub_state ev_state = PJSIP_EVSUB_STATE_ACTIVE;
540
541
Benny Prijonoa91a0032006-02-26 21:23:45 +0000542 switch (call->inv->state) {
Benny Prijono26ff9062006-02-21 23:47:00 +0000543 case PJSIP_INV_STATE_NULL:
544 case PJSIP_INV_STATE_CALLING:
545 /* Do nothing */
546 break;
547
548 case PJSIP_INV_STATE_EARLY:
549 case PJSIP_INV_STATE_CONNECTING:
550 st_code = e->body.tsx_state.tsx->status_code;
551 ev_state = PJSIP_EVSUB_STATE_ACTIVE;
552 break;
553
554 case PJSIP_INV_STATE_CONFIRMED:
555 /* When state is confirmed, send the final 200/OK and terminate
556 * subscription.
557 */
558 st_code = e->body.tsx_state.tsx->status_code;
559 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
560 break;
561
562 case PJSIP_INV_STATE_DISCONNECTED:
563 st_code = e->body.tsx_state.tsx->status_code;
564 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
565 break;
Benny Prijono8befd9f2006-05-13 22:46:23 +0000566
567 default:
568 /* Nothing to do. Just to keep gcc from complaining about
569 * unused enums.
570 */
571 break;
Benny Prijono26ff9062006-02-21 23:47:00 +0000572 }
573
574 if (st_code != -1) {
575 pjsip_tx_data *tdata;
576 pj_status_t status;
577
Benny Prijonoa91a0032006-02-26 21:23:45 +0000578 status = pjsip_xfer_notify( call->xfer_sub,
Benny Prijono26ff9062006-02-21 23:47:00 +0000579 ev_state, st_code,
580 NULL, &tdata);
581 if (status != PJ_SUCCESS) {
582 pjsua_perror(THIS_FILE, "Unable to create NOTIFY", status);
583 } else {
Benny Prijonoa91a0032006-02-26 21:23:45 +0000584 status = pjsip_xfer_send_request(call->xfer_sub, tdata);
Benny Prijono26ff9062006-02-21 23:47:00 +0000585 if (status != PJ_SUCCESS) {
586 pjsua_perror(THIS_FILE, "Unable to send NOTIFY", status);
587 }
588 }
589 }
590 }
591
Benny Prijono84126ab2006-02-09 09:30:09 +0000592
Benny Prijonodc39fe82006-05-26 12:17:46 +0000593 if (pjsua.cb.on_call_state)
594 (*pjsua.cb.on_call_state)(call->index, e);
Benny Prijonoa91a0032006-02-26 21:23:45 +0000595
596 /* call->inv may be NULL now */
597
Benny Prijono84126ab2006-02-09 09:30:09 +0000598 /* Destroy media session when invite session is disconnected. */
599 if (inv->state == PJSIP_INV_STATE_DISCONNECTED) {
Benny Prijono632ce712006-02-09 14:01:40 +0000600
Benny Prijonoa91a0032006-02-26 21:23:45 +0000601 pj_assert(call != NULL);
Benny Prijono632ce712006-02-09 14:01:40 +0000602
Benny Prijono275fd682006-03-22 11:59:11 +0000603 if (call)
604 call_destroy_media(call->index);
Benny Prijono84126ab2006-02-09 09:30:09 +0000605
Benny Prijono105217f2006-03-06 16:25:59 +0000606 /* Remove timers. */
607 schedule_call_timer(call, &call->refresh_tm, REFRESH_CALL_TIMER, 0);
608 schedule_call_timer(call, &call->hangup_tm, HANGUP_CALL_TIMER, 0);
609
610 /* Free call */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000611 call->inv = NULL;
612 --pjsua.call_cnt;
Benny Prijono84126ab2006-02-09 09:30:09 +0000613 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000614}
615
616
617/*
Benny Prijono26ff9062006-02-21 23:47:00 +0000618 * Callback called by event framework when the xfer subscription state
619 * has changed.
620 */
621static void xfer_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
622{
623
624 PJ_UNUSED_ARG(event);
625
626 /*
627 * We're only interested when subscription is terminated, to
628 * clear the xfer_sub member of the inv_data.
629 */
630 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
Benny Prijonoa91a0032006-02-26 21:23:45 +0000631 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +0000632
Benny Prijonoa91a0032006-02-26 21:23:45 +0000633 call = pjsip_evsub_get_mod_data(sub, pjsua.mod.id);
634 if (!call)
Benny Prijono26ff9062006-02-21 23:47:00 +0000635 return;
636
637 pjsip_evsub_set_mod_data(sub, pjsua.mod.id, NULL);
Benny Prijonoa91a0032006-02-26 21:23:45 +0000638 call->xfer_sub = NULL;
Benny Prijono26ff9062006-02-21 23:47:00 +0000639
640 PJ_LOG(3,(THIS_FILE, "Xfer subscription terminated"));
641 }
642}
643
644
645/*
646 * Follow transfer (REFER) request.
647 */
648static void on_call_transfered( pjsip_inv_session *inv,
649 pjsip_rx_data *rdata )
650{
651 pj_status_t status;
652 pjsip_tx_data *tdata;
Benny Prijonoa91a0032006-02-26 21:23:45 +0000653 pjsua_call *existing_call;
654 int new_call;
Benny Prijono26ff9062006-02-21 23:47:00 +0000655 const pj_str_t str_refer_to = { "Refer-To", 8};
656 pjsip_generic_string_hdr *refer_to;
657 char *uri;
658 struct pjsip_evsub_user xfer_cb;
659 pjsip_evsub *sub;
660
Benny Prijonoa91a0032006-02-26 21:23:45 +0000661 existing_call = inv->dlg->mod_data[pjsua.mod.id];
662
Benny Prijono26ff9062006-02-21 23:47:00 +0000663 /* Find the Refer-To header */
664 refer_to = (pjsip_generic_string_hdr*)
665 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_to, NULL);
666
667 if (refer_to == NULL) {
668 /* Invalid Request.
669 * No Refer-To header!
670 */
671 PJ_LOG(4,(THIS_FILE, "Received REFER without Refer-To header!"));
Benny Prijonob0808372006-03-02 21:18:58 +0000672 pjsip_dlg_respond( inv->dlg, rdata, 400, NULL, NULL, NULL);
Benny Prijono26ff9062006-02-21 23:47:00 +0000673 return;
674 }
675
676 PJ_LOG(3,(THIS_FILE, "Call to %.*s is being transfered to %.*s",
677 (int)inv->dlg->remote.info_str.slen,
678 inv->dlg->remote.info_str.ptr,
679 (int)refer_to->hvalue.slen,
680 refer_to->hvalue.ptr));
681
682 /* Init callback */
683 pj_memset(&xfer_cb, 0, sizeof(xfer_cb));
684 xfer_cb.on_evsub_state = &xfer_on_evsub_state;
685
686 /* Create transferee event subscription */
687 status = pjsip_xfer_create_uas( inv->dlg, &xfer_cb, rdata, &sub);
688 if (status != PJ_SUCCESS) {
689 pjsua_perror(THIS_FILE, "Unable to create xfer uas", status);
Benny Prijonob0808372006-03-02 21:18:58 +0000690 pjsip_dlg_respond( inv->dlg, rdata, 500, NULL, NULL, NULL);
Benny Prijono26ff9062006-02-21 23:47:00 +0000691 return;
692 }
693
694 /* Accept the REFER request, send 200 (OK). */
695 pjsip_xfer_accept(sub, rdata, 200, NULL);
696
697 /* Create initial NOTIFY request */
698 status = pjsip_xfer_notify( sub, PJSIP_EVSUB_STATE_ACTIVE,
699 100, NULL, &tdata);
700 if (status != PJ_SUCCESS) {
701 pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER", status);
702 return;
703 }
704
705 /* Send initial NOTIFY request */
706 status = pjsip_xfer_send_request( sub, tdata);
707 if (status != PJ_SUCCESS) {
708 pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER", status);
709 return;
710 }
711
712 /* We're cheating here.
713 * We need to get a null terminated string from a pj_str_t.
714 * So grab the pointer from the hvalue and NULL terminate it, knowing
715 * that the NULL position will be occupied by a newline.
716 */
717 uri = refer_to->hvalue.ptr;
718 uri[refer_to->hvalue.slen] = '\0';
719
720 /* Now make the outgoing call. */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000721 status = pjsua_make_call(existing_call->acc_index, uri, &new_call);
Benny Prijono26ff9062006-02-21 23:47:00 +0000722 if (status != PJ_SUCCESS) {
723
724 /* Notify xferer about the error */
725 status = pjsip_xfer_notify(sub, PJSIP_EVSUB_STATE_TERMINATED,
726 500, NULL, &tdata);
727 if (status != PJ_SUCCESS) {
728 pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER",
729 status);
730 return;
731 }
732 status = pjsip_xfer_send_request(sub, tdata);
733 if (status != PJ_SUCCESS) {
734 pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER",
735 status);
736 return;
737 }
738 return;
739 }
740
741 /* Put the server subscription in inv_data.
742 * Subsequent state changed in pjsua_inv_on_state_changed() will be
743 * reported back to the server subscription.
744 */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000745 pjsua.calls[new_call].xfer_sub = sub;
Benny Prijono26ff9062006-02-21 23:47:00 +0000746
747 /* Put the invite_data in the subscription. */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000748 pjsip_evsub_set_mod_data(sub, pjsua.mod.id, &pjsua.calls[new_call]);
Benny Prijono26ff9062006-02-21 23:47:00 +0000749}
750
751
752/*
753 * This callback is called when transaction state has changed in INVITE
Benny Prijonob0808372006-03-02 21:18:58 +0000754 * session. We use this to trap:
755 * - incoming REFER request.
756 * - incoming MESSAGE request.
Benny Prijono26ff9062006-02-21 23:47:00 +0000757 */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000758static void pjsua_call_on_tsx_state_changed(pjsip_inv_session *inv,
759 pjsip_transaction *tsx,
760 pjsip_event *e)
Benny Prijono26ff9062006-02-21 23:47:00 +0000761{
Benny Prijonoa91a0032006-02-26 21:23:45 +0000762 pjsua_call *call = inv->dlg->mod_data[pjsua.mod.id];
763
Benny Prijono26ff9062006-02-21 23:47:00 +0000764 if (tsx->role==PJSIP_ROLE_UAS &&
765 tsx->state==PJSIP_TSX_STATE_TRYING &&
766 pjsip_method_cmp(&tsx->method, &pjsip_refer_method)==0)
767 {
768 /*
769 * Incoming REFER request.
770 */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000771 on_call_transfered(call->inv, e->body.tsx_state.src.rdata);
Benny Prijonob0808372006-03-02 21:18:58 +0000772
Benny Prijono26ff9062006-02-21 23:47:00 +0000773 }
Benny Prijonob0808372006-03-02 21:18:58 +0000774 else if (tsx->role==PJSIP_ROLE_UAS &&
775 tsx->state==PJSIP_TSX_STATE_TRYING &&
776 pjsip_method_cmp(&tsx->method, &pjsip_message_method)==0)
777 {
778 /*
779 * Incoming MESSAGE request!
780 */
781 pjsip_rx_data *rdata;
782 pjsip_msg *msg;
783 pjsip_accept_hdr *accept_hdr;
784 pj_status_t status;
785
786 rdata = e->body.tsx_state.src.rdata;
787 msg = rdata->msg_info.msg;
788
789 /* Request MUST have message body, with Content-Type equal to
790 * "text/plain".
791 */
792 if (pjsua_im_accept_pager(rdata, &accept_hdr) == PJ_FALSE) {
793
794 pjsip_hdr hdr_list;
795
796 pj_list_init(&hdr_list);
797 pj_list_push_back(&hdr_list, accept_hdr);
798
799 pjsip_dlg_respond( inv->dlg, rdata, PJSIP_SC_NOT_ACCEPTABLE_HERE,
800 NULL, &hdr_list, NULL );
801 return;
802 }
803
804 /* Respond with 200 first, so that remote doesn't retransmit in case
805 * the UI takes too long to process the message.
806 */
807 status = pjsip_dlg_respond( inv->dlg, rdata, 200, NULL, NULL, NULL);
808
809 /* Process MESSAGE request */
810 pjsua_im_process_pager(call->index, &inv->dlg->remote.info_str,
811 &inv->dlg->local.info_str, rdata);
812 }
813
Benny Prijono26ff9062006-02-21 23:47:00 +0000814}
815
816
817/*
Benny Prijono84126ab2006-02-09 09:30:09 +0000818 * This callback is called by invite session framework when UAC session
819 * has forked.
820 */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000821static void pjsua_call_on_forked( pjsip_inv_session *inv,
822 pjsip_event *e)
Benny Prijono84126ab2006-02-09 09:30:09 +0000823{
824 PJ_UNUSED_ARG(inv);
825 PJ_UNUSED_ARG(e);
826
827 PJ_TODO(HANDLE_FORKED_DIALOG);
828}
829
830
831/*
Benny Prijono26ff9062006-02-21 23:47:00 +0000832 * Create inactive SDP for call hold.
833 */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000834static pj_status_t create_inactive_sdp(pjsua_call *call,
Benny Prijono26ff9062006-02-21 23:47:00 +0000835 pjmedia_sdp_session **p_answer)
836{
837 pj_status_t status;
838 pjmedia_sdp_conn *conn;
839 pjmedia_sdp_attr *attr;
840 pjmedia_sdp_session *sdp;
841
842 /* Create new offer */
843 status = pjmedia_endpt_create_sdp(pjsua.med_endpt, pjsua.pool, 1,
Benny Prijonoa91a0032006-02-26 21:23:45 +0000844 &call->skinfo, &sdp);
Benny Prijono26ff9062006-02-21 23:47:00 +0000845 if (status != PJ_SUCCESS) {
846 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
847 return status;
848 }
849
850 /* Get SDP media connection line */
851 conn = sdp->media[0]->conn;
852 if (!conn)
853 conn = sdp->conn;
854
855 /* Modify address */
856 conn->addr = pj_str("0.0.0.0");
857
858 /* Remove existing directions attributes */
859 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "sendrecv");
860 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "sendonly");
861 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "recvonly");
862 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "inactive");
863
864 /* Add inactive attribute */
865 attr = pjmedia_sdp_attr_create(pjsua.pool, "inactive", NULL);
866 pjmedia_sdp_media_add_attr(sdp->media[0], attr);
867
868 *p_answer = sdp;
869
870 return status;
871}
872
873/*
874 * Called when session received new offer.
875 */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000876static void pjsua_call_on_rx_offer(pjsip_inv_session *inv,
877 const pjmedia_sdp_session *offer)
Benny Prijono26ff9062006-02-21 23:47:00 +0000878{
Benny Prijonoa91a0032006-02-26 21:23:45 +0000879 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +0000880 pjmedia_sdp_conn *conn;
881 pjmedia_sdp_session *answer;
882 pj_bool_t is_remote_active;
883 pj_status_t status;
884
Benny Prijonoa91a0032006-02-26 21:23:45 +0000885 call = inv->dlg->mod_data[pjsua.mod.id];
Benny Prijono26ff9062006-02-21 23:47:00 +0000886
887 /*
888 * See if remote is offering active media (i.e. not on-hold)
889 */
890 is_remote_active = PJ_TRUE;
891
892 conn = offer->media[0]->conn;
893 if (!conn)
894 conn = offer->conn;
895
896 if (pj_strcmp2(&conn->addr, "0.0.0.0")==0 ||
897 pj_strcmp2(&conn->addr, "0")==0)
898 {
899 is_remote_active = PJ_FALSE;
900
901 }
902 else if (pjmedia_sdp_media_find_attr2(offer->media[0], "inactive", NULL))
903 {
904 is_remote_active = PJ_FALSE;
905 }
906
907 PJ_LOG(4,(THIS_FILE, "Received SDP offer, remote media is %s",
908 (is_remote_active ? "active" : "inactive")));
909
910 /* Supply candidate answer */
911 if (is_remote_active) {
Benny Prijonoa91a0032006-02-26 21:23:45 +0000912 status = pjmedia_endpt_create_sdp( pjsua.med_endpt, call->inv->pool, 1,
913 &call->skinfo, &answer);
Benny Prijono26ff9062006-02-21 23:47:00 +0000914 } else {
Benny Prijonoa91a0032006-02-26 21:23:45 +0000915 status = create_inactive_sdp( call, &answer );
Benny Prijono26ff9062006-02-21 23:47:00 +0000916 }
917
918 if (status != PJ_SUCCESS) {
919 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
920 return;
921 }
922
Benny Prijonoa91a0032006-02-26 21:23:45 +0000923 status = pjsip_inv_set_sdp_answer(call->inv, answer);
Benny Prijono26ff9062006-02-21 23:47:00 +0000924 if (status != PJ_SUCCESS) {
925 pjsua_perror(THIS_FILE, "Unable to set answer", status);
926 return;
927 }
928
929}
930
Benny Prijono8befd9f2006-05-13 22:46:23 +0000931#if 0
Benny Prijono1c2bf462006-03-05 11:54:02 +0000932/* Disconnect call */
933static void call_disconnect(pjsip_inv_session *inv,
934 int st_code)
935{
936 pjsip_tx_data *tdata;
937 pj_status_t status;
938
939 status = pjsip_inv_end_session(inv, st_code, NULL, &tdata);
940 if (status == PJ_SUCCESS)
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +0000941 status = pjsip_inv_send_msg(inv, tdata);
Benny Prijono1c2bf462006-03-05 11:54:02 +0000942
943 if (status != PJ_SUCCESS) {
944 pjsua_perror(THIS_FILE, "Unable to disconnect call", status);
945 }
946}
Benny Prijono8befd9f2006-05-13 22:46:23 +0000947#endif
Benny Prijono26ff9062006-02-21 23:47:00 +0000948
949/*
Benny Prijono84126ab2006-02-09 09:30:09 +0000950 * Callback to be called when SDP offer/answer negotiation has just completed
951 * in the session. This function will start/update media if negotiation
952 * has succeeded.
953 */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000954static void pjsua_call_on_media_update(pjsip_inv_session *inv,
955 pj_status_t status)
Benny Prijono84126ab2006-02-09 09:30:09 +0000956{
Benny Prijonoa91a0032006-02-26 21:23:45 +0000957 pjsua_call *call;
Benny Prijono8befd9f2006-05-13 22:46:23 +0000958 pjmedia_session_info sess_info;
Benny Prijono84126ab2006-02-09 09:30:09 +0000959 const pjmedia_sdp_session *local_sdp;
960 const pjmedia_sdp_session *remote_sdp;
Benny Prijono26ff9062006-02-21 23:47:00 +0000961 pjmedia_port *media_port;
962 pj_str_t port_name;
963 char tmp[PJSIP_MAX_URL_SIZE];
Benny Prijono84126ab2006-02-09 09:30:09 +0000964
Benny Prijonoa91a0032006-02-26 21:23:45 +0000965 call = inv->dlg->mod_data[pjsua.mod.id];
966
Benny Prijono84126ab2006-02-09 09:30:09 +0000967 if (status != PJ_SUCCESS) {
968
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000969 pjsua_perror(THIS_FILE, "SDP negotiation has failed", status);
Benny Prijono1c2bf462006-03-05 11:54:02 +0000970
Benny Prijonoccb03fa2006-03-06 13:35:47 +0000971 /* Disconnect call if we're not in the middle of initializing an
972 * UAS dialog and if this is not a re-INVITE
973 */
974 if (inv->state != PJSIP_INV_STATE_NULL &&
975 inv->state != PJSIP_INV_STATE_CONFIRMED)
976 {
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +0000977 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
Benny Prijono1c2bf462006-03-05 11:54:02 +0000978 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000979 return;
980
981 }
982
983 /* Destroy existing media session, if any. */
984
Benny Prijono275fd682006-03-22 11:59:11 +0000985 if (call)
986 call_destroy_media(call->index);
Benny Prijono84126ab2006-02-09 09:30:09 +0000987
988 /* Get local and remote SDP */
989
Benny Prijonoa91a0032006-02-26 21:23:45 +0000990 status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &local_sdp);
Benny Prijono84126ab2006-02-09 09:30:09 +0000991 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000992 pjsua_perror(THIS_FILE,
993 "Unable to retrieve currently active local SDP",
994 status);
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +0000995 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
Benny Prijono84126ab2006-02-09 09:30:09 +0000996 return;
997 }
998
999
Benny Prijonoa91a0032006-02-26 21:23:45 +00001000 status = pjmedia_sdp_neg_get_active_remote(call->inv->neg, &remote_sdp);
Benny Prijono84126ab2006-02-09 09:30:09 +00001001 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +00001002 pjsua_perror(THIS_FILE,
1003 "Unable to retrieve currently active remote SDP",
1004 status);
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +00001005 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
Benny Prijono84126ab2006-02-09 09:30:09 +00001006 return;
1007 }
1008
Benny Prijonodc39fe82006-05-26 12:17:46 +00001009 if (pjsua.config.null_audio)
Benny Prijono26ff9062006-02-21 23:47:00 +00001010 return;
Benny Prijono8befd9f2006-05-13 22:46:23 +00001011
1012 /* Create media session info based on SDP parameters.
1013 * We only support one stream per session at the moment
1014 */
1015 status = pjmedia_session_info_from_sdp( call->inv->dlg->pool,
Benny Prijonob04c9e02006-05-17 17:17:39 +00001016 pjsua.med_endpt,
1017 1,&sess_info,
Benny Prijono8befd9f2006-05-13 22:46:23 +00001018 local_sdp, remote_sdp);
1019 if (status != PJ_SUCCESS) {
1020 pjsua_perror(THIS_FILE, "Unable to create media session",
1021 status);
1022 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
1023 return;
1024 }
1025
1026 /* Override ptime, if this option is specified. */
Benny Prijonodc39fe82006-05-26 12:17:46 +00001027 if (pjsua.config.ptime) {
Benny Prijono8befd9f2006-05-13 22:46:23 +00001028 sess_info.stream_info[0].param->setting.frm_per_pkt = (pj_uint8_t)
Benny Prijonodc39fe82006-05-26 12:17:46 +00001029 (pjsua.config.ptime /
1030 sess_info.stream_info[0].param->info.frm_ptime);
Benny Prijono8befd9f2006-05-13 22:46:23 +00001031 if (sess_info.stream_info[0].param->setting.frm_per_pkt==0)
1032 sess_info.stream_info[0].param->setting.frm_per_pkt = 1;
1033 }
1034
1035 /* Optionally, application may modify other stream settings here
1036 * (such as jitter buffer parameters, codec ptime, etc.)
1037 */
1038
1039 /* Create session based on session info. */
1040 status = pjmedia_session_create( pjsua.med_endpt, &sess_info,
Benny Prijonob04c9e02006-05-17 17:17:39 +00001041 &call->med_tp,
Benny Prijono8befd9f2006-05-13 22:46:23 +00001042 call, &call->session );
Benny Prijono26ff9062006-02-21 23:47:00 +00001043 if (status != PJ_SUCCESS) {
1044 pjsua_perror(THIS_FILE, "Unable to create media session",
1045 status);
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +00001046 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
Benny Prijono26ff9062006-02-21 23:47:00 +00001047 return;
Benny Prijono84126ab2006-02-09 09:30:09 +00001048 }
Benny Prijono26ff9062006-02-21 23:47:00 +00001049
1050
1051 /* Get the port interface of the first stream in the session.
1052 * We need the port interface to add to the conference bridge.
1053 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00001054 pjmedia_session_get_port(call->session, 0, &media_port);
Benny Prijono26ff9062006-02-21 23:47:00 +00001055
1056
1057 /*
1058 * Add the call to conference bridge.
1059 */
1060 port_name.ptr = tmp;
1061 port_name.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI,
Benny Prijonoa91a0032006-02-26 21:23:45 +00001062 call->inv->dlg->remote.info->uri,
Benny Prijono26ff9062006-02-21 23:47:00 +00001063 tmp, sizeof(tmp));
1064 if (port_name.slen < 1) {
1065 port_name = pj_str("call");
1066 }
Benny Prijonoa91a0032006-02-26 21:23:45 +00001067 status = pjmedia_conf_add_port( pjsua.mconf, call->inv->pool,
Benny Prijono26ff9062006-02-21 23:47:00 +00001068 media_port,
1069 &port_name,
Benny Prijonoa91a0032006-02-26 21:23:45 +00001070 &call->conf_slot);
Benny Prijono26ff9062006-02-21 23:47:00 +00001071 if (status != PJ_SUCCESS) {
1072 pjsua_perror(THIS_FILE, "Unable to create conference slot",
1073 status);
Benny Prijono275fd682006-03-22 11:59:11 +00001074 call_destroy_media(call->index);
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +00001075 //call_disconnect(inv, PJSIP_SC_INTERNAL_SERVER_ERROR);
Benny Prijono26ff9062006-02-21 23:47:00 +00001076 return;
1077 }
1078
Benny Prijono64f851e2006-02-23 13:49:28 +00001079 /* If auto-play is configured, connect the call to the file player
1080 * port
Benny Prijono26ff9062006-02-21 23:47:00 +00001081 */
Benny Prijonodc39fe82006-05-26 12:17:46 +00001082 if (pjsua.config.auto_play && pjsua.config.wav_file.slen &&
Benny Prijonoa91a0032006-02-26 21:23:45 +00001083 call->inv->role == PJSIP_ROLE_UAS)
1084 {
Benny Prijono64f851e2006-02-23 13:49:28 +00001085
1086 pjmedia_conf_connect_port( pjsua.mconf, pjsua.wav_slot,
Benny Prijonob100d692006-03-17 00:16:01 +00001087 call->conf_slot, 0);
Benny Prijonoa91a0032006-02-26 21:23:45 +00001088
Benny Prijonob8c25182006-04-29 08:31:09 +00001089 }
Benny Prijonodc39fe82006-05-26 12:17:46 +00001090 if (pjsua.config.auto_loop && call->inv->role == PJSIP_ROLE_UAS) {
Benny Prijonoa91a0032006-02-26 21:23:45 +00001091
1092 pjmedia_conf_connect_port( pjsua.mconf, call->conf_slot,
Benny Prijonob100d692006-03-17 00:16:01 +00001093 call->conf_slot, 0);
Benny Prijonoa91a0032006-02-26 21:23:45 +00001094
Benny Prijonob8c25182006-04-29 08:31:09 +00001095 }
Benny Prijonodc39fe82006-05-26 12:17:46 +00001096 if (pjsua.config.auto_conf) {
1097 unsigned i;
Benny Prijonoa91a0032006-02-26 21:23:45 +00001098
Benny Prijonob100d692006-03-17 00:16:01 +00001099 pjmedia_conf_connect_port( pjsua.mconf, 0, call->conf_slot, 0);
1100 pjmedia_conf_connect_port( pjsua.mconf, call->conf_slot, 0, 0);
Benny Prijonoa91a0032006-02-26 21:23:45 +00001101
Benny Prijonodc39fe82006-05-26 12:17:46 +00001102 for (i=0; i < pjsua.config.max_calls; ++i) {
Benny Prijonoa91a0032006-02-26 21:23:45 +00001103
1104 if (!pjsua.calls[i].session)
1105 continue;
1106
1107 pjmedia_conf_connect_port( pjsua.mconf, call->conf_slot,
Benny Prijonob100d692006-03-17 00:16:01 +00001108 pjsua.calls[i].conf_slot, 0);
Benny Prijonoa91a0032006-02-26 21:23:45 +00001109 pjmedia_conf_connect_port( pjsua.mconf, pjsua.calls[i].conf_slot,
Benny Prijonob100d692006-03-17 00:16:01 +00001110 call->conf_slot, 0);
Benny Prijonoa91a0032006-02-26 21:23:45 +00001111 }
Benny Prijono64f851e2006-02-23 13:49:28 +00001112
Benny Prijonob8c25182006-04-29 08:31:09 +00001113 }
1114
1115 /* Normal operation: if no auto_xx is given, connect new call to
1116 * the sound device port (port zero) in the main conference bridge.
1117 */
Benny Prijonodc39fe82006-05-26 12:17:46 +00001118 if (pjsua.config.auto_play == 0 && pjsua.config.auto_loop == 0 &&
1119 pjsua.config.auto_conf == 0)
Benny Prijonob8c25182006-04-29 08:31:09 +00001120 {
Benny Prijonob100d692006-03-17 00:16:01 +00001121 pjmedia_conf_connect_port( pjsua.mconf, 0, call->conf_slot, 0);
1122 pjmedia_conf_connect_port( pjsua.mconf, call->conf_slot, 0, 0);
Benny Prijono64f851e2006-02-23 13:49:28 +00001123 }
1124
Benny Prijono26ff9062006-02-21 23:47:00 +00001125
1126 /* Done. */
1127 {
1128 struct pjmedia_session_info sess_info;
1129 char info[80];
1130 int info_len = 0;
1131 unsigned i;
1132
Benny Prijonoa91a0032006-02-26 21:23:45 +00001133 pjmedia_session_get_info(call->session, &sess_info);
Benny Prijono26ff9062006-02-21 23:47:00 +00001134 for (i=0; i<sess_info.stream_cnt; ++i) {
1135 int len;
1136 const char *dir;
1137 pjmedia_stream_info *strm_info = &sess_info.stream_info[i];
1138
1139 switch (strm_info->dir) {
1140 case PJMEDIA_DIR_NONE:
1141 dir = "inactive";
1142 break;
1143 case PJMEDIA_DIR_ENCODING:
1144 dir = "sendonly";
1145 break;
1146 case PJMEDIA_DIR_DECODING:
1147 dir = "recvonly";
1148 break;
1149 case PJMEDIA_DIR_ENCODING_DECODING:
1150 dir = "sendrecv";
1151 break;
1152 default:
1153 dir = "unknown";
1154 break;
1155 }
1156 len = pj_ansi_sprintf( info+info_len,
1157 ", stream #%d: %.*s (%s)", i,
1158 (int)strm_info->fmt.encoding_name.slen,
Benny Prijonoab7399b2006-02-27 00:40:31 +00001159 strm_info->fmt.encoding_name.ptr,
Benny Prijono26ff9062006-02-21 23:47:00 +00001160 dir);
1161 if (len > 0)
1162 info_len += len;
1163 }
1164 PJ_LOG(3,(THIS_FILE,"Media started%s", info));
1165 }
1166}
1167
1168
1169/*
1170 * Hangup call.
1171 */
Benny Prijonodc39fe82006-05-26 12:17:46 +00001172PJ_DEF(void) pjsua_call_hangup(int call_index)
Benny Prijono26ff9062006-02-21 23:47:00 +00001173{
Benny Prijonoa91a0032006-02-26 21:23:45 +00001174 pjsua_call *call;
Benny Prijono1c2bf462006-03-05 11:54:02 +00001175 int code;
Benny Prijono26ff9062006-02-21 23:47:00 +00001176 pj_status_t status;
1177 pjsip_tx_data *tdata;
1178
Benny Prijonoa91a0032006-02-26 21:23:45 +00001179
1180 call = &pjsua.calls[call_index];
1181
1182 if (!call->inv) {
1183 PJ_LOG(3,(THIS_FILE,"Call has been disconnected"));
1184 return;
1185 }
1186
Benny Prijono1c2bf462006-03-05 11:54:02 +00001187 if (call->inv->state == PJSIP_INV_STATE_CONFIRMED)
1188 code = PJSIP_SC_OK;
1189 else if (call->inv->role == PJSIP_ROLE_UAS)
1190 code = PJSIP_SC_DECLINE;
1191 else
1192 code = PJSIP_SC_REQUEST_TERMINATED;
1193
Benny Prijonoa91a0032006-02-26 21:23:45 +00001194 status = pjsip_inv_end_session(call->inv, code, NULL, &tdata);
Benny Prijono26ff9062006-02-21 23:47:00 +00001195 if (status != PJ_SUCCESS) {
1196 pjsua_perror(THIS_FILE,
1197 "Failed to create end session message",
1198 status);
1199 return;
1200 }
1201
Benny Prijonofccab712006-02-22 22:23:22 +00001202 /* pjsip_inv_end_session may return PJ_SUCCESS with NULL
1203 * as p_tdata when INVITE transaction has not been answered
1204 * with any provisional responses.
1205 */
1206 if (tdata == NULL)
1207 return;
1208
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +00001209 status = pjsip_inv_send_msg(call->inv, tdata);
Benny Prijono26ff9062006-02-21 23:47:00 +00001210 if (status != PJ_SUCCESS) {
1211 pjsua_perror(THIS_FILE,
1212 "Failed to send end session message",
1213 status);
1214 return;
1215 }
1216}
1217
1218
1219/*
1220 * Put call on-Hold.
1221 */
Benny Prijonodc39fe82006-05-26 12:17:46 +00001222PJ_DEF(void) pjsua_call_set_hold(int call_index)
Benny Prijono26ff9062006-02-21 23:47:00 +00001223{
1224 pjmedia_sdp_session *sdp;
Benny Prijonoa91a0032006-02-26 21:23:45 +00001225 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +00001226 pjsip_tx_data *tdata;
1227 pj_status_t status;
1228
Benny Prijonoa91a0032006-02-26 21:23:45 +00001229 call = &pjsua.calls[call_index];
1230
1231 if (!call->inv) {
1232 PJ_LOG(3,(THIS_FILE,"Call has been disconnected"));
1233 return;
1234 }
1235
1236 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
Benny Prijono26ff9062006-02-21 23:47:00 +00001237 PJ_LOG(3,(THIS_FILE, "Can not hold call that is not confirmed"));
1238 return;
1239 }
1240
Benny Prijonoa91a0032006-02-26 21:23:45 +00001241 status = create_inactive_sdp(call, &sdp);
Benny Prijono26ff9062006-02-21 23:47:00 +00001242 if (status != PJ_SUCCESS)
1243 return;
1244
1245 /* Send re-INVITE with new offer */
Benny Prijonoa91a0032006-02-26 21:23:45 +00001246 status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
Benny Prijono26ff9062006-02-21 23:47:00 +00001247 if (status != PJ_SUCCESS) {
1248 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
1249 return;
1250 }
1251
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +00001252 status = pjsip_inv_send_msg( call->inv, tdata);
Benny Prijono26ff9062006-02-21 23:47:00 +00001253 if (status != PJ_SUCCESS) {
1254 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
1255 return;
1256 }
1257}
1258
1259
1260/*
1261 * re-INVITE.
1262 */
Benny Prijonodc39fe82006-05-26 12:17:46 +00001263PJ_DEF(void) pjsua_call_reinvite(int call_index)
Benny Prijono26ff9062006-02-21 23:47:00 +00001264{
1265 pjmedia_sdp_session *sdp;
1266 pjsip_tx_data *tdata;
Benny Prijonoa91a0032006-02-26 21:23:45 +00001267 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +00001268 pj_status_t status;
1269
Benny Prijonoa91a0032006-02-26 21:23:45 +00001270 call = &pjsua.calls[call_index];
Benny Prijono26ff9062006-02-21 23:47:00 +00001271
Benny Prijonoa91a0032006-02-26 21:23:45 +00001272 if (!call->inv) {
1273 PJ_LOG(3,(THIS_FILE,"Call has been disconnected"));
1274 return;
1275 }
1276
1277
1278 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
Benny Prijono26ff9062006-02-21 23:47:00 +00001279 PJ_LOG(3,(THIS_FILE, "Can not re-INVITE call that is not confirmed"));
1280 return;
1281 }
1282
1283 /* Create SDP */
Benny Prijonoa91a0032006-02-26 21:23:45 +00001284 status = pjmedia_endpt_create_sdp( pjsua.med_endpt, call->inv->pool, 1,
1285 &call->skinfo, &sdp);
Benny Prijono26ff9062006-02-21 23:47:00 +00001286 if (status != PJ_SUCCESS) {
Benny Prijonoa91a0032006-02-26 21:23:45 +00001287 pjsua_perror(THIS_FILE, "Unable to get SDP from media endpoint",
1288 status);
Benny Prijono26ff9062006-02-21 23:47:00 +00001289 return;
1290 }
1291
1292 /* Send re-INVITE with new offer */
Benny Prijonoa91a0032006-02-26 21:23:45 +00001293 status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
Benny Prijono26ff9062006-02-21 23:47:00 +00001294 if (status != PJ_SUCCESS) {
1295 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
1296 return;
1297 }
1298
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +00001299 status = pjsip_inv_send_msg( call->inv, tdata);
Benny Prijono26ff9062006-02-21 23:47:00 +00001300 if (status != PJ_SUCCESS) {
1301 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
1302 return;
1303 }
1304}
1305
1306
1307/*
1308 * Transfer call.
1309 */
Benny Prijonodc39fe82006-05-26 12:17:46 +00001310PJ_DEF(void) pjsua_call_xfer(int call_index, const char *dest)
Benny Prijono26ff9062006-02-21 23:47:00 +00001311{
1312 pjsip_evsub *sub;
1313 pjsip_tx_data *tdata;
Benny Prijonoa91a0032006-02-26 21:23:45 +00001314 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +00001315 pj_str_t tmp;
1316 pj_status_t status;
Benny Prijonoa91a0032006-02-26 21:23:45 +00001317
Benny Prijono26ff9062006-02-21 23:47:00 +00001318
Benny Prijonoa91a0032006-02-26 21:23:45 +00001319 call = &pjsua.calls[call_index];
1320
1321 if (!call->inv) {
1322 PJ_LOG(3,(THIS_FILE,"Call has been disconnected"));
1323 return;
1324 }
1325
Benny Prijono26ff9062006-02-21 23:47:00 +00001326 /* Create xfer client subscription.
1327 * We're not interested in knowing the transfer result, so we
1328 * put NULL as the callback.
1329 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00001330 status = pjsip_xfer_create_uac(call->inv->dlg, NULL, &sub);
Benny Prijono26ff9062006-02-21 23:47:00 +00001331 if (status != PJ_SUCCESS) {
1332 pjsua_perror(THIS_FILE, "Unable to create xfer", status);
1333 return;
1334 }
1335
1336 /*
1337 * Create REFER request.
1338 */
1339 status = pjsip_xfer_initiate(sub, pj_cstr(&tmp, dest), &tdata);
1340 if (status != PJ_SUCCESS) {
1341 pjsua_perror(THIS_FILE, "Unable to create REFER request", status);
1342 return;
1343 }
1344
1345 /* Send. */
1346 status = pjsip_xfer_send_request(sub, tdata);
1347 if (status != PJ_SUCCESS) {
1348 pjsua_perror(THIS_FILE, "Unable to send REFER request", status);
1349 return;
1350 }
1351
1352 /* For simplicity (that's what this program is intended to be!),
1353 * leave the original invite session as it is. More advanced application
1354 * may want to hold the INVITE, or terminate the invite, or whatever.
1355 */
Benny Prijono84126ab2006-02-09 09:30:09 +00001356}
Benny Prijono834aee32006-02-19 01:38:06 +00001357
1358
Benny Prijonob0808372006-03-02 21:18:58 +00001359/**
1360 * Send instant messaging inside INVITE session.
1361 */
Benny Prijonodc39fe82006-05-26 12:17:46 +00001362PJ_DECL(void) pjsua_call_send_im(int call_index, const char *str)
Benny Prijonob0808372006-03-02 21:18:58 +00001363{
1364 pjsua_call *call;
1365 const pj_str_t mime_text = pj_str("text");
1366 const pj_str_t mime_plain = pj_str("plain");
1367 pj_str_t text;
1368 pjsip_tx_data *tdata;
1369 pj_status_t status;
1370
1371 call = &pjsua.calls[call_index];
1372
1373 if (!call->inv) {
1374 PJ_LOG(3,(THIS_FILE,"Call has been disconnected"));
1375 return;
1376 }
1377
1378 /* Lock dialog. */
1379 pjsip_dlg_inc_lock(call->inv->dlg);
1380
1381 /* Create request message. */
1382 status = pjsip_dlg_create_request( call->inv->dlg, &pjsip_message_method,
1383 -1, &tdata);
1384 if (status != PJ_SUCCESS) {
1385 pjsua_perror(THIS_FILE, "Unable to create MESSAGE request", status);
1386 goto on_return;
1387 }
1388
1389 /* Add accept header. */
1390 pjsip_msg_add_hdr( tdata->msg,
1391 (pjsip_hdr*)pjsua_im_create_accept(tdata->pool));
1392
1393 /* Create "text/plain" message body. */
1394 tdata->msg->body = pjsip_msg_body_create( tdata->pool, &mime_text,
1395 &mime_plain,
1396 pj_cstr(&text, str));
1397 if (tdata->msg->body == NULL) {
1398 pjsua_perror(THIS_FILE, "Unable to create msg body", PJ_ENOMEM);
1399 pjsip_tx_data_dec_ref(tdata);
1400 goto on_return;
1401 }
1402
1403 /* Send the request. */
Benny Prijono64158af2006-04-04 11:06:34 +00001404 status = pjsip_dlg_send_request( call->inv->dlg, tdata, -1, NULL);
Benny Prijonob0808372006-03-02 21:18:58 +00001405 if (status != PJ_SUCCESS) {
1406 pjsua_perror(THIS_FILE, "Unable to send MESSAGE request", status);
1407 goto on_return;
1408 }
1409
1410on_return:
1411 pjsip_dlg_dec_lock(call->inv->dlg);
1412}
1413
1414
1415/**
1416 * Send IM typing indication inside INVITE session.
1417 */
Benny Prijonodc39fe82006-05-26 12:17:46 +00001418PJ_DECL(void) pjsua_call_typing(int call_index, pj_bool_t is_typing)
Benny Prijonob0808372006-03-02 21:18:58 +00001419{
1420 pjsua_call *call;
1421 pjsip_tx_data *tdata;
1422 pj_status_t status;
1423
1424 call = &pjsua.calls[call_index];
1425
1426 if (!call->inv) {
1427 PJ_LOG(3,(THIS_FILE,"Call has been disconnected"));
1428 return;
1429 }
1430
1431 /* Lock dialog. */
1432 pjsip_dlg_inc_lock(call->inv->dlg);
1433
1434 /* Create request message. */
1435 status = pjsip_dlg_create_request( call->inv->dlg, &pjsip_message_method,
1436 -1, &tdata);
1437 if (status != PJ_SUCCESS) {
1438 pjsua_perror(THIS_FILE, "Unable to create MESSAGE request", status);
1439 goto on_return;
1440 }
1441
1442 /* Create "application/im-iscomposing+xml" msg body. */
1443 tdata->msg->body = pjsip_iscomposing_create_body(tdata->pool, is_typing,
1444 NULL, NULL, -1);
1445
1446 /* Send the request. */
Benny Prijono64158af2006-04-04 11:06:34 +00001447 status = pjsip_dlg_send_request( call->inv->dlg, tdata, -1, NULL);
Benny Prijonob0808372006-03-02 21:18:58 +00001448 if (status != PJ_SUCCESS) {
1449 pjsua_perror(THIS_FILE, "Unable to send MESSAGE request", status);
1450 goto on_return;
1451 }
1452
1453on_return:
1454 pjsip_dlg_dec_lock(call->inv->dlg);}
1455
1456
Benny Prijono834aee32006-02-19 01:38:06 +00001457/*
1458 * Terminate all calls.
1459 */
Benny Prijonodc39fe82006-05-26 12:17:46 +00001460PJ_DEF(void) pjsua_call_hangup_all(void)
Benny Prijono834aee32006-02-19 01:38:06 +00001461{
Benny Prijonodc39fe82006-05-26 12:17:46 +00001462 unsigned i;
Benny Prijono834aee32006-02-19 01:38:06 +00001463
Benny Prijonodc39fe82006-05-26 12:17:46 +00001464 for (i=0; i<pjsua.config.max_calls; ++i) {
Benny Prijono834aee32006-02-19 01:38:06 +00001465 pjsip_tx_data *tdata;
Benny Prijono1c2bf462006-03-05 11:54:02 +00001466 int st_code;
Benny Prijonoa91a0032006-02-26 21:23:45 +00001467 pjsua_call *call;
Benny Prijono834aee32006-02-19 01:38:06 +00001468
Benny Prijonoa91a0032006-02-26 21:23:45 +00001469 if (pjsua.calls[i].inv == NULL)
1470 continue;
Benny Prijono834aee32006-02-19 01:38:06 +00001471
Benny Prijonoa91a0032006-02-26 21:23:45 +00001472 call = &pjsua.calls[i];
1473
Benny Prijono1c2bf462006-03-05 11:54:02 +00001474 if (call->inv->state == PJSIP_INV_STATE_CONFIRMED) {
1475 st_code = 200;
1476 } else {
1477 st_code = PJSIP_SC_GONE;
1478 }
1479
1480 if (pjsip_inv_end_session(call->inv, st_code, NULL, &tdata)==0) {
Benny Prijonofccab712006-02-22 22:23:22 +00001481 if (tdata)
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +00001482 pjsip_inv_send_msg(call->inv, tdata);
Benny Prijonofccab712006-02-22 22:23:22 +00001483 }
Benny Prijono834aee32006-02-19 01:38:06 +00001484 }
1485}
1486
Benny Prijono26ff9062006-02-21 23:47:00 +00001487
Benny Prijonoa91a0032006-02-26 21:23:45 +00001488pj_status_t pjsua_call_init(void)
1489{
1490 /* Initialize invite session callback. */
1491 pjsip_inv_callback inv_cb;
1492 pj_status_t status;
1493
1494 pj_memset(&inv_cb, 0, sizeof(inv_cb));
1495 inv_cb.on_state_changed = &pjsua_call_on_state_changed;
1496 inv_cb.on_new_session = &pjsua_call_on_forked;
1497 inv_cb.on_media_update = &pjsua_call_on_media_update;
1498 inv_cb.on_rx_offer = &pjsua_call_on_rx_offer;
1499 inv_cb.on_tsx_state_changed = &pjsua_call_on_tsx_state_changed;
1500
1501
1502 /* Initialize invite session module: */
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +00001503 status = pjsip_inv_usage_init(pjsua.endpt, &inv_cb);
Benny Prijonoa91a0032006-02-26 21:23:45 +00001504
1505 return status;
1506}