blob: db36b2365c378af41372c233aac9a2e2660d5255 [file] [log] [blame]
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001/* $Id$ */
2/*
Nanang Izzuddina62ffc92011-05-05 06:14:19 +00003 * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
Benny Prijono32177c02008-06-20 22:44:47 +00004 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
Benny Prijonoeebe9af2006-06-13 22:57:13 +00005 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20#include <pjsua-lib/pjsua.h>
21#include <pjsua-lib/pjsua_internal.h>
22
23
24#define THIS_FILE "pjsua_media.c"
25
Nanang Izzuddin68559c32008-06-13 17:01:46 +000026#define DEFAULT_RTP_PORT 4000
27
Benny Prijono80eee892007-11-03 22:43:23 +000028#ifndef PJSUA_REQUIRE_CONSECUTIVE_RTCP_PORT
29# define PJSUA_REQUIRE_CONSECUTIVE_RTCP_PORT 0
30#endif
31
Benny Prijonode479562007-03-15 10:23:55 +000032/* Next RTP port to be used */
33static pj_uint16_t next_rtp_port;
Benny Prijonoeebe9af2006-06-13 22:57:13 +000034
Benny Prijonof76e1392008-06-06 14:51:48 +000035static void pjsua_media_config_dup(pj_pool_t *pool,
36 pjsua_media_config *dst,
37 const pjsua_media_config *src)
38{
39 pj_memcpy(dst, src, sizeof(*src));
40 pj_strdup(pool, &dst->turn_server, &src->turn_server);
41 pj_stun_auth_cred_dup(pool, &dst->turn_auth_cred, &src->turn_auth_cred);
42}
43
Nanang Izzuddin50fae732011-03-22 09:49:23 +000044
Benny Prijonoeebe9af2006-06-13 22:57:13 +000045/**
46 * Init media subsystems.
47 */
48pj_status_t pjsua_media_subsys_init(const pjsua_media_config *cfg)
49{
Benny Prijonoeebe9af2006-06-13 22:57:13 +000050 pj_status_t status;
51
Benny Prijonob90fd382011-09-18 14:59:56 +000052 pj_log_push_indent();
53
Benny Prijonof798e502009-03-09 13:08:16 +000054 /* Specify which audio device settings are save-able */
55 pjsua_var.aud_svmask = 0xFFFFFFFF;
56 /* These are not-settable */
57 pjsua_var.aud_svmask &= ~(PJMEDIA_AUD_DEV_CAP_EXT_FORMAT |
58 PJMEDIA_AUD_DEV_CAP_INPUT_SIGNAL_METER |
59 PJMEDIA_AUD_DEV_CAP_OUTPUT_SIGNAL_METER);
Benny Prijonoe506c8c2009-03-10 13:28:43 +000060 /* EC settings use different API */
61 pjsua_var.aud_svmask &= ~(PJMEDIA_AUD_DEV_CAP_EC |
62 PJMEDIA_AUD_DEV_CAP_EC_TAIL);
Benny Prijonof798e502009-03-09 13:08:16 +000063
Benny Prijonoeebe9af2006-06-13 22:57:13 +000064 /* Copy configuration */
Benny Prijonof76e1392008-06-06 14:51:48 +000065 pjsua_media_config_dup(pjsua_var.pool, &pjsua_var.media_cfg, cfg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +000066
67 /* Normalize configuration */
Benny Prijono50f19b32008-03-11 13:15:43 +000068 if (pjsua_var.media_cfg.snd_clock_rate == 0) {
69 pjsua_var.media_cfg.snd_clock_rate = pjsua_var.media_cfg.clock_rate;
70 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +000071
72 if (pjsua_var.media_cfg.has_ioqueue &&
73 pjsua_var.media_cfg.thread_cnt == 0)
74 {
75 pjsua_var.media_cfg.thread_cnt = 1;
76 }
77
78 if (pjsua_var.media_cfg.max_media_ports < pjsua_var.ua_cfg.max_calls) {
79 pjsua_var.media_cfg.max_media_ports = pjsua_var.ua_cfg.max_calls + 2;
80 }
81
82 /* Create media endpoint. */
83 status = pjmedia_endpt_create(&pjsua_var.cp.factory,
84 pjsua_var.media_cfg.has_ioqueue? NULL :
85 pjsip_endpt_get_ioqueue(pjsua_var.endpt),
86 pjsua_var.media_cfg.thread_cnt,
87 &pjsua_var.med_endpt);
88 if (status != PJ_SUCCESS) {
89 pjsua_perror(THIS_FILE,
90 "Media stack initialization has returned error",
91 status);
Benny Prijonob90fd382011-09-18 14:59:56 +000092 goto on_error;
Benny Prijonoeebe9af2006-06-13 22:57:13 +000093 }
94
Benny Prijono1312e752012-03-22 09:56:52 +000095 status = pjsua_aud_subsys_init();
96 if (status != PJ_SUCCESS)
Benny Prijonob90fd382011-09-18 14:59:56 +000097 goto on_error;
Benny Prijonoeebe9af2006-06-13 22:57:13 +000098
Nanang Izzuddin69b69ae2009-04-14 15:18:30 +000099#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
Benny Prijono28d3c562012-03-30 07:10:13 +0000100 /* Initialize SRTP library (ticket #788). */
101 status = pjmedia_srtp_init_lib(pjsua_var.med_endpt);
Nanang Izzuddin69b69ae2009-04-14 15:18:30 +0000102 if (status != PJ_SUCCESS) {
103 pjsua_perror(THIS_FILE, "Error initializing SRTP library",
104 status);
Benny Prijonob90fd382011-09-18 14:59:56 +0000105 goto on_error;
Nanang Izzuddin69b69ae2009-04-14 15:18:30 +0000106 }
107#endif
108
Benny Prijono9f468d12011-07-07 07:46:33 +0000109 /* Video */
110#if PJMEDIA_HAS_VIDEO
111 status = pjsua_vid_subsys_init();
112 if (status != PJ_SUCCESS)
Benny Prijonob90fd382011-09-18 14:59:56 +0000113 goto on_error;
Benny Prijono9f468d12011-07-07 07:46:33 +0000114#endif
115
Benny Prijonob90fd382011-09-18 14:59:56 +0000116 pj_log_pop_indent();
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000117 return PJ_SUCCESS;
Benny Prijonob90fd382011-09-18 14:59:56 +0000118
119on_error:
120 pj_log_pop_indent();
121 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000122}
123
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000124/*
125 * Start pjsua media subsystem.
126 */
127pj_status_t pjsua_media_subsys_start(void)
128{
129 pj_status_t status;
130
Benny Prijonob90fd382011-09-18 14:59:56 +0000131 pj_log_push_indent();
132
Benny Prijono0bc99a92011-03-17 04:34:43 +0000133#if DISABLED_FOR_TICKET_1185
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000134 /* Create media for calls, if none is specified */
Benny Prijono0bc99a92011-03-17 04:34:43 +0000135 if (pjsua_var.calls[0].media[0].tp == NULL) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000136 pjsua_transport_config transport_cfg;
137
138 /* Create default transport config */
139 pjsua_transport_config_default(&transport_cfg);
140 transport_cfg.port = DEFAULT_RTP_PORT;
141
142 status = pjsua_media_transports_create(&transport_cfg);
Benny Prijonob90fd382011-09-18 14:59:56 +0000143 if (status != PJ_SUCCESS) {
144 pj_log_pop_indent();
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000145 return status;
Benny Prijonob90fd382011-09-18 14:59:56 +0000146 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000147 }
Benny Prijono0bc99a92011-03-17 04:34:43 +0000148#endif
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000149
Benny Prijono1312e752012-03-22 09:56:52 +0000150 /* Audio */
151 status = pjsua_aud_subsys_start();
152 if (status != PJ_SUCCESS) {
153 pj_log_pop_indent();
154 return status;
155 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000156
Benny Prijono9f468d12011-07-07 07:46:33 +0000157 /* Video */
158#if PJMEDIA_HAS_VIDEO
159 status = pjsua_vid_subsys_start();
Benny Prijonob90fd382011-09-18 14:59:56 +0000160 if (status != PJ_SUCCESS) {
Benny Prijono1312e752012-03-22 09:56:52 +0000161 pjsua_aud_subsys_destroy();
Benny Prijonob90fd382011-09-18 14:59:56 +0000162 pj_log_pop_indent();
Benny Prijono9f468d12011-07-07 07:46:33 +0000163 return status;
Benny Prijonob90fd382011-09-18 14:59:56 +0000164 }
Benny Prijono9f468d12011-07-07 07:46:33 +0000165#endif
166
Benny Prijonobf53b002010-01-04 13:08:31 +0000167 /* Perform NAT detection */
Benny Prijono0bc99a92011-03-17 04:34:43 +0000168 status = pjsua_detect_nat_type();
169 if (status != PJ_SUCCESS) {
170 PJ_PERROR(1,(THIS_FILE, status, "NAT type detection failed"));
171 }
Benny Prijonobf53b002010-01-04 13:08:31 +0000172
Benny Prijonob90fd382011-09-18 14:59:56 +0000173 pj_log_pop_indent();
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000174 return PJ_SUCCESS;
175}
176
177
178/*
179 * Destroy pjsua media subsystem.
180 */
Sauw Minge7dbbc82011-10-24 09:28:13 +0000181pj_status_t pjsua_media_subsys_destroy(unsigned flags)
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000182{
183 unsigned i;
184
Benny Prijono384dab42009-10-14 01:58:04 +0000185 PJ_LOG(4,(THIS_FILE, "Shutting down media.."));
Benny Prijonob90fd382011-09-18 14:59:56 +0000186 pj_log_push_indent();
Benny Prijono384dab42009-10-14 01:58:04 +0000187
Benny Prijono1312e752012-03-22 09:56:52 +0000188 if (pjsua_var.med_endpt) {
189 pjsua_aud_subsys_destroy();
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000190 }
191
192 /* Close media transports */
Benny Prijono0875ae82006-12-26 00:11:48 +0000193 for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
Benny Prijono0bc99a92011-03-17 04:34:43 +0000194 unsigned strm_idx;
195 pjsua_call *call = &pjsua_var.calls[i];
196 for (strm_idx=0; strm_idx<call->med_cnt; ++strm_idx) {
197 pjsua_call_media *call_med = &call->media[strm_idx];
198 if (call_med->tp_st != PJSUA_MED_TP_IDLE) {
199 pjsua_media_channel_deinit(i);
200 }
201 if (call_med->tp && call_med->tp_auto_del) {
Sauw Minge7dbbc82011-10-24 09:28:13 +0000202 /* TODO: check if we're not allowed to send to network in the
203 * "flags", and if so do not do TURN allocation...
204 */
205 PJ_UNUSED_ARG(flags);
Benny Prijono0bc99a92011-03-17 04:34:43 +0000206 pjmedia_transport_close(call_med->tp);
207 }
208 call_med->tp = NULL;
Benny Prijono311b63f2008-07-14 11:31:40 +0000209 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000210 }
211
212 /* Destroy media endpoint. */
213 if (pjsua_var.med_endpt) {
214
Benny Prijono0bc99a92011-03-17 04:34:43 +0000215# if PJMEDIA_HAS_VIDEO
Benny Prijono9f468d12011-07-07 07:46:33 +0000216 pjsua_vid_subsys_destroy();
Benny Prijono0bc99a92011-03-17 04:34:43 +0000217# endif
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000218
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000219 pjmedia_endpt_destroy(pjsua_var.med_endpt);
220 pjsua_var.med_endpt = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000221
Benny Prijonoad2e0ca2007-04-29 12:31:51 +0000222 /* Deinitialize sound subsystem */
Benny Prijonoa41d66e2007-10-01 12:17:23 +0000223 // Not necessary, as pjmedia_snd_deinit() should have been called
224 // in pjmedia_endpt_destroy().
225 //pjmedia_snd_deinit();
Benny Prijonoad2e0ca2007-04-29 12:31:51 +0000226 }
Benny Prijonoaf1bb1e2006-11-21 12:39:31 +0000227
Benny Prijonode479562007-03-15 10:23:55 +0000228 /* Reset RTP port */
229 next_rtp_port = 0;
230
Benny Prijonob90fd382011-09-18 14:59:56 +0000231 pj_log_pop_indent();
232
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000233 return PJ_SUCCESS;
234}
235
Benny Prijono0bc99a92011-03-17 04:34:43 +0000236/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000237 * Create RTP and RTCP socket pair, and possibly resolve their public
238 * address via STUN.
239 */
240static pj_status_t create_rtp_rtcp_sock(const pjsua_transport_config *cfg,
241 pjmedia_sock_info *skinfo)
242{
Benny Prijono0bc99a92011-03-17 04:34:43 +0000243 enum {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000244 RTP_RETRY = 100
245 };
246 int i;
247 pj_sockaddr_in bound_addr;
248 pj_sockaddr_in mapped_addr[2];
249 pj_status_t status = PJ_SUCCESS;
250 char addr_buf[PJ_INET6_ADDRSTRLEN+2];
251 pj_sock_t sock[2];
252
253 /* Make sure STUN server resolution has completed */
254 status = resolve_stun_server(PJ_TRUE);
255 if (status != PJ_SUCCESS) {
256 pjsua_perror(THIS_FILE, "Error resolving STUN server", status);
257 return status;
258 }
259
260 if (next_rtp_port == 0)
261 next_rtp_port = (pj_uint16_t)cfg->port;
262
Benny Prijono0bc99a92011-03-17 04:34:43 +0000263 if (next_rtp_port == 0)
264 next_rtp_port = (pj_uint16_t)40000;
265
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000266 for (i=0; i<2; ++i)
267 sock[i] = PJ_INVALID_SOCKET;
268
269 bound_addr.sin_addr.s_addr = PJ_INADDR_ANY;
270 if (cfg->bound_addr.slen) {
271 status = pj_sockaddr_in_set_str_addr(&bound_addr, &cfg->bound_addr);
272 if (status != PJ_SUCCESS) {
273 pjsua_perror(THIS_FILE, "Unable to resolve transport bind address",
274 status);
275 return status;
276 }
277 }
278
279 /* Loop retry to bind RTP and RTCP sockets. */
280 for (i=0; i<RTP_RETRY; ++i, next_rtp_port += 2) {
281
282 /* Create RTP socket. */
283 status = pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &sock[0]);
284 if (status != PJ_SUCCESS) {
285 pjsua_perror(THIS_FILE, "socket() error", status);
286 return status;
287 }
288
289 /* Apply QoS to RTP socket, if specified */
Benny Prijono0bc99a92011-03-17 04:34:43 +0000290 status = pj_sock_apply_qos2(sock[0], cfg->qos_type,
291 &cfg->qos_params,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000292 2, THIS_FILE, "RTP socket");
293
294 /* Bind RTP socket */
Benny Prijono0bc99a92011-03-17 04:34:43 +0000295 status=pj_sock_bind_in(sock[0], pj_ntohl(bound_addr.sin_addr.s_addr),
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000296 next_rtp_port);
297 if (status != PJ_SUCCESS) {
Benny Prijono0bc99a92011-03-17 04:34:43 +0000298 pj_sock_close(sock[0]);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000299 sock[0] = PJ_INVALID_SOCKET;
300 continue;
301 }
302
303 /* Create RTCP socket. */
304 status = pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &sock[1]);
305 if (status != PJ_SUCCESS) {
306 pjsua_perror(THIS_FILE, "socket() error", status);
307 pj_sock_close(sock[0]);
308 return status;
309 }
310
311 /* Apply QoS to RTCP socket, if specified */
Benny Prijono0bc99a92011-03-17 04:34:43 +0000312 status = pj_sock_apply_qos2(sock[1], cfg->qos_type,
313 &cfg->qos_params,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000314 2, THIS_FILE, "RTCP socket");
315
316 /* Bind RTCP socket */
Benny Prijono0bc99a92011-03-17 04:34:43 +0000317 status=pj_sock_bind_in(sock[1], pj_ntohl(bound_addr.sin_addr.s_addr),
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000318 (pj_uint16_t)(next_rtp_port+1));
319 if (status != PJ_SUCCESS) {
Benny Prijono0bc99a92011-03-17 04:34:43 +0000320 pj_sock_close(sock[0]);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000321 sock[0] = PJ_INVALID_SOCKET;
322
Benny Prijono0bc99a92011-03-17 04:34:43 +0000323 pj_sock_close(sock[1]);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000324 sock[1] = PJ_INVALID_SOCKET;
325 continue;
326 }
327
328 /*
329 * If we're configured to use STUN, then find out the mapped address,
330 * and make sure that the mapped RTCP port is adjacent with the RTP.
331 */
332 if (pjsua_var.stun_srv.addr.sa_family != 0) {
333 char ip_addr[32];
334 pj_str_t stun_srv;
335
Benny Prijono0bc99a92011-03-17 04:34:43 +0000336 pj_ansi_strcpy(ip_addr,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000337 pj_inet_ntoa(pjsua_var.stun_srv.ipv4.sin_addr));
338 stun_srv = pj_str(ip_addr);
339
340 status=pjstun_get_mapped_addr(&pjsua_var.cp.factory, 2, sock,
341 &stun_srv, pj_ntohs(pjsua_var.stun_srv.ipv4.sin_port),
342 &stun_srv, pj_ntohs(pjsua_var.stun_srv.ipv4.sin_port),
343 mapped_addr);
344 if (status != PJ_SUCCESS) {
345 pjsua_perror(THIS_FILE, "STUN resolve error", status);
346 goto on_error;
347 }
348
349#if PJSUA_REQUIRE_CONSECUTIVE_RTCP_PORT
Benny Prijono0bc99a92011-03-17 04:34:43 +0000350 if (pj_ntohs(mapped_addr[1].sin_port) ==
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000351 pj_ntohs(mapped_addr[0].sin_port)+1)
352 {
353 /* Success! */
354 break;
355 }
356
Benny Prijono0bc99a92011-03-17 04:34:43 +0000357 pj_sock_close(sock[0]);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000358 sock[0] = PJ_INVALID_SOCKET;
359
Benny Prijono0bc99a92011-03-17 04:34:43 +0000360 pj_sock_close(sock[1]);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000361 sock[1] = PJ_INVALID_SOCKET;
362#else
Benny Prijono0bc99a92011-03-17 04:34:43 +0000363 if (pj_ntohs(mapped_addr[1].sin_port) !=
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000364 pj_ntohs(mapped_addr[0].sin_port)+1)
365 {
Benny Prijono0bc99a92011-03-17 04:34:43 +0000366 PJ_LOG(4,(THIS_FILE,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000367 "Note: STUN mapped RTCP port %d is not adjacent"
368 " to RTP port %d",
369 pj_ntohs(mapped_addr[1].sin_port),
370 pj_ntohs(mapped_addr[0].sin_port)));
371 }
372 /* Success! */
373 break;
374#endif
375
376 } else if (cfg->public_addr.slen) {
377
378 status = pj_sockaddr_in_init(&mapped_addr[0], &cfg->public_addr,
379 (pj_uint16_t)next_rtp_port);
380 if (status != PJ_SUCCESS)
381 goto on_error;
382
383 status = pj_sockaddr_in_init(&mapped_addr[1], &cfg->public_addr,
384 (pj_uint16_t)(next_rtp_port+1));
385 if (status != PJ_SUCCESS)
386 goto on_error;
387
388 break;
389
390 } else {
391
392 if (bound_addr.sin_addr.s_addr == 0) {
393 pj_sockaddr addr;
394
395 /* Get local IP address. */
396 status = pj_gethostip(pj_AF_INET(), &addr);
397 if (status != PJ_SUCCESS)
398 goto on_error;
399
400 bound_addr.sin_addr.s_addr = addr.ipv4.sin_addr.s_addr;
401 }
402
403 for (i=0; i<2; ++i) {
404 pj_sockaddr_in_init(&mapped_addr[i], NULL, 0);
405 mapped_addr[i].sin_addr.s_addr = bound_addr.sin_addr.s_addr;
406 }
407
408 mapped_addr[0].sin_port=pj_htons((pj_uint16_t)next_rtp_port);
409 mapped_addr[1].sin_port=pj_htons((pj_uint16_t)(next_rtp_port+1));
410 break;
411 }
412 }
413
414 if (sock[0] == PJ_INVALID_SOCKET) {
Benny Prijono0bc99a92011-03-17 04:34:43 +0000415 PJ_LOG(1,(THIS_FILE,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000416 "Unable to find appropriate RTP/RTCP ports combination"));
417 goto on_error;
418 }
419
420
421 skinfo->rtp_sock = sock[0];
Benny Prijono0bc99a92011-03-17 04:34:43 +0000422 pj_memcpy(&skinfo->rtp_addr_name,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000423 &mapped_addr[0], sizeof(pj_sockaddr_in));
424
425 skinfo->rtcp_sock = sock[1];
Benny Prijono0bc99a92011-03-17 04:34:43 +0000426 pj_memcpy(&skinfo->rtcp_addr_name,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000427 &mapped_addr[1], sizeof(pj_sockaddr_in));
428
429 PJ_LOG(4,(THIS_FILE, "RTP socket reachable at %s",
430 pj_sockaddr_print(&skinfo->rtp_addr_name, addr_buf,
431 sizeof(addr_buf), 3)));
432 PJ_LOG(4,(THIS_FILE, "RTCP socket reachable at %s",
433 pj_sockaddr_print(&skinfo->rtcp_addr_name, addr_buf,
434 sizeof(addr_buf), 3)));
435
436 next_rtp_port += 2;
437 return PJ_SUCCESS;
438
439on_error:
440 for (i=0; i<2; ++i) {
441 if (sock[i] != PJ_INVALID_SOCKET)
442 pj_sock_close(sock[i]);
443 }
444 return status;
445}
446
Benny Prijono0bc99a92011-03-17 04:34:43 +0000447/* Create normal UDP media transports */
448static pj_status_t create_udp_media_transport(const pjsua_transport_config *cfg,
449 pjsua_call_media *call_med)
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000450{
Benny Prijono0bc99a92011-03-17 04:34:43 +0000451 pjmedia_sock_info skinfo;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000452 pj_status_t status;
453
Benny Prijono0bc99a92011-03-17 04:34:43 +0000454 status = create_rtp_rtcp_sock(cfg, &skinfo);
455 if (status != PJ_SUCCESS) {
456 pjsua_perror(THIS_FILE, "Unable to create RTP/RTCP socket",
457 status);
458 goto on_error;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000459 }
460
Benny Prijono0bc99a92011-03-17 04:34:43 +0000461 status = pjmedia_transport_udp_attach(pjsua_var.med_endpt, NULL,
462 &skinfo, 0, &call_med->tp);
463 if (status != PJ_SUCCESS) {
464 pjsua_perror(THIS_FILE, "Unable to create media transport",
465 status);
466 goto on_error;
467 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000468
Benny Prijono0bc99a92011-03-17 04:34:43 +0000469 pjmedia_transport_simulate_lost(call_med->tp, PJMEDIA_DIR_ENCODING,
470 pjsua_var.media_cfg.tx_drop_pct);
471
472 pjmedia_transport_simulate_lost(call_med->tp, PJMEDIA_DIR_DECODING,
473 pjsua_var.media_cfg.rx_drop_pct);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000474
Sauw Ming73ecfe82011-09-21 10:20:01 +0000475 call_med->tp_ready = PJ_SUCCESS;
476
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000477 return PJ_SUCCESS;
Benny Prijono0bc99a92011-03-17 04:34:43 +0000478
479on_error:
480 if (call_med->tp)
481 pjmedia_transport_close(call_med->tp);
482
483 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000484}
485
Benny Prijono0bc99a92011-03-17 04:34:43 +0000486#if DISABLED_FOR_TICKET_1185
Benny Prijonoc97608e2007-03-23 16:34:20 +0000487/* Create normal UDP media transports */
488static pj_status_t create_udp_media_transports(pjsua_transport_config *cfg)
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000489{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000490 unsigned i;
491 pj_status_t status;
492
Benny Prijono0bc99a92011-03-17 04:34:43 +0000493 for (i=0; i < pjsua_var.ua_cfg.max_calls; ++i) {
494 pjsua_call *call = &pjsua_var.calls[i];
495 unsigned strm_idx;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000496
Benny Prijono0bc99a92011-03-17 04:34:43 +0000497 for (strm_idx=0; strm_idx < call->med_cnt; ++strm_idx) {
498 pjsua_call_media *call_med = &call->media[strm_idx];
499
500 status = create_udp_media_transport(cfg, &call_med->tp);
501 if (status != PJ_SUCCESS)
502 goto on_error;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000503 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000504 }
505
Benny Prijonoc97608e2007-03-23 16:34:20 +0000506 return PJ_SUCCESS;
507
508on_error:
Benny Prijono0bc99a92011-03-17 04:34:43 +0000509 for (i=0; i < pjsua_var.ua_cfg.max_calls; ++i) {
510 pjsua_call *call = &pjsua_var.calls[i];
511 unsigned strm_idx;
512
513 for (strm_idx=0; strm_idx < call->med_cnt; ++strm_idx) {
514 pjsua_call_media *call_med = &call->media[strm_idx];
515
516 if (call_med->tp) {
517 pjmedia_transport_close(call_med->tp);
518 call_med->tp = NULL;
519 }
Benny Prijonoc97608e2007-03-23 16:34:20 +0000520 }
521 }
Benny Prijonoc97608e2007-03-23 16:34:20 +0000522 return status;
523}
Benny Prijono0bc99a92011-03-17 04:34:43 +0000524#endif
Benny Prijonoc97608e2007-03-23 16:34:20 +0000525
Sauw Ming2a899232012-01-09 11:51:56 +0000526static void med_tp_timer_cb(void *user_data)
527{
528 pjsua_call_media *call_med = (pjsua_call_media*)user_data;
529
530 PJSUA_LOCK();
531
532 call_med->tp_ready = call_med->tp_result;
533 if (call_med->med_create_cb)
534 (*call_med->med_create_cb)(call_med, call_med->tp_ready,
535 call_med->call->secure_level, NULL);
536
537 PJSUA_UNLOCK();
538}
539
Benny Prijono096c56c2007-09-15 08:30:16 +0000540/* This callback is called when ICE negotiation completes */
Benny Prijonof76e1392008-06-06 14:51:48 +0000541static void on_ice_complete(pjmedia_transport *tp,
542 pj_ice_strans_op op,
543 pj_status_t result)
Benny Prijono096c56c2007-09-15 08:30:16 +0000544{
Benny Prijono0bc99a92011-03-17 04:34:43 +0000545 pjsua_call_media *call_med = (pjsua_call_media*)tp->user_data;
Benny Prijono096c56c2007-09-15 08:30:16 +0000546
Benny Prijono0bc99a92011-03-17 04:34:43 +0000547 if (!call_med)
Benny Prijonof76e1392008-06-06 14:51:48 +0000548 return;
549
550 switch (op) {
551 case PJ_ICE_STRANS_OP_INIT:
Sauw Ming2a899232012-01-09 11:51:56 +0000552 call_med->tp_result = result;
553 pjsua_schedule_timer2(&med_tp_timer_cb, call_med, 1);
Benny Prijonof76e1392008-06-06 14:51:48 +0000554 break;
555 case PJ_ICE_STRANS_OP_NEGOTIATION:
556 if (result != PJ_SUCCESS) {
Benny Prijono0bc99a92011-03-17 04:34:43 +0000557 call_med->state = PJSUA_CALL_MEDIA_ERROR;
558 call_med->dir = PJMEDIA_DIR_NONE;
Benny Prijonof76e1392008-06-06 14:51:48 +0000559
Benny Prijono0bc99a92011-03-17 04:34:43 +0000560 if (call_med->call && pjsua_var.ua_cfg.cb.on_call_media_state) {
561 pjsua_var.ua_cfg.cb.on_call_media_state(call_med->call->index);
Benny Prijonof76e1392008-06-06 14:51:48 +0000562 }
Benny Prijono0bc99a92011-03-17 04:34:43 +0000563 } else if (call_med->call) {
Benny Prijonof5d9f1f2009-10-14 13:13:18 +0000564 /* Send UPDATE if default transport address is different than
565 * what was advertised (ticket #881)
566 */
567 pjmedia_transport_info tpinfo;
568 pjmedia_ice_transport_info *ii = NULL;
569 unsigned i;
570
571 pjmedia_transport_info_init(&tpinfo);
572 pjmedia_transport_get_info(tp, &tpinfo);
573 for (i=0; i<tpinfo.specific_info_cnt; ++i) {
574 if (tpinfo.spc_info[i].type==PJMEDIA_TRANSPORT_TYPE_ICE) {
575 ii = (pjmedia_ice_transport_info*)
576 tpinfo.spc_info[i].buffer;
577 break;
578 }
579 }
580
581 if (ii && ii->role==PJ_ICE_SESS_ROLE_CONTROLLING &&
582 pj_sockaddr_cmp(&tpinfo.sock_info.rtp_addr_name,
Benny Prijono0bc99a92011-03-17 04:34:43 +0000583 &call_med->rtp_addr))
Benny Prijonof5d9f1f2009-10-14 13:13:18 +0000584 {
Benny Prijonoa8f9e622010-06-21 13:28:55 +0000585 pj_bool_t use_update;
586 const pj_str_t STR_UPDATE = { "UPDATE", 6 };
587 pjsip_dialog_cap_status support_update;
588 pjsip_dialog *dlg;
589
Benny Prijono0bc99a92011-03-17 04:34:43 +0000590 dlg = call_med->call->inv->dlg;
Benny Prijonoa8f9e622010-06-21 13:28:55 +0000591 support_update = pjsip_dlg_remote_has_cap(dlg, PJSIP_H_ALLOW,
592 NULL, &STR_UPDATE);
593 use_update = (support_update == PJSIP_DIALOG_CAP_SUPPORTED);
594
Benny Prijonof5d9f1f2009-10-14 13:13:18 +0000595 PJ_LOG(4,(THIS_FILE,
596 "ICE default transport address has changed for "
Benny Prijonodb5d89d2011-10-25 13:39:06 +0000597 "call %d, sending %s",
598 call_med->call->index,
Benny Prijonoa8f9e622010-06-21 13:28:55 +0000599 (use_update ? "UPDATE" : "re-INVITE")));
600
601 if (use_update)
Benny Prijono0bc99a92011-03-17 04:34:43 +0000602 pjsua_call_update(call_med->call->index, 0, NULL);
Benny Prijonoa8f9e622010-06-21 13:28:55 +0000603 else
Benny Prijono0bc99a92011-03-17 04:34:43 +0000604 pjsua_call_reinvite(call_med->call->index, 0, NULL);
Benny Prijonof5d9f1f2009-10-14 13:13:18 +0000605 }
Benny Prijonof76e1392008-06-06 14:51:48 +0000606 }
607 break;
Benny Prijono4d6ff4d2010-06-19 12:35:33 +0000608 case PJ_ICE_STRANS_OP_KEEP_ALIVE:
609 if (result != PJ_SUCCESS) {
610 PJ_PERROR(4,(THIS_FILE, result,
Benny Prijono0bc99a92011-03-17 04:34:43 +0000611 "ICE keep alive failure for transport %d:%d",
612 call_med->call->index, call_med->idx));
Benny Prijono4d6ff4d2010-06-19 12:35:33 +0000613 }
Sauw Ming73ecfe82011-09-21 10:20:01 +0000614 if (pjsua_var.ua_cfg.cb.on_call_media_transport_state) {
615 pjsua_med_tp_state_info info;
616
617 pj_bzero(&info, sizeof(info));
618 info.med_idx = call_med->idx;
619 info.state = call_med->tp_st;
620 info.status = result;
621 info.ext_info = &op;
622 (*pjsua_var.ua_cfg.cb.on_call_media_transport_state)(
623 call_med->call->index, &info);
624 }
Benny Prijono4d6ff4d2010-06-19 12:35:33 +0000625 if (pjsua_var.ua_cfg.cb.on_ice_transport_error) {
Benny Prijono0bc99a92011-03-17 04:34:43 +0000626 pjsua_call_id id = call_med->call->index;
Benny Prijono4d6ff4d2010-06-19 12:35:33 +0000627 (*pjsua_var.ua_cfg.cb.on_ice_transport_error)(id, op, result,
628 NULL);
629 }
630 break;
Benny Prijono096c56c2007-09-15 08:30:16 +0000631 }
632}
633
634
Benny Prijonof76e1392008-06-06 14:51:48 +0000635/* Parse "HOST:PORT" format */
636static pj_status_t parse_host_port(const pj_str_t *host_port,
637 pj_str_t *host, pj_uint16_t *port)
Benny Prijonoc97608e2007-03-23 16:34:20 +0000638{
Benny Prijonof76e1392008-06-06 14:51:48 +0000639 pj_str_t str_port;
640
641 str_port.ptr = pj_strchr(host_port, ':');
642 if (str_port.ptr != NULL) {
643 int iport;
644
645 host->ptr = host_port->ptr;
646 host->slen = (str_port.ptr - host->ptr);
647 str_port.ptr++;
648 str_port.slen = host_port->slen - host->slen - 1;
649 iport = (int)pj_strtoul(&str_port);
650 if (iport < 1 || iport > 65535)
651 return PJ_EINVAL;
652 *port = (pj_uint16_t)iport;
653 } else {
654 *host = *host_port;
655 *port = 0;
656 }
657
658 return PJ_SUCCESS;
659}
660
661/* Create ICE media transports (when ice is enabled) */
Benny Prijono0bc99a92011-03-17 04:34:43 +0000662static pj_status_t create_ice_media_transport(
663 const pjsua_transport_config *cfg,
Sauw Ming73ecfe82011-09-21 10:20:01 +0000664 pjsua_call_media *call_med,
665 pj_bool_t async)
Benny Prijonof76e1392008-06-06 14:51:48 +0000666{
667 char stunip[PJ_INET6_ADDRSTRLEN];
668 pj_ice_strans_cfg ice_cfg;
Benny Prijono0bc99a92011-03-17 04:34:43 +0000669 pjmedia_ice_cb ice_cb;
670 char name[32];
671 unsigned comp_cnt;
Benny Prijonoc97608e2007-03-23 16:34:20 +0000672 pj_status_t status;
673
Benny Prijonoda9785b2007-04-02 20:43:06 +0000674 /* Make sure STUN server resolution has completed */
Benny Prijonobb995fd2009-08-12 11:03:23 +0000675 status = resolve_stun_server(PJ_TRUE);
Benny Prijonoda9785b2007-04-02 20:43:06 +0000676 if (status != PJ_SUCCESS) {
677 pjsua_perror(THIS_FILE, "Error resolving STUN server", status);
678 return status;
679 }
680
Benny Prijonof76e1392008-06-06 14:51:48 +0000681 /* Create ICE stream transport configuration */
682 pj_ice_strans_cfg_default(&ice_cfg);
683 pj_stun_config_init(&ice_cfg.stun_cfg, &pjsua_var.cp.factory, 0,
684 pjsip_endpt_get_ioqueue(pjsua_var.endpt),
685 pjsip_endpt_get_timer_heap(pjsua_var.endpt));
686
687 ice_cfg.af = pj_AF_INET();
688 ice_cfg.resolver = pjsua_var.resolver;
689
Benny Prijono329d6382009-05-29 13:04:03 +0000690 ice_cfg.opt = pjsua_var.media_cfg.ice_opt;
691
Benny Prijonof76e1392008-06-06 14:51:48 +0000692 /* Configure STUN settings */
693 if (pj_sockaddr_has_addr(&pjsua_var.stun_srv)) {
694 pj_sockaddr_print(&pjsua_var.stun_srv, stunip, sizeof(stunip), 0);
695 ice_cfg.stun.server = pj_str(stunip);
696 ice_cfg.stun.port = pj_sockaddr_get_port(&pjsua_var.stun_srv);
697 }
Benny Prijono329d6382009-05-29 13:04:03 +0000698 if (pjsua_var.media_cfg.ice_max_host_cands >= 0)
699 ice_cfg.stun.max_host_cands = pjsua_var.media_cfg.ice_max_host_cands;
Benny Prijonof76e1392008-06-06 14:51:48 +0000700
Benny Prijono4d79b0f2009-10-25 09:02:07 +0000701 /* Copy QoS setting to STUN setting */
702 ice_cfg.stun.cfg.qos_type = cfg->qos_type;
703 pj_memcpy(&ice_cfg.stun.cfg.qos_params, &cfg->qos_params,
704 sizeof(cfg->qos_params));
705
Benny Prijonof76e1392008-06-06 14:51:48 +0000706 /* Configure TURN settings */
707 if (pjsua_var.media_cfg.enable_turn) {
708 status = parse_host_port(&pjsua_var.media_cfg.turn_server,
709 &ice_cfg.turn.server,
710 &ice_cfg.turn.port);
711 if (status != PJ_SUCCESS || ice_cfg.turn.server.slen == 0) {
712 PJ_LOG(1,(THIS_FILE, "Invalid TURN server setting"));
713 return PJ_EINVAL;
714 }
715 if (ice_cfg.turn.port == 0)
716 ice_cfg.turn.port = 3479;
717 ice_cfg.turn.conn_type = pjsua_var.media_cfg.turn_conn_type;
718 pj_memcpy(&ice_cfg.turn.auth_cred,
719 &pjsua_var.media_cfg.turn_auth_cred,
720 sizeof(ice_cfg.turn.auth_cred));
Benny Prijono4d79b0f2009-10-25 09:02:07 +0000721
722 /* Copy QoS setting to TURN setting */
723 ice_cfg.turn.cfg.qos_type = cfg->qos_type;
724 pj_memcpy(&ice_cfg.turn.cfg.qos_params, &cfg->qos_params,
725 sizeof(cfg->qos_params));
Benny Prijonof76e1392008-06-06 14:51:48 +0000726 }
Benny Prijonob681a2f2007-03-25 18:44:51 +0000727
Benny Prijono0bc99a92011-03-17 04:34:43 +0000728 pj_bzero(&ice_cb, sizeof(pjmedia_ice_cb));
729 ice_cb.on_ice_complete = &on_ice_complete;
730 pj_ansi_snprintf(name, sizeof(name), "icetp%02d", call_med->idx);
731 call_med->tp_ready = PJ_EPENDING;
Benny Prijonoc97608e2007-03-23 16:34:20 +0000732
Benny Prijono0bc99a92011-03-17 04:34:43 +0000733 comp_cnt = 1;
734 if (PJMEDIA_ADVERTISE_RTCP && !pjsua_var.media_cfg.ice_no_rtcp)
735 ++comp_cnt;
Benny Prijonof76e1392008-06-06 14:51:48 +0000736
Benny Prijonobd6613f2011-04-11 17:27:14 +0000737 status = pjmedia_ice_create3(pjsua_var.med_endpt, name, comp_cnt,
738 &ice_cfg, &ice_cb, 0, call_med,
739 &call_med->tp);
Benny Prijono0bc99a92011-03-17 04:34:43 +0000740 if (status != PJ_SUCCESS) {
741 pjsua_perror(THIS_FILE, "Unable to create ICE media transport",
742 status);
743 goto on_error;
Benny Prijonoc97608e2007-03-23 16:34:20 +0000744 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000745
Benny Prijono0bc99a92011-03-17 04:34:43 +0000746 /* Wait until transport is initialized, or time out */
Sauw Ming73ecfe82011-09-21 10:20:01 +0000747 if (!async) {
Nanang Izzuddin1c565422011-12-28 09:52:07 +0000748 pj_bool_t has_pjsua_lock = PJSUA_LOCK_IS_LOCKED();
749 if (has_pjsua_lock)
750 PJSUA_UNLOCK();
Sauw Ming73ecfe82011-09-21 10:20:01 +0000751 while (call_med->tp_ready == PJ_EPENDING) {
752 pjsua_handle_events(100);
753 }
Nanang Izzuddin1c565422011-12-28 09:52:07 +0000754 if (has_pjsua_lock)
755 PJSUA_LOCK();
Benny Prijono0bc99a92011-03-17 04:34:43 +0000756 }
Sauw Ming73ecfe82011-09-21 10:20:01 +0000757
758 if (async && call_med->tp_ready == PJ_EPENDING) {
759 return PJ_EPENDING;
760 } else if (call_med->tp_ready != PJ_SUCCESS) {
Benny Prijono0bc99a92011-03-17 04:34:43 +0000761 pjsua_perror(THIS_FILE, "Error initializing ICE media transport",
762 call_med->tp_ready);
763 status = call_med->tp_ready;
764 goto on_error;
765 }
766
767 pjmedia_transport_simulate_lost(call_med->tp, PJMEDIA_DIR_ENCODING,
768 pjsua_var.media_cfg.tx_drop_pct);
769
770 pjmedia_transport_simulate_lost(call_med->tp, PJMEDIA_DIR_DECODING,
771 pjsua_var.media_cfg.rx_drop_pct);
772
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000773 return PJ_SUCCESS;
774
775on_error:
Benny Prijono0bc99a92011-03-17 04:34:43 +0000776 if (call_med->tp != NULL) {
777 pjmedia_transport_close(call_med->tp);
778 call_med->tp = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000779 }
780
Benny Prijonoc97608e2007-03-23 16:34:20 +0000781 return status;
782}
783
Benny Prijono0bc99a92011-03-17 04:34:43 +0000784#if DISABLED_FOR_TICKET_1185
785/* Create ICE media transports (when ice is enabled) */
786static pj_status_t create_ice_media_transports(pjsua_transport_config *cfg)
787{
788 unsigned i;
789 pj_status_t status;
Benny Prijonoc97608e2007-03-23 16:34:20 +0000790
Benny Prijono0bc99a92011-03-17 04:34:43 +0000791 for (i=0; i < pjsua_var.ua_cfg.max_calls; ++i) {
792 pjsua_call *call = &pjsua_var.calls[i];
793 unsigned strm_idx;
794
795 for (strm_idx=0; strm_idx < call->med_cnt; ++strm_idx) {
796 pjsua_call_media *call_med = &call->media[strm_idx];
797
798 status = create_ice_media_transport(cfg, call_med);
799 if (status != PJ_SUCCESS)
800 goto on_error;
801 }
802 }
803
804 return PJ_SUCCESS;
805
806on_error:
807 for (i=0; i < pjsua_var.ua_cfg.max_calls; ++i) {
808 pjsua_call *call = &pjsua_var.calls[i];
809 unsigned strm_idx;
810
811 for (strm_idx=0; strm_idx < call->med_cnt; ++strm_idx) {
812 pjsua_call_media *call_med = &call->media[strm_idx];
813
814 if (call_med->tp) {
815 pjmedia_transport_close(call_med->tp);
816 call_med->tp = NULL;
817 }
818 }
819 }
820 return status;
821}
822#endif
823
824#if DISABLED_FOR_TICKET_1185
Benny Prijonoc97608e2007-03-23 16:34:20 +0000825/*
Benny Prijono0bc99a92011-03-17 04:34:43 +0000826 * Create media transports for all the calls. This function creates
Benny Prijonoc97608e2007-03-23 16:34:20 +0000827 * one UDP media transport for each call.
828 */
Benny Prijono1f61a8f2007-08-16 10:11:44 +0000829PJ_DEF(pj_status_t) pjsua_media_transports_create(
830 const pjsua_transport_config *app_cfg)
Benny Prijonoc97608e2007-03-23 16:34:20 +0000831{
832 pjsua_transport_config cfg;
833 unsigned i;
834 pj_status_t status;
835
836
837 /* Make sure pjsua_init() has been called */
838 PJ_ASSERT_RETURN(pjsua_var.ua_cfg.max_calls>0, PJ_EINVALIDOP);
839
840 PJSUA_LOCK();
841
842 /* Delete existing media transports */
843 for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
Benny Prijono0bc99a92011-03-17 04:34:43 +0000844 pjsua_call *call = &pjsua_var.calls[i];
845 unsigned strm_idx;
846
847 for (strm_idx=0; strm_idx < call->med_cnt; ++strm_idx) {
848 pjsua_call_media *call_med = &call->media[strm_idx];
849
850 if (call_med->tp && call_med->tp_auto_del) {
851 pjmedia_transport_close(call_med->tp);
852 call_med->tp = NULL;
853 call_med->tp_orig = NULL;
854 }
Benny Prijonoc97608e2007-03-23 16:34:20 +0000855 }
856 }
857
858 /* Copy config */
859 pjsua_transport_config_dup(pjsua_var.pool, &cfg, app_cfg);
860
Benny Prijono40860c32008-09-04 13:55:33 +0000861 /* Create the transports */
Benny Prijonoc97608e2007-03-23 16:34:20 +0000862 if (pjsua_var.media_cfg.enable_ice) {
Benny Prijono4d79b0f2009-10-25 09:02:07 +0000863 status = create_ice_media_transports(&cfg);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000864 } else {
865 status = create_udp_media_transports(&cfg);
866 }
867
Benny Prijono40860c32008-09-04 13:55:33 +0000868 /* Set media transport auto_delete to True */
869 for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
Benny Prijono0bc99a92011-03-17 04:34:43 +0000870 pjsua_call *call = &pjsua_var.calls[i];
871 unsigned strm_idx;
872
873 for (strm_idx=0; strm_idx < call->med_cnt; ++strm_idx) {
874 pjsua_call_media *call_med = &call->media[strm_idx];
875
876 call_med->tp_auto_del = PJ_TRUE;
877 }
Benny Prijono40860c32008-09-04 13:55:33 +0000878 }
Benny Prijonoc97608e2007-03-23 16:34:20 +0000879
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000880 PJSUA_UNLOCK();
881
882 return status;
883}
884
Benny Prijono40860c32008-09-04 13:55:33 +0000885/*
886 * Attach application's created media transports.
887 */
888PJ_DEF(pj_status_t) pjsua_media_transports_attach(pjsua_media_transport tp[],
889 unsigned count,
890 pj_bool_t auto_delete)
891{
892 unsigned i;
893
894 PJ_ASSERT_RETURN(tp && count==pjsua_var.ua_cfg.max_calls, PJ_EINVAL);
895
896 /* Assign the media transports */
897 for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
Benny Prijono0bc99a92011-03-17 04:34:43 +0000898 pjsua_call *call = &pjsua_var.calls[i];
899 unsigned strm_idx;
900
901 for (strm_idx=0; strm_idx < call->med_cnt; ++strm_idx) {
902 pjsua_call_media *call_med = &call->media[strm_idx];
903
904 if (call_med->tp && call_med->tp_auto_del) {
905 pjmedia_transport_close(call_med->tp);
906 call_med->tp = NULL;
907 call_med->tp_orig = NULL;
908 }
Benny Prijono40860c32008-09-04 13:55:33 +0000909 }
910
Benny Prijono0bc99a92011-03-17 04:34:43 +0000911 PJ_TODO(remove_pjsua_media_transports_attach);
912
913 call->media[0].tp = tp[i].transport;
914 call->media[0].tp_auto_del = auto_delete;
Benny Prijono40860c32008-09-04 13:55:33 +0000915 }
916
917 return PJ_SUCCESS;
918}
Benny Prijono0bc99a92011-03-17 04:34:43 +0000919#endif
Benny Prijono40860c32008-09-04 13:55:33 +0000920
Benny Prijono0bc99a92011-03-17 04:34:43 +0000921/* Go through the list of media in the SDP, find acceptable media, and
922 * sort them based on the "quality" of the media, and store the indexes
923 * in the specified array. Media with the best quality will be listed
924 * first in the array. The quality factors considered currently is
925 * encryption.
926 */
927static void sort_media(const pjmedia_sdp_session *sdp,
928 const pj_str_t *type,
929 pjmedia_srtp_use use_srtp,
930 pj_uint8_t midx[],
Nanang Izzuddindebd48a2011-12-01 09:06:14 +0000931 unsigned *p_count,
932 unsigned *p_total_count)
Nanang Izzuddin1e952a82010-10-05 16:32:04 +0000933{
934 unsigned i;
Benny Prijono0bc99a92011-03-17 04:34:43 +0000935 unsigned count = 0;
936 int score[PJSUA_MAX_CALL_MEDIA];
Nanang Izzuddin1e952a82010-10-05 16:32:04 +0000937
Benny Prijono0bc99a92011-03-17 04:34:43 +0000938 pj_assert(*p_count >= PJSUA_MAX_CALL_MEDIA);
Nanang Izzuddindebd48a2011-12-01 09:06:14 +0000939 pj_assert(*p_total_count >= PJSUA_MAX_CALL_MEDIA);
Benny Prijono0bc99a92011-03-17 04:34:43 +0000940
941 *p_count = 0;
Nanang Izzuddindebd48a2011-12-01 09:06:14 +0000942 *p_total_count = 0;
Benny Prijono09c0d672011-04-11 05:03:24 +0000943 for (i=0; i<PJSUA_MAX_CALL_MEDIA; ++i)
944 score[i] = 1;
Benny Prijono0bc99a92011-03-17 04:34:43 +0000945
946 /* Score each media */
947 for (i=0; i<sdp->media_count && count<PJSUA_MAX_CALL_MEDIA; ++i) {
Nanang Izzuddin1e952a82010-10-05 16:32:04 +0000948 const pjmedia_sdp_media *m = sdp->media[i];
Nanang Izzuddina6414292011-04-08 04:26:18 +0000949 const pjmedia_sdp_conn *c;
Nanang Izzuddin1e952a82010-10-05 16:32:04 +0000950
Benny Prijono0bc99a92011-03-17 04:34:43 +0000951 /* Skip different media */
952 if (pj_stricmp(&m->desc.media, type) != 0) {
953 score[count++] = -22000;
Nanang Izzuddin1e952a82010-10-05 16:32:04 +0000954 continue;
955 }
956
Nanang Izzuddina6414292011-04-08 04:26:18 +0000957 c = m->conn? m->conn : sdp->conn;
958
Benny Prijono0bc99a92011-03-17 04:34:43 +0000959 /* Supported transports */
960 if (pj_stricmp2(&m->desc.transport, "RTP/SAVP")==0) {
961 switch (use_srtp) {
962 case PJMEDIA_SRTP_MANDATORY:
963 case PJMEDIA_SRTP_OPTIONAL:
964 ++score[i];
Nanang Izzuddin1e952a82010-10-05 16:32:04 +0000965 break;
Benny Prijono0bc99a92011-03-17 04:34:43 +0000966 case PJMEDIA_SRTP_DISABLED:
Nanang Izzuddind8c33d82011-08-18 18:30:55 +0000967 //--score[i];
968 score[i] -= 5;
Benny Prijono0bc99a92011-03-17 04:34:43 +0000969 break;
Nanang Izzuddin1e952a82010-10-05 16:32:04 +0000970 }
Benny Prijono0bc99a92011-03-17 04:34:43 +0000971 } else if (pj_stricmp2(&m->desc.transport, "RTP/AVP")==0) {
972 switch (use_srtp) {
973 case PJMEDIA_SRTP_MANDATORY:
Nanang Izzuddind8c33d82011-08-18 18:30:55 +0000974 //--score[i];
975 score[i] -= 5;
Benny Prijono0bc99a92011-03-17 04:34:43 +0000976 break;
977 case PJMEDIA_SRTP_OPTIONAL:
978 /* No change in score */
979 break;
980 case PJMEDIA_SRTP_DISABLED:
981 ++score[i];
982 break;
983 }
984 } else {
985 score[i] -= 10;
986 }
987
988 /* Is media disabled? */
989 if (m->desc.port == 0)
990 score[i] -= 10;
991
992 /* Is media inactive? */
Nanang Izzuddina6414292011-04-08 04:26:18 +0000993 if (pjmedia_sdp_media_find_attr2(m, "inactive", NULL) ||
994 pj_strcmp2(&c->addr, "0.0.0.0") == 0)
995 {
996 //score[i] -= 10;
997 score[i] -= 1;
998 }
Benny Prijono0bc99a92011-03-17 04:34:43 +0000999
1000 ++count;
1001 }
1002
1003 /* Created sorted list based on quality */
1004 for (i=0; i<count; ++i) {
1005 unsigned j;
1006 int best = 0;
1007
1008 for (j=1; j<count; ++j) {
1009 if (score[j] > score[best])
1010 best = j;
1011 }
1012 /* Don't put media with negative score, that media is unacceptable
1013 * for us.
1014 */
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001015 midx[i] = (pj_uint8_t)best;
1016 if (score[best] >= 0)
Benny Prijono0bc99a92011-03-17 04:34:43 +00001017 (*p_count)++;
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001018 if (score[best] > -22000)
1019 (*p_total_count)++;
Benny Prijono0bc99a92011-03-17 04:34:43 +00001020
1021 score[best] = -22000;
1022
1023 }
1024}
1025
Benny Prijonoee0ba182011-07-15 06:18:29 +00001026/* Callback to receive media events */
Sauw Ming0fabe1b2011-12-01 10:49:07 +00001027pj_status_t call_media_on_event(pjmedia_event *event,
1028 void *user_data)
Benny Prijonoee0ba182011-07-15 06:18:29 +00001029{
Sauw Ming0fabe1b2011-12-01 10:49:07 +00001030 pjsua_call_media *call_med = (pjsua_call_media*)user_data;
Benny Prijonoee0ba182011-07-15 06:18:29 +00001031 pjsua_call *call = call_med->call;
Nanang Izzuddin3da4ad82011-12-07 10:43:28 +00001032 pj_status_t status = PJ_SUCCESS;
1033
1034 switch(event->type) {
1035 case PJMEDIA_EVENT_KEYFRAME_MISSING:
1036 if (call->opt.req_keyframe_method & PJSUA_VID_REQ_KEYFRAME_SIP_INFO)
1037 {
1038 pj_timestamp now;
1039
1040 pj_get_timestamp(&now);
1041 if (pj_elapsed_msec(&call_med->last_req_keyframe, &now) >=
1042 PJSUA_VID_REQ_KEYFRAME_INTERVAL)
1043 {
1044 pjsua_msg_data msg_data;
1045 const pj_str_t SIP_INFO = {"INFO", 4};
1046 const char *BODY_TYPE = "application/media_control+xml";
1047 const char *BODY =
1048 "<?xml version=\"1.0\" encoding=\"utf-8\" ?>"
1049 "<media_control><vc_primitive><to_encoder>"
1050 "<picture_fast_update/>"
1051 "</to_encoder></vc_primitive></media_control>";
1052
1053 PJ_LOG(4,(THIS_FILE,
1054 "Sending video keyframe request via SIP INFO"));
1055
1056 pjsua_msg_data_init(&msg_data);
1057 pj_cstr(&msg_data.content_type, BODY_TYPE);
1058 pj_cstr(&msg_data.msg_body, BODY);
1059 status = pjsua_call_send_request(call->index, &SIP_INFO,
1060 &msg_data);
1061 if (status != PJ_SUCCESS) {
1062 pj_perror(3, THIS_FILE, status,
1063 "Failed requesting keyframe via SIP INFO");
1064 } else {
1065 call_med->last_req_keyframe = now;
1066 }
1067 }
1068 }
1069 break;
1070
1071 default:
1072 break;
1073 }
Benny Prijonoee0ba182011-07-15 06:18:29 +00001074
1075 if (pjsua_var.ua_cfg.cb.on_call_media_event && call) {
Benny Prijonoee0ba182011-07-15 06:18:29 +00001076 (*pjsua_var.ua_cfg.cb.on_call_media_event)(call->index,
1077 call_med->idx, event);
Benny Prijono53a7c702008-04-14 02:57:29 +00001078 }
1079
Nanang Izzuddin3da4ad82011-12-07 10:43:28 +00001080 return status;
Benny Prijono53a7c702008-04-14 02:57:29 +00001081}
1082
Sauw Ming73ecfe82011-09-21 10:20:01 +00001083/* Set media transport state and notify the application via the callback. */
Benny Prijono1312e752012-03-22 09:56:52 +00001084void pjsua_set_media_tp_state(pjsua_call_media *call_med,
1085 pjsua_med_tp_st tp_st)
Sauw Ming73ecfe82011-09-21 10:20:01 +00001086{
1087 if (pjsua_var.ua_cfg.cb.on_call_media_transport_state &&
1088 call_med->tp_st != tp_st)
1089 {
1090 pjsua_med_tp_state_info info;
1091
1092 pj_bzero(&info, sizeof(info));
1093 info.med_idx = call_med->idx;
1094 info.state = tp_st;
1095 info.status = call_med->tp_ready;
1096 (*pjsua_var.ua_cfg.cb.on_call_media_transport_state)(
1097 call_med->call->index, &info);
1098 }
1099
1100 call_med->tp_st = tp_st;
1101}
1102
1103/* Callback to resume pjsua_call_media_init() after media transport
1104 * creation is completed.
1105 */
1106static pj_status_t call_media_init_cb(pjsua_call_media *call_med,
1107 pj_status_t status,
1108 int security_level,
1109 int *sip_err_code)
Nanang Izzuddin1e952a82010-10-05 16:32:04 +00001110{
Benny Prijono0bc99a92011-03-17 04:34:43 +00001111 pjsua_acc *acc = &pjsua_var.acc[call_med->call->acc_id];
Benny Prijonodb5d89d2011-10-25 13:39:06 +00001112 pjmedia_transport_info tpinfo;
Sauw Ming73ecfe82011-09-21 10:20:01 +00001113 int err_code = 0;
Nanang Izzuddin1e952a82010-10-05 16:32:04 +00001114
Sauw Ming73ecfe82011-09-21 10:20:01 +00001115 if (status != PJ_SUCCESS)
Benny Prijono0ec6cfb2011-12-26 09:00:42 +00001116 goto on_return;
Nanang Izzuddin1e952a82010-10-05 16:32:04 +00001117
Sauw Ming73ecfe82011-09-21 10:20:01 +00001118 if (call_med->tp_st == PJSUA_MED_TP_CREATING)
Benny Prijono1312e752012-03-22 09:56:52 +00001119 pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_IDLE);
Nanang Izzuddin1e952a82010-10-05 16:32:04 +00001120
Sauw Minge7dbbc82011-10-24 09:28:13 +00001121 if (!call_med->tp_orig &&
1122 pjsua_var.ua_cfg.cb.on_create_media_transport)
1123 {
1124 call_med->use_custom_med_tp = PJ_TRUE;
1125 } else
1126 call_med->use_custom_med_tp = PJ_FALSE;
1127
Benny Prijono0bc99a92011-03-17 04:34:43 +00001128#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
1129 /* This function may be called when SRTP transport already exists
1130 * (e.g: in re-invite, update), don't need to destroy/re-create.
1131 */
Sauw Minge7dbbc82011-10-24 09:28:13 +00001132 if (!call_med->tp_orig) {
Benny Prijono0bc99a92011-03-17 04:34:43 +00001133 pjmedia_srtp_setting srtp_opt;
1134 pjmedia_transport *srtp = NULL;
Nanang Izzuddin1e952a82010-10-05 16:32:04 +00001135
Benny Prijono0bc99a92011-03-17 04:34:43 +00001136 /* Check if SRTP requires secure signaling */
1137 if (acc->cfg.use_srtp != PJMEDIA_SRTP_DISABLED) {
1138 if (security_level < acc->cfg.srtp_secure_signaling) {
Sauw Ming73ecfe82011-09-21 10:20:01 +00001139 err_code = PJSIP_SC_NOT_ACCEPTABLE;
Benny Prijono0bc99a92011-03-17 04:34:43 +00001140 status = PJSIP_ESESSIONINSECURE;
Benny Prijono0ec6cfb2011-12-26 09:00:42 +00001141 goto on_return;
Benny Prijono0bc99a92011-03-17 04:34:43 +00001142 }
1143 }
1144
1145 /* Always create SRTP adapter */
1146 pjmedia_srtp_setting_default(&srtp_opt);
Nanang Izzuddinb6c239c2011-05-10 05:42:28 +00001147 srtp_opt.close_member_tp = PJ_TRUE;
Sauw Minge7dbbc82011-10-24 09:28:13 +00001148 /* If media session has been ever established, let's use remote's
Benny Prijono0bc99a92011-03-17 04:34:43 +00001149 * preference in SRTP usage policy, especially when it is stricter.
1150 */
1151 if (call_med->rem_srtp_use > acc->cfg.use_srtp)
1152 srtp_opt.use = call_med->rem_srtp_use;
1153 else
1154 srtp_opt.use = acc->cfg.use_srtp;
1155
1156 status = pjmedia_transport_srtp_create(pjsua_var.med_endpt,
1157 call_med->tp,
1158 &srtp_opt, &srtp);
1159 if (status != PJ_SUCCESS) {
Sauw Ming73ecfe82011-09-21 10:20:01 +00001160 err_code = PJSIP_SC_INTERNAL_SERVER_ERROR;
Benny Prijono0ec6cfb2011-12-26 09:00:42 +00001161 goto on_return;
Benny Prijono0bc99a92011-03-17 04:34:43 +00001162 }
1163
1164 /* Set SRTP as current media transport */
1165 call_med->tp_orig = call_med->tp;
1166 call_med->tp = srtp;
1167 }
1168#else
Benny Prijono7df19342011-07-23 02:54:03 +00001169 call_med->tp_orig = call_med->tp;
Benny Prijono0bc99a92011-03-17 04:34:43 +00001170 PJ_UNUSED_ARG(security_level);
1171#endif
1172
Benny Prijonodb5d89d2011-10-25 13:39:06 +00001173
1174 pjmedia_transport_info_init(&tpinfo);
1175 pjmedia_transport_get_info(call_med->tp, &tpinfo);
1176
1177 pj_sockaddr_cp(&call_med->rtp_addr, &tpinfo.sock_info.rtp_addr_name);
1178
1179
Benny Prijono0ec6cfb2011-12-26 09:00:42 +00001180on_return:
Sauw Ming73ecfe82011-09-21 10:20:01 +00001181 if (status != PJ_SUCCESS && call_med->tp) {
Benny Prijono0bc99a92011-03-17 04:34:43 +00001182 pjmedia_transport_close(call_med->tp);
1183 call_med->tp = NULL;
1184 }
Sauw Ming73ecfe82011-09-21 10:20:01 +00001185
1186 if (sip_err_code)
1187 *sip_err_code = err_code;
1188
1189 if (call_med->med_init_cb) {
1190 pjsua_med_tp_state_info info;
1191
1192 pj_bzero(&info, sizeof(info));
1193 info.status = status;
1194 info.state = call_med->tp_st;
1195 info.med_idx = call_med->idx;
1196 info.sip_err_code = err_code;
1197 (*call_med->med_init_cb)(call_med->call->index, &info);
1198 }
1199
1200 return status;
1201}
1202
1203/* Initialize the media line */
1204pj_status_t pjsua_call_media_init(pjsua_call_media *call_med,
1205 pjmedia_type type,
1206 const pjsua_transport_config *tcfg,
1207 int security_level,
1208 int *sip_err_code,
1209 pj_bool_t async,
1210 pjsua_med_tp_state_cb cb)
1211{
Sauw Ming73ecfe82011-09-21 10:20:01 +00001212 pj_status_t status = PJ_SUCCESS;
1213
1214 /*
1215 * Note: this function may be called when the media already exists
1216 * (e.g. in reinvites, updates, etc.)
1217 */
1218 call_med->type = type;
1219
1220 /* Create the media transport for initial call. */
1221 if (call_med->tp == NULL) {
1222#if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0)
1223 /* While in initial call, set default video devices */
1224 if (type == PJMEDIA_TYPE_VIDEO) {
Benny Prijono1312e752012-03-22 09:56:52 +00001225 status = pjsua_vid_channel_init(call_med);
1226 if (status != PJ_SUCCESS)
1227 return status;
Sauw Ming73ecfe82011-09-21 10:20:01 +00001228 }
1229#endif
1230
Benny Prijono1312e752012-03-22 09:56:52 +00001231 pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_CREATING);
Sauw Ming73ecfe82011-09-21 10:20:01 +00001232
Sauw Ming73ecfe82011-09-21 10:20:01 +00001233 if (pjsua_var.media_cfg.enable_ice) {
1234 status = create_ice_media_transport(tcfg, call_med, async);
Sauw Mingc8e12942011-10-25 08:51:02 +00001235 if (async && status == PJ_EPENDING) {
Sauw Ming848742f2011-09-28 04:20:30 +00001236 /* We will resume call media initialization in the
1237 * on_ice_complete() callback.
1238 */
Sauw Mingc8e12942011-10-25 08:51:02 +00001239 call_med->med_create_cb = &call_media_init_cb;
1240 call_med->med_init_cb = cb;
1241
Sauw Ming848742f2011-09-28 04:20:30 +00001242 return PJ_EPENDING;
1243 }
Sauw Ming73ecfe82011-09-21 10:20:01 +00001244 } else {
1245 status = create_udp_media_transport(tcfg, call_med);
1246 }
1247
Sauw Ming848742f2011-09-28 04:20:30 +00001248 if (status != PJ_SUCCESS) {
Sauw Ming73ecfe82011-09-21 10:20:01 +00001249 PJ_PERROR(1,(THIS_FILE, status, "Error creating media transport"));
1250 return status;
1251 }
1252
1253 /* Media transport creation completed immediately, so
1254 * we don't need to call the callback.
1255 */
1256 call_med->med_init_cb = NULL;
1257
1258 } else if (call_med->tp_st == PJSUA_MED_TP_DISABLED) {
1259 /* Media is being reenabled. */
Benny Prijono1312e752012-03-22 09:56:52 +00001260 pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_INIT);
Sauw Ming73ecfe82011-09-21 10:20:01 +00001261 }
1262
1263 return call_media_init_cb(call_med, status, security_level,
1264 sip_err_code);
1265}
1266
1267/* Callback to resume pjsua_media_channel_init() after media transport
1268 * initialization is completed.
1269 */
1270static pj_status_t media_channel_init_cb(pjsua_call_id call_id,
1271 const pjsua_med_tp_state_info *info)
1272{
1273 pjsua_call *call = &pjsua_var.calls[call_id];
1274 pj_status_t status = (info? info->status : PJ_SUCCESS);
1275 unsigned mi;
1276
1277 if (info) {
1278 pj_mutex_lock(call->med_ch_mutex);
1279
1280 /* Set the callback to NULL to indicate that the async operation
1281 * has completed.
1282 */
1283 call->media[info->med_idx].med_init_cb = NULL;
1284
1285 /* In case of failure, save the information to be returned
1286 * by the last media transport to finish.
1287 */
1288 if (info->status != PJ_SUCCESS)
1289 pj_memcpy(&call->med_ch_info, info, sizeof(info));
1290
1291 /* Check whether all the call's medias have finished calling their
1292 * callbacks.
1293 */
1294 for (mi=0; mi < call->med_cnt; ++mi) {
1295 pjsua_call_media *call_med = &call->media[mi];
1296
Sauw Ming2a899232012-01-09 11:51:56 +00001297 if (call_med->med_init_cb) {
Sauw Ming73ecfe82011-09-21 10:20:01 +00001298 pj_mutex_unlock(call->med_ch_mutex);
1299 return PJ_SUCCESS;
1300 }
1301
1302 if (call_med->tp_ready != PJ_SUCCESS)
1303 status = call_med->tp_ready;
1304 }
1305
1306 /* OK, we are called by the last media transport finished. */
1307 pj_mutex_unlock(call->med_ch_mutex);
1308 }
1309
1310 if (call->med_ch_mutex) {
1311 pj_mutex_destroy(call->med_ch_mutex);
1312 call->med_ch_mutex = NULL;
1313 }
1314
1315 if (status != PJ_SUCCESS) {
1316 pjsua_media_channel_deinit(call_id);
Benny Prijono0ec6cfb2011-12-26 09:00:42 +00001317 goto on_return;
Sauw Ming73ecfe82011-09-21 10:20:01 +00001318 }
1319
1320 /* Tell the media transport of a new offer/answer session */
1321 for (mi=0; mi < call->med_cnt; ++mi) {
1322 pjsua_call_media *call_med = &call->media[mi];
1323
1324 /* Note: tp may be NULL if this media line is disabled */
1325 if (call_med->tp && call_med->tp_st == PJSUA_MED_TP_IDLE) {
Sauw Ming99cc8ff2011-09-22 04:24:56 +00001326 pj_pool_t *tmp_pool = call->async_call.pool_prov;
1327
1328 if (!tmp_pool) {
1329 tmp_pool = (call->inv? call->inv->pool_prov:
1330 call->async_call.dlg->pool);
1331 }
Sauw Ming73ecfe82011-09-21 10:20:01 +00001332
Sauw Minge7dbbc82011-10-24 09:28:13 +00001333 if (call_med->use_custom_med_tp) {
1334 unsigned custom_med_tp_flags = 0;
1335
1336 /* Use custom media transport returned by the application */
1337 call_med->tp =
1338 (*pjsua_var.ua_cfg.cb.on_create_media_transport)
1339 (call_id, mi, call_med->tp,
1340 custom_med_tp_flags);
1341 if (!call_med->tp) {
1342 status =
1343 PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_NOT_ACCEPTABLE);
1344 }
1345 }
1346
1347 if (call_med->tp) {
1348 status = pjmedia_transport_media_create(
1349 call_med->tp, tmp_pool,
1350 0, call->async_call.rem_sdp, mi);
1351 }
Sauw Ming73ecfe82011-09-21 10:20:01 +00001352 if (status != PJ_SUCCESS) {
1353 call->med_ch_info.status = status;
1354 call->med_ch_info.med_idx = mi;
1355 call->med_ch_info.state = call_med->tp_st;
1356 call->med_ch_info.sip_err_code = PJSIP_SC_NOT_ACCEPTABLE;
1357 pjsua_media_channel_deinit(call_id);
Benny Prijono0ec6cfb2011-12-26 09:00:42 +00001358 goto on_return;
Sauw Ming73ecfe82011-09-21 10:20:01 +00001359 }
1360
Benny Prijono1312e752012-03-22 09:56:52 +00001361 pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_INIT);
Sauw Ming73ecfe82011-09-21 10:20:01 +00001362 }
1363 }
1364
1365 call->med_ch_info.status = PJ_SUCCESS;
1366
Benny Prijono0ec6cfb2011-12-26 09:00:42 +00001367on_return:
Sauw Ming73ecfe82011-09-21 10:20:01 +00001368 if (call->med_ch_cb)
1369 (*call->med_ch_cb)(call->index, &call->med_ch_info);
1370
Benny Prijono0bc99a92011-03-17 04:34:43 +00001371 return status;
1372}
Nanang Izzuddin1e952a82010-10-05 16:32:04 +00001373
Benny Prijonod8179652008-01-23 20:39:07 +00001374pj_status_t pjsua_media_channel_init(pjsua_call_id call_id,
1375 pjsip_role_e role,
1376 int security_level,
1377 pj_pool_t *tmp_pool,
1378 const pjmedia_sdp_session *rem_sdp,
Sauw Ming73ecfe82011-09-21 10:20:01 +00001379 int *sip_err_code,
1380 pj_bool_t async,
1381 pjsua_med_tp_state_cb cb)
Benny Prijonoc97608e2007-03-23 16:34:20 +00001382{
Benny Prijono0bc99a92011-03-17 04:34:43 +00001383 const pj_str_t STR_AUDIO = { "audio", 5 };
1384 const pj_str_t STR_VIDEO = { "video", 5 };
Benny Prijonod8179652008-01-23 20:39:07 +00001385 pjsua_call *call = &pjsua_var.calls[call_id];
Benny Prijonod8179652008-01-23 20:39:07 +00001386 pjsua_acc *acc = &pjsua_var.acc[call->acc_id];
Benny Prijono0bc99a92011-03-17 04:34:43 +00001387 pj_uint8_t maudidx[PJSUA_MAX_CALL_MEDIA];
1388 unsigned maudcnt = PJ_ARRAY_SIZE(maudidx);
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001389 unsigned mtotaudcnt = PJ_ARRAY_SIZE(maudidx);
Benny Prijono0bc99a92011-03-17 04:34:43 +00001390 pj_uint8_t mvididx[PJSUA_MAX_CALL_MEDIA];
1391 unsigned mvidcnt = PJ_ARRAY_SIZE(mvididx);
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001392 unsigned mtotvidcnt = PJ_ARRAY_SIZE(mvididx);
Benny Prijono0bc99a92011-03-17 04:34:43 +00001393 unsigned mi;
Sauw Ming73ecfe82011-09-21 10:20:01 +00001394 pj_bool_t pending_med_tp = PJ_FALSE;
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001395 pj_bool_t reinit = PJ_FALSE;
Benny Prijono0bc99a92011-03-17 04:34:43 +00001396 pj_status_t status;
Benny Prijonoc97608e2007-03-23 16:34:20 +00001397
Benny Prijonod8179652008-01-23 20:39:07 +00001398 PJ_UNUSED_ARG(role);
1399
Nanang Izzuddinb6c239c2011-05-10 05:42:28 +00001400 /*
1401 * Note: this function may be called when the media already exists
1402 * (e.g. in reinvites, updates, etc).
1403 */
1404
Benny Prijono0bc99a92011-03-17 04:34:43 +00001405 if (pjsua_get_state() != PJSUA_STATE_RUNNING)
1406 return PJ_EBUSY;
1407
Sauw Ming73ecfe82011-09-21 10:20:01 +00001408 if (async) {
1409 pj_pool_t *tmppool = (call->inv? call->inv->pool_prov:
1410 call->async_call.dlg->pool);
1411
1412 status = pj_mutex_create_simple(tmppool, NULL, &call->med_ch_mutex);
1413 if (status != PJ_SUCCESS)
1414 return status;
1415 }
1416
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001417 if (call->inv && call->inv->state == PJSIP_INV_STATE_CONFIRMED)
1418 reinit = PJ_TRUE;
1419
1420 PJ_LOG(4,(THIS_FILE, "Call %d: %sinitializing media..",
1421 call_id, (reinit?"re-":"") ));
1422
Benny Prijonob90fd382011-09-18 14:59:56 +00001423 pj_log_push_indent();
1424
Benny Prijono0bc99a92011-03-17 04:34:43 +00001425#if DISABLED_FOR_TICKET_1185
Benny Prijonod8179652008-01-23 20:39:07 +00001426 /* Return error if media transport has not been created yet
Benny Prijono68f9e4f2008-03-21 08:56:02 +00001427 * (e.g. application is starting)
1428 */
Benny Prijono0bc99a92011-03-17 04:34:43 +00001429 for (i=0; i<call->med_cnt; ++i) {
1430 if (call->media[i].tp == NULL) {
Benny Prijonob90fd382011-09-18 14:59:56 +00001431 status = PJ_EBUSY;
1432 goto on_error;
Benny Prijonod8179652008-01-23 20:39:07 +00001433 }
Benny Prijonoc97608e2007-03-23 16:34:20 +00001434 }
Benny Prijonod8179652008-01-23 20:39:07 +00001435#endif
Benny Prijonoc97608e2007-03-23 16:34:20 +00001436
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001437 /* Get media count for each media type */
Benny Prijono0bc99a92011-03-17 04:34:43 +00001438 if (rem_sdp) {
1439 sort_media(rem_sdp, &STR_AUDIO, acc->cfg.use_srtp,
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001440 maudidx, &maudcnt, &mtotaudcnt);
Benny Prijono0bc99a92011-03-17 04:34:43 +00001441 if (maudcnt==0) {
1442 /* Expecting audio in the offer */
1443 if (sip_err_code) *sip_err_code = PJSIP_SC_NOT_ACCEPTABLE_HERE;
1444 pjsua_media_channel_deinit(call_id);
Benny Prijonob90fd382011-09-18 14:59:56 +00001445 status = PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_NOT_ACCEPTABLE_HERE);
1446 goto on_error;
Benny Prijonoc97608e2007-03-23 16:34:20 +00001447 }
1448
Nanang Izzuddind8c33d82011-08-18 18:30:55 +00001449#if PJMEDIA_HAS_VIDEO
Benny Prijono0bc99a92011-03-17 04:34:43 +00001450 sort_media(rem_sdp, &STR_VIDEO, acc->cfg.use_srtp,
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001451 mvididx, &mvidcnt, &mtotvidcnt);
Nanang Izzuddind8c33d82011-08-18 18:30:55 +00001452#else
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001453 mvidcnt = mtotvidcnt = 0;
1454 PJ_UNUSED_ARG(STR_VIDEO);
Nanang Izzuddind8c33d82011-08-18 18:30:55 +00001455#endif
Nanang Izzuddinb6c239c2011-05-10 05:42:28 +00001456
1457 /* Update media count only when remote add any media, this media count
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001458 * must never decrease. Also note that we shouldn't apply the media
1459 * count setting (of the call setting) before the SDP negotiation.
Benny Prijonod8179652008-01-23 20:39:07 +00001460 */
Nanang Izzuddinb6c239c2011-05-10 05:42:28 +00001461 if (call->med_cnt < rem_sdp->media_count)
1462 call->med_cnt = PJ_MIN(rem_sdp->media_count, PJSUA_MAX_CALL_MEDIA);
Benny Prijonod8179652008-01-23 20:39:07 +00001463
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001464 call->rem_offerer = PJ_TRUE;
1465 call->rem_aud_cnt = maudcnt;
1466 call->rem_vid_cnt = mvidcnt;
1467
Benny Prijono0bc99a92011-03-17 04:34:43 +00001468 } else {
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001469
1470 /* If call already established, calculate media count from current
1471 * local active SDP and call setting. Otherwise, calculate media
1472 * count from the call setting only.
1473 */
1474 if (reinit) {
Benny Prijono0ec6cfb2011-12-26 09:00:42 +00001475 const pjmedia_sdp_session *sdp;
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001476
1477 status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &sdp);
1478 pj_assert(status == PJ_SUCCESS);
1479
1480 sort_media(sdp, &STR_AUDIO, acc->cfg.use_srtp,
1481 maudidx, &maudcnt, &mtotaudcnt);
1482 pj_assert(maudcnt > 0);
1483
1484 sort_media(sdp, &STR_VIDEO, acc->cfg.use_srtp,
1485 mvididx, &mvidcnt, &mtotvidcnt);
1486
1487 /* Call setting may add or remove media. Adding media is done by
1488 * enabling any disabled/port-zeroed media first, then adding new
1489 * media whenever needed. Removing media is done by disabling
1490 * media with the lowest 'quality'.
1491 */
1492
1493 /* Check if we need to add new audio */
Benny Prijonoae86e6a2011-12-27 12:47:52 +00001494 if (maudcnt < call->opt.aud_cnt &&
1495 mtotaudcnt < call->opt.aud_cnt)
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001496 {
Benny Prijonoae86e6a2011-12-27 12:47:52 +00001497 for (mi = 0; mi < call->opt.aud_cnt - mtotaudcnt; ++mi)
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001498 maudidx[maudcnt++] = (pj_uint8_t)call->med_cnt++;
1499
Benny Prijonoae86e6a2011-12-27 12:47:52 +00001500 mtotaudcnt = call->opt.aud_cnt;
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001501 }
Benny Prijonoae86e6a2011-12-27 12:47:52 +00001502 maudcnt = call->opt.aud_cnt;
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001503
1504 /* Check if we need to add new video */
Benny Prijonoae86e6a2011-12-27 12:47:52 +00001505 if (mvidcnt < call->opt.vid_cnt &&
1506 mtotvidcnt < call->opt.vid_cnt)
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001507 {
Benny Prijonoae86e6a2011-12-27 12:47:52 +00001508 for (mi = 0; mi < call->opt.vid_cnt - mtotvidcnt; ++mi)
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001509 mvididx[mvidcnt++] = (pj_uint8_t)call->med_cnt++;
1510
Benny Prijonoae86e6a2011-12-27 12:47:52 +00001511 mtotvidcnt = call->opt.vid_cnt;
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001512 }
Benny Prijonoae86e6a2011-12-27 12:47:52 +00001513 mvidcnt = call->opt.vid_cnt;
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001514
1515 } else {
1516
Benny Prijonoae86e6a2011-12-27 12:47:52 +00001517 maudcnt = mtotaudcnt = call->opt.aud_cnt;
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001518 for (mi=0; mi<maudcnt; ++mi) {
1519 maudidx[mi] = (pj_uint8_t)mi;
1520 }
Benny Prijonoae86e6a2011-12-27 12:47:52 +00001521 mvidcnt = mtotvidcnt = call->opt.vid_cnt;
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001522 for (mi=0; mi<mvidcnt; ++mi) {
1523 mvididx[mi] = (pj_uint8_t)(maudcnt + mi);
1524 }
1525 call->med_cnt = maudcnt + mvidcnt;
1526
1527 /* Need to publish supported media? */
1528 if (call->opt.flag & PJSUA_CALL_INCLUDE_DISABLED_MEDIA) {
1529 if (mtotaudcnt == 0) {
1530 mtotaudcnt = 1;
1531 maudidx[0] = (pj_uint8_t)call->med_cnt++;
1532 }
Nanang Izzuddind8c33d82011-08-18 18:30:55 +00001533#if PJMEDIA_HAS_VIDEO
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001534 if (mtotvidcnt == 0) {
1535 mtotvidcnt = 1;
1536 mvididx[0] = (pj_uint8_t)call->med_cnt++;
1537 }
1538#endif
1539 }
Benny Prijono0bc99a92011-03-17 04:34:43 +00001540 }
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001541
1542 call->rem_offerer = PJ_FALSE;
Benny Prijonoa310bd22008-06-27 21:19:44 +00001543 }
1544
Benny Prijono0bc99a92011-03-17 04:34:43 +00001545 if (call->med_cnt == 0) {
1546 /* Expecting at least one media */
Benny Prijonoab8dba92008-06-27 21:59:15 +00001547 if (sip_err_code) *sip_err_code = PJSIP_SC_NOT_ACCEPTABLE_HERE;
Benny Prijonoa310bd22008-06-27 21:19:44 +00001548 pjsua_media_channel_deinit(call_id);
Benny Prijonob90fd382011-09-18 14:59:56 +00001549 status = PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_NOT_ACCEPTABLE_HERE);
1550 goto on_error;
Benny Prijonoa310bd22008-06-27 21:19:44 +00001551 }
1552
Sauw Ming73ecfe82011-09-21 10:20:01 +00001553 if (async) {
1554 call->med_ch_cb = cb;
Benny Prijono7eff5ef2011-10-26 06:53:30 +00001555 }
1556
1557 if (rem_sdp) {
1558 call->async_call.rem_sdp =
1559 pjmedia_sdp_session_clone(call->inv->pool_prov, rem_sdp);
1560 } else {
1561 call->async_call.rem_sdp = NULL;
Sauw Ming73ecfe82011-09-21 10:20:01 +00001562 }
1563
Sauw Ming99cc8ff2011-09-22 04:24:56 +00001564 call->async_call.pool_prov = tmp_pool;
1565
Benny Prijono0bc99a92011-03-17 04:34:43 +00001566 /* Initialize each media line */
1567 for (mi=0; mi < call->med_cnt; ++mi) {
1568 pjsua_call_media *call_med = &call->media[mi];
1569 pj_bool_t enabled = PJ_FALSE;
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001570 pjmedia_type media_type = PJMEDIA_TYPE_UNKNOWN;
Benny Prijonoa310bd22008-06-27 21:19:44 +00001571
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001572 if (pj_memchr(maudidx, mi, mtotaudcnt * sizeof(maudidx[0]))) {
1573 media_type = PJMEDIA_TYPE_AUDIO;
Benny Prijonoae86e6a2011-12-27 12:47:52 +00001574 if (call->opt.aud_cnt &&
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001575 pj_memchr(maudidx, mi, maudcnt * sizeof(maudidx[0])))
1576 {
1577 enabled = PJ_TRUE;
Benny Prijono0bc99a92011-03-17 04:34:43 +00001578 }
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001579 } else if (pj_memchr(mvididx, mi, mtotvidcnt * sizeof(mvididx[0]))) {
1580 media_type = PJMEDIA_TYPE_VIDEO;
Benny Prijonoae86e6a2011-12-27 12:47:52 +00001581 if (call->opt.vid_cnt &&
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001582 pj_memchr(mvididx, mi, mvidcnt * sizeof(mvididx[0])))
1583 {
1584 enabled = PJ_TRUE;
Benny Prijono0bc99a92011-03-17 04:34:43 +00001585 }
Benny Prijono0bc99a92011-03-17 04:34:43 +00001586 }
1587
1588 if (enabled) {
1589 status = pjsua_call_media_init(call_med, media_type,
1590 &acc->cfg.rtp_cfg,
Sauw Ming73ecfe82011-09-21 10:20:01 +00001591 security_level, sip_err_code,
1592 async,
Sauw Ming3a55bb92011-10-06 06:49:09 +00001593 (async? &media_channel_init_cb:
1594 NULL));
Sauw Ming73ecfe82011-09-21 10:20:01 +00001595 if (status == PJ_EPENDING) {
1596 pending_med_tp = PJ_TRUE;
1597 } else if (status != PJ_SUCCESS) {
1598 if (pending_med_tp) {
1599 /* Save failure information. */
1600 call_med->tp_ready = status;
1601 pj_bzero(&call->med_ch_info, sizeof(call->med_ch_info));
1602 call->med_ch_info.status = status;
1603 call->med_ch_info.state = call_med->tp_st;
1604 call->med_ch_info.med_idx = call_med->idx;
1605 if (sip_err_code)
1606 call->med_ch_info.sip_err_code = *sip_err_code;
1607
1608 /* We will return failure in the callback later. */
1609 return PJ_EPENDING;
1610 }
1611
1612 pjsua_media_channel_deinit(call_id);
Benny Prijonob90fd382011-09-18 14:59:56 +00001613 goto on_error;
Benny Prijono0bc99a92011-03-17 04:34:43 +00001614 }
1615 } else {
Nanang Izzuddinb6c239c2011-05-10 05:42:28 +00001616 /* By convention, the media is disabled if transport is NULL
1617 * or transport state is PJSUA_MED_TP_DISABLED.
1618 */
Benny Prijono0bc99a92011-03-17 04:34:43 +00001619 if (call_med->tp) {
Nanang Izzuddinb6c239c2011-05-10 05:42:28 +00001620 // Don't close transport here, as SDP negotiation has not been
1621 // done and stream may be still active.
1622 //pjmedia_transport_close(call_med->tp);
1623 //call_med->tp = NULL;
1624 pj_assert(call_med->tp_st == PJSUA_MED_TP_INIT ||
1625 call_med->tp_st == PJSUA_MED_TP_RUNNING);
Benny Prijono1312e752012-03-22 09:56:52 +00001626 pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_DISABLED);
Benny Prijono0bc99a92011-03-17 04:34:43 +00001627 }
Nanang Izzuddinb6c239c2011-05-10 05:42:28 +00001628
1629 /* Put media type just for info */
1630 call_med->type = media_type;
Benny Prijono0bc99a92011-03-17 04:34:43 +00001631 }
Benny Prijono224b4e22008-06-19 14:10:28 +00001632 }
1633
Benny Prijono0bc99a92011-03-17 04:34:43 +00001634 call->audio_idx = maudidx[0];
1635
1636 PJ_LOG(4,(THIS_FILE, "Media index %d selected for audio call %d",
1637 call->audio_idx, call->index));
1638
Sauw Ming73ecfe82011-09-21 10:20:01 +00001639 if (pending_med_tp) {
Sauw Ming99cc8ff2011-09-22 04:24:56 +00001640 /* We shouldn't use temporary pool anymore. */
1641 call->async_call.pool_prov = NULL;
Sauw Ming73ecfe82011-09-21 10:20:01 +00001642 /* We have a pending media transport initialization. */
1643 pj_log_pop_indent();
1644 return PJ_EPENDING;
Benny Prijono0bc99a92011-03-17 04:34:43 +00001645 }
1646
Sauw Ming73ecfe82011-09-21 10:20:01 +00001647 /* Media transport initialization completed immediately, so
1648 * we don't need to call the callback.
1649 */
1650 call->med_ch_cb = NULL;
1651
1652 status = media_channel_init_cb(call_id, NULL);
1653 if (status != PJ_SUCCESS && sip_err_code)
1654 *sip_err_code = call->med_ch_info.sip_err_code;
1655
Benny Prijonob90fd382011-09-18 14:59:56 +00001656 pj_log_pop_indent();
Sauw Ming73ecfe82011-09-21 10:20:01 +00001657 return status;
Benny Prijonob90fd382011-09-18 14:59:56 +00001658
1659on_error:
Sauw Ming73ecfe82011-09-21 10:20:01 +00001660 if (call->med_ch_mutex) {
1661 pj_mutex_destroy(call->med_ch_mutex);
1662 call->med_ch_mutex = NULL;
1663 }
1664
Benny Prijonob90fd382011-09-18 14:59:56 +00001665 pj_log_pop_indent();
1666 return status;
Benny Prijonoc97608e2007-03-23 16:34:20 +00001667}
1668
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001669
1670/* Create SDP based on the current media channel. Note that, this function
1671 * will not modify the media channel, so when receiving new offer or
1672 * updating media count (via call setting), media channel must be reinit'd
1673 * (using pjsua_media_channel_init()) first before calling this function.
1674 */
Benny Prijonoc97608e2007-03-23 16:34:20 +00001675pj_status_t pjsua_media_channel_create_sdp(pjsua_call_id call_id,
1676 pj_pool_t *pool,
Benny Prijonod8179652008-01-23 20:39:07 +00001677 const pjmedia_sdp_session *rem_sdp,
Benny Prijono25b2ea12008-01-24 19:20:54 +00001678 pjmedia_sdp_session **p_sdp,
Benny Prijono0bc99a92011-03-17 04:34:43 +00001679 int *sip_err_code)
Benny Prijonoc97608e2007-03-23 16:34:20 +00001680{
Benny Prijono0bc99a92011-03-17 04:34:43 +00001681 enum { MAX_MEDIA = PJSUA_MAX_CALL_MEDIA };
Benny Prijonoc97608e2007-03-23 16:34:20 +00001682 pjmedia_sdp_session *sdp;
Benny Prijono0bc99a92011-03-17 04:34:43 +00001683 pj_sockaddr origin;
Benny Prijonoc97608e2007-03-23 16:34:20 +00001684 pjsua_call *call = &pjsua_var.calls[call_id];
Nanang Izzuddin1e952a82010-10-05 16:32:04 +00001685 pjmedia_sdp_neg_state sdp_neg_state = PJMEDIA_SDP_NEG_STATE_NULL;
Benny Prijono0bc99a92011-03-17 04:34:43 +00001686 unsigned mi;
Nanang Izzuddin2f296352012-01-27 09:12:59 +00001687 unsigned tot_bandw_tias = 0;
Benny Prijonoc97608e2007-03-23 16:34:20 +00001688 pj_status_t status;
1689
Benny Prijono0bc99a92011-03-17 04:34:43 +00001690 if (pjsua_get_state() != PJSUA_STATE_RUNNING)
Benny Prijono55e82352007-05-10 20:49:08 +00001691 return PJ_EBUSY;
Benny Prijono55e82352007-05-10 20:49:08 +00001692
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001693#if 0
1694 // This function should not really change the media channel.
Benny Prijono0bc99a92011-03-17 04:34:43 +00001695 if (rem_sdp) {
Nanang Izzuddinb6c239c2011-05-10 05:42:28 +00001696 /* If this is a re-offer, let's re-initialize media as remote may
1697 * add or remove media
1698 */
1699 if (call->inv && call->inv->state == PJSIP_INV_STATE_CONFIRMED) {
1700 status = pjsua_media_channel_init(call_id, PJSIP_ROLE_UAS,
1701 call->secure_level, pool,
Sauw Ming73ecfe82011-09-21 10:20:01 +00001702 rem_sdp, sip_err_code,
1703 PJ_FALSE, NULL);
Nanang Izzuddinb6c239c2011-05-10 05:42:28 +00001704 if (status != PJ_SUCCESS)
1705 return status;
Benny Prijono0324ba52010-12-02 10:41:46 +00001706 }
Benny Prijono0bc99a92011-03-17 04:34:43 +00001707 } else {
1708 /* Audio is first in our offer, by convention */
Nanang Izzuddinb6c239c2011-05-10 05:42:28 +00001709 // The audio_idx should not be changed here, as this function may be
1710 // called in generating re-offer and the current active audio index
1711 // can be anywhere.
1712 //call->audio_idx = 0;
Nanang Izzuddin3150d8b2010-12-01 08:20:28 +00001713 }
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001714#endif
Nanang Izzuddin3150d8b2010-12-01 08:20:28 +00001715
Nanang Izzuddinb6c239c2011-05-10 05:42:28 +00001716#if 0
1717 // Since r3512, old-style hold should have got transport, created by
1718 // pjsua_media_channel_init() in initial offer/answer or remote reoffer.
Benny Prijono224b4e22008-06-19 14:10:28 +00001719 /* Create media if it's not created. This could happen when call is
Benny Prijono0bc99a92011-03-17 04:34:43 +00001720 * currently on-hold (with the old style hold)
Benny Prijono224b4e22008-06-19 14:10:28 +00001721 */
Benny Prijono0bc99a92011-03-17 04:34:43 +00001722 if (call->media[call->audio_idx].tp == NULL) {
Benny Prijono224b4e22008-06-19 14:10:28 +00001723 pjsip_role_e role;
1724 role = (rem_sdp ? PJSIP_ROLE_UAS : PJSIP_ROLE_UAC);
1725 status = pjsua_media_channel_init(call_id, role, call->secure_level,
Benny Prijono0bc99a92011-03-17 04:34:43 +00001726 pool, rem_sdp, sip_err_code);
Benny Prijono224b4e22008-06-19 14:10:28 +00001727 if (status != PJ_SUCCESS)
1728 return status;
1729 }
Nanang Izzuddinb6c239c2011-05-10 05:42:28 +00001730#endif
Benny Prijono224b4e22008-06-19 14:10:28 +00001731
Nanang Izzuddin1e952a82010-10-05 16:32:04 +00001732 /* Get SDP negotiator state */
1733 if (call->inv && call->inv->neg)
1734 sdp_neg_state = pjmedia_sdp_neg_get_state(call->inv->neg);
1735
Benny Prijono0bc99a92011-03-17 04:34:43 +00001736 /* Get one address to use in the origin field */
1737 pj_bzero(&origin, sizeof(origin));
1738 for (mi=0; mi<call->med_cnt; ++mi) {
1739 pjmedia_transport_info tpinfo;
Benny Prijono617c5bc2007-04-02 19:51:21 +00001740
Benny Prijono0bc99a92011-03-17 04:34:43 +00001741 if (call->media[mi].tp == NULL)
1742 continue;
1743
1744 pjmedia_transport_info_init(&tpinfo);
1745 pjmedia_transport_get_info(call->media[mi].tp, &tpinfo);
1746 pj_sockaddr_cp(&origin, &tpinfo.sock_info.rtp_addr_name);
1747 break;
Benny Prijono25b2ea12008-01-24 19:20:54 +00001748 }
Benny Prijonoc97608e2007-03-23 16:34:20 +00001749
Benny Prijono0bc99a92011-03-17 04:34:43 +00001750 /* Create the base (blank) SDP */
1751 status = pjmedia_endpt_create_base_sdp(pjsua_var.med_endpt, pool, NULL,
1752 &origin, &sdp);
1753 if (status != PJ_SUCCESS)
1754 return status;
Benny Prijonoa310bd22008-06-27 21:19:44 +00001755
Benny Prijono0bc99a92011-03-17 04:34:43 +00001756 /* Process each media line */
1757 for (mi=0; mi<call->med_cnt; ++mi) {
1758 pjsua_call_media *call_med = &call->media[mi];
1759 pjmedia_sdp_media *m = NULL;
1760 pjmedia_transport_info tpinfo;
Nanang Izzuddin2f296352012-01-27 09:12:59 +00001761 unsigned i;
Benny Prijono0bc99a92011-03-17 04:34:43 +00001762
Nanang Izzuddinb6c239c2011-05-10 05:42:28 +00001763 if (rem_sdp && mi >= rem_sdp->media_count) {
1764 /* Remote might have removed some media lines. */
1765 break;
Nanang Izzuddin1e952a82010-10-05 16:32:04 +00001766 }
1767
Nanang Izzuddinb6c239c2011-05-10 05:42:28 +00001768 if (call_med->tp == NULL || call_med->tp_st == PJSUA_MED_TP_DISABLED)
1769 {
Benny Prijono0bc99a92011-03-17 04:34:43 +00001770 /*
Nanang Izzuddinb6c239c2011-05-10 05:42:28 +00001771 * This media is disabled. Just create a valid SDP with zero
Benny Prijono0bc99a92011-03-17 04:34:43 +00001772 * port.
1773 */
Nanang Izzuddind8c33d82011-08-18 18:30:55 +00001774 if (rem_sdp) {
1775 /* Just clone the remote media and deactivate it */
1776 m = pjmedia_sdp_media_clone_deactivate(pool,
1777 rem_sdp->media[mi]);
1778 } else {
1779 m = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_media);
1780 m->desc.transport = pj_str("RTP/AVP");
1781 m->desc.fmt_count = 1;
1782 m->conn = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_conn);
1783 m->conn->net_type = pj_str("IN");
1784 m->conn->addr_type = pj_str("IP4");
1785 m->conn->addr = pj_str("127.0.0.1");
Benny Prijonoa310bd22008-06-27 21:19:44 +00001786
Nanang Izzuddind8c33d82011-08-18 18:30:55 +00001787 switch (call_med->type) {
1788 case PJMEDIA_TYPE_AUDIO:
1789 m->desc.media = pj_str("audio");
1790 m->desc.fmt[0] = pj_str("0");
1791 break;
1792 case PJMEDIA_TYPE_VIDEO:
1793 m->desc.media = pj_str("video");
1794 m->desc.fmt[0] = pj_str("31");
1795 break;
1796 default:
Nanang Izzuddincfa312c2011-10-26 16:57:05 +00001797 /* This must be us generating re-offer, and some unknown
1798 * media may exist, so just clone from active local SDP
1799 * (and it should have been deactivated already).
1800 */
1801 pj_assert(call->inv && call->inv->neg &&
1802 sdp_neg_state == PJMEDIA_SDP_NEG_STATE_DONE);
1803 {
1804 const pjmedia_sdp_session *s_;
1805 pjmedia_sdp_neg_get_active_local(call->inv->neg, &s_);
1806
1807 pj_assert(mi < s_->media_count);
1808 m = pjmedia_sdp_media_clone(pool, s_->media[mi]);
1809 m->desc.port = 0;
Nanang Izzuddind8c33d82011-08-18 18:30:55 +00001810 }
Nanang Izzuddincfa312c2011-10-26 16:57:05 +00001811 break;
Benny Prijono0bc99a92011-03-17 04:34:43 +00001812 }
Benny Prijonoa310bd22008-06-27 21:19:44 +00001813 }
Benny Prijono0bc99a92011-03-17 04:34:43 +00001814
1815 sdp->media[sdp->media_count++] = m;
1816 continue;
Benny Prijonoa310bd22008-06-27 21:19:44 +00001817 }
1818
Benny Prijono0bc99a92011-03-17 04:34:43 +00001819 /* Get transport address info */
1820 pjmedia_transport_info_init(&tpinfo);
1821 pjmedia_transport_get_info(call_med->tp, &tpinfo);
Benny Prijonoa310bd22008-06-27 21:19:44 +00001822
Benny Prijono0bc99a92011-03-17 04:34:43 +00001823 /* Ask pjmedia endpoint to create SDP media line */
1824 switch (call_med->type) {
1825 case PJMEDIA_TYPE_AUDIO:
1826 status = pjmedia_endpt_create_audio_sdp(pjsua_var.med_endpt, pool,
1827 &tpinfo.sock_info, 0, &m);
1828 break;
Nanang Izzuddin63b3c132011-07-19 11:11:07 +00001829#if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0)
Benny Prijono0bc99a92011-03-17 04:34:43 +00001830 case PJMEDIA_TYPE_VIDEO:
1831 status = pjmedia_endpt_create_video_sdp(pjsua_var.med_endpt, pool,
1832 &tpinfo.sock_info, 0, &m);
1833 break;
Nanang Izzuddin63b3c132011-07-19 11:11:07 +00001834#endif
Benny Prijono0bc99a92011-03-17 04:34:43 +00001835 default:
1836 pj_assert(!"Invalid call_med media type");
1837 return PJ_EBUG;
1838 }
Benny Prijonoa310bd22008-06-27 21:19:44 +00001839
Benny Prijono0bc99a92011-03-17 04:34:43 +00001840 if (status != PJ_SUCCESS)
1841 return status;
1842
1843 sdp->media[sdp->media_count++] = m;
1844
1845 /* Give to transport */
1846 status = pjmedia_transport_encode_sdp(call_med->tp, pool,
1847 sdp, rem_sdp, mi);
1848 if (status != PJ_SUCCESS) {
1849 if (sip_err_code) *sip_err_code = PJSIP_SC_NOT_ACCEPTABLE;
1850 return status;
Benny Prijonoa310bd22008-06-27 21:19:44 +00001851 }
Nanang Izzuddin91ba2a22011-04-11 19:59:09 +00001852
1853 /* Copy c= line of the first media to session level,
1854 * if there's none.
1855 */
1856 if (sdp->conn == NULL) {
1857 sdp->conn = pjmedia_sdp_conn_clone(pool, m->conn);
Benny Prijonoa310bd22008-06-27 21:19:44 +00001858 }
Nanang Izzuddin2f296352012-01-27 09:12:59 +00001859
1860
1861 /* Find media bandwidth info */
1862 for (i = 0; i < m->bandw_count; ++i) {
1863 const pj_str_t STR_BANDW_MODIFIER_TIAS = { "TIAS", 4 };
1864 if (!pj_stricmp(&m->bandw[i]->modifier, &STR_BANDW_MODIFIER_TIAS))
1865 {
1866 tot_bandw_tias += m->bandw[i]->value;
1867 break;
1868 }
1869 }
Benny Prijonoa310bd22008-06-27 21:19:44 +00001870 }
1871
Benny Prijono6ba8c542007-10-16 01:34:14 +00001872 /* Add NAT info in the SDP */
1873 if (pjsua_var.ua_cfg.nat_type_in_sdp) {
1874 pjmedia_sdp_attr *a;
1875 pj_str_t value;
1876 char nat_info[80];
1877
1878 value.ptr = nat_info;
1879 if (pjsua_var.ua_cfg.nat_type_in_sdp == 1) {
1880 value.slen = pj_ansi_snprintf(nat_info, sizeof(nat_info),
1881 "%d", pjsua_var.nat_type);
1882 } else {
1883 const char *type_name = pj_stun_get_nat_name(pjsua_var.nat_type);
1884 value.slen = pj_ansi_snprintf(nat_info, sizeof(nat_info),
1885 "%d %s",
1886 pjsua_var.nat_type,
1887 type_name);
1888 }
1889
1890 a = pjmedia_sdp_attr_create(pool, "X-nat", &value);
1891
1892 pjmedia_sdp_attr_add(&sdp->attr_count, sdp->attr, a);
1893
1894 }
1895
Benny Prijonoc97608e2007-03-23 16:34:20 +00001896
Nanang Izzuddin2f296352012-01-27 09:12:59 +00001897 /* Add bandwidth info in session level using bandwidth modifier "AS". */
1898 if (tot_bandw_tias) {
1899 unsigned bandw;
1900 const pj_str_t STR_BANDW_MODIFIER_AS = { "AS", 2 };
1901 pjmedia_sdp_bandw *b;
1902
1903 /* AS bandwidth = RTP bitrate + RTCP bitrate.
1904 * RTP bitrate = payload bitrate (total TIAS) + overheads (~16kbps).
1905 * RTCP bitrate = est. 5% of RTP bitrate.
1906 * Note that AS bandwidth is in kbps.
1907 */
1908 bandw = tot_bandw_tias + 16000;
1909 bandw += bandw * 5 / 100;
1910 b = PJ_POOL_ALLOC_T(pool, pjmedia_sdp_bandw);
1911 b->modifier = STR_BANDW_MODIFIER_AS;
1912 b->value = bandw / 1000;
1913 sdp->bandw[sdp->bandw_count++] = b;
1914 }
1915
1916
Benny Prijono0bc99a92011-03-17 04:34:43 +00001917#if DISABLED_FOR_TICKET_1185 && defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
Nanang Izzuddind89cc3a2010-05-13 05:22:51 +00001918 /* Check if SRTP is in optional mode and configured to use duplicated
1919 * media, i.e: secured and unsecured version, in the SDP offer.
1920 */
1921 if (!rem_sdp &&
1922 pjsua_var.acc[call->acc_id].cfg.use_srtp == PJMEDIA_SRTP_OPTIONAL &&
1923 pjsua_var.acc[call->acc_id].cfg.srtp_optional_dup_offer)
1924 {
1925 unsigned i;
1926
1927 for (i = 0; i < sdp->media_count; ++i) {
1928 pjmedia_sdp_media *m = sdp->media[i];
1929
Benny Prijono0bc99a92011-03-17 04:34:43 +00001930 /* Check if this media is unsecured but has SDP "crypto"
Nanang Izzuddind89cc3a2010-05-13 05:22:51 +00001931 * attribute.
1932 */
1933 if (pj_stricmp2(&m->desc.transport, "RTP/AVP") == 0 &&
1934 pjmedia_sdp_media_find_attr2(m, "crypto", NULL) != NULL)
1935 {
Benny Prijono0bc99a92011-03-17 04:34:43 +00001936 if (i == (unsigned)call->audio_idx &&
Nanang Izzuddin1e952a82010-10-05 16:32:04 +00001937 sdp_neg_state == PJMEDIA_SDP_NEG_STATE_DONE)
1938 {
1939 /* This is a session update, and peer has chosen the
1940 * unsecured version, so let's make this unsecured too.
1941 */
1942 pjmedia_sdp_media_remove_all_attr(m, "crypto");
1943 } else {
1944 /* This is new offer, duplicate media so we'll have
1945 * secured (with "RTP/SAVP" transport) and and unsecured
1946 * versions.
1947 */
1948 pjmedia_sdp_media *new_m;
Nanang Izzuddind89cc3a2010-05-13 05:22:51 +00001949
Nanang Izzuddin1e952a82010-10-05 16:32:04 +00001950 /* Duplicate this media and apply secured transport */
1951 new_m = pjmedia_sdp_media_clone(pool, m);
1952 pj_strdup2(pool, &new_m->desc.transport, "RTP/SAVP");
Nanang Izzuddind89cc3a2010-05-13 05:22:51 +00001953
Nanang Izzuddin1e952a82010-10-05 16:32:04 +00001954 /* Remove the "crypto" attribute in the unsecured media */
1955 pjmedia_sdp_media_remove_all_attr(m, "crypto");
Nanang Izzuddind89cc3a2010-05-13 05:22:51 +00001956
Nanang Izzuddin1e952a82010-10-05 16:32:04 +00001957 /* Insert the new media before the unsecured media */
1958 if (sdp->media_count < PJMEDIA_MAX_SDP_MEDIA) {
Benny Prijono0bc99a92011-03-17 04:34:43 +00001959 pj_array_insert(sdp->media, sizeof(new_m),
Nanang Izzuddin1e952a82010-10-05 16:32:04 +00001960 sdp->media_count, i, &new_m);
1961 ++sdp->media_count;
1962 ++i;
1963 }
Nanang Izzuddind89cc3a2010-05-13 05:22:51 +00001964 }
1965 }
1966 }
1967 }
1968#endif
1969
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001970 call->rem_offerer = (rem_sdp != NULL);
1971
Benny Prijonoc97608e2007-03-23 16:34:20 +00001972 *p_sdp = sdp;
1973 return PJ_SUCCESS;
Benny Prijonoc97608e2007-03-23 16:34:20 +00001974}
1975
1976
1977static void stop_media_session(pjsua_call_id call_id)
1978{
1979 pjsua_call *call = &pjsua_var.calls[call_id];
Benny Prijono0bc99a92011-03-17 04:34:43 +00001980 unsigned mi;
Benny Prijonoc97608e2007-03-23 16:34:20 +00001981
Benny Prijonob90fd382011-09-18 14:59:56 +00001982 pj_log_push_indent();
1983
Benny Prijono0bc99a92011-03-17 04:34:43 +00001984 for (mi=0; mi<call->med_cnt; ++mi) {
1985 pjsua_call_media *call_med = &call->media[mi];
1986
1987 if (call_med->type == PJMEDIA_TYPE_AUDIO) {
Benny Prijono1312e752012-03-22 09:56:52 +00001988 pjsua_aud_stop_stream(call_med);
Nanang Izzuddinfd461eb2008-06-09 09:35:59 +00001989 }
Nanang Izzuddin50fae732011-03-22 09:49:23 +00001990
1991#if PJMEDIA_HAS_VIDEO
1992 else if (call_med->type == PJMEDIA_TYPE_VIDEO) {
Benny Prijono1312e752012-03-22 09:56:52 +00001993 pjsua_vid_stop_stream(call_med);
Benny Prijonoc97608e2007-03-23 16:34:20 +00001994 }
Nanang Izzuddin50fae732011-03-22 09:49:23 +00001995#endif
Nanang Izzuddinbf26db12011-03-18 07:54:50 +00001996
1997 PJ_LOG(4,(THIS_FILE, "Media session call%02d:%d is destroyed",
1998 call_id, mi));
Sauw Minge7dbbc82011-10-24 09:28:13 +00001999 call_med->prev_state = call_med->state;
Benny Prijono0bc99a92011-03-17 04:34:43 +00002000 call_med->state = PJSUA_CALL_MEDIA_NONE;
Benny Prijonoc97608e2007-03-23 16:34:20 +00002001 }
Benny Prijonob90fd382011-09-18 14:59:56 +00002002
2003 pj_log_pop_indent();
Benny Prijonoc97608e2007-03-23 16:34:20 +00002004}
2005
2006pj_status_t pjsua_media_channel_deinit(pjsua_call_id call_id)
2007{
2008 pjsua_call *call = &pjsua_var.calls[call_id];
Benny Prijono0bc99a92011-03-17 04:34:43 +00002009 unsigned mi;
Benny Prijonoc97608e2007-03-23 16:34:20 +00002010
Sauw Ming903154f2011-10-03 08:22:48 +00002011 PJSUA_LOCK();
Sauw Mingec765352011-10-03 02:04:36 +00002012 for (mi=0; mi<call->med_cnt; ++mi) {
2013 pjsua_call_media *call_med = &call->media[mi];
2014
Sauw Ming903154f2011-10-03 08:22:48 +00002015 if (call_med->tp_st == PJSUA_MED_TP_CREATING) {
2016 /* We will do the deinitialization after media transport
2017 * creation is completed.
2018 */
2019 call->async_call.med_ch_deinit = PJ_TRUE;
2020 PJSUA_UNLOCK();
2021 return PJ_SUCCESS;
2022 }
Sauw Mingec765352011-10-03 02:04:36 +00002023 }
Sauw Ming903154f2011-10-03 08:22:48 +00002024 PJSUA_UNLOCK();
Sauw Mingec765352011-10-03 02:04:36 +00002025
Benny Prijonob90fd382011-09-18 14:59:56 +00002026 PJ_LOG(4,(THIS_FILE, "Call %d: deinitializing media..", call_id));
2027 pj_log_push_indent();
2028
Benny Prijonoc97608e2007-03-23 16:34:20 +00002029 stop_media_session(call_id);
2030
Benny Prijono0bc99a92011-03-17 04:34:43 +00002031 for (mi=0; mi<call->med_cnt; ++mi) {
2032 pjsua_call_media *call_med = &call->media[mi];
Benny Prijonoc97608e2007-03-23 16:34:20 +00002033
Sauw Minge7dbbc82011-10-24 09:28:13 +00002034 if (call_med->tp_st > PJSUA_MED_TP_IDLE) {
Benny Prijono0bc99a92011-03-17 04:34:43 +00002035 pjmedia_transport_media_stop(call_med->tp);
Benny Prijono1312e752012-03-22 09:56:52 +00002036 pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_IDLE);
Benny Prijono0bc99a92011-03-17 04:34:43 +00002037 }
2038
Nanang Izzuddinb6c239c2011-05-10 05:42:28 +00002039 //if (call_med->tp_orig && call_med->tp &&
2040 // call_med->tp != call_med->tp_orig)
2041 //{
2042 // pjmedia_transport_close(call_med->tp);
2043 // call_med->tp = call_med->tp_orig;
2044 //}
2045 if (call_med->tp) {
Benny Prijono0bc99a92011-03-17 04:34:43 +00002046 pjmedia_transport_close(call_med->tp);
Nanang Izzuddinb6c239c2011-05-10 05:42:28 +00002047 call_med->tp = call_med->tp_orig = NULL;
Benny Prijono0bc99a92011-03-17 04:34:43 +00002048 }
Sauw Minge7dbbc82011-10-24 09:28:13 +00002049 call_med->tp_orig = NULL;
Benny Prijonod8179652008-01-23 20:39:07 +00002050 }
Nanang Izzuddin670f71b2009-01-20 14:05:54 +00002051
Benny Prijonob90fd382011-09-18 14:59:56 +00002052 pj_log_pop_indent();
Nanang Izzuddin670f71b2009-01-20 14:05:54 +00002053
Benny Prijonoc97608e2007-03-23 16:34:20 +00002054 return PJ_SUCCESS;
2055}
2056
2057
Benny Prijono0bc99a92011-03-17 04:34:43 +00002058pj_status_t pjsua_media_channel_update(pjsua_call_id call_id,
2059 const pjmedia_sdp_session *local_sdp,
2060 const pjmedia_sdp_session *remote_sdp)
2061{
2062 pjsua_call *call = &pjsua_var.calls[call_id];
Nanang Izzuddind8c33d82011-08-18 18:30:55 +00002063 pjsua_acc *acc = &pjsua_var.acc[call->acc_id];
Benny Prijono0bc99a92011-03-17 04:34:43 +00002064 pj_pool_t *tmp_pool = call->inv->pool_prov;
2065 unsigned mi;
Nanang Izzuddinb6c239c2011-05-10 05:42:28 +00002066 pj_bool_t got_media = PJ_FALSE;
Nanang Izzuddinbf26db12011-03-18 07:54:50 +00002067 pj_status_t status = PJ_SUCCESS;
Benny Prijono0bc99a92011-03-17 04:34:43 +00002068
Nanang Izzuddind8c33d82011-08-18 18:30:55 +00002069 const pj_str_t STR_AUDIO = { "audio", 5 };
2070 const pj_str_t STR_VIDEO = { "video", 5 };
2071 pj_uint8_t maudidx[PJSUA_MAX_CALL_MEDIA];
2072 unsigned maudcnt = PJ_ARRAY_SIZE(maudidx);
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00002073 unsigned mtotaudcnt = PJ_ARRAY_SIZE(maudidx);
Nanang Izzuddind8c33d82011-08-18 18:30:55 +00002074 pj_uint8_t mvididx[PJSUA_MAX_CALL_MEDIA];
2075 unsigned mvidcnt = PJ_ARRAY_SIZE(mvididx);
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00002076 unsigned mtotvidcnt = PJ_ARRAY_SIZE(mvididx);
Nanang Izzuddind8c33d82011-08-18 18:30:55 +00002077 pj_bool_t need_renego_sdp = PJ_FALSE;
2078
Benny Prijono0bc99a92011-03-17 04:34:43 +00002079 if (pjsua_get_state() != PJSUA_STATE_RUNNING)
2080 return PJ_EBUSY;
2081
Benny Prijonob90fd382011-09-18 14:59:56 +00002082 PJ_LOG(4,(THIS_FILE, "Call %d: updating media..", call_id));
2083 pj_log_push_indent();
2084
Benny Prijono0bc99a92011-03-17 04:34:43 +00002085 /* Destroy existing media session, if any. */
2086 stop_media_session(call->index);
2087
Nanang Izzuddind8c33d82011-08-18 18:30:55 +00002088 /* Call media count must be at least equal to SDP media. Note that
2089 * it may not be equal when remote removed any SDP media line.
2090 */
2091 pj_assert(call->med_cnt >= local_sdp->media_count);
2092
Benny Prijono0bc99a92011-03-17 04:34:43 +00002093 /* Reset audio_idx first */
2094 call->audio_idx = -1;
2095
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00002096 /* Sort audio/video based on "quality" */
Nanang Izzuddind8c33d82011-08-18 18:30:55 +00002097 sort_media(local_sdp, &STR_AUDIO, acc->cfg.use_srtp,
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00002098 maudidx, &maudcnt, &mtotaudcnt);
Nanang Izzuddind8c33d82011-08-18 18:30:55 +00002099#if PJMEDIA_HAS_VIDEO
2100 sort_media(local_sdp, &STR_VIDEO, acc->cfg.use_srtp,
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00002101 mvididx, &mvidcnt, &mtotvidcnt);
Nanang Izzuddind8c33d82011-08-18 18:30:55 +00002102#else
2103 PJ_UNUSED_ARG(STR_VIDEO);
Nanang Izzuddin3d7385c2011-12-01 10:02:54 +00002104 mvidcnt = mtotvidcnt = 0;
Nanang Izzuddind8c33d82011-08-18 18:30:55 +00002105#endif
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00002106
2107 /* Applying media count limitation. Note that in generating SDP answer,
2108 * no media count limitation applied, as we didn't know yet which media
2109 * would pass the SDP negotiation.
2110 */
Benny Prijonoae86e6a2011-12-27 12:47:52 +00002111 if (maudcnt > call->opt.aud_cnt || mvidcnt > call->opt.vid_cnt)
Nanang Izzuddind8c33d82011-08-18 18:30:55 +00002112 {
2113 pjmedia_sdp_session *local_sdp2;
2114
Benny Prijonoae86e6a2011-12-27 12:47:52 +00002115 maudcnt = PJ_MIN(maudcnt, call->opt.aud_cnt);
2116 mvidcnt = PJ_MIN(mvidcnt, call->opt.vid_cnt);
Nanang Izzuddind8c33d82011-08-18 18:30:55 +00002117 local_sdp2 = pjmedia_sdp_session_clone(tmp_pool, local_sdp);
2118
2119 for (mi=0; mi < local_sdp2->media_count; ++mi) {
2120 pjmedia_sdp_media *m = local_sdp2->media[mi];
2121
2122 if (m->desc.port == 0 ||
2123 pj_memchr(maudidx, mi, maudcnt*sizeof(maudidx[0])) ||
2124 pj_memchr(mvididx, mi, mvidcnt*sizeof(mvididx[0])))
2125 {
2126 continue;
2127 }
2128
2129 /* Deactivate this media */
2130 pjmedia_sdp_media_deactivate(tmp_pool, m);
2131 }
2132
2133 local_sdp = local_sdp2;
2134 need_renego_sdp = PJ_TRUE;
2135 }
2136
Benny Prijono0bc99a92011-03-17 04:34:43 +00002137 /* Process each media stream */
2138 for (mi=0; mi < call->med_cnt; ++mi) {
2139 pjsua_call_media *call_med = &call->media[mi];
2140
Nanang Izzuddinb6c239c2011-05-10 05:42:28 +00002141 if (mi >= local_sdp->media_count ||
2142 mi >= remote_sdp->media_count)
Benny Prijono0bc99a92011-03-17 04:34:43 +00002143 {
Nanang Izzuddinb6c239c2011-05-10 05:42:28 +00002144 /* This may happen when remote removed any SDP media lines in
2145 * its re-offer.
2146 */
2147 continue;
2148#if 0
Benny Prijono0bc99a92011-03-17 04:34:43 +00002149 /* Something is wrong */
2150 PJ_LOG(1,(THIS_FILE, "Error updating media for call %d: "
2151 "invalid media index %d in SDP", call_id, mi));
Benny Prijonob90fd382011-09-18 14:59:56 +00002152 status = PJMEDIA_SDP_EINSDP;
2153 goto on_error;
Nanang Izzuddinb6c239c2011-05-10 05:42:28 +00002154#endif
Benny Prijono0bc99a92011-03-17 04:34:43 +00002155 }
2156
Benny Prijono1312e752012-03-22 09:56:52 +00002157 if (call_med->type==PJMEDIA_TYPE_AUDIO) {
2158 pjmedia_stream_info the_si, *si = &the_si;
2159
2160 status = pjmedia_stream_info_from_sdp(si, tmp_pool, pjsua_var.med_endpt,
2161 local_sdp, remote_sdp, mi);
2162 if (status != PJ_SUCCESS) {
2163 PJ_PERROR(1,(THIS_FILE, status,
2164 "pjmedia_stream_info_from_sdp() failed "
2165 "for call_id %d media %d",
2166 call_id, mi));
2167 continue;
2168 }
2169
2170 /* Check if no media is active */
2171 if (si->dir == PJMEDIA_DIR_NONE) {
2172 /* Update call media state and direction */
2173 call_med->state = PJSUA_CALL_MEDIA_NONE;
2174 call_med->dir = PJMEDIA_DIR_NONE;
2175
2176 } else {
2177 pjmedia_transport_info tp_info;
2178
2179 /* Start/restart media transport based on info in SDP */
2180 status = pjmedia_transport_media_start(call_med->tp,
2181 tmp_pool, local_sdp,
2182 remote_sdp, mi);
2183 if (status != PJ_SUCCESS) {
2184 PJ_PERROR(1,(THIS_FILE, status,
2185 "pjmedia_transport_media_start() failed "
2186 "for call_id %d media %d",
2187 call_id, mi));
2188 continue;
2189 }
2190
2191 pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_RUNNING);
2192
2193 /* Get remote SRTP usage policy */
2194 pjmedia_transport_info_init(&tp_info);
2195 pjmedia_transport_get_info(call_med->tp, &tp_info);
2196 if (tp_info.specific_info_cnt > 0) {
2197 unsigned i;
2198 for (i = 0; i < tp_info.specific_info_cnt; ++i) {
2199 if (tp_info.spc_info[i].type == PJMEDIA_TRANSPORT_TYPE_SRTP)
2200 {
2201 pjmedia_srtp_info *srtp_info =
2202 (pjmedia_srtp_info*) tp_info.spc_info[i].buffer;
2203
2204 call_med->rem_srtp_use = srtp_info->peer_use;
2205 break;
2206 }
2207 }
2208 }
2209
2210 /* Call media direction */
2211 call_med->dir = si->dir;
2212
2213 /* Call media state */
2214 if (call->local_hold)
2215 call_med->state = PJSUA_CALL_MEDIA_LOCAL_HOLD;
2216 else if (call_med->dir == PJMEDIA_DIR_DECODING)
2217 call_med->state = PJSUA_CALL_MEDIA_REMOTE_HOLD;
2218 else
2219 call_med->state = PJSUA_CALL_MEDIA_ACTIVE;
2220 }
2221
2222 /* Call implementation */
2223 status = pjsua_aud_channel_update(call_med, tmp_pool, si,
2224 local_sdp, remote_sdp);
2225 if (status != PJ_SUCCESS) {
2226 PJ_PERROR(1,(THIS_FILE, status,
2227 "pjsua_aud_channel_update() failed "
2228 "for call_id %d media %d",
2229 call_id, mi));
2230 continue;
2231 }
2232
2233 /* Print info. */
2234 if (status == PJ_SUCCESS) {
2235 char info[80];
2236 int info_len = 0;
2237 int len;
2238 const char *dir;
2239
2240 switch (si->dir) {
2241 case PJMEDIA_DIR_NONE:
2242 dir = "inactive";
2243 break;
2244 case PJMEDIA_DIR_ENCODING:
2245 dir = "sendonly";
2246 break;
2247 case PJMEDIA_DIR_DECODING:
2248 dir = "recvonly";
2249 break;
2250 case PJMEDIA_DIR_ENCODING_DECODING:
2251 dir = "sendrecv";
2252 break;
2253 default:
2254 dir = "unknown";
2255 break;
2256 }
2257 len = pj_ansi_sprintf( info+info_len,
2258 ", stream #%d: %.*s (%s)", mi,
2259 (int)si->fmt.encoding_name.slen,
2260 si->fmt.encoding_name.ptr,
2261 dir);
2262 if (len > 0)
2263 info_len += len;
2264 PJ_LOG(4,(THIS_FILE,"Audio updated%s", info));
2265 }
2266
2267
Benny Prijono0bc99a92011-03-17 04:34:43 +00002268 if (call->audio_idx==-1 && status==PJ_SUCCESS &&
Benny Prijono1312e752012-03-22 09:56:52 +00002269 si->dir != PJMEDIA_DIR_NONE)
Benny Prijono0bc99a92011-03-17 04:34:43 +00002270 {
2271 call->audio_idx = mi;
2272 }
Benny Prijono1312e752012-03-22 09:56:52 +00002273
Nanang Izzuddin63b3c132011-07-19 11:11:07 +00002274#if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0)
Benny Prijono1312e752012-03-22 09:56:52 +00002275 } else if (call_med->type==PJMEDIA_TYPE_VIDEO) {
2276 pjmedia_vid_stream_info the_si, *si = &the_si;
2277
2278 status = pjmedia_vid_stream_info_from_sdp(si, tmp_pool, pjsua_var.med_endpt,
2279 local_sdp, remote_sdp, mi);
2280 if (status != PJ_SUCCESS) {
2281 PJ_PERROR(1,(THIS_FILE, status,
2282 "pjmedia_vid_stream_info_from_sdp() failed "
2283 "for call_id %d media %d",
2284 call_id, mi));
2285 continue;
2286 }
2287
2288 /* Check if no media is active */
2289 if (si->dir == PJMEDIA_DIR_NONE) {
2290 /* Call media state */
2291 call_med->state = PJSUA_CALL_MEDIA_NONE;
2292
2293 /* Call media direction */
2294 call_med->dir = PJMEDIA_DIR_NONE;
2295
2296 } else {
2297 pjmedia_transport_info tp_info;
2298
2299 /* Start/restart media transport */
2300 status = pjmedia_transport_media_start(call_med->tp,
2301 tmp_pool, local_sdp,
2302 remote_sdp, mi);
2303 if (status != PJ_SUCCESS) {
2304 PJ_PERROR(1,(THIS_FILE, status,
2305 "pjmedia_transport_media_start() failed "
2306 "for call_id %d media %d",
2307 call_id, mi));
2308 continue;
2309 }
2310
2311 pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_RUNNING);
2312
2313 /* Get remote SRTP usage policy */
2314 pjmedia_transport_info_init(&tp_info);
2315 pjmedia_transport_get_info(call_med->tp, &tp_info);
2316 if (tp_info.specific_info_cnt > 0) {
2317 unsigned i;
2318 for (i = 0; i < tp_info.specific_info_cnt; ++i) {
2319 if (tp_info.spc_info[i].type ==
2320 PJMEDIA_TRANSPORT_TYPE_SRTP)
2321 {
2322 pjmedia_srtp_info *sri;
2323 sri=(pjmedia_srtp_info*)tp_info.spc_info[i].buffer;
2324 call_med->rem_srtp_use = sri->peer_use;
2325 break;
2326 }
2327 }
2328 }
2329
2330 /* Call media direction */
2331 call_med->dir = si->dir;
2332
2333 /* Call media state */
2334 if (call->local_hold)
2335 call_med->state = PJSUA_CALL_MEDIA_LOCAL_HOLD;
2336 else if (call_med->dir == PJMEDIA_DIR_DECODING)
2337 call_med->state = PJSUA_CALL_MEDIA_REMOTE_HOLD;
2338 else
2339 call_med->state = PJSUA_CALL_MEDIA_ACTIVE;
2340 }
2341
2342 status = pjsua_vid_channel_update(call_med, tmp_pool, si,
2343 local_sdp, remote_sdp);
2344 if (status != PJ_SUCCESS) {
2345 PJ_PERROR(1,(THIS_FILE, status,
Nanang Izzuddin1dc6d212012-04-03 08:02:12 +00002346 "pjsua_vid_channel_update() failed "
Benny Prijono1312e752012-03-22 09:56:52 +00002347 "for call_id %d media %d",
2348 call_id, mi));
2349 continue;
2350 }
2351
2352 /* Print info. */
2353 {
2354 char info[80];
2355 int info_len = 0;
2356 int len;
2357 const char *dir;
2358
2359 switch (si->dir) {
2360 case PJMEDIA_DIR_NONE:
2361 dir = "inactive";
2362 break;
2363 case PJMEDIA_DIR_ENCODING:
2364 dir = "sendonly";
2365 break;
2366 case PJMEDIA_DIR_DECODING:
2367 dir = "recvonly";
2368 break;
2369 case PJMEDIA_DIR_ENCODING_DECODING:
2370 dir = "sendrecv";
2371 break;
2372 default:
2373 dir = "unknown";
2374 break;
2375 }
2376 len = pj_ansi_sprintf( info+info_len,
2377 ", stream #%d: %.*s (%s)", mi,
2378 (int)si->codec_info.encoding_name.slen,
2379 si->codec_info.encoding_name.ptr,
2380 dir);
2381 if (len > 0)
2382 info_len += len;
2383 PJ_LOG(4,(THIS_FILE,"Video updated%s", info));
2384 }
2385
Nanang Izzuddin50fae732011-03-22 09:49:23 +00002386#endif
Benny Prijono1312e752012-03-22 09:56:52 +00002387 } else {
Nanang Izzuddinb6c239c2011-05-10 05:42:28 +00002388 status = PJMEDIA_EINVALIMEDIATYPE;
Benny Prijono0bc99a92011-03-17 04:34:43 +00002389 }
2390
Nanang Izzuddind8c33d82011-08-18 18:30:55 +00002391 /* Close the transport of deactivated media, need this here as media
2392 * can be deactivated by the SDP negotiation and the max media count
2393 * (account) setting.
2394 */
2395 if (local_sdp->media[mi]->desc.port==0 && call_med->tp) {
2396 pjmedia_transport_close(call_med->tp);
2397 call_med->tp = call_med->tp_orig = NULL;
Benny Prijono1312e752012-03-22 09:56:52 +00002398 pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_IDLE);
Nanang Izzuddind8c33d82011-08-18 18:30:55 +00002399 }
2400
Benny Prijono0bc99a92011-03-17 04:34:43 +00002401 if (status != PJ_SUCCESS) {
Nanang Izzuddinb6c239c2011-05-10 05:42:28 +00002402 PJ_PERROR(1,(THIS_FILE, status, "Error updating media call%02d:%d",
Benny Prijono0bc99a92011-03-17 04:34:43 +00002403 call_id, mi));
Nanang Izzuddinb6c239c2011-05-10 05:42:28 +00002404 } else {
2405 got_media = PJ_TRUE;
Benny Prijono0bc99a92011-03-17 04:34:43 +00002406 }
2407 }
2408
Nanang Izzuddind8c33d82011-08-18 18:30:55 +00002409 /* Perform SDP re-negotiation if needed. */
2410 if (got_media && need_renego_sdp) {
2411 pjmedia_sdp_neg *neg = call->inv->neg;
2412
2413 /* This should only happen when we are the answerer. */
2414 PJ_ASSERT_RETURN(neg && !pjmedia_sdp_neg_was_answer_remote(neg),
2415 PJMEDIA_SDPNEG_EINSTATE);
2416
2417 status = pjmedia_sdp_neg_set_remote_offer(tmp_pool, neg, remote_sdp);
2418 if (status != PJ_SUCCESS)
Benny Prijonob90fd382011-09-18 14:59:56 +00002419 goto on_error;
Nanang Izzuddind8c33d82011-08-18 18:30:55 +00002420
2421 status = pjmedia_sdp_neg_set_local_answer(tmp_pool, neg, local_sdp);
2422 if (status != PJ_SUCCESS)
Benny Prijonob90fd382011-09-18 14:59:56 +00002423 goto on_error;
Nanang Izzuddind8c33d82011-08-18 18:30:55 +00002424
2425 status = pjmedia_sdp_neg_negotiate(tmp_pool, neg, 0);
2426 if (status != PJ_SUCCESS)
Benny Prijonob90fd382011-09-18 14:59:56 +00002427 goto on_error;
Nanang Izzuddind8c33d82011-08-18 18:30:55 +00002428 }
2429
Benny Prijonob90fd382011-09-18 14:59:56 +00002430 pj_log_pop_indent();
Nanang Izzuddinb6c239c2011-05-10 05:42:28 +00002431 return (got_media? PJ_SUCCESS : PJMEDIA_SDPNEG_ENOMEDIA);
Benny Prijonob90fd382011-09-18 14:59:56 +00002432
2433on_error:
2434 pj_log_pop_indent();
2435 return status;
Benny Prijono0bc99a92011-03-17 04:34:43 +00002436}
2437
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002438/*****************************************************************************
2439 * Codecs.
2440 */
2441
2442/*
2443 * Enum all supported codecs in the system.
2444 */
2445PJ_DEF(pj_status_t) pjsua_enum_codecs( pjsua_codec_info id[],
2446 unsigned *p_count )
2447{
2448 pjmedia_codec_mgr *codec_mgr;
2449 pjmedia_codec_info info[32];
2450 unsigned i, count, prio[32];
2451 pj_status_t status;
2452
2453 codec_mgr = pjmedia_endpt_get_codec_mgr(pjsua_var.med_endpt);
2454 count = PJ_ARRAY_SIZE(info);
2455 status = pjmedia_codec_mgr_enum_codecs( codec_mgr, &count, info, prio);
2456 if (status != PJ_SUCCESS) {
2457 *p_count = 0;
2458 return status;
2459 }
2460
2461 if (count > *p_count) count = *p_count;
2462
2463 for (i=0; i<count; ++i) {
Nanang Izzuddin56b2ce42011-04-06 13:55:01 +00002464 pj_bzero(&id[i], sizeof(pjsua_codec_info));
2465
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002466 pjmedia_codec_info_to_id(&info[i], id[i].buf_, sizeof(id[i].buf_));
2467 id[i].codec_id = pj_str(id[i].buf_);
2468 id[i].priority = (pj_uint8_t) prio[i];
2469 }
2470
2471 *p_count = count;
2472
2473 return PJ_SUCCESS;
2474}
2475
2476
2477/*
2478 * Change codec priority.
2479 */
2480PJ_DEF(pj_status_t) pjsua_codec_set_priority( const pj_str_t *codec_id,
2481 pj_uint8_t priority )
2482{
Benny Prijono88accae2008-06-26 15:48:14 +00002483 const pj_str_t all = { NULL, 0 };
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002484 pjmedia_codec_mgr *codec_mgr;
2485
2486 codec_mgr = pjmedia_endpt_get_codec_mgr(pjsua_var.med_endpt);
2487
Benny Prijono88accae2008-06-26 15:48:14 +00002488 if (codec_id->slen==1 && *codec_id->ptr=='*')
2489 codec_id = &all;
2490
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002491 return pjmedia_codec_mgr_set_codec_priority(codec_mgr, codec_id,
2492 priority);
2493}
2494
2495
2496/*
2497 * Get codec parameters.
2498 */
2499PJ_DEF(pj_status_t) pjsua_codec_get_param( const pj_str_t *codec_id,
2500 pjmedia_codec_param *param )
2501{
Benny Prijono88accae2008-06-26 15:48:14 +00002502 const pj_str_t all = { NULL, 0 };
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002503 const pjmedia_codec_info *info;
2504 pjmedia_codec_mgr *codec_mgr;
2505 unsigned count = 1;
2506 pj_status_t status;
2507
2508 codec_mgr = pjmedia_endpt_get_codec_mgr(pjsua_var.med_endpt);
2509
Benny Prijono88accae2008-06-26 15:48:14 +00002510 if (codec_id->slen==1 && *codec_id->ptr=='*')
2511 codec_id = &all;
2512
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002513 status = pjmedia_codec_mgr_find_codecs_by_id(codec_mgr, codec_id,
2514 &count, &info, NULL);
2515 if (status != PJ_SUCCESS)
2516 return status;
2517
2518 if (count != 1)
Nanang Izzuddin50fae732011-03-22 09:49:23 +00002519 return (count > 1? PJ_ETOOMANY : PJ_ENOTFOUND);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002520
2521 status = pjmedia_codec_mgr_get_default_param( codec_mgr, info, param);
2522 return status;
2523}
2524
2525
2526/*
2527 * Set codec parameters.
2528 */
Nanang Izzuddin06839e72010-01-27 11:48:31 +00002529PJ_DEF(pj_status_t) pjsua_codec_set_param( const pj_str_t *codec_id,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002530 const pjmedia_codec_param *param)
2531{
Nanang Izzuddin06839e72010-01-27 11:48:31 +00002532 const pjmedia_codec_info *info[2];
2533 pjmedia_codec_mgr *codec_mgr;
2534 unsigned count = 2;
2535 pj_status_t status;
2536
2537 codec_mgr = pjmedia_endpt_get_codec_mgr(pjsua_var.med_endpt);
2538
2539 status = pjmedia_codec_mgr_find_codecs_by_id(codec_mgr, codec_id,
2540 &count, info, NULL);
2541 if (status != PJ_SUCCESS)
2542 return status;
2543
2544 /* Codec ID should be specific, except for G.722.1 */
2545 if (count > 1 &&
2546 pj_strnicmp2(codec_id, "G7221/16", 8) != 0 &&
2547 pj_strnicmp2(codec_id, "G7221/32", 8) != 0)
2548 {
2549 pj_assert(!"Codec ID is not specific");
2550 return PJ_ETOOMANY;
2551 }
2552
2553 status = pjmedia_codec_mgr_set_default_param(codec_mgr, info[0], param);
2554 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002555}
Nanang Izzuddin50fae732011-03-22 09:49:23 +00002556
2557
Nanang Izzuddin3da4ad82011-12-07 10:43:28 +00002558pj_status_t pjsua_media_apply_xml_control(pjsua_call_id call_id,
2559 const pj_str_t *xml_st)
2560{
2561 pjsua_call *call = &pjsua_var.calls[call_id];
2562 const pj_str_t PICT_FAST_UPDATE = {"picture_fast_update", 19};
2563
2564#if PJMEDIA_HAS_VIDEO
2565 if (pj_strstr(xml_st, &PICT_FAST_UPDATE)) {
2566 unsigned i;
2567
2568 PJ_LOG(4,(THIS_FILE, "Received keyframe request via SIP INFO"));
2569
2570 for (i = 0; i < call->med_cnt; ++i) {
2571 pjsua_call_media *cm = &call->media[i];
2572 if (cm->type != PJMEDIA_TYPE_VIDEO || !cm->strm.v.stream)
2573 continue;
2574
2575 pjmedia_vid_stream_send_keyframe(cm->strm.v.stream);
2576 }
2577
2578 return PJ_SUCCESS;
2579 }
2580#endif
2581
2582 /* Just to avoid compiler warning of unused var */
2583 PJ_UNUSED_ARG(xml_st);
2584
2585 return PJ_ENOTSUP;
2586}
2587