blob: 8e7db5ac53f9d8e8216da7da4b7fa47f54d42449 [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) {
Nanang Izzuddin0d2e9e92012-04-26 18:41:33 +0000194 /* TODO: check if we're not allowed to send to network in the
195 * "flags", and if so do not do TURN allocation...
196 */
197 PJ_UNUSED_ARG(flags);
198 pjsua_media_channel_deinit(i);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000199 }
200
201 /* Destroy media endpoint. */
202 if (pjsua_var.med_endpt) {
203
Benny Prijono0bc99a92011-03-17 04:34:43 +0000204# if PJMEDIA_HAS_VIDEO
Benny Prijono9f468d12011-07-07 07:46:33 +0000205 pjsua_vid_subsys_destroy();
Benny Prijono0bc99a92011-03-17 04:34:43 +0000206# endif
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000207
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000208 pjmedia_endpt_destroy(pjsua_var.med_endpt);
209 pjsua_var.med_endpt = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000210
Benny Prijonoad2e0ca2007-04-29 12:31:51 +0000211 /* Deinitialize sound subsystem */
Benny Prijonoa41d66e2007-10-01 12:17:23 +0000212 // Not necessary, as pjmedia_snd_deinit() should have been called
213 // in pjmedia_endpt_destroy().
214 //pjmedia_snd_deinit();
Benny Prijonoad2e0ca2007-04-29 12:31:51 +0000215 }
Benny Prijonoaf1bb1e2006-11-21 12:39:31 +0000216
Benny Prijonode479562007-03-15 10:23:55 +0000217 /* Reset RTP port */
218 next_rtp_port = 0;
219
Benny Prijonob90fd382011-09-18 14:59:56 +0000220 pj_log_pop_indent();
221
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000222 return PJ_SUCCESS;
223}
224
Benny Prijono0bc99a92011-03-17 04:34:43 +0000225/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000226 * Create RTP and RTCP socket pair, and possibly resolve their public
227 * address via STUN.
228 */
229static pj_status_t create_rtp_rtcp_sock(const pjsua_transport_config *cfg,
230 pjmedia_sock_info *skinfo)
231{
Benny Prijono0bc99a92011-03-17 04:34:43 +0000232 enum {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000233 RTP_RETRY = 100
234 };
235 int i;
236 pj_sockaddr_in bound_addr;
237 pj_sockaddr_in mapped_addr[2];
238 pj_status_t status = PJ_SUCCESS;
239 char addr_buf[PJ_INET6_ADDRSTRLEN+2];
240 pj_sock_t sock[2];
241
242 /* Make sure STUN server resolution has completed */
243 status = resolve_stun_server(PJ_TRUE);
244 if (status != PJ_SUCCESS) {
245 pjsua_perror(THIS_FILE, "Error resolving STUN server", status);
246 return status;
247 }
248
249 if (next_rtp_port == 0)
250 next_rtp_port = (pj_uint16_t)cfg->port;
251
Benny Prijono0bc99a92011-03-17 04:34:43 +0000252 if (next_rtp_port == 0)
253 next_rtp_port = (pj_uint16_t)40000;
254
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000255 for (i=0; i<2; ++i)
256 sock[i] = PJ_INVALID_SOCKET;
257
258 bound_addr.sin_addr.s_addr = PJ_INADDR_ANY;
259 if (cfg->bound_addr.slen) {
260 status = pj_sockaddr_in_set_str_addr(&bound_addr, &cfg->bound_addr);
261 if (status != PJ_SUCCESS) {
262 pjsua_perror(THIS_FILE, "Unable to resolve transport bind address",
263 status);
264 return status;
265 }
266 }
267
268 /* Loop retry to bind RTP and RTCP sockets. */
269 for (i=0; i<RTP_RETRY; ++i, next_rtp_port += 2) {
270
271 /* Create RTP socket. */
272 status = pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &sock[0]);
273 if (status != PJ_SUCCESS) {
274 pjsua_perror(THIS_FILE, "socket() error", status);
275 return status;
276 }
277
278 /* Apply QoS to RTP socket, if specified */
Benny Prijono0bc99a92011-03-17 04:34:43 +0000279 status = pj_sock_apply_qos2(sock[0], cfg->qos_type,
280 &cfg->qos_params,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000281 2, THIS_FILE, "RTP socket");
282
283 /* Bind RTP socket */
Benny Prijono0bc99a92011-03-17 04:34:43 +0000284 status=pj_sock_bind_in(sock[0], pj_ntohl(bound_addr.sin_addr.s_addr),
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000285 next_rtp_port);
286 if (status != PJ_SUCCESS) {
Benny Prijono0bc99a92011-03-17 04:34:43 +0000287 pj_sock_close(sock[0]);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000288 sock[0] = PJ_INVALID_SOCKET;
289 continue;
290 }
291
292 /* Create RTCP socket. */
293 status = pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &sock[1]);
294 if (status != PJ_SUCCESS) {
295 pjsua_perror(THIS_FILE, "socket() error", status);
296 pj_sock_close(sock[0]);
297 return status;
298 }
299
300 /* Apply QoS to RTCP socket, if specified */
Benny Prijono0bc99a92011-03-17 04:34:43 +0000301 status = pj_sock_apply_qos2(sock[1], cfg->qos_type,
302 &cfg->qos_params,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000303 2, THIS_FILE, "RTCP socket");
304
305 /* Bind RTCP socket */
Benny Prijono0bc99a92011-03-17 04:34:43 +0000306 status=pj_sock_bind_in(sock[1], pj_ntohl(bound_addr.sin_addr.s_addr),
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000307 (pj_uint16_t)(next_rtp_port+1));
308 if (status != PJ_SUCCESS) {
Benny Prijono0bc99a92011-03-17 04:34:43 +0000309 pj_sock_close(sock[0]);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000310 sock[0] = PJ_INVALID_SOCKET;
311
Benny Prijono0bc99a92011-03-17 04:34:43 +0000312 pj_sock_close(sock[1]);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000313 sock[1] = PJ_INVALID_SOCKET;
314 continue;
315 }
316
317 /*
318 * If we're configured to use STUN, then find out the mapped address,
319 * and make sure that the mapped RTCP port is adjacent with the RTP.
320 */
321 if (pjsua_var.stun_srv.addr.sa_family != 0) {
322 char ip_addr[32];
323 pj_str_t stun_srv;
324
Benny Prijono0bc99a92011-03-17 04:34:43 +0000325 pj_ansi_strcpy(ip_addr,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000326 pj_inet_ntoa(pjsua_var.stun_srv.ipv4.sin_addr));
327 stun_srv = pj_str(ip_addr);
328
329 status=pjstun_get_mapped_addr(&pjsua_var.cp.factory, 2, sock,
330 &stun_srv, pj_ntohs(pjsua_var.stun_srv.ipv4.sin_port),
331 &stun_srv, pj_ntohs(pjsua_var.stun_srv.ipv4.sin_port),
332 mapped_addr);
333 if (status != PJ_SUCCESS) {
334 pjsua_perror(THIS_FILE, "STUN resolve error", status);
335 goto on_error;
336 }
337
338#if PJSUA_REQUIRE_CONSECUTIVE_RTCP_PORT
Benny Prijono0bc99a92011-03-17 04:34:43 +0000339 if (pj_ntohs(mapped_addr[1].sin_port) ==
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000340 pj_ntohs(mapped_addr[0].sin_port)+1)
341 {
342 /* Success! */
343 break;
344 }
345
Benny Prijono0bc99a92011-03-17 04:34:43 +0000346 pj_sock_close(sock[0]);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000347 sock[0] = PJ_INVALID_SOCKET;
348
Benny Prijono0bc99a92011-03-17 04:34:43 +0000349 pj_sock_close(sock[1]);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000350 sock[1] = PJ_INVALID_SOCKET;
351#else
Benny Prijono0bc99a92011-03-17 04:34:43 +0000352 if (pj_ntohs(mapped_addr[1].sin_port) !=
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000353 pj_ntohs(mapped_addr[0].sin_port)+1)
354 {
Benny Prijono0bc99a92011-03-17 04:34:43 +0000355 PJ_LOG(4,(THIS_FILE,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000356 "Note: STUN mapped RTCP port %d is not adjacent"
357 " to RTP port %d",
358 pj_ntohs(mapped_addr[1].sin_port),
359 pj_ntohs(mapped_addr[0].sin_port)));
360 }
361 /* Success! */
362 break;
363#endif
364
365 } else if (cfg->public_addr.slen) {
366
367 status = pj_sockaddr_in_init(&mapped_addr[0], &cfg->public_addr,
368 (pj_uint16_t)next_rtp_port);
369 if (status != PJ_SUCCESS)
370 goto on_error;
371
372 status = pj_sockaddr_in_init(&mapped_addr[1], &cfg->public_addr,
373 (pj_uint16_t)(next_rtp_port+1));
374 if (status != PJ_SUCCESS)
375 goto on_error;
376
377 break;
378
379 } else {
380
381 if (bound_addr.sin_addr.s_addr == 0) {
382 pj_sockaddr addr;
383
384 /* Get local IP address. */
385 status = pj_gethostip(pj_AF_INET(), &addr);
386 if (status != PJ_SUCCESS)
387 goto on_error;
388
389 bound_addr.sin_addr.s_addr = addr.ipv4.sin_addr.s_addr;
390 }
391
392 for (i=0; i<2; ++i) {
393 pj_sockaddr_in_init(&mapped_addr[i], NULL, 0);
394 mapped_addr[i].sin_addr.s_addr = bound_addr.sin_addr.s_addr;
395 }
396
397 mapped_addr[0].sin_port=pj_htons((pj_uint16_t)next_rtp_port);
398 mapped_addr[1].sin_port=pj_htons((pj_uint16_t)(next_rtp_port+1));
399 break;
400 }
401 }
402
403 if (sock[0] == PJ_INVALID_SOCKET) {
Benny Prijono0bc99a92011-03-17 04:34:43 +0000404 PJ_LOG(1,(THIS_FILE,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000405 "Unable to find appropriate RTP/RTCP ports combination"));
406 goto on_error;
407 }
408
409
410 skinfo->rtp_sock = sock[0];
Benny Prijono0bc99a92011-03-17 04:34:43 +0000411 pj_memcpy(&skinfo->rtp_addr_name,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000412 &mapped_addr[0], sizeof(pj_sockaddr_in));
413
414 skinfo->rtcp_sock = sock[1];
Benny Prijono0bc99a92011-03-17 04:34:43 +0000415 pj_memcpy(&skinfo->rtcp_addr_name,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000416 &mapped_addr[1], sizeof(pj_sockaddr_in));
417
418 PJ_LOG(4,(THIS_FILE, "RTP socket reachable at %s",
419 pj_sockaddr_print(&skinfo->rtp_addr_name, addr_buf,
420 sizeof(addr_buf), 3)));
421 PJ_LOG(4,(THIS_FILE, "RTCP socket reachable at %s",
422 pj_sockaddr_print(&skinfo->rtcp_addr_name, addr_buf,
423 sizeof(addr_buf), 3)));
424
425 next_rtp_port += 2;
426 return PJ_SUCCESS;
427
428on_error:
429 for (i=0; i<2; ++i) {
430 if (sock[i] != PJ_INVALID_SOCKET)
431 pj_sock_close(sock[i]);
432 }
433 return status;
434}
435
Benny Prijono0bc99a92011-03-17 04:34:43 +0000436/* Create normal UDP media transports */
437static pj_status_t create_udp_media_transport(const pjsua_transport_config *cfg,
438 pjsua_call_media *call_med)
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000439{
Benny Prijono0bc99a92011-03-17 04:34:43 +0000440 pjmedia_sock_info skinfo;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000441 pj_status_t status;
442
Benny Prijono0bc99a92011-03-17 04:34:43 +0000443 status = create_rtp_rtcp_sock(cfg, &skinfo);
444 if (status != PJ_SUCCESS) {
445 pjsua_perror(THIS_FILE, "Unable to create RTP/RTCP socket",
446 status);
447 goto on_error;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000448 }
449
Benny Prijono0bc99a92011-03-17 04:34:43 +0000450 status = pjmedia_transport_udp_attach(pjsua_var.med_endpt, NULL,
451 &skinfo, 0, &call_med->tp);
452 if (status != PJ_SUCCESS) {
453 pjsua_perror(THIS_FILE, "Unable to create media transport",
454 status);
455 goto on_error;
456 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000457
Benny Prijono0bc99a92011-03-17 04:34:43 +0000458 pjmedia_transport_simulate_lost(call_med->tp, PJMEDIA_DIR_ENCODING,
459 pjsua_var.media_cfg.tx_drop_pct);
460
461 pjmedia_transport_simulate_lost(call_med->tp, PJMEDIA_DIR_DECODING,
462 pjsua_var.media_cfg.rx_drop_pct);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000463
Sauw Ming73ecfe82011-09-21 10:20:01 +0000464 call_med->tp_ready = PJ_SUCCESS;
465
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000466 return PJ_SUCCESS;
Benny Prijono0bc99a92011-03-17 04:34:43 +0000467
468on_error:
469 if (call_med->tp)
470 pjmedia_transport_close(call_med->tp);
471
472 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000473}
474
Benny Prijono0bc99a92011-03-17 04:34:43 +0000475#if DISABLED_FOR_TICKET_1185
Benny Prijonoc97608e2007-03-23 16:34:20 +0000476/* Create normal UDP media transports */
477static pj_status_t create_udp_media_transports(pjsua_transport_config *cfg)
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000478{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000479 unsigned i;
480 pj_status_t status;
481
Benny Prijono0bc99a92011-03-17 04:34:43 +0000482 for (i=0; i < pjsua_var.ua_cfg.max_calls; ++i) {
483 pjsua_call *call = &pjsua_var.calls[i];
484 unsigned strm_idx;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000485
Benny Prijono0bc99a92011-03-17 04:34:43 +0000486 for (strm_idx=0; strm_idx < call->med_cnt; ++strm_idx) {
487 pjsua_call_media *call_med = &call->media[strm_idx];
488
489 status = create_udp_media_transport(cfg, &call_med->tp);
490 if (status != PJ_SUCCESS)
491 goto on_error;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000492 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000493 }
494
Benny Prijonoc97608e2007-03-23 16:34:20 +0000495 return PJ_SUCCESS;
496
497on_error:
Benny Prijono0bc99a92011-03-17 04:34:43 +0000498 for (i=0; i < pjsua_var.ua_cfg.max_calls; ++i) {
499 pjsua_call *call = &pjsua_var.calls[i];
500 unsigned strm_idx;
501
502 for (strm_idx=0; strm_idx < call->med_cnt; ++strm_idx) {
503 pjsua_call_media *call_med = &call->media[strm_idx];
504
505 if (call_med->tp) {
506 pjmedia_transport_close(call_med->tp);
507 call_med->tp = NULL;
508 }
Benny Prijonoc97608e2007-03-23 16:34:20 +0000509 }
510 }
Benny Prijonoc97608e2007-03-23 16:34:20 +0000511 return status;
512}
Benny Prijono0bc99a92011-03-17 04:34:43 +0000513#endif
Benny Prijonoc97608e2007-03-23 16:34:20 +0000514
Sauw Ming2a899232012-01-09 11:51:56 +0000515static void med_tp_timer_cb(void *user_data)
516{
517 pjsua_call_media *call_med = (pjsua_call_media*)user_data;
Benny Prijono8ddeaa82012-06-11 05:15:59 +0000518 pjsua_call *call = NULL;
519 pjsip_dialog *dlg = NULL;
Sauw Ming2a899232012-01-09 11:51:56 +0000520
Benny Prijono8ddeaa82012-06-11 05:15:59 +0000521 acquire_call("med_tp_timer_cb", call_med->call->index, &call, &dlg);
Sauw Ming2a899232012-01-09 11:51:56 +0000522
523 call_med->tp_ready = call_med->tp_result;
524 if (call_med->med_create_cb)
525 (*call_med->med_create_cb)(call_med, call_med->tp_ready,
526 call_med->call->secure_level, NULL);
527
Benny Prijono8ddeaa82012-06-11 05:15:59 +0000528 if (dlg)
529 pjsip_dlg_dec_lock(dlg);
Sauw Ming2a899232012-01-09 11:51:56 +0000530}
531
Benny Prijono096c56c2007-09-15 08:30:16 +0000532/* This callback is called when ICE negotiation completes */
Benny Prijonof76e1392008-06-06 14:51:48 +0000533static void on_ice_complete(pjmedia_transport *tp,
534 pj_ice_strans_op op,
535 pj_status_t result)
Benny Prijono096c56c2007-09-15 08:30:16 +0000536{
Benny Prijono0bc99a92011-03-17 04:34:43 +0000537 pjsua_call_media *call_med = (pjsua_call_media*)tp->user_data;
Benny Prijono096c56c2007-09-15 08:30:16 +0000538
Benny Prijono0bc99a92011-03-17 04:34:43 +0000539 if (!call_med)
Benny Prijonof76e1392008-06-06 14:51:48 +0000540 return;
541
542 switch (op) {
543 case PJ_ICE_STRANS_OP_INIT:
Sauw Ming2a899232012-01-09 11:51:56 +0000544 call_med->tp_result = result;
545 pjsua_schedule_timer2(&med_tp_timer_cb, call_med, 1);
Benny Prijonof76e1392008-06-06 14:51:48 +0000546 break;
547 case PJ_ICE_STRANS_OP_NEGOTIATION:
548 if (result != PJ_SUCCESS) {
Benny Prijono0bc99a92011-03-17 04:34:43 +0000549 call_med->state = PJSUA_CALL_MEDIA_ERROR;
550 call_med->dir = PJMEDIA_DIR_NONE;
Benny Prijonof76e1392008-06-06 14:51:48 +0000551
Benny Prijono0bc99a92011-03-17 04:34:43 +0000552 if (call_med->call && pjsua_var.ua_cfg.cb.on_call_media_state) {
553 pjsua_var.ua_cfg.cb.on_call_media_state(call_med->call->index);
Benny Prijonof76e1392008-06-06 14:51:48 +0000554 }
Benny Prijono0bc99a92011-03-17 04:34:43 +0000555 } else if (call_med->call) {
Benny Prijonof5d9f1f2009-10-14 13:13:18 +0000556 /* Send UPDATE if default transport address is different than
557 * what was advertised (ticket #881)
558 */
559 pjmedia_transport_info tpinfo;
560 pjmedia_ice_transport_info *ii = NULL;
561 unsigned i;
562
563 pjmedia_transport_info_init(&tpinfo);
564 pjmedia_transport_get_info(tp, &tpinfo);
565 for (i=0; i<tpinfo.specific_info_cnt; ++i) {
566 if (tpinfo.spc_info[i].type==PJMEDIA_TRANSPORT_TYPE_ICE) {
567 ii = (pjmedia_ice_transport_info*)
568 tpinfo.spc_info[i].buffer;
569 break;
570 }
571 }
572
573 if (ii && ii->role==PJ_ICE_SESS_ROLE_CONTROLLING &&
574 pj_sockaddr_cmp(&tpinfo.sock_info.rtp_addr_name,
Benny Prijono0bc99a92011-03-17 04:34:43 +0000575 &call_med->rtp_addr))
Benny Prijonof5d9f1f2009-10-14 13:13:18 +0000576 {
Benny Prijonoa8f9e622010-06-21 13:28:55 +0000577 pj_bool_t use_update;
578 const pj_str_t STR_UPDATE = { "UPDATE", 6 };
579 pjsip_dialog_cap_status support_update;
580 pjsip_dialog *dlg;
581
Benny Prijono0bc99a92011-03-17 04:34:43 +0000582 dlg = call_med->call->inv->dlg;
Benny Prijonoa8f9e622010-06-21 13:28:55 +0000583 support_update = pjsip_dlg_remote_has_cap(dlg, PJSIP_H_ALLOW,
584 NULL, &STR_UPDATE);
585 use_update = (support_update == PJSIP_DIALOG_CAP_SUPPORTED);
586
Benny Prijonof5d9f1f2009-10-14 13:13:18 +0000587 PJ_LOG(4,(THIS_FILE,
588 "ICE default transport address has changed for "
Benny Prijonodb5d89d2011-10-25 13:39:06 +0000589 "call %d, sending %s",
590 call_med->call->index,
Benny Prijonoa8f9e622010-06-21 13:28:55 +0000591 (use_update ? "UPDATE" : "re-INVITE")));
592
593 if (use_update)
Benny Prijono0bc99a92011-03-17 04:34:43 +0000594 pjsua_call_update(call_med->call->index, 0, NULL);
Benny Prijonoa8f9e622010-06-21 13:28:55 +0000595 else
Benny Prijono0bc99a92011-03-17 04:34:43 +0000596 pjsua_call_reinvite(call_med->call->index, 0, NULL);
Benny Prijonof5d9f1f2009-10-14 13:13:18 +0000597 }
Benny Prijonof76e1392008-06-06 14:51:48 +0000598 }
599 break;
Benny Prijono4d6ff4d2010-06-19 12:35:33 +0000600 case PJ_ICE_STRANS_OP_KEEP_ALIVE:
601 if (result != PJ_SUCCESS) {
602 PJ_PERROR(4,(THIS_FILE, result,
Benny Prijono0bc99a92011-03-17 04:34:43 +0000603 "ICE keep alive failure for transport %d:%d",
604 call_med->call->index, call_med->idx));
Benny Prijono4d6ff4d2010-06-19 12:35:33 +0000605 }
Sauw Ming73ecfe82011-09-21 10:20:01 +0000606 if (pjsua_var.ua_cfg.cb.on_call_media_transport_state) {
607 pjsua_med_tp_state_info info;
608
609 pj_bzero(&info, sizeof(info));
610 info.med_idx = call_med->idx;
611 info.state = call_med->tp_st;
612 info.status = result;
613 info.ext_info = &op;
614 (*pjsua_var.ua_cfg.cb.on_call_media_transport_state)(
615 call_med->call->index, &info);
616 }
Benny Prijono4d6ff4d2010-06-19 12:35:33 +0000617 if (pjsua_var.ua_cfg.cb.on_ice_transport_error) {
Benny Prijono0bc99a92011-03-17 04:34:43 +0000618 pjsua_call_id id = call_med->call->index;
Benny Prijono4d6ff4d2010-06-19 12:35:33 +0000619 (*pjsua_var.ua_cfg.cb.on_ice_transport_error)(id, op, result,
620 NULL);
621 }
622 break;
Benny Prijono096c56c2007-09-15 08:30:16 +0000623 }
624}
625
626
Benny Prijonof76e1392008-06-06 14:51:48 +0000627/* Parse "HOST:PORT" format */
628static pj_status_t parse_host_port(const pj_str_t *host_port,
629 pj_str_t *host, pj_uint16_t *port)
Benny Prijonoc97608e2007-03-23 16:34:20 +0000630{
Benny Prijonof76e1392008-06-06 14:51:48 +0000631 pj_str_t str_port;
632
633 str_port.ptr = pj_strchr(host_port, ':');
634 if (str_port.ptr != NULL) {
635 int iport;
636
637 host->ptr = host_port->ptr;
638 host->slen = (str_port.ptr - host->ptr);
639 str_port.ptr++;
640 str_port.slen = host_port->slen - host->slen - 1;
641 iport = (int)pj_strtoul(&str_port);
642 if (iport < 1 || iport > 65535)
643 return PJ_EINVAL;
644 *port = (pj_uint16_t)iport;
645 } else {
646 *host = *host_port;
647 *port = 0;
648 }
649
650 return PJ_SUCCESS;
651}
652
653/* Create ICE media transports (when ice is enabled) */
Benny Prijono0bc99a92011-03-17 04:34:43 +0000654static pj_status_t create_ice_media_transport(
655 const pjsua_transport_config *cfg,
Sauw Ming73ecfe82011-09-21 10:20:01 +0000656 pjsua_call_media *call_med,
657 pj_bool_t async)
Benny Prijonof76e1392008-06-06 14:51:48 +0000658{
659 char stunip[PJ_INET6_ADDRSTRLEN];
660 pj_ice_strans_cfg ice_cfg;
Benny Prijono0bc99a92011-03-17 04:34:43 +0000661 pjmedia_ice_cb ice_cb;
662 char name[32];
663 unsigned comp_cnt;
Benny Prijonoc97608e2007-03-23 16:34:20 +0000664 pj_status_t status;
665
Benny Prijonoda9785b2007-04-02 20:43:06 +0000666 /* Make sure STUN server resolution has completed */
Benny Prijonobb995fd2009-08-12 11:03:23 +0000667 status = resolve_stun_server(PJ_TRUE);
Benny Prijonoda9785b2007-04-02 20:43:06 +0000668 if (status != PJ_SUCCESS) {
669 pjsua_perror(THIS_FILE, "Error resolving STUN server", status);
670 return status;
671 }
672
Benny Prijonof76e1392008-06-06 14:51:48 +0000673 /* Create ICE stream transport configuration */
674 pj_ice_strans_cfg_default(&ice_cfg);
675 pj_stun_config_init(&ice_cfg.stun_cfg, &pjsua_var.cp.factory, 0,
676 pjsip_endpt_get_ioqueue(pjsua_var.endpt),
677 pjsip_endpt_get_timer_heap(pjsua_var.endpt));
678
679 ice_cfg.af = pj_AF_INET();
680 ice_cfg.resolver = pjsua_var.resolver;
681
Benny Prijono329d6382009-05-29 13:04:03 +0000682 ice_cfg.opt = pjsua_var.media_cfg.ice_opt;
683
Benny Prijonof76e1392008-06-06 14:51:48 +0000684 /* Configure STUN settings */
685 if (pj_sockaddr_has_addr(&pjsua_var.stun_srv)) {
686 pj_sockaddr_print(&pjsua_var.stun_srv, stunip, sizeof(stunip), 0);
687 ice_cfg.stun.server = pj_str(stunip);
688 ice_cfg.stun.port = pj_sockaddr_get_port(&pjsua_var.stun_srv);
689 }
Benny Prijono329d6382009-05-29 13:04:03 +0000690 if (pjsua_var.media_cfg.ice_max_host_cands >= 0)
691 ice_cfg.stun.max_host_cands = pjsua_var.media_cfg.ice_max_host_cands;
Benny Prijonof76e1392008-06-06 14:51:48 +0000692
Benny Prijono4d79b0f2009-10-25 09:02:07 +0000693 /* Copy QoS setting to STUN setting */
694 ice_cfg.stun.cfg.qos_type = cfg->qos_type;
695 pj_memcpy(&ice_cfg.stun.cfg.qos_params, &cfg->qos_params,
696 sizeof(cfg->qos_params));
697
Benny Prijonof76e1392008-06-06 14:51:48 +0000698 /* Configure TURN settings */
699 if (pjsua_var.media_cfg.enable_turn) {
700 status = parse_host_port(&pjsua_var.media_cfg.turn_server,
701 &ice_cfg.turn.server,
702 &ice_cfg.turn.port);
703 if (status != PJ_SUCCESS || ice_cfg.turn.server.slen == 0) {
704 PJ_LOG(1,(THIS_FILE, "Invalid TURN server setting"));
705 return PJ_EINVAL;
706 }
707 if (ice_cfg.turn.port == 0)
708 ice_cfg.turn.port = 3479;
709 ice_cfg.turn.conn_type = pjsua_var.media_cfg.turn_conn_type;
710 pj_memcpy(&ice_cfg.turn.auth_cred,
711 &pjsua_var.media_cfg.turn_auth_cred,
712 sizeof(ice_cfg.turn.auth_cred));
Benny Prijono4d79b0f2009-10-25 09:02:07 +0000713
714 /* Copy QoS setting to TURN setting */
715 ice_cfg.turn.cfg.qos_type = cfg->qos_type;
716 pj_memcpy(&ice_cfg.turn.cfg.qos_params, &cfg->qos_params,
717 sizeof(cfg->qos_params));
Benny Prijonof76e1392008-06-06 14:51:48 +0000718 }
Benny Prijonob681a2f2007-03-25 18:44:51 +0000719
Benny Prijono0bc99a92011-03-17 04:34:43 +0000720 pj_bzero(&ice_cb, sizeof(pjmedia_ice_cb));
721 ice_cb.on_ice_complete = &on_ice_complete;
722 pj_ansi_snprintf(name, sizeof(name), "icetp%02d", call_med->idx);
723 call_med->tp_ready = PJ_EPENDING;
Benny Prijonoc97608e2007-03-23 16:34:20 +0000724
Benny Prijono0bc99a92011-03-17 04:34:43 +0000725 comp_cnt = 1;
726 if (PJMEDIA_ADVERTISE_RTCP && !pjsua_var.media_cfg.ice_no_rtcp)
727 ++comp_cnt;
Benny Prijonof76e1392008-06-06 14:51:48 +0000728
Benny Prijonobd6613f2011-04-11 17:27:14 +0000729 status = pjmedia_ice_create3(pjsua_var.med_endpt, name, comp_cnt,
730 &ice_cfg, &ice_cb, 0, call_med,
731 &call_med->tp);
Benny Prijono0bc99a92011-03-17 04:34:43 +0000732 if (status != PJ_SUCCESS) {
733 pjsua_perror(THIS_FILE, "Unable to create ICE media transport",
734 status);
735 goto on_error;
Benny Prijonoc97608e2007-03-23 16:34:20 +0000736 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000737
Benny Prijono0bc99a92011-03-17 04:34:43 +0000738 /* Wait until transport is initialized, or time out */
Sauw Ming73ecfe82011-09-21 10:20:01 +0000739 if (!async) {
Nanang Izzuddin1c565422011-12-28 09:52:07 +0000740 pj_bool_t has_pjsua_lock = PJSUA_LOCK_IS_LOCKED();
741 if (has_pjsua_lock)
742 PJSUA_UNLOCK();
Sauw Ming73ecfe82011-09-21 10:20:01 +0000743 while (call_med->tp_ready == PJ_EPENDING) {
744 pjsua_handle_events(100);
745 }
Nanang Izzuddin1c565422011-12-28 09:52:07 +0000746 if (has_pjsua_lock)
747 PJSUA_LOCK();
Benny Prijono0bc99a92011-03-17 04:34:43 +0000748 }
Sauw Ming73ecfe82011-09-21 10:20:01 +0000749
750 if (async && call_med->tp_ready == PJ_EPENDING) {
751 return PJ_EPENDING;
752 } else if (call_med->tp_ready != PJ_SUCCESS) {
Benny Prijono0bc99a92011-03-17 04:34:43 +0000753 pjsua_perror(THIS_FILE, "Error initializing ICE media transport",
754 call_med->tp_ready);
755 status = call_med->tp_ready;
756 goto on_error;
757 }
758
759 pjmedia_transport_simulate_lost(call_med->tp, PJMEDIA_DIR_ENCODING,
760 pjsua_var.media_cfg.tx_drop_pct);
761
762 pjmedia_transport_simulate_lost(call_med->tp, PJMEDIA_DIR_DECODING,
763 pjsua_var.media_cfg.rx_drop_pct);
764
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000765 return PJ_SUCCESS;
766
767on_error:
Benny Prijono0bc99a92011-03-17 04:34:43 +0000768 if (call_med->tp != NULL) {
769 pjmedia_transport_close(call_med->tp);
770 call_med->tp = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000771 }
772
Benny Prijonoc97608e2007-03-23 16:34:20 +0000773 return status;
774}
775
Benny Prijono0bc99a92011-03-17 04:34:43 +0000776#if DISABLED_FOR_TICKET_1185
777/* Create ICE media transports (when ice is enabled) */
778static pj_status_t create_ice_media_transports(pjsua_transport_config *cfg)
779{
780 unsigned i;
781 pj_status_t status;
Benny Prijonoc97608e2007-03-23 16:34:20 +0000782
Benny Prijono0bc99a92011-03-17 04:34:43 +0000783 for (i=0; i < pjsua_var.ua_cfg.max_calls; ++i) {
784 pjsua_call *call = &pjsua_var.calls[i];
785 unsigned strm_idx;
786
787 for (strm_idx=0; strm_idx < call->med_cnt; ++strm_idx) {
788 pjsua_call_media *call_med = &call->media[strm_idx];
789
790 status = create_ice_media_transport(cfg, call_med);
791 if (status != PJ_SUCCESS)
792 goto on_error;
793 }
794 }
795
796 return PJ_SUCCESS;
797
798on_error:
799 for (i=0; i < pjsua_var.ua_cfg.max_calls; ++i) {
800 pjsua_call *call = &pjsua_var.calls[i];
801 unsigned strm_idx;
802
803 for (strm_idx=0; strm_idx < call->med_cnt; ++strm_idx) {
804 pjsua_call_media *call_med = &call->media[strm_idx];
805
806 if (call_med->tp) {
807 pjmedia_transport_close(call_med->tp);
808 call_med->tp = NULL;
809 }
810 }
811 }
812 return status;
813}
814#endif
815
816#if DISABLED_FOR_TICKET_1185
Benny Prijonoc97608e2007-03-23 16:34:20 +0000817/*
Benny Prijono0bc99a92011-03-17 04:34:43 +0000818 * Create media transports for all the calls. This function creates
Benny Prijonoc97608e2007-03-23 16:34:20 +0000819 * one UDP media transport for each call.
820 */
Benny Prijono1f61a8f2007-08-16 10:11:44 +0000821PJ_DEF(pj_status_t) pjsua_media_transports_create(
822 const pjsua_transport_config *app_cfg)
Benny Prijonoc97608e2007-03-23 16:34:20 +0000823{
824 pjsua_transport_config cfg;
825 unsigned i;
826 pj_status_t status;
827
828
829 /* Make sure pjsua_init() has been called */
830 PJ_ASSERT_RETURN(pjsua_var.ua_cfg.max_calls>0, PJ_EINVALIDOP);
831
832 PJSUA_LOCK();
833
834 /* Delete existing media transports */
835 for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
Benny Prijono0bc99a92011-03-17 04:34:43 +0000836 pjsua_call *call = &pjsua_var.calls[i];
837 unsigned strm_idx;
838
839 for (strm_idx=0; strm_idx < call->med_cnt; ++strm_idx) {
840 pjsua_call_media *call_med = &call->media[strm_idx];
841
842 if (call_med->tp && call_med->tp_auto_del) {
843 pjmedia_transport_close(call_med->tp);
844 call_med->tp = NULL;
845 call_med->tp_orig = NULL;
846 }
Benny Prijonoc97608e2007-03-23 16:34:20 +0000847 }
848 }
849
850 /* Copy config */
851 pjsua_transport_config_dup(pjsua_var.pool, &cfg, app_cfg);
852
Benny Prijono40860c32008-09-04 13:55:33 +0000853 /* Create the transports */
Benny Prijonoc97608e2007-03-23 16:34:20 +0000854 if (pjsua_var.media_cfg.enable_ice) {
Benny Prijono4d79b0f2009-10-25 09:02:07 +0000855 status = create_ice_media_transports(&cfg);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000856 } else {
857 status = create_udp_media_transports(&cfg);
858 }
859
Benny Prijono40860c32008-09-04 13:55:33 +0000860 /* Set media transport auto_delete to True */
861 for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
Benny Prijono0bc99a92011-03-17 04:34:43 +0000862 pjsua_call *call = &pjsua_var.calls[i];
863 unsigned strm_idx;
864
865 for (strm_idx=0; strm_idx < call->med_cnt; ++strm_idx) {
866 pjsua_call_media *call_med = &call->media[strm_idx];
867
868 call_med->tp_auto_del = PJ_TRUE;
869 }
Benny Prijono40860c32008-09-04 13:55:33 +0000870 }
Benny Prijonoc97608e2007-03-23 16:34:20 +0000871
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000872 PJSUA_UNLOCK();
873
874 return status;
875}
876
Benny Prijono40860c32008-09-04 13:55:33 +0000877/*
878 * Attach application's created media transports.
879 */
880PJ_DEF(pj_status_t) pjsua_media_transports_attach(pjsua_media_transport tp[],
881 unsigned count,
882 pj_bool_t auto_delete)
883{
884 unsigned i;
885
886 PJ_ASSERT_RETURN(tp && count==pjsua_var.ua_cfg.max_calls, PJ_EINVAL);
887
888 /* Assign the media transports */
889 for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
Benny Prijono0bc99a92011-03-17 04:34:43 +0000890 pjsua_call *call = &pjsua_var.calls[i];
891 unsigned strm_idx;
892
893 for (strm_idx=0; strm_idx < call->med_cnt; ++strm_idx) {
894 pjsua_call_media *call_med = &call->media[strm_idx];
895
896 if (call_med->tp && call_med->tp_auto_del) {
897 pjmedia_transport_close(call_med->tp);
898 call_med->tp = NULL;
899 call_med->tp_orig = NULL;
900 }
Benny Prijono40860c32008-09-04 13:55:33 +0000901 }
902
Benny Prijono0bc99a92011-03-17 04:34:43 +0000903 PJ_TODO(remove_pjsua_media_transports_attach);
904
905 call->media[0].tp = tp[i].transport;
906 call->media[0].tp_auto_del = auto_delete;
Benny Prijono40860c32008-09-04 13:55:33 +0000907 }
908
909 return PJ_SUCCESS;
910}
Benny Prijono0bc99a92011-03-17 04:34:43 +0000911#endif
Benny Prijono40860c32008-09-04 13:55:33 +0000912
Benny Prijono0bc99a92011-03-17 04:34:43 +0000913/* Go through the list of media in the SDP, find acceptable media, and
914 * sort them based on the "quality" of the media, and store the indexes
915 * in the specified array. Media with the best quality will be listed
916 * first in the array. The quality factors considered currently is
917 * encryption.
918 */
919static void sort_media(const pjmedia_sdp_session *sdp,
920 const pj_str_t *type,
921 pjmedia_srtp_use use_srtp,
922 pj_uint8_t midx[],
Nanang Izzuddindebd48a2011-12-01 09:06:14 +0000923 unsigned *p_count,
924 unsigned *p_total_count)
Nanang Izzuddin1e952a82010-10-05 16:32:04 +0000925{
926 unsigned i;
Benny Prijono0bc99a92011-03-17 04:34:43 +0000927 unsigned count = 0;
928 int score[PJSUA_MAX_CALL_MEDIA];
Nanang Izzuddin1e952a82010-10-05 16:32:04 +0000929
Benny Prijono0bc99a92011-03-17 04:34:43 +0000930 pj_assert(*p_count >= PJSUA_MAX_CALL_MEDIA);
Nanang Izzuddindebd48a2011-12-01 09:06:14 +0000931 pj_assert(*p_total_count >= PJSUA_MAX_CALL_MEDIA);
Benny Prijono0bc99a92011-03-17 04:34:43 +0000932
933 *p_count = 0;
Nanang Izzuddindebd48a2011-12-01 09:06:14 +0000934 *p_total_count = 0;
Benny Prijono09c0d672011-04-11 05:03:24 +0000935 for (i=0; i<PJSUA_MAX_CALL_MEDIA; ++i)
936 score[i] = 1;
Benny Prijono0bc99a92011-03-17 04:34:43 +0000937
938 /* Score each media */
939 for (i=0; i<sdp->media_count && count<PJSUA_MAX_CALL_MEDIA; ++i) {
Nanang Izzuddin1e952a82010-10-05 16:32:04 +0000940 const pjmedia_sdp_media *m = sdp->media[i];
Nanang Izzuddina6414292011-04-08 04:26:18 +0000941 const pjmedia_sdp_conn *c;
Nanang Izzuddin1e952a82010-10-05 16:32:04 +0000942
Benny Prijono0bc99a92011-03-17 04:34:43 +0000943 /* Skip different media */
944 if (pj_stricmp(&m->desc.media, type) != 0) {
945 score[count++] = -22000;
Nanang Izzuddin1e952a82010-10-05 16:32:04 +0000946 continue;
947 }
948
Nanang Izzuddina6414292011-04-08 04:26:18 +0000949 c = m->conn? m->conn : sdp->conn;
950
Benny Prijono0bc99a92011-03-17 04:34:43 +0000951 /* Supported transports */
952 if (pj_stricmp2(&m->desc.transport, "RTP/SAVP")==0) {
953 switch (use_srtp) {
954 case PJMEDIA_SRTP_MANDATORY:
955 case PJMEDIA_SRTP_OPTIONAL:
956 ++score[i];
Nanang Izzuddin1e952a82010-10-05 16:32:04 +0000957 break;
Benny Prijono0bc99a92011-03-17 04:34:43 +0000958 case PJMEDIA_SRTP_DISABLED:
Nanang Izzuddind8c33d82011-08-18 18:30:55 +0000959 //--score[i];
960 score[i] -= 5;
Benny Prijono0bc99a92011-03-17 04:34:43 +0000961 break;
Nanang Izzuddin1e952a82010-10-05 16:32:04 +0000962 }
Benny Prijono0bc99a92011-03-17 04:34:43 +0000963 } else if (pj_stricmp2(&m->desc.transport, "RTP/AVP")==0) {
964 switch (use_srtp) {
965 case PJMEDIA_SRTP_MANDATORY:
Nanang Izzuddind8c33d82011-08-18 18:30:55 +0000966 //--score[i];
967 score[i] -= 5;
Benny Prijono0bc99a92011-03-17 04:34:43 +0000968 break;
969 case PJMEDIA_SRTP_OPTIONAL:
970 /* No change in score */
971 break;
972 case PJMEDIA_SRTP_DISABLED:
973 ++score[i];
974 break;
975 }
976 } else {
977 score[i] -= 10;
978 }
979
980 /* Is media disabled? */
981 if (m->desc.port == 0)
982 score[i] -= 10;
983
984 /* Is media inactive? */
Nanang Izzuddina6414292011-04-08 04:26:18 +0000985 if (pjmedia_sdp_media_find_attr2(m, "inactive", NULL) ||
986 pj_strcmp2(&c->addr, "0.0.0.0") == 0)
987 {
988 //score[i] -= 10;
989 score[i] -= 1;
990 }
Benny Prijono0bc99a92011-03-17 04:34:43 +0000991
992 ++count;
993 }
994
995 /* Created sorted list based on quality */
996 for (i=0; i<count; ++i) {
997 unsigned j;
998 int best = 0;
999
1000 for (j=1; j<count; ++j) {
1001 if (score[j] > score[best])
1002 best = j;
1003 }
1004 /* Don't put media with negative score, that media is unacceptable
1005 * for us.
1006 */
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001007 midx[i] = (pj_uint8_t)best;
1008 if (score[best] >= 0)
Benny Prijono0bc99a92011-03-17 04:34:43 +00001009 (*p_count)++;
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001010 if (score[best] > -22000)
1011 (*p_total_count)++;
Benny Prijono0bc99a92011-03-17 04:34:43 +00001012
1013 score[best] = -22000;
1014
1015 }
1016}
1017
Benny Prijonoee0ba182011-07-15 06:18:29 +00001018/* Callback to receive media events */
Sauw Ming0fabe1b2011-12-01 10:49:07 +00001019pj_status_t call_media_on_event(pjmedia_event *event,
1020 void *user_data)
Benny Prijonoee0ba182011-07-15 06:18:29 +00001021{
Sauw Ming0fabe1b2011-12-01 10:49:07 +00001022 pjsua_call_media *call_med = (pjsua_call_media*)user_data;
Benny Prijonoee0ba182011-07-15 06:18:29 +00001023 pjsua_call *call = call_med->call;
Nanang Izzuddin3da4ad82011-12-07 10:43:28 +00001024 pj_status_t status = PJ_SUCCESS;
1025
1026 switch(event->type) {
1027 case PJMEDIA_EVENT_KEYFRAME_MISSING:
1028 if (call->opt.req_keyframe_method & PJSUA_VID_REQ_KEYFRAME_SIP_INFO)
1029 {
1030 pj_timestamp now;
1031
1032 pj_get_timestamp(&now);
1033 if (pj_elapsed_msec(&call_med->last_req_keyframe, &now) >=
1034 PJSUA_VID_REQ_KEYFRAME_INTERVAL)
1035 {
1036 pjsua_msg_data msg_data;
1037 const pj_str_t SIP_INFO = {"INFO", 4};
1038 const char *BODY_TYPE = "application/media_control+xml";
1039 const char *BODY =
1040 "<?xml version=\"1.0\" encoding=\"utf-8\" ?>"
1041 "<media_control><vc_primitive><to_encoder>"
1042 "<picture_fast_update/>"
1043 "</to_encoder></vc_primitive></media_control>";
1044
1045 PJ_LOG(4,(THIS_FILE,
1046 "Sending video keyframe request via SIP INFO"));
1047
1048 pjsua_msg_data_init(&msg_data);
1049 pj_cstr(&msg_data.content_type, BODY_TYPE);
1050 pj_cstr(&msg_data.msg_body, BODY);
1051 status = pjsua_call_send_request(call->index, &SIP_INFO,
1052 &msg_data);
1053 if (status != PJ_SUCCESS) {
1054 pj_perror(3, THIS_FILE, status,
1055 "Failed requesting keyframe via SIP INFO");
1056 } else {
1057 call_med->last_req_keyframe = now;
1058 }
1059 }
1060 }
1061 break;
1062
1063 default:
1064 break;
1065 }
Benny Prijonoee0ba182011-07-15 06:18:29 +00001066
1067 if (pjsua_var.ua_cfg.cb.on_call_media_event && call) {
Benny Prijonoee0ba182011-07-15 06:18:29 +00001068 (*pjsua_var.ua_cfg.cb.on_call_media_event)(call->index,
1069 call_med->idx, event);
Benny Prijono53a7c702008-04-14 02:57:29 +00001070 }
1071
Nanang Izzuddin3da4ad82011-12-07 10:43:28 +00001072 return status;
Benny Prijono53a7c702008-04-14 02:57:29 +00001073}
1074
Sauw Ming73ecfe82011-09-21 10:20:01 +00001075/* Set media transport state and notify the application via the callback. */
Benny Prijono1312e752012-03-22 09:56:52 +00001076void pjsua_set_media_tp_state(pjsua_call_media *call_med,
1077 pjsua_med_tp_st tp_st)
Sauw Ming73ecfe82011-09-21 10:20:01 +00001078{
1079 if (pjsua_var.ua_cfg.cb.on_call_media_transport_state &&
1080 call_med->tp_st != tp_st)
1081 {
1082 pjsua_med_tp_state_info info;
1083
1084 pj_bzero(&info, sizeof(info));
1085 info.med_idx = call_med->idx;
1086 info.state = tp_st;
1087 info.status = call_med->tp_ready;
1088 (*pjsua_var.ua_cfg.cb.on_call_media_transport_state)(
1089 call_med->call->index, &info);
1090 }
1091
1092 call_med->tp_st = tp_st;
1093}
1094
1095/* Callback to resume pjsua_call_media_init() after media transport
1096 * creation is completed.
1097 */
1098static pj_status_t call_media_init_cb(pjsua_call_media *call_med,
1099 pj_status_t status,
1100 int security_level,
1101 int *sip_err_code)
Nanang Izzuddin1e952a82010-10-05 16:32:04 +00001102{
Benny Prijono0bc99a92011-03-17 04:34:43 +00001103 pjsua_acc *acc = &pjsua_var.acc[call_med->call->acc_id];
Benny Prijonodb5d89d2011-10-25 13:39:06 +00001104 pjmedia_transport_info tpinfo;
Sauw Ming73ecfe82011-09-21 10:20:01 +00001105 int err_code = 0;
Nanang Izzuddin1e952a82010-10-05 16:32:04 +00001106
Sauw Ming73ecfe82011-09-21 10:20:01 +00001107 if (status != PJ_SUCCESS)
Benny Prijono0ec6cfb2011-12-26 09:00:42 +00001108 goto on_return;
Nanang Izzuddin1e952a82010-10-05 16:32:04 +00001109
Sauw Mingb47391e2012-06-27 07:12:23 +00001110 pjmedia_transport_simulate_lost(call_med->tp, PJMEDIA_DIR_ENCODING,
1111 pjsua_var.media_cfg.tx_drop_pct);
1112
1113 pjmedia_transport_simulate_lost(call_med->tp, PJMEDIA_DIR_DECODING,
1114 pjsua_var.media_cfg.rx_drop_pct);
1115
Sauw Ming73ecfe82011-09-21 10:20:01 +00001116 if (call_med->tp_st == PJSUA_MED_TP_CREATING)
Benny Prijono1312e752012-03-22 09:56:52 +00001117 pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_IDLE);
Nanang Izzuddin1e952a82010-10-05 16:32:04 +00001118
Sauw Minge7dbbc82011-10-24 09:28:13 +00001119 if (!call_med->tp_orig &&
1120 pjsua_var.ua_cfg.cb.on_create_media_transport)
1121 {
1122 call_med->use_custom_med_tp = PJ_TRUE;
1123 } else
1124 call_med->use_custom_med_tp = PJ_FALSE;
1125
Benny Prijono0bc99a92011-03-17 04:34:43 +00001126#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
1127 /* This function may be called when SRTP transport already exists
1128 * (e.g: in re-invite, update), don't need to destroy/re-create.
1129 */
Sauw Minge7dbbc82011-10-24 09:28:13 +00001130 if (!call_med->tp_orig) {
Benny Prijono0bc99a92011-03-17 04:34:43 +00001131 pjmedia_srtp_setting srtp_opt;
1132 pjmedia_transport *srtp = NULL;
Nanang Izzuddin1e952a82010-10-05 16:32:04 +00001133
Benny Prijono0bc99a92011-03-17 04:34:43 +00001134 /* Check if SRTP requires secure signaling */
1135 if (acc->cfg.use_srtp != PJMEDIA_SRTP_DISABLED) {
1136 if (security_level < acc->cfg.srtp_secure_signaling) {
Sauw Ming73ecfe82011-09-21 10:20:01 +00001137 err_code = PJSIP_SC_NOT_ACCEPTABLE;
Benny Prijono0bc99a92011-03-17 04:34:43 +00001138 status = PJSIP_ESESSIONINSECURE;
Benny Prijono0ec6cfb2011-12-26 09:00:42 +00001139 goto on_return;
Benny Prijono0bc99a92011-03-17 04:34:43 +00001140 }
1141 }
1142
1143 /* Always create SRTP adapter */
1144 pjmedia_srtp_setting_default(&srtp_opt);
Nanang Izzuddinb6c239c2011-05-10 05:42:28 +00001145 srtp_opt.close_member_tp = PJ_TRUE;
Nanang Izzuddin1d568722012-04-24 05:40:32 +00001146
Sauw Minge7dbbc82011-10-24 09:28:13 +00001147 /* If media session has been ever established, let's use remote's
Benny Prijono0bc99a92011-03-17 04:34:43 +00001148 * preference in SRTP usage policy, especially when it is stricter.
1149 */
1150 if (call_med->rem_srtp_use > acc->cfg.use_srtp)
1151 srtp_opt.use = call_med->rem_srtp_use;
1152 else
1153 srtp_opt.use = acc->cfg.use_srtp;
1154
1155 status = pjmedia_transport_srtp_create(pjsua_var.med_endpt,
1156 call_med->tp,
1157 &srtp_opt, &srtp);
1158 if (status != PJ_SUCCESS) {
Sauw Ming73ecfe82011-09-21 10:20:01 +00001159 err_code = PJSIP_SC_INTERNAL_SERVER_ERROR;
Benny Prijono0ec6cfb2011-12-26 09:00:42 +00001160 goto on_return;
Benny Prijono0bc99a92011-03-17 04:34:43 +00001161 }
1162
1163 /* Set SRTP as current media transport */
1164 call_med->tp_orig = call_med->tp;
1165 call_med->tp = srtp;
1166 }
1167#else
Benny Prijono7df19342011-07-23 02:54:03 +00001168 call_med->tp_orig = call_med->tp;
Benny Prijono0bc99a92011-03-17 04:34:43 +00001169 PJ_UNUSED_ARG(security_level);
1170#endif
1171
Benny Prijonodb5d89d2011-10-25 13:39:06 +00001172
1173 pjmedia_transport_info_init(&tpinfo);
1174 pjmedia_transport_get_info(call_med->tp, &tpinfo);
1175
1176 pj_sockaddr_cp(&call_med->rtp_addr, &tpinfo.sock_info.rtp_addr_name);
1177
1178
Benny Prijono0ec6cfb2011-12-26 09:00:42 +00001179on_return:
Sauw Ming73ecfe82011-09-21 10:20:01 +00001180 if (status != PJ_SUCCESS && call_med->tp) {
Nanang Izzuddin1d568722012-04-24 05:40:32 +00001181 pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_NULL);
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
Nanang Izzuddin1d568722012-04-24 05:40:32 +00001220 /* Create the media transport for initial call. Here are the possible
1221 * media transport state and the action needed:
1222 * - PJSUA_MED_TP_NULL or call_med->tp==NULL, create one.
1223 * - PJSUA_MED_TP_RUNNING, do nothing.
1224 * - PJSUA_MED_TP_DISABLED, re-init (media_create(), etc). Currently,
1225 * this won't happen as media_channel_update() will always clean up
1226 * the unused transport of a disabled media.
1227 */
Sauw Ming73ecfe82011-09-21 10:20:01 +00001228 if (call_med->tp == NULL) {
1229#if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0)
1230 /* While in initial call, set default video devices */
1231 if (type == PJMEDIA_TYPE_VIDEO) {
Benny Prijono1312e752012-03-22 09:56:52 +00001232 status = pjsua_vid_channel_init(call_med);
1233 if (status != PJ_SUCCESS)
1234 return status;
Sauw Ming73ecfe82011-09-21 10:20:01 +00001235 }
1236#endif
1237
Benny Prijono1312e752012-03-22 09:56:52 +00001238 pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_CREATING);
Sauw Ming73ecfe82011-09-21 10:20:01 +00001239
Sauw Ming73ecfe82011-09-21 10:20:01 +00001240 if (pjsua_var.media_cfg.enable_ice) {
1241 status = create_ice_media_transport(tcfg, call_med, async);
Sauw Mingc8e12942011-10-25 08:51:02 +00001242 if (async && status == PJ_EPENDING) {
Sauw Ming848742f2011-09-28 04:20:30 +00001243 /* We will resume call media initialization in the
1244 * on_ice_complete() callback.
1245 */
Sauw Mingc8e12942011-10-25 08:51:02 +00001246 call_med->med_create_cb = &call_media_init_cb;
1247 call_med->med_init_cb = cb;
1248
Sauw Ming848742f2011-09-28 04:20:30 +00001249 return PJ_EPENDING;
1250 }
Sauw Ming73ecfe82011-09-21 10:20:01 +00001251 } else {
1252 status = create_udp_media_transport(tcfg, call_med);
1253 }
1254
Sauw Ming848742f2011-09-28 04:20:30 +00001255 if (status != PJ_SUCCESS) {
Sauw Ming73ecfe82011-09-21 10:20:01 +00001256 PJ_PERROR(1,(THIS_FILE, status, "Error creating media transport"));
1257 return status;
1258 }
1259
1260 /* Media transport creation completed immediately, so
1261 * we don't need to call the callback.
1262 */
1263 call_med->med_init_cb = NULL;
1264
1265 } else if (call_med->tp_st == PJSUA_MED_TP_DISABLED) {
1266 /* Media is being reenabled. */
Nanang Izzuddin1d568722012-04-24 05:40:32 +00001267 //pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_IDLE);
1268
1269 pj_assert(!"Currently no media transport reuse");
Sauw Ming73ecfe82011-09-21 10:20:01 +00001270 }
1271
1272 return call_media_init_cb(call_med, status, security_level,
1273 sip_err_code);
1274}
1275
1276/* Callback to resume pjsua_media_channel_init() after media transport
1277 * initialization is completed.
1278 */
1279static pj_status_t media_channel_init_cb(pjsua_call_id call_id,
1280 const pjsua_med_tp_state_info *info)
1281{
1282 pjsua_call *call = &pjsua_var.calls[call_id];
1283 pj_status_t status = (info? info->status : PJ_SUCCESS);
1284 unsigned mi;
1285
1286 if (info) {
1287 pj_mutex_lock(call->med_ch_mutex);
1288
1289 /* Set the callback to NULL to indicate that the async operation
1290 * has completed.
1291 */
Nanang Izzuddin1d568722012-04-24 05:40:32 +00001292 call->media_prov[info->med_idx].med_init_cb = NULL;
Sauw Ming73ecfe82011-09-21 10:20:01 +00001293
1294 /* In case of failure, save the information to be returned
1295 * by the last media transport to finish.
1296 */
1297 if (info->status != PJ_SUCCESS)
1298 pj_memcpy(&call->med_ch_info, info, sizeof(info));
1299
1300 /* Check whether all the call's medias have finished calling their
1301 * callbacks.
1302 */
Nanang Izzuddin1d568722012-04-24 05:40:32 +00001303 for (mi=0; mi < call->med_prov_cnt; ++mi) {
1304 pjsua_call_media *call_med = &call->media_prov[mi];
Sauw Ming73ecfe82011-09-21 10:20:01 +00001305
Sauw Ming2a899232012-01-09 11:51:56 +00001306 if (call_med->med_init_cb) {
Sauw Ming73ecfe82011-09-21 10:20:01 +00001307 pj_mutex_unlock(call->med_ch_mutex);
1308 return PJ_SUCCESS;
1309 }
1310
1311 if (call_med->tp_ready != PJ_SUCCESS)
1312 status = call_med->tp_ready;
1313 }
1314
1315 /* OK, we are called by the last media transport finished. */
1316 pj_mutex_unlock(call->med_ch_mutex);
1317 }
1318
1319 if (call->med_ch_mutex) {
1320 pj_mutex_destroy(call->med_ch_mutex);
1321 call->med_ch_mutex = NULL;
1322 }
1323
1324 if (status != PJ_SUCCESS) {
Benny Prijono2d966c12012-05-22 04:09:53 +00001325 if (call->med_ch_info.status == PJ_SUCCESS) {
Benny Prijono5fefe8e2012-05-22 03:29:28 +00001326 call->med_ch_info.status = status;
Benny Prijono2d966c12012-05-22 04:09:53 +00001327 call->med_ch_info.sip_err_code = PJSIP_SC_TEMPORARILY_UNAVAILABLE;
1328 }
Nanang Izzuddin1d568722012-04-24 05:40:32 +00001329 pjsua_media_prov_clean_up(call_id);
Benny Prijono0ec6cfb2011-12-26 09:00:42 +00001330 goto on_return;
Sauw Ming73ecfe82011-09-21 10:20:01 +00001331 }
1332
1333 /* Tell the media transport of a new offer/answer session */
Nanang Izzuddin1d568722012-04-24 05:40:32 +00001334 for (mi=0; mi < call->med_prov_cnt; ++mi) {
1335 pjsua_call_media *call_med = &call->media_prov[mi];
Sauw Ming73ecfe82011-09-21 10:20:01 +00001336
1337 /* Note: tp may be NULL if this media line is disabled */
1338 if (call_med->tp && call_med->tp_st == PJSUA_MED_TP_IDLE) {
Sauw Ming99cc8ff2011-09-22 04:24:56 +00001339 pj_pool_t *tmp_pool = call->async_call.pool_prov;
1340
1341 if (!tmp_pool) {
1342 tmp_pool = (call->inv? call->inv->pool_prov:
1343 call->async_call.dlg->pool);
1344 }
Sauw Ming73ecfe82011-09-21 10:20:01 +00001345
Sauw Minge7dbbc82011-10-24 09:28:13 +00001346 if (call_med->use_custom_med_tp) {
1347 unsigned custom_med_tp_flags = 0;
1348
1349 /* Use custom media transport returned by the application */
1350 call_med->tp =
1351 (*pjsua_var.ua_cfg.cb.on_create_media_transport)
1352 (call_id, mi, call_med->tp,
1353 custom_med_tp_flags);
1354 if (!call_med->tp) {
1355 status =
Benny Prijono2d966c12012-05-22 04:09:53 +00001356 PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_TEMPORARILY_UNAVAILABLE);
Sauw Minge7dbbc82011-10-24 09:28:13 +00001357 }
1358 }
1359
1360 if (call_med->tp) {
1361 status = pjmedia_transport_media_create(
1362 call_med->tp, tmp_pool,
1363 0, call->async_call.rem_sdp, mi);
1364 }
Sauw Ming73ecfe82011-09-21 10:20:01 +00001365 if (status != PJ_SUCCESS) {
1366 call->med_ch_info.status = status;
1367 call->med_ch_info.med_idx = mi;
1368 call->med_ch_info.state = call_med->tp_st;
Benny Prijono2d966c12012-05-22 04:09:53 +00001369 call->med_ch_info.sip_err_code = PJSIP_SC_TEMPORARILY_UNAVAILABLE;
Nanang Izzuddin1d568722012-04-24 05:40:32 +00001370 pjsua_media_prov_clean_up(call_id);
Benny Prijono0ec6cfb2011-12-26 09:00:42 +00001371 goto on_return;
Sauw Ming73ecfe82011-09-21 10:20:01 +00001372 }
1373
Benny Prijono1312e752012-03-22 09:56:52 +00001374 pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_INIT);
Sauw Ming73ecfe82011-09-21 10:20:01 +00001375 }
1376 }
1377
1378 call->med_ch_info.status = PJ_SUCCESS;
1379
Benny Prijono0ec6cfb2011-12-26 09:00:42 +00001380on_return:
Sauw Ming73ecfe82011-09-21 10:20:01 +00001381 if (call->med_ch_cb)
1382 (*call->med_ch_cb)(call->index, &call->med_ch_info);
1383
Benny Prijono0bc99a92011-03-17 04:34:43 +00001384 return status;
1385}
Nanang Izzuddin1e952a82010-10-05 16:32:04 +00001386
Nanang Izzuddin1d568722012-04-24 05:40:32 +00001387
1388/* Clean up media transports in provisional media that is not used
1389 * by call media.
1390 */
1391void pjsua_media_prov_clean_up(pjsua_call_id call_id)
1392{
1393 pjsua_call *call = &pjsua_var.calls[call_id];
1394 unsigned i;
1395
1396 for (i = 0; i < call->med_prov_cnt; ++i) {
1397 pjsua_call_media *call_med = &call->media_prov[i];
1398 unsigned j;
1399 pj_bool_t used = PJ_FALSE;
1400
1401 if (call_med->tp == NULL)
1402 continue;
1403
1404 for (j = 0; j < call->med_cnt; ++j) {
1405 if (call->media[j].tp == call_med->tp) {
1406 used = PJ_TRUE;
1407 break;
1408 }
1409 }
1410
1411 if (!used) {
1412 if (call_med->tp_st > PJSUA_MED_TP_IDLE) {
1413 pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_IDLE);
1414 pjmedia_transport_media_stop(call_med->tp);
1415 }
1416 pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_NULL);
1417 pjmedia_transport_close(call_med->tp);
1418 call_med->tp = call_med->tp_orig = NULL;
1419 }
1420 }
1421}
1422
1423
Benny Prijonod8179652008-01-23 20:39:07 +00001424pj_status_t pjsua_media_channel_init(pjsua_call_id call_id,
1425 pjsip_role_e role,
1426 int security_level,
1427 pj_pool_t *tmp_pool,
1428 const pjmedia_sdp_session *rem_sdp,
Sauw Ming73ecfe82011-09-21 10:20:01 +00001429 int *sip_err_code,
1430 pj_bool_t async,
1431 pjsua_med_tp_state_cb cb)
Benny Prijonoc97608e2007-03-23 16:34:20 +00001432{
Benny Prijono0bc99a92011-03-17 04:34:43 +00001433 const pj_str_t STR_AUDIO = { "audio", 5 };
1434 const pj_str_t STR_VIDEO = { "video", 5 };
Benny Prijonod8179652008-01-23 20:39:07 +00001435 pjsua_call *call = &pjsua_var.calls[call_id];
Benny Prijonod8179652008-01-23 20:39:07 +00001436 pjsua_acc *acc = &pjsua_var.acc[call->acc_id];
Benny Prijono0bc99a92011-03-17 04:34:43 +00001437 pj_uint8_t maudidx[PJSUA_MAX_CALL_MEDIA];
1438 unsigned maudcnt = PJ_ARRAY_SIZE(maudidx);
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001439 unsigned mtotaudcnt = PJ_ARRAY_SIZE(maudidx);
Benny Prijono0bc99a92011-03-17 04:34:43 +00001440 pj_uint8_t mvididx[PJSUA_MAX_CALL_MEDIA];
1441 unsigned mvidcnt = PJ_ARRAY_SIZE(mvididx);
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001442 unsigned mtotvidcnt = PJ_ARRAY_SIZE(mvididx);
Benny Prijono0bc99a92011-03-17 04:34:43 +00001443 unsigned mi;
Sauw Ming73ecfe82011-09-21 10:20:01 +00001444 pj_bool_t pending_med_tp = PJ_FALSE;
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001445 pj_bool_t reinit = PJ_FALSE;
Benny Prijono0bc99a92011-03-17 04:34:43 +00001446 pj_status_t status;
Benny Prijonoc97608e2007-03-23 16:34:20 +00001447
Benny Prijonod8179652008-01-23 20:39:07 +00001448 PJ_UNUSED_ARG(role);
1449
Nanang Izzuddinb6c239c2011-05-10 05:42:28 +00001450 /*
1451 * Note: this function may be called when the media already exists
1452 * (e.g. in reinvites, updates, etc).
1453 */
1454
Benny Prijono0bc99a92011-03-17 04:34:43 +00001455 if (pjsua_get_state() != PJSUA_STATE_RUNNING)
1456 return PJ_EBUSY;
1457
Sauw Ming73ecfe82011-09-21 10:20:01 +00001458 if (async) {
1459 pj_pool_t *tmppool = (call->inv? call->inv->pool_prov:
1460 call->async_call.dlg->pool);
1461
1462 status = pj_mutex_create_simple(tmppool, NULL, &call->med_ch_mutex);
1463 if (status != PJ_SUCCESS)
1464 return status;
1465 }
1466
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001467 if (call->inv && call->inv->state == PJSIP_INV_STATE_CONFIRMED)
1468 reinit = PJ_TRUE;
1469
1470 PJ_LOG(4,(THIS_FILE, "Call %d: %sinitializing media..",
1471 call_id, (reinit?"re-":"") ));
1472
Benny Prijonob90fd382011-09-18 14:59:56 +00001473 pj_log_push_indent();
1474
Nanang Izzuddin1d568722012-04-24 05:40:32 +00001475 /* Init provisional media state */
1476 if (call->med_cnt == 0) {
1477 /* New media session, just copy whole from call media state. */
1478 pj_memcpy(call->media_prov, call->media, sizeof(call->media));
1479 } else {
1480 /* Clean up any unused transports. Note that when local SDP reoffer
1481 * is rejected by remote, there may be any initialized transports that
1482 * are not used by call media and currently there is no notification
1483 * from PJSIP level regarding the reoffer rejection.
1484 */
1485 pjsua_media_prov_clean_up(call_id);
1486
1487 /* Updating media session, copy from call media state. */
1488 pj_memcpy(call->media_prov, call->media,
1489 sizeof(call->media[0]) * call->med_cnt);
1490 }
1491 call->med_prov_cnt = call->med_cnt;
1492
Benny Prijono0bc99a92011-03-17 04:34:43 +00001493#if DISABLED_FOR_TICKET_1185
Benny Prijonod8179652008-01-23 20:39:07 +00001494 /* Return error if media transport has not been created yet
Benny Prijono68f9e4f2008-03-21 08:56:02 +00001495 * (e.g. application is starting)
1496 */
Benny Prijono0bc99a92011-03-17 04:34:43 +00001497 for (i=0; i<call->med_cnt; ++i) {
1498 if (call->media[i].tp == NULL) {
Benny Prijonob90fd382011-09-18 14:59:56 +00001499 status = PJ_EBUSY;
1500 goto on_error;
Benny Prijonod8179652008-01-23 20:39:07 +00001501 }
Benny Prijonoc97608e2007-03-23 16:34:20 +00001502 }
Benny Prijonod8179652008-01-23 20:39:07 +00001503#endif
Benny Prijonoc97608e2007-03-23 16:34:20 +00001504
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001505 /* Get media count for each media type */
Benny Prijono0bc99a92011-03-17 04:34:43 +00001506 if (rem_sdp) {
1507 sort_media(rem_sdp, &STR_AUDIO, acc->cfg.use_srtp,
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001508 maudidx, &maudcnt, &mtotaudcnt);
Benny Prijono0bc99a92011-03-17 04:34:43 +00001509 if (maudcnt==0) {
1510 /* Expecting audio in the offer */
1511 if (sip_err_code) *sip_err_code = PJSIP_SC_NOT_ACCEPTABLE_HERE;
Benny Prijonob90fd382011-09-18 14:59:56 +00001512 status = PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_NOT_ACCEPTABLE_HERE);
1513 goto on_error;
Benny Prijonoc97608e2007-03-23 16:34:20 +00001514 }
1515
Nanang Izzuddind8c33d82011-08-18 18:30:55 +00001516#if PJMEDIA_HAS_VIDEO
Benny Prijono0bc99a92011-03-17 04:34:43 +00001517 sort_media(rem_sdp, &STR_VIDEO, acc->cfg.use_srtp,
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001518 mvididx, &mvidcnt, &mtotvidcnt);
Nanang Izzuddind8c33d82011-08-18 18:30:55 +00001519#else
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001520 mvidcnt = mtotvidcnt = 0;
1521 PJ_UNUSED_ARG(STR_VIDEO);
Nanang Izzuddind8c33d82011-08-18 18:30:55 +00001522#endif
Nanang Izzuddinb6c239c2011-05-10 05:42:28 +00001523
1524 /* Update media count only when remote add any media, this media count
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001525 * must never decrease. Also note that we shouldn't apply the media
1526 * count setting (of the call setting) before the SDP negotiation.
Benny Prijonod8179652008-01-23 20:39:07 +00001527 */
Nanang Izzuddin1d568722012-04-24 05:40:32 +00001528 if (call->med_prov_cnt < rem_sdp->media_count)
1529 call->med_prov_cnt = PJ_MIN(rem_sdp->media_count,
1530 PJSUA_MAX_CALL_MEDIA);
Benny Prijonod8179652008-01-23 20:39:07 +00001531
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001532 call->rem_offerer = PJ_TRUE;
1533 call->rem_aud_cnt = maudcnt;
1534 call->rem_vid_cnt = mvidcnt;
1535
Benny Prijono0bc99a92011-03-17 04:34:43 +00001536 } else {
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001537
1538 /* If call already established, calculate media count from current
1539 * local active SDP and call setting. Otherwise, calculate media
1540 * count from the call setting only.
1541 */
1542 if (reinit) {
Benny Prijono0ec6cfb2011-12-26 09:00:42 +00001543 const pjmedia_sdp_session *sdp;
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001544
1545 status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &sdp);
1546 pj_assert(status == PJ_SUCCESS);
1547
1548 sort_media(sdp, &STR_AUDIO, acc->cfg.use_srtp,
1549 maudidx, &maudcnt, &mtotaudcnt);
1550 pj_assert(maudcnt > 0);
1551
1552 sort_media(sdp, &STR_VIDEO, acc->cfg.use_srtp,
1553 mvididx, &mvidcnt, &mtotvidcnt);
1554
1555 /* Call setting may add or remove media. Adding media is done by
1556 * enabling any disabled/port-zeroed media first, then adding new
1557 * media whenever needed. Removing media is done by disabling
1558 * media with the lowest 'quality'.
1559 */
1560
1561 /* Check if we need to add new audio */
Benny Prijonoae86e6a2011-12-27 12:47:52 +00001562 if (maudcnt < call->opt.aud_cnt &&
1563 mtotaudcnt < call->opt.aud_cnt)
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001564 {
Benny Prijonoae86e6a2011-12-27 12:47:52 +00001565 for (mi = 0; mi < call->opt.aud_cnt - mtotaudcnt; ++mi)
Nanang Izzuddin1d568722012-04-24 05:40:32 +00001566 maudidx[maudcnt++] = (pj_uint8_t)call->med_prov_cnt++;
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001567
Benny Prijonoae86e6a2011-12-27 12:47:52 +00001568 mtotaudcnt = call->opt.aud_cnt;
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001569 }
Benny Prijonoae86e6a2011-12-27 12:47:52 +00001570 maudcnt = call->opt.aud_cnt;
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001571
1572 /* Check if we need to add new video */
Benny Prijonoae86e6a2011-12-27 12:47:52 +00001573 if (mvidcnt < call->opt.vid_cnt &&
1574 mtotvidcnt < call->opt.vid_cnt)
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001575 {
Benny Prijonoae86e6a2011-12-27 12:47:52 +00001576 for (mi = 0; mi < call->opt.vid_cnt - mtotvidcnt; ++mi)
Nanang Izzuddin1d568722012-04-24 05:40:32 +00001577 mvididx[mvidcnt++] = (pj_uint8_t)call->med_prov_cnt++;
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001578
Benny Prijonoae86e6a2011-12-27 12:47:52 +00001579 mtotvidcnt = call->opt.vid_cnt;
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001580 }
Benny Prijonoae86e6a2011-12-27 12:47:52 +00001581 mvidcnt = call->opt.vid_cnt;
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001582
1583 } else {
1584
Benny Prijonoae86e6a2011-12-27 12:47:52 +00001585 maudcnt = mtotaudcnt = call->opt.aud_cnt;
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001586 for (mi=0; mi<maudcnt; ++mi) {
1587 maudidx[mi] = (pj_uint8_t)mi;
1588 }
Benny Prijonoae86e6a2011-12-27 12:47:52 +00001589 mvidcnt = mtotvidcnt = call->opt.vid_cnt;
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001590 for (mi=0; mi<mvidcnt; ++mi) {
1591 mvididx[mi] = (pj_uint8_t)(maudcnt + mi);
1592 }
Nanang Izzuddin1d568722012-04-24 05:40:32 +00001593 call->med_prov_cnt = maudcnt + mvidcnt;
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001594
1595 /* Need to publish supported media? */
1596 if (call->opt.flag & PJSUA_CALL_INCLUDE_DISABLED_MEDIA) {
1597 if (mtotaudcnt == 0) {
1598 mtotaudcnt = 1;
Nanang Izzuddin1d568722012-04-24 05:40:32 +00001599 maudidx[0] = (pj_uint8_t)call->med_prov_cnt++;
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001600 }
Nanang Izzuddind8c33d82011-08-18 18:30:55 +00001601#if PJMEDIA_HAS_VIDEO
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001602 if (mtotvidcnt == 0) {
1603 mtotvidcnt = 1;
Nanang Izzuddin1d568722012-04-24 05:40:32 +00001604 mvididx[0] = (pj_uint8_t)call->med_prov_cnt++;
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001605 }
1606#endif
1607 }
Benny Prijono0bc99a92011-03-17 04:34:43 +00001608 }
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001609
1610 call->rem_offerer = PJ_FALSE;
Benny Prijonoa310bd22008-06-27 21:19:44 +00001611 }
1612
Nanang Izzuddin1d568722012-04-24 05:40:32 +00001613 if (call->med_prov_cnt == 0) {
Benny Prijono0bc99a92011-03-17 04:34:43 +00001614 /* Expecting at least one media */
Benny Prijonoab8dba92008-06-27 21:59:15 +00001615 if (sip_err_code) *sip_err_code = PJSIP_SC_NOT_ACCEPTABLE_HERE;
Benny Prijonob90fd382011-09-18 14:59:56 +00001616 status = PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_NOT_ACCEPTABLE_HERE);
1617 goto on_error;
Benny Prijonoa310bd22008-06-27 21:19:44 +00001618 }
1619
Sauw Ming73ecfe82011-09-21 10:20:01 +00001620 if (async) {
1621 call->med_ch_cb = cb;
Benny Prijono7eff5ef2011-10-26 06:53:30 +00001622 }
1623
1624 if (rem_sdp) {
1625 call->async_call.rem_sdp =
1626 pjmedia_sdp_session_clone(call->inv->pool_prov, rem_sdp);
1627 } else {
1628 call->async_call.rem_sdp = NULL;
Sauw Ming73ecfe82011-09-21 10:20:01 +00001629 }
1630
Sauw Ming99cc8ff2011-09-22 04:24:56 +00001631 call->async_call.pool_prov = tmp_pool;
1632
Benny Prijono0bc99a92011-03-17 04:34:43 +00001633 /* Initialize each media line */
Nanang Izzuddin1d568722012-04-24 05:40:32 +00001634 for (mi=0; mi < call->med_prov_cnt; ++mi) {
1635 pjsua_call_media *call_med = &call->media_prov[mi];
Benny Prijono0bc99a92011-03-17 04:34:43 +00001636 pj_bool_t enabled = PJ_FALSE;
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001637 pjmedia_type media_type = PJMEDIA_TYPE_UNKNOWN;
Benny Prijonoa310bd22008-06-27 21:19:44 +00001638
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001639 if (pj_memchr(maudidx, mi, mtotaudcnt * sizeof(maudidx[0]))) {
1640 media_type = PJMEDIA_TYPE_AUDIO;
Benny Prijonoae86e6a2011-12-27 12:47:52 +00001641 if (call->opt.aud_cnt &&
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001642 pj_memchr(maudidx, mi, maudcnt * sizeof(maudidx[0])))
1643 {
1644 enabled = PJ_TRUE;
Benny Prijono0bc99a92011-03-17 04:34:43 +00001645 }
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001646 } else if (pj_memchr(mvididx, mi, mtotvidcnt * sizeof(mvididx[0]))) {
1647 media_type = PJMEDIA_TYPE_VIDEO;
Benny Prijonoae86e6a2011-12-27 12:47:52 +00001648 if (call->opt.vid_cnt &&
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001649 pj_memchr(mvididx, mi, mvidcnt * sizeof(mvididx[0])))
1650 {
1651 enabled = PJ_TRUE;
Benny Prijono0bc99a92011-03-17 04:34:43 +00001652 }
Benny Prijono0bc99a92011-03-17 04:34:43 +00001653 }
1654
1655 if (enabled) {
1656 status = pjsua_call_media_init(call_med, media_type,
1657 &acc->cfg.rtp_cfg,
Sauw Ming73ecfe82011-09-21 10:20:01 +00001658 security_level, sip_err_code,
1659 async,
Sauw Ming3a55bb92011-10-06 06:49:09 +00001660 (async? &media_channel_init_cb:
1661 NULL));
Sauw Ming73ecfe82011-09-21 10:20:01 +00001662 if (status == PJ_EPENDING) {
1663 pending_med_tp = PJ_TRUE;
1664 } else if (status != PJ_SUCCESS) {
1665 if (pending_med_tp) {
1666 /* Save failure information. */
1667 call_med->tp_ready = status;
1668 pj_bzero(&call->med_ch_info, sizeof(call->med_ch_info));
1669 call->med_ch_info.status = status;
1670 call->med_ch_info.state = call_med->tp_st;
1671 call->med_ch_info.med_idx = call_med->idx;
1672 if (sip_err_code)
1673 call->med_ch_info.sip_err_code = *sip_err_code;
1674
1675 /* We will return failure in the callback later. */
1676 return PJ_EPENDING;
1677 }
1678
Nanang Izzuddin1d568722012-04-24 05:40:32 +00001679 pjsua_media_prov_clean_up(call_id);
Benny Prijonob90fd382011-09-18 14:59:56 +00001680 goto on_error;
Benny Prijono0bc99a92011-03-17 04:34:43 +00001681 }
1682 } else {
Nanang Izzuddinb6c239c2011-05-10 05:42:28 +00001683 /* By convention, the media is disabled if transport is NULL
1684 * or transport state is PJSUA_MED_TP_DISABLED.
1685 */
Benny Prijono0bc99a92011-03-17 04:34:43 +00001686 if (call_med->tp) {
Nanang Izzuddinb6c239c2011-05-10 05:42:28 +00001687 // Don't close transport here, as SDP negotiation has not been
Nanang Izzuddin1d568722012-04-24 05:40:32 +00001688 // done and stream may be still active. Once SDP negotiation
1689 // is done (channel_update() invoked), this transport will be
1690 // closed there.
Nanang Izzuddinb6c239c2011-05-10 05:42:28 +00001691 //pjmedia_transport_close(call_med->tp);
1692 //call_med->tp = NULL;
1693 pj_assert(call_med->tp_st == PJSUA_MED_TP_INIT ||
1694 call_med->tp_st == PJSUA_MED_TP_RUNNING);
Benny Prijono1312e752012-03-22 09:56:52 +00001695 pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_DISABLED);
Benny Prijono0bc99a92011-03-17 04:34:43 +00001696 }
Nanang Izzuddinb6c239c2011-05-10 05:42:28 +00001697
1698 /* Put media type just for info */
1699 call_med->type = media_type;
Benny Prijono0bc99a92011-03-17 04:34:43 +00001700 }
Benny Prijono224b4e22008-06-19 14:10:28 +00001701 }
1702
Benny Prijono0bc99a92011-03-17 04:34:43 +00001703 call->audio_idx = maudidx[0];
1704
1705 PJ_LOG(4,(THIS_FILE, "Media index %d selected for audio call %d",
1706 call->audio_idx, call->index));
1707
Sauw Ming73ecfe82011-09-21 10:20:01 +00001708 if (pending_med_tp) {
Sauw Ming99cc8ff2011-09-22 04:24:56 +00001709 /* We shouldn't use temporary pool anymore. */
1710 call->async_call.pool_prov = NULL;
Sauw Ming73ecfe82011-09-21 10:20:01 +00001711 /* We have a pending media transport initialization. */
1712 pj_log_pop_indent();
1713 return PJ_EPENDING;
Benny Prijono0bc99a92011-03-17 04:34:43 +00001714 }
1715
Sauw Ming73ecfe82011-09-21 10:20:01 +00001716 /* Media transport initialization completed immediately, so
1717 * we don't need to call the callback.
1718 */
1719 call->med_ch_cb = NULL;
1720
1721 status = media_channel_init_cb(call_id, NULL);
1722 if (status != PJ_SUCCESS && sip_err_code)
1723 *sip_err_code = call->med_ch_info.sip_err_code;
1724
Benny Prijonob90fd382011-09-18 14:59:56 +00001725 pj_log_pop_indent();
Sauw Ming73ecfe82011-09-21 10:20:01 +00001726 return status;
Benny Prijonob90fd382011-09-18 14:59:56 +00001727
1728on_error:
Sauw Ming73ecfe82011-09-21 10:20:01 +00001729 if (call->med_ch_mutex) {
1730 pj_mutex_destroy(call->med_ch_mutex);
1731 call->med_ch_mutex = NULL;
1732 }
1733
Benny Prijonob90fd382011-09-18 14:59:56 +00001734 pj_log_pop_indent();
1735 return status;
Benny Prijonoc97608e2007-03-23 16:34:20 +00001736}
1737
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001738
1739/* Create SDP based on the current media channel. Note that, this function
1740 * will not modify the media channel, so when receiving new offer or
1741 * updating media count (via call setting), media channel must be reinit'd
1742 * (using pjsua_media_channel_init()) first before calling this function.
1743 */
Benny Prijonoc97608e2007-03-23 16:34:20 +00001744pj_status_t pjsua_media_channel_create_sdp(pjsua_call_id call_id,
1745 pj_pool_t *pool,
Benny Prijonod8179652008-01-23 20:39:07 +00001746 const pjmedia_sdp_session *rem_sdp,
Benny Prijono25b2ea12008-01-24 19:20:54 +00001747 pjmedia_sdp_session **p_sdp,
Benny Prijono0bc99a92011-03-17 04:34:43 +00001748 int *sip_err_code)
Benny Prijonoc97608e2007-03-23 16:34:20 +00001749{
Benny Prijono0bc99a92011-03-17 04:34:43 +00001750 enum { MAX_MEDIA = PJSUA_MAX_CALL_MEDIA };
Benny Prijonoc97608e2007-03-23 16:34:20 +00001751 pjmedia_sdp_session *sdp;
Benny Prijono0bc99a92011-03-17 04:34:43 +00001752 pj_sockaddr origin;
Benny Prijonoc97608e2007-03-23 16:34:20 +00001753 pjsua_call *call = &pjsua_var.calls[call_id];
Nanang Izzuddin1e952a82010-10-05 16:32:04 +00001754 pjmedia_sdp_neg_state sdp_neg_state = PJMEDIA_SDP_NEG_STATE_NULL;
Benny Prijono0bc99a92011-03-17 04:34:43 +00001755 unsigned mi;
Nanang Izzuddin2f296352012-01-27 09:12:59 +00001756 unsigned tot_bandw_tias = 0;
Benny Prijonoc97608e2007-03-23 16:34:20 +00001757 pj_status_t status;
1758
Benny Prijono0bc99a92011-03-17 04:34:43 +00001759 if (pjsua_get_state() != PJSUA_STATE_RUNNING)
Benny Prijono55e82352007-05-10 20:49:08 +00001760 return PJ_EBUSY;
Benny Prijono55e82352007-05-10 20:49:08 +00001761
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001762#if 0
1763 // This function should not really change the media channel.
Benny Prijono0bc99a92011-03-17 04:34:43 +00001764 if (rem_sdp) {
Nanang Izzuddinb6c239c2011-05-10 05:42:28 +00001765 /* If this is a re-offer, let's re-initialize media as remote may
1766 * add or remove media
1767 */
1768 if (call->inv && call->inv->state == PJSIP_INV_STATE_CONFIRMED) {
1769 status = pjsua_media_channel_init(call_id, PJSIP_ROLE_UAS,
1770 call->secure_level, pool,
Sauw Ming73ecfe82011-09-21 10:20:01 +00001771 rem_sdp, sip_err_code,
1772 PJ_FALSE, NULL);
Nanang Izzuddinb6c239c2011-05-10 05:42:28 +00001773 if (status != PJ_SUCCESS)
1774 return status;
Benny Prijono0324ba52010-12-02 10:41:46 +00001775 }
Benny Prijono0bc99a92011-03-17 04:34:43 +00001776 } else {
1777 /* Audio is first in our offer, by convention */
Nanang Izzuddinb6c239c2011-05-10 05:42:28 +00001778 // The audio_idx should not be changed here, as this function may be
1779 // called in generating re-offer and the current active audio index
1780 // can be anywhere.
1781 //call->audio_idx = 0;
Nanang Izzuddin3150d8b2010-12-01 08:20:28 +00001782 }
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001783#endif
Nanang Izzuddin3150d8b2010-12-01 08:20:28 +00001784
Nanang Izzuddinb6c239c2011-05-10 05:42:28 +00001785#if 0
1786 // Since r3512, old-style hold should have got transport, created by
1787 // pjsua_media_channel_init() in initial offer/answer or remote reoffer.
Benny Prijono224b4e22008-06-19 14:10:28 +00001788 /* Create media if it's not created. This could happen when call is
Benny Prijono0bc99a92011-03-17 04:34:43 +00001789 * currently on-hold (with the old style hold)
Benny Prijono224b4e22008-06-19 14:10:28 +00001790 */
Benny Prijono0bc99a92011-03-17 04:34:43 +00001791 if (call->media[call->audio_idx].tp == NULL) {
Benny Prijono224b4e22008-06-19 14:10:28 +00001792 pjsip_role_e role;
1793 role = (rem_sdp ? PJSIP_ROLE_UAS : PJSIP_ROLE_UAC);
1794 status = pjsua_media_channel_init(call_id, role, call->secure_level,
Benny Prijono0bc99a92011-03-17 04:34:43 +00001795 pool, rem_sdp, sip_err_code);
Benny Prijono224b4e22008-06-19 14:10:28 +00001796 if (status != PJ_SUCCESS)
1797 return status;
1798 }
Nanang Izzuddinb6c239c2011-05-10 05:42:28 +00001799#endif
Benny Prijono224b4e22008-06-19 14:10:28 +00001800
Nanang Izzuddin1e952a82010-10-05 16:32:04 +00001801 /* Get SDP negotiator state */
1802 if (call->inv && call->inv->neg)
1803 sdp_neg_state = pjmedia_sdp_neg_get_state(call->inv->neg);
1804
Benny Prijono0bc99a92011-03-17 04:34:43 +00001805 /* Get one address to use in the origin field */
1806 pj_bzero(&origin, sizeof(origin));
Nanang Izzuddin1d568722012-04-24 05:40:32 +00001807 for (mi=0; mi<call->med_prov_cnt; ++mi) {
Benny Prijono0bc99a92011-03-17 04:34:43 +00001808 pjmedia_transport_info tpinfo;
Benny Prijono617c5bc2007-04-02 19:51:21 +00001809
Nanang Izzuddin1d568722012-04-24 05:40:32 +00001810 if (call->media_prov[mi].tp == NULL)
Benny Prijono0bc99a92011-03-17 04:34:43 +00001811 continue;
1812
1813 pjmedia_transport_info_init(&tpinfo);
Nanang Izzuddin1d568722012-04-24 05:40:32 +00001814 pjmedia_transport_get_info(call->media_prov[mi].tp, &tpinfo);
Benny Prijono0bc99a92011-03-17 04:34:43 +00001815 pj_sockaddr_cp(&origin, &tpinfo.sock_info.rtp_addr_name);
1816 break;
Benny Prijono25b2ea12008-01-24 19:20:54 +00001817 }
Benny Prijonoc97608e2007-03-23 16:34:20 +00001818
Benny Prijono0bc99a92011-03-17 04:34:43 +00001819 /* Create the base (blank) SDP */
1820 status = pjmedia_endpt_create_base_sdp(pjsua_var.med_endpt, pool, NULL,
1821 &origin, &sdp);
1822 if (status != PJ_SUCCESS)
1823 return status;
Benny Prijonoa310bd22008-06-27 21:19:44 +00001824
Benny Prijono0bc99a92011-03-17 04:34:43 +00001825 /* Process each media line */
Nanang Izzuddin1d568722012-04-24 05:40:32 +00001826 for (mi=0; mi<call->med_prov_cnt; ++mi) {
1827 pjsua_call_media *call_med = &call->media_prov[mi];
Benny Prijono0bc99a92011-03-17 04:34:43 +00001828 pjmedia_sdp_media *m = NULL;
1829 pjmedia_transport_info tpinfo;
Nanang Izzuddin2f296352012-01-27 09:12:59 +00001830 unsigned i;
Benny Prijono0bc99a92011-03-17 04:34:43 +00001831
Nanang Izzuddinb6c239c2011-05-10 05:42:28 +00001832 if (rem_sdp && mi >= rem_sdp->media_count) {
1833 /* Remote might have removed some media lines. */
1834 break;
Nanang Izzuddin1e952a82010-10-05 16:32:04 +00001835 }
1836
Nanang Izzuddinb6c239c2011-05-10 05:42:28 +00001837 if (call_med->tp == NULL || call_med->tp_st == PJSUA_MED_TP_DISABLED)
1838 {
Benny Prijono0bc99a92011-03-17 04:34:43 +00001839 /*
Nanang Izzuddinb6c239c2011-05-10 05:42:28 +00001840 * This media is disabled. Just create a valid SDP with zero
Benny Prijono0bc99a92011-03-17 04:34:43 +00001841 * port.
1842 */
Nanang Izzuddind8c33d82011-08-18 18:30:55 +00001843 if (rem_sdp) {
1844 /* Just clone the remote media and deactivate it */
1845 m = pjmedia_sdp_media_clone_deactivate(pool,
1846 rem_sdp->media[mi]);
1847 } else {
1848 m = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_media);
1849 m->desc.transport = pj_str("RTP/AVP");
1850 m->desc.fmt_count = 1;
1851 m->conn = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_conn);
1852 m->conn->net_type = pj_str("IN");
1853 m->conn->addr_type = pj_str("IP4");
1854 m->conn->addr = pj_str("127.0.0.1");
Benny Prijonoa310bd22008-06-27 21:19:44 +00001855
Nanang Izzuddind8c33d82011-08-18 18:30:55 +00001856 switch (call_med->type) {
1857 case PJMEDIA_TYPE_AUDIO:
1858 m->desc.media = pj_str("audio");
1859 m->desc.fmt[0] = pj_str("0");
1860 break;
1861 case PJMEDIA_TYPE_VIDEO:
1862 m->desc.media = pj_str("video");
1863 m->desc.fmt[0] = pj_str("31");
1864 break;
1865 default:
Nanang Izzuddincfa312c2011-10-26 16:57:05 +00001866 /* This must be us generating re-offer, and some unknown
1867 * media may exist, so just clone from active local SDP
1868 * (and it should have been deactivated already).
1869 */
1870 pj_assert(call->inv && call->inv->neg &&
1871 sdp_neg_state == PJMEDIA_SDP_NEG_STATE_DONE);
1872 {
1873 const pjmedia_sdp_session *s_;
1874 pjmedia_sdp_neg_get_active_local(call->inv->neg, &s_);
1875
1876 pj_assert(mi < s_->media_count);
1877 m = pjmedia_sdp_media_clone(pool, s_->media[mi]);
1878 m->desc.port = 0;
Nanang Izzuddind8c33d82011-08-18 18:30:55 +00001879 }
Nanang Izzuddincfa312c2011-10-26 16:57:05 +00001880 break;
Benny Prijono0bc99a92011-03-17 04:34:43 +00001881 }
Benny Prijonoa310bd22008-06-27 21:19:44 +00001882 }
Benny Prijono0bc99a92011-03-17 04:34:43 +00001883
1884 sdp->media[sdp->media_count++] = m;
1885 continue;
Benny Prijonoa310bd22008-06-27 21:19:44 +00001886 }
1887
Benny Prijono0bc99a92011-03-17 04:34:43 +00001888 /* Get transport address info */
1889 pjmedia_transport_info_init(&tpinfo);
1890 pjmedia_transport_get_info(call_med->tp, &tpinfo);
Benny Prijonoa310bd22008-06-27 21:19:44 +00001891
Benny Prijono0bc99a92011-03-17 04:34:43 +00001892 /* Ask pjmedia endpoint to create SDP media line */
1893 switch (call_med->type) {
1894 case PJMEDIA_TYPE_AUDIO:
1895 status = pjmedia_endpt_create_audio_sdp(pjsua_var.med_endpt, pool,
1896 &tpinfo.sock_info, 0, &m);
1897 break;
Nanang Izzuddin63b3c132011-07-19 11:11:07 +00001898#if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0)
Benny Prijono0bc99a92011-03-17 04:34:43 +00001899 case PJMEDIA_TYPE_VIDEO:
1900 status = pjmedia_endpt_create_video_sdp(pjsua_var.med_endpt, pool,
1901 &tpinfo.sock_info, 0, &m);
1902 break;
Nanang Izzuddin63b3c132011-07-19 11:11:07 +00001903#endif
Benny Prijono0bc99a92011-03-17 04:34:43 +00001904 default:
1905 pj_assert(!"Invalid call_med media type");
1906 return PJ_EBUG;
1907 }
Benny Prijonoa310bd22008-06-27 21:19:44 +00001908
Benny Prijono0bc99a92011-03-17 04:34:43 +00001909 if (status != PJ_SUCCESS)
1910 return status;
1911
1912 sdp->media[sdp->media_count++] = m;
1913
1914 /* Give to transport */
1915 status = pjmedia_transport_encode_sdp(call_med->tp, pool,
1916 sdp, rem_sdp, mi);
1917 if (status != PJ_SUCCESS) {
1918 if (sip_err_code) *sip_err_code = PJSIP_SC_NOT_ACCEPTABLE;
1919 return status;
Benny Prijonoa310bd22008-06-27 21:19:44 +00001920 }
Nanang Izzuddin91ba2a22011-04-11 19:59:09 +00001921
1922 /* Copy c= line of the first media to session level,
1923 * if there's none.
1924 */
1925 if (sdp->conn == NULL) {
1926 sdp->conn = pjmedia_sdp_conn_clone(pool, m->conn);
Benny Prijonoa310bd22008-06-27 21:19:44 +00001927 }
Nanang Izzuddin2f296352012-01-27 09:12:59 +00001928
1929
1930 /* Find media bandwidth info */
1931 for (i = 0; i < m->bandw_count; ++i) {
1932 const pj_str_t STR_BANDW_MODIFIER_TIAS = { "TIAS", 4 };
1933 if (!pj_stricmp(&m->bandw[i]->modifier, &STR_BANDW_MODIFIER_TIAS))
1934 {
1935 tot_bandw_tias += m->bandw[i]->value;
1936 break;
1937 }
1938 }
Benny Prijonoa310bd22008-06-27 21:19:44 +00001939 }
1940
Benny Prijono6ba8c542007-10-16 01:34:14 +00001941 /* Add NAT info in the SDP */
1942 if (pjsua_var.ua_cfg.nat_type_in_sdp) {
1943 pjmedia_sdp_attr *a;
1944 pj_str_t value;
1945 char nat_info[80];
1946
1947 value.ptr = nat_info;
1948 if (pjsua_var.ua_cfg.nat_type_in_sdp == 1) {
1949 value.slen = pj_ansi_snprintf(nat_info, sizeof(nat_info),
1950 "%d", pjsua_var.nat_type);
1951 } else {
1952 const char *type_name = pj_stun_get_nat_name(pjsua_var.nat_type);
1953 value.slen = pj_ansi_snprintf(nat_info, sizeof(nat_info),
1954 "%d %s",
1955 pjsua_var.nat_type,
1956 type_name);
1957 }
1958
1959 a = pjmedia_sdp_attr_create(pool, "X-nat", &value);
1960
1961 pjmedia_sdp_attr_add(&sdp->attr_count, sdp->attr, a);
1962
1963 }
1964
Benny Prijonoc97608e2007-03-23 16:34:20 +00001965
Nanang Izzuddin2f296352012-01-27 09:12:59 +00001966 /* Add bandwidth info in session level using bandwidth modifier "AS". */
1967 if (tot_bandw_tias) {
1968 unsigned bandw;
1969 const pj_str_t STR_BANDW_MODIFIER_AS = { "AS", 2 };
1970 pjmedia_sdp_bandw *b;
1971
1972 /* AS bandwidth = RTP bitrate + RTCP bitrate.
1973 * RTP bitrate = payload bitrate (total TIAS) + overheads (~16kbps).
1974 * RTCP bitrate = est. 5% of RTP bitrate.
1975 * Note that AS bandwidth is in kbps.
1976 */
1977 bandw = tot_bandw_tias + 16000;
1978 bandw += bandw * 5 / 100;
1979 b = PJ_POOL_ALLOC_T(pool, pjmedia_sdp_bandw);
1980 b->modifier = STR_BANDW_MODIFIER_AS;
1981 b->value = bandw / 1000;
1982 sdp->bandw[sdp->bandw_count++] = b;
1983 }
1984
1985
Benny Prijono0bc99a92011-03-17 04:34:43 +00001986#if DISABLED_FOR_TICKET_1185 && defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
Nanang Izzuddind89cc3a2010-05-13 05:22:51 +00001987 /* Check if SRTP is in optional mode and configured to use duplicated
1988 * media, i.e: secured and unsecured version, in the SDP offer.
1989 */
1990 if (!rem_sdp &&
1991 pjsua_var.acc[call->acc_id].cfg.use_srtp == PJMEDIA_SRTP_OPTIONAL &&
1992 pjsua_var.acc[call->acc_id].cfg.srtp_optional_dup_offer)
1993 {
1994 unsigned i;
1995
1996 for (i = 0; i < sdp->media_count; ++i) {
1997 pjmedia_sdp_media *m = sdp->media[i];
1998
Benny Prijono0bc99a92011-03-17 04:34:43 +00001999 /* Check if this media is unsecured but has SDP "crypto"
Nanang Izzuddind89cc3a2010-05-13 05:22:51 +00002000 * attribute.
2001 */
2002 if (pj_stricmp2(&m->desc.transport, "RTP/AVP") == 0 &&
2003 pjmedia_sdp_media_find_attr2(m, "crypto", NULL) != NULL)
2004 {
Benny Prijono0bc99a92011-03-17 04:34:43 +00002005 if (i == (unsigned)call->audio_idx &&
Nanang Izzuddin1e952a82010-10-05 16:32:04 +00002006 sdp_neg_state == PJMEDIA_SDP_NEG_STATE_DONE)
2007 {
2008 /* This is a session update, and peer has chosen the
2009 * unsecured version, so let's make this unsecured too.
2010 */
2011 pjmedia_sdp_media_remove_all_attr(m, "crypto");
2012 } else {
2013 /* This is new offer, duplicate media so we'll have
2014 * secured (with "RTP/SAVP" transport) and and unsecured
2015 * versions.
2016 */
2017 pjmedia_sdp_media *new_m;
Nanang Izzuddind89cc3a2010-05-13 05:22:51 +00002018
Nanang Izzuddin1e952a82010-10-05 16:32:04 +00002019 /* Duplicate this media and apply secured transport */
2020 new_m = pjmedia_sdp_media_clone(pool, m);
2021 pj_strdup2(pool, &new_m->desc.transport, "RTP/SAVP");
Nanang Izzuddind89cc3a2010-05-13 05:22:51 +00002022
Nanang Izzuddin1e952a82010-10-05 16:32:04 +00002023 /* Remove the "crypto" attribute in the unsecured media */
2024 pjmedia_sdp_media_remove_all_attr(m, "crypto");
Nanang Izzuddind89cc3a2010-05-13 05:22:51 +00002025
Nanang Izzuddin1e952a82010-10-05 16:32:04 +00002026 /* Insert the new media before the unsecured media */
2027 if (sdp->media_count < PJMEDIA_MAX_SDP_MEDIA) {
Benny Prijono0bc99a92011-03-17 04:34:43 +00002028 pj_array_insert(sdp->media, sizeof(new_m),
Nanang Izzuddin1e952a82010-10-05 16:32:04 +00002029 sdp->media_count, i, &new_m);
2030 ++sdp->media_count;
2031 ++i;
2032 }
Nanang Izzuddind89cc3a2010-05-13 05:22:51 +00002033 }
2034 }
2035 }
2036 }
2037#endif
2038
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00002039 call->rem_offerer = (rem_sdp != NULL);
2040
Nanang Izzuddinac23f002012-05-17 08:14:05 +00002041 /* Notify application */
2042 if (pjsua_var.ua_cfg.cb.on_call_sdp_created) {
2043 (*pjsua_var.ua_cfg.cb.on_call_sdp_created)(call_id, sdp,
2044 pool, rem_sdp);
2045 }
2046
Benny Prijonoc97608e2007-03-23 16:34:20 +00002047 *p_sdp = sdp;
2048 return PJ_SUCCESS;
Benny Prijonoc97608e2007-03-23 16:34:20 +00002049}
2050
2051
2052static void stop_media_session(pjsua_call_id call_id)
2053{
2054 pjsua_call *call = &pjsua_var.calls[call_id];
Benny Prijono0bc99a92011-03-17 04:34:43 +00002055 unsigned mi;
Benny Prijonoc97608e2007-03-23 16:34:20 +00002056
Benny Prijonob90fd382011-09-18 14:59:56 +00002057 pj_log_push_indent();
2058
Benny Prijono0bc99a92011-03-17 04:34:43 +00002059 for (mi=0; mi<call->med_cnt; ++mi) {
2060 pjsua_call_media *call_med = &call->media[mi];
2061
2062 if (call_med->type == PJMEDIA_TYPE_AUDIO) {
Benny Prijono1312e752012-03-22 09:56:52 +00002063 pjsua_aud_stop_stream(call_med);
Nanang Izzuddinfd461eb2008-06-09 09:35:59 +00002064 }
Nanang Izzuddin50fae732011-03-22 09:49:23 +00002065
2066#if PJMEDIA_HAS_VIDEO
2067 else if (call_med->type == PJMEDIA_TYPE_VIDEO) {
Benny Prijono1312e752012-03-22 09:56:52 +00002068 pjsua_vid_stop_stream(call_med);
Benny Prijonoc97608e2007-03-23 16:34:20 +00002069 }
Nanang Izzuddin50fae732011-03-22 09:49:23 +00002070#endif
Nanang Izzuddinbf26db12011-03-18 07:54:50 +00002071
2072 PJ_LOG(4,(THIS_FILE, "Media session call%02d:%d is destroyed",
2073 call_id, mi));
Sauw Minge7dbbc82011-10-24 09:28:13 +00002074 call_med->prev_state = call_med->state;
Benny Prijono0bc99a92011-03-17 04:34:43 +00002075 call_med->state = PJSUA_CALL_MEDIA_NONE;
Nanang Izzuddin1d568722012-04-24 05:40:32 +00002076
2077 /* Try to sync recent changes to provisional media */
2078 if (mi<call->med_prov_cnt && call->media_prov[mi].tp==call_med->tp)
2079 {
2080 pjsua_call_media *prov_med = &call->media_prov[mi];
2081
2082 /* Media state */
2083 prov_med->prev_state = call_med->prev_state;
2084 prov_med->state = call_med->state;
2085
2086 /* RTP seq/ts */
2087 prov_med->rtp_tx_seq_ts_set = call_med->rtp_tx_seq_ts_set;
2088 prov_med->rtp_tx_seq = call_med->rtp_tx_seq;
2089 prov_med->rtp_tx_ts = call_med->rtp_tx_ts;
2090
2091 /* Stream */
2092 if (call_med->type == PJMEDIA_TYPE_AUDIO) {
2093 prov_med->strm.a.conf_slot = call_med->strm.a.conf_slot;
2094 prov_med->strm.a.stream = call_med->strm.a.stream;
2095 }
2096#if PJMEDIA_HAS_VIDEO
2097 else if (call_med->type == PJMEDIA_TYPE_VIDEO) {
2098 prov_med->strm.v.cap_win_id = call_med->strm.v.cap_win_id;
2099 prov_med->strm.v.rdr_win_id = call_med->strm.v.rdr_win_id;
2100 prov_med->strm.v.stream = call_med->strm.v.stream;
2101 }
2102#endif
2103 }
Benny Prijonoc97608e2007-03-23 16:34:20 +00002104 }
Benny Prijonob90fd382011-09-18 14:59:56 +00002105
2106 pj_log_pop_indent();
Benny Prijonoc97608e2007-03-23 16:34:20 +00002107}
2108
2109pj_status_t pjsua_media_channel_deinit(pjsua_call_id call_id)
2110{
2111 pjsua_call *call = &pjsua_var.calls[call_id];
Benny Prijono0bc99a92011-03-17 04:34:43 +00002112 unsigned mi;
Benny Prijonoc97608e2007-03-23 16:34:20 +00002113
Sauw Mingec765352011-10-03 02:04:36 +00002114 for (mi=0; mi<call->med_cnt; ++mi) {
2115 pjsua_call_media *call_med = &call->media[mi];
2116
Sauw Ming903154f2011-10-03 08:22:48 +00002117 if (call_med->tp_st == PJSUA_MED_TP_CREATING) {
2118 /* We will do the deinitialization after media transport
2119 * creation is completed.
2120 */
2121 call->async_call.med_ch_deinit = PJ_TRUE;
Sauw Ming903154f2011-10-03 08:22:48 +00002122 return PJ_SUCCESS;
2123 }
Sauw Mingec765352011-10-03 02:04:36 +00002124 }
2125
Benny Prijonob90fd382011-09-18 14:59:56 +00002126 PJ_LOG(4,(THIS_FILE, "Call %d: deinitializing media..", call_id));
2127 pj_log_push_indent();
2128
Benny Prijonoc97608e2007-03-23 16:34:20 +00002129 stop_media_session(call_id);
2130
Nanang Izzuddin1d568722012-04-24 05:40:32 +00002131 /* Clean up media transports */
2132 pjsua_media_prov_clean_up(call_id);
2133 call->med_prov_cnt = 0;
Benny Prijono0bc99a92011-03-17 04:34:43 +00002134 for (mi=0; mi<call->med_cnt; ++mi) {
2135 pjsua_call_media *call_med = &call->media[mi];
Benny Prijonoc97608e2007-03-23 16:34:20 +00002136
Sauw Minge7dbbc82011-10-24 09:28:13 +00002137 if (call_med->tp_st > PJSUA_MED_TP_IDLE) {
Benny Prijono1312e752012-03-22 09:56:52 +00002138 pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_IDLE);
Nanang Izzuddin1d568722012-04-24 05:40:32 +00002139 pjmedia_transport_media_stop(call_med->tp);
Benny Prijono0bc99a92011-03-17 04:34:43 +00002140 }
2141
Nanang Izzuddinb6c239c2011-05-10 05:42:28 +00002142 if (call_med->tp) {
Nanang Izzuddin1d568722012-04-24 05:40:32 +00002143 pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_NULL);
Benny Prijono0bc99a92011-03-17 04:34:43 +00002144 pjmedia_transport_close(call_med->tp);
Nanang Izzuddinb6c239c2011-05-10 05:42:28 +00002145 call_med->tp = call_med->tp_orig = NULL;
Benny Prijono0bc99a92011-03-17 04:34:43 +00002146 }
Sauw Minge7dbbc82011-10-24 09:28:13 +00002147 call_med->tp_orig = NULL;
Benny Prijonod8179652008-01-23 20:39:07 +00002148 }
Nanang Izzuddin670f71b2009-01-20 14:05:54 +00002149
Benny Prijonob90fd382011-09-18 14:59:56 +00002150 pj_log_pop_indent();
Nanang Izzuddin670f71b2009-01-20 14:05:54 +00002151
Benny Prijonoc97608e2007-03-23 16:34:20 +00002152 return PJ_SUCCESS;
2153}
2154
2155
Benny Prijono0bc99a92011-03-17 04:34:43 +00002156pj_status_t pjsua_media_channel_update(pjsua_call_id call_id,
2157 const pjmedia_sdp_session *local_sdp,
2158 const pjmedia_sdp_session *remote_sdp)
2159{
2160 pjsua_call *call = &pjsua_var.calls[call_id];
Nanang Izzuddind8c33d82011-08-18 18:30:55 +00002161 pjsua_acc *acc = &pjsua_var.acc[call->acc_id];
Benny Prijono0bc99a92011-03-17 04:34:43 +00002162 pj_pool_t *tmp_pool = call->inv->pool_prov;
2163 unsigned mi;
Nanang Izzuddinb6c239c2011-05-10 05:42:28 +00002164 pj_bool_t got_media = PJ_FALSE;
Nanang Izzuddinbf26db12011-03-18 07:54:50 +00002165 pj_status_t status = PJ_SUCCESS;
Benny Prijono0bc99a92011-03-17 04:34:43 +00002166
Nanang Izzuddind8c33d82011-08-18 18:30:55 +00002167 const pj_str_t STR_AUDIO = { "audio", 5 };
2168 const pj_str_t STR_VIDEO = { "video", 5 };
2169 pj_uint8_t maudidx[PJSUA_MAX_CALL_MEDIA];
2170 unsigned maudcnt = PJ_ARRAY_SIZE(maudidx);
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00002171 unsigned mtotaudcnt = PJ_ARRAY_SIZE(maudidx);
Nanang Izzuddind8c33d82011-08-18 18:30:55 +00002172 pj_uint8_t mvididx[PJSUA_MAX_CALL_MEDIA];
2173 unsigned mvidcnt = PJ_ARRAY_SIZE(mvididx);
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00002174 unsigned mtotvidcnt = PJ_ARRAY_SIZE(mvididx);
Nanang Izzuddind8c33d82011-08-18 18:30:55 +00002175 pj_bool_t need_renego_sdp = PJ_FALSE;
2176
Benny Prijono0bc99a92011-03-17 04:34:43 +00002177 if (pjsua_get_state() != PJSUA_STATE_RUNNING)
2178 return PJ_EBUSY;
2179
Benny Prijonob90fd382011-09-18 14:59:56 +00002180 PJ_LOG(4,(THIS_FILE, "Call %d: updating media..", call_id));
2181 pj_log_push_indent();
2182
Benny Prijono0bc99a92011-03-17 04:34:43 +00002183 /* Destroy existing media session, if any. */
2184 stop_media_session(call->index);
2185
Nanang Izzuddind8c33d82011-08-18 18:30:55 +00002186 /* Call media count must be at least equal to SDP media. Note that
2187 * it may not be equal when remote removed any SDP media line.
2188 */
Nanang Izzuddin1d568722012-04-24 05:40:32 +00002189 pj_assert(call->med_prov_cnt >= local_sdp->media_count);
Nanang Izzuddind8c33d82011-08-18 18:30:55 +00002190
Benny Prijono0bc99a92011-03-17 04:34:43 +00002191 /* Reset audio_idx first */
2192 call->audio_idx = -1;
2193
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00002194 /* Sort audio/video based on "quality" */
Nanang Izzuddind8c33d82011-08-18 18:30:55 +00002195 sort_media(local_sdp, &STR_AUDIO, acc->cfg.use_srtp,
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00002196 maudidx, &maudcnt, &mtotaudcnt);
Nanang Izzuddind8c33d82011-08-18 18:30:55 +00002197#if PJMEDIA_HAS_VIDEO
2198 sort_media(local_sdp, &STR_VIDEO, acc->cfg.use_srtp,
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00002199 mvididx, &mvidcnt, &mtotvidcnt);
Nanang Izzuddind8c33d82011-08-18 18:30:55 +00002200#else
2201 PJ_UNUSED_ARG(STR_VIDEO);
Nanang Izzuddin3d7385c2011-12-01 10:02:54 +00002202 mvidcnt = mtotvidcnt = 0;
Nanang Izzuddind8c33d82011-08-18 18:30:55 +00002203#endif
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00002204
2205 /* Applying media count limitation. Note that in generating SDP answer,
2206 * no media count limitation applied, as we didn't know yet which media
2207 * would pass the SDP negotiation.
2208 */
Benny Prijonoae86e6a2011-12-27 12:47:52 +00002209 if (maudcnt > call->opt.aud_cnt || mvidcnt > call->opt.vid_cnt)
Nanang Izzuddind8c33d82011-08-18 18:30:55 +00002210 {
2211 pjmedia_sdp_session *local_sdp2;
2212
Benny Prijonoae86e6a2011-12-27 12:47:52 +00002213 maudcnt = PJ_MIN(maudcnt, call->opt.aud_cnt);
2214 mvidcnt = PJ_MIN(mvidcnt, call->opt.vid_cnt);
Nanang Izzuddind8c33d82011-08-18 18:30:55 +00002215 local_sdp2 = pjmedia_sdp_session_clone(tmp_pool, local_sdp);
2216
2217 for (mi=0; mi < local_sdp2->media_count; ++mi) {
2218 pjmedia_sdp_media *m = local_sdp2->media[mi];
2219
2220 if (m->desc.port == 0 ||
2221 pj_memchr(maudidx, mi, maudcnt*sizeof(maudidx[0])) ||
2222 pj_memchr(mvididx, mi, mvidcnt*sizeof(mvididx[0])))
2223 {
2224 continue;
2225 }
2226
2227 /* Deactivate this media */
2228 pjmedia_sdp_media_deactivate(tmp_pool, m);
2229 }
2230
2231 local_sdp = local_sdp2;
2232 need_renego_sdp = PJ_TRUE;
2233 }
2234
Benny Prijono0bc99a92011-03-17 04:34:43 +00002235 /* Process each media stream */
Nanang Izzuddin1d568722012-04-24 05:40:32 +00002236 for (mi=0; mi < call->med_prov_cnt; ++mi) {
2237 pjsua_call_media *call_med = &call->media_prov[mi];
Benny Prijono0bc99a92011-03-17 04:34:43 +00002238
Nanang Izzuddinb6c239c2011-05-10 05:42:28 +00002239 if (mi >= local_sdp->media_count ||
2240 mi >= remote_sdp->media_count)
Benny Prijono0bc99a92011-03-17 04:34:43 +00002241 {
Nanang Izzuddinb6c239c2011-05-10 05:42:28 +00002242 /* This may happen when remote removed any SDP media lines in
2243 * its re-offer.
2244 */
Nanang Izzuddina485e972012-05-21 12:39:39 +00002245 if (call_med->tp) {
2246 /* Close the media transport */
2247 pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_NULL);
2248 pjmedia_transport_close(call_med->tp);
2249 call_med->tp = call_med->tp_orig = NULL;
2250 }
Nanang Izzuddinb6c239c2011-05-10 05:42:28 +00002251 continue;
2252#if 0
Benny Prijono0bc99a92011-03-17 04:34:43 +00002253 /* Something is wrong */
2254 PJ_LOG(1,(THIS_FILE, "Error updating media for call %d: "
2255 "invalid media index %d in SDP", call_id, mi));
Benny Prijonob90fd382011-09-18 14:59:56 +00002256 status = PJMEDIA_SDP_EINSDP;
2257 goto on_error;
Nanang Izzuddinb6c239c2011-05-10 05:42:28 +00002258#endif
Benny Prijono0bc99a92011-03-17 04:34:43 +00002259 }
2260
Benny Prijono1312e752012-03-22 09:56:52 +00002261 if (call_med->type==PJMEDIA_TYPE_AUDIO) {
2262 pjmedia_stream_info the_si, *si = &the_si;
2263
2264 status = pjmedia_stream_info_from_sdp(si, tmp_pool, pjsua_var.med_endpt,
2265 local_sdp, remote_sdp, mi);
2266 if (status != PJ_SUCCESS) {
2267 PJ_PERROR(1,(THIS_FILE, status,
2268 "pjmedia_stream_info_from_sdp() failed "
2269 "for call_id %d media %d",
2270 call_id, mi));
2271 continue;
2272 }
2273
2274 /* Check if no media is active */
2275 if (si->dir == PJMEDIA_DIR_NONE) {
2276 /* Update call media state and direction */
2277 call_med->state = PJSUA_CALL_MEDIA_NONE;
2278 call_med->dir = PJMEDIA_DIR_NONE;
2279
2280 } else {
2281 pjmedia_transport_info tp_info;
2282
2283 /* Start/restart media transport based on info in SDP */
2284 status = pjmedia_transport_media_start(call_med->tp,
2285 tmp_pool, local_sdp,
2286 remote_sdp, mi);
2287 if (status != PJ_SUCCESS) {
2288 PJ_PERROR(1,(THIS_FILE, status,
2289 "pjmedia_transport_media_start() failed "
2290 "for call_id %d media %d",
2291 call_id, mi));
2292 continue;
2293 }
2294
2295 pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_RUNNING);
2296
2297 /* Get remote SRTP usage policy */
2298 pjmedia_transport_info_init(&tp_info);
2299 pjmedia_transport_get_info(call_med->tp, &tp_info);
2300 if (tp_info.specific_info_cnt > 0) {
2301 unsigned i;
2302 for (i = 0; i < tp_info.specific_info_cnt; ++i) {
2303 if (tp_info.spc_info[i].type == PJMEDIA_TRANSPORT_TYPE_SRTP)
2304 {
2305 pjmedia_srtp_info *srtp_info =
2306 (pjmedia_srtp_info*) tp_info.spc_info[i].buffer;
2307
2308 call_med->rem_srtp_use = srtp_info->peer_use;
2309 break;
2310 }
2311 }
2312 }
2313
2314 /* Call media direction */
2315 call_med->dir = si->dir;
2316
2317 /* Call media state */
2318 if (call->local_hold)
2319 call_med->state = PJSUA_CALL_MEDIA_LOCAL_HOLD;
2320 else if (call_med->dir == PJMEDIA_DIR_DECODING)
2321 call_med->state = PJSUA_CALL_MEDIA_REMOTE_HOLD;
2322 else
2323 call_med->state = PJSUA_CALL_MEDIA_ACTIVE;
2324 }
2325
2326 /* Call implementation */
2327 status = pjsua_aud_channel_update(call_med, tmp_pool, si,
2328 local_sdp, remote_sdp);
2329 if (status != PJ_SUCCESS) {
2330 PJ_PERROR(1,(THIS_FILE, status,
2331 "pjsua_aud_channel_update() failed "
2332 "for call_id %d media %d",
2333 call_id, mi));
2334 continue;
2335 }
2336
2337 /* Print info. */
2338 if (status == PJ_SUCCESS) {
2339 char info[80];
2340 int info_len = 0;
2341 int len;
2342 const char *dir;
2343
2344 switch (si->dir) {
2345 case PJMEDIA_DIR_NONE:
2346 dir = "inactive";
2347 break;
2348 case PJMEDIA_DIR_ENCODING:
2349 dir = "sendonly";
2350 break;
2351 case PJMEDIA_DIR_DECODING:
2352 dir = "recvonly";
2353 break;
2354 case PJMEDIA_DIR_ENCODING_DECODING:
2355 dir = "sendrecv";
2356 break;
2357 default:
2358 dir = "unknown";
2359 break;
2360 }
2361 len = pj_ansi_sprintf( info+info_len,
2362 ", stream #%d: %.*s (%s)", mi,
2363 (int)si->fmt.encoding_name.slen,
2364 si->fmt.encoding_name.ptr,
2365 dir);
2366 if (len > 0)
2367 info_len += len;
2368 PJ_LOG(4,(THIS_FILE,"Audio updated%s", info));
2369 }
2370
2371
Benny Prijono0bc99a92011-03-17 04:34:43 +00002372 if (call->audio_idx==-1 && status==PJ_SUCCESS &&
Benny Prijono1312e752012-03-22 09:56:52 +00002373 si->dir != PJMEDIA_DIR_NONE)
Benny Prijono0bc99a92011-03-17 04:34:43 +00002374 {
2375 call->audio_idx = mi;
2376 }
Benny Prijono1312e752012-03-22 09:56:52 +00002377
Nanang Izzuddin63b3c132011-07-19 11:11:07 +00002378#if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0)
Benny Prijono1312e752012-03-22 09:56:52 +00002379 } else if (call_med->type==PJMEDIA_TYPE_VIDEO) {
2380 pjmedia_vid_stream_info the_si, *si = &the_si;
2381
2382 status = pjmedia_vid_stream_info_from_sdp(si, tmp_pool, pjsua_var.med_endpt,
2383 local_sdp, remote_sdp, mi);
2384 if (status != PJ_SUCCESS) {
2385 PJ_PERROR(1,(THIS_FILE, status,
2386 "pjmedia_vid_stream_info_from_sdp() failed "
2387 "for call_id %d media %d",
2388 call_id, mi));
2389 continue;
2390 }
2391
2392 /* Check if no media is active */
2393 if (si->dir == PJMEDIA_DIR_NONE) {
Nanang Izzuddin1d568722012-04-24 05:40:32 +00002394 /* Update call media state and direction */
Benny Prijono1312e752012-03-22 09:56:52 +00002395 call_med->state = PJSUA_CALL_MEDIA_NONE;
Benny Prijono1312e752012-03-22 09:56:52 +00002396 call_med->dir = PJMEDIA_DIR_NONE;
2397
2398 } else {
2399 pjmedia_transport_info tp_info;
2400
2401 /* Start/restart media transport */
2402 status = pjmedia_transport_media_start(call_med->tp,
2403 tmp_pool, local_sdp,
2404 remote_sdp, mi);
2405 if (status != PJ_SUCCESS) {
2406 PJ_PERROR(1,(THIS_FILE, status,
2407 "pjmedia_transport_media_start() failed "
2408 "for call_id %d media %d",
2409 call_id, mi));
2410 continue;
2411 }
2412
2413 pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_RUNNING);
2414
2415 /* Get remote SRTP usage policy */
2416 pjmedia_transport_info_init(&tp_info);
2417 pjmedia_transport_get_info(call_med->tp, &tp_info);
2418 if (tp_info.specific_info_cnt > 0) {
2419 unsigned i;
2420 for (i = 0; i < tp_info.specific_info_cnt; ++i) {
2421 if (tp_info.spc_info[i].type ==
2422 PJMEDIA_TRANSPORT_TYPE_SRTP)
2423 {
2424 pjmedia_srtp_info *sri;
2425 sri=(pjmedia_srtp_info*)tp_info.spc_info[i].buffer;
2426 call_med->rem_srtp_use = sri->peer_use;
2427 break;
2428 }
2429 }
2430 }
2431
2432 /* Call media direction */
2433 call_med->dir = si->dir;
2434
2435 /* Call media state */
2436 if (call->local_hold)
2437 call_med->state = PJSUA_CALL_MEDIA_LOCAL_HOLD;
2438 else if (call_med->dir == PJMEDIA_DIR_DECODING)
2439 call_med->state = PJSUA_CALL_MEDIA_REMOTE_HOLD;
2440 else
2441 call_med->state = PJSUA_CALL_MEDIA_ACTIVE;
2442 }
2443
2444 status = pjsua_vid_channel_update(call_med, tmp_pool, si,
2445 local_sdp, remote_sdp);
2446 if (status != PJ_SUCCESS) {
2447 PJ_PERROR(1,(THIS_FILE, status,
Nanang Izzuddin1dc6d212012-04-03 08:02:12 +00002448 "pjsua_vid_channel_update() failed "
Benny Prijono1312e752012-03-22 09:56:52 +00002449 "for call_id %d media %d",
2450 call_id, mi));
2451 continue;
2452 }
2453
2454 /* Print info. */
2455 {
2456 char info[80];
2457 int info_len = 0;
2458 int len;
2459 const char *dir;
2460
2461 switch (si->dir) {
2462 case PJMEDIA_DIR_NONE:
2463 dir = "inactive";
2464 break;
2465 case PJMEDIA_DIR_ENCODING:
2466 dir = "sendonly";
2467 break;
2468 case PJMEDIA_DIR_DECODING:
2469 dir = "recvonly";
2470 break;
2471 case PJMEDIA_DIR_ENCODING_DECODING:
2472 dir = "sendrecv";
2473 break;
2474 default:
2475 dir = "unknown";
2476 break;
2477 }
2478 len = pj_ansi_sprintf( info+info_len,
2479 ", stream #%d: %.*s (%s)", mi,
2480 (int)si->codec_info.encoding_name.slen,
2481 si->codec_info.encoding_name.ptr,
2482 dir);
2483 if (len > 0)
2484 info_len += len;
2485 PJ_LOG(4,(THIS_FILE,"Video updated%s", info));
2486 }
2487
Nanang Izzuddin50fae732011-03-22 09:49:23 +00002488#endif
Benny Prijono1312e752012-03-22 09:56:52 +00002489 } else {
Nanang Izzuddinb6c239c2011-05-10 05:42:28 +00002490 status = PJMEDIA_EINVALIMEDIATYPE;
Benny Prijono0bc99a92011-03-17 04:34:43 +00002491 }
2492
Nanang Izzuddind8c33d82011-08-18 18:30:55 +00002493 /* Close the transport of deactivated media, need this here as media
2494 * can be deactivated by the SDP negotiation and the max media count
2495 * (account) setting.
2496 */
2497 if (local_sdp->media[mi]->desc.port==0 && call_med->tp) {
Nanang Izzuddin1d568722012-04-24 05:40:32 +00002498 pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_NULL);
Nanang Izzuddind8c33d82011-08-18 18:30:55 +00002499 pjmedia_transport_close(call_med->tp);
2500 call_med->tp = call_med->tp_orig = NULL;
Nanang Izzuddind8c33d82011-08-18 18:30:55 +00002501 }
2502
Benny Prijono0bc99a92011-03-17 04:34:43 +00002503 if (status != PJ_SUCCESS) {
Nanang Izzuddinb6c239c2011-05-10 05:42:28 +00002504 PJ_PERROR(1,(THIS_FILE, status, "Error updating media call%02d:%d",
Benny Prijono0bc99a92011-03-17 04:34:43 +00002505 call_id, mi));
Nanang Izzuddinb6c239c2011-05-10 05:42:28 +00002506 } else {
2507 got_media = PJ_TRUE;
Benny Prijono0bc99a92011-03-17 04:34:43 +00002508 }
2509 }
2510
Nanang Izzuddin1d568722012-04-24 05:40:32 +00002511 /* Update call media from provisional media */
2512 call->med_cnt = call->med_prov_cnt;
2513 pj_memcpy(call->media, call->media_prov,
2514 sizeof(call->media_prov[0]) * call->med_prov_cnt);
2515
Nanang Izzuddind8c33d82011-08-18 18:30:55 +00002516 /* Perform SDP re-negotiation if needed. */
2517 if (got_media && need_renego_sdp) {
2518 pjmedia_sdp_neg *neg = call->inv->neg;
2519
2520 /* This should only happen when we are the answerer. */
2521 PJ_ASSERT_RETURN(neg && !pjmedia_sdp_neg_was_answer_remote(neg),
2522 PJMEDIA_SDPNEG_EINSTATE);
2523
2524 status = pjmedia_sdp_neg_set_remote_offer(tmp_pool, neg, remote_sdp);
2525 if (status != PJ_SUCCESS)
Benny Prijonob90fd382011-09-18 14:59:56 +00002526 goto on_error;
Nanang Izzuddind8c33d82011-08-18 18:30:55 +00002527
2528 status = pjmedia_sdp_neg_set_local_answer(tmp_pool, neg, local_sdp);
2529 if (status != PJ_SUCCESS)
Benny Prijonob90fd382011-09-18 14:59:56 +00002530 goto on_error;
Nanang Izzuddind8c33d82011-08-18 18:30:55 +00002531
2532 status = pjmedia_sdp_neg_negotiate(tmp_pool, neg, 0);
2533 if (status != PJ_SUCCESS)
Benny Prijonob90fd382011-09-18 14:59:56 +00002534 goto on_error;
Nanang Izzuddind8c33d82011-08-18 18:30:55 +00002535 }
2536
Benny Prijonob90fd382011-09-18 14:59:56 +00002537 pj_log_pop_indent();
Nanang Izzuddinb6c239c2011-05-10 05:42:28 +00002538 return (got_media? PJ_SUCCESS : PJMEDIA_SDPNEG_ENOMEDIA);
Benny Prijonob90fd382011-09-18 14:59:56 +00002539
2540on_error:
2541 pj_log_pop_indent();
2542 return status;
Benny Prijono0bc99a92011-03-17 04:34:43 +00002543}
2544
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002545/*****************************************************************************
2546 * Codecs.
2547 */
2548
2549/*
2550 * Enum all supported codecs in the system.
2551 */
2552PJ_DEF(pj_status_t) pjsua_enum_codecs( pjsua_codec_info id[],
2553 unsigned *p_count )
2554{
2555 pjmedia_codec_mgr *codec_mgr;
2556 pjmedia_codec_info info[32];
2557 unsigned i, count, prio[32];
2558 pj_status_t status;
2559
2560 codec_mgr = pjmedia_endpt_get_codec_mgr(pjsua_var.med_endpt);
2561 count = PJ_ARRAY_SIZE(info);
2562 status = pjmedia_codec_mgr_enum_codecs( codec_mgr, &count, info, prio);
2563 if (status != PJ_SUCCESS) {
2564 *p_count = 0;
2565 return status;
2566 }
2567
2568 if (count > *p_count) count = *p_count;
2569
2570 for (i=0; i<count; ++i) {
Nanang Izzuddin56b2ce42011-04-06 13:55:01 +00002571 pj_bzero(&id[i], sizeof(pjsua_codec_info));
2572
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002573 pjmedia_codec_info_to_id(&info[i], id[i].buf_, sizeof(id[i].buf_));
2574 id[i].codec_id = pj_str(id[i].buf_);
2575 id[i].priority = (pj_uint8_t) prio[i];
2576 }
2577
2578 *p_count = count;
2579
2580 return PJ_SUCCESS;
2581}
2582
2583
2584/*
2585 * Change codec priority.
2586 */
2587PJ_DEF(pj_status_t) pjsua_codec_set_priority( const pj_str_t *codec_id,
2588 pj_uint8_t priority )
2589{
Benny Prijono88accae2008-06-26 15:48:14 +00002590 const pj_str_t all = { NULL, 0 };
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002591 pjmedia_codec_mgr *codec_mgr;
2592
2593 codec_mgr = pjmedia_endpt_get_codec_mgr(pjsua_var.med_endpt);
2594
Benny Prijono88accae2008-06-26 15:48:14 +00002595 if (codec_id->slen==1 && *codec_id->ptr=='*')
2596 codec_id = &all;
2597
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002598 return pjmedia_codec_mgr_set_codec_priority(codec_mgr, codec_id,
2599 priority);
2600}
2601
2602
2603/*
2604 * Get codec parameters.
2605 */
2606PJ_DEF(pj_status_t) pjsua_codec_get_param( const pj_str_t *codec_id,
2607 pjmedia_codec_param *param )
2608{
Benny Prijono88accae2008-06-26 15:48:14 +00002609 const pj_str_t all = { NULL, 0 };
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002610 const pjmedia_codec_info *info;
2611 pjmedia_codec_mgr *codec_mgr;
2612 unsigned count = 1;
2613 pj_status_t status;
2614
2615 codec_mgr = pjmedia_endpt_get_codec_mgr(pjsua_var.med_endpt);
2616
Benny Prijono88accae2008-06-26 15:48:14 +00002617 if (codec_id->slen==1 && *codec_id->ptr=='*')
2618 codec_id = &all;
2619
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002620 status = pjmedia_codec_mgr_find_codecs_by_id(codec_mgr, codec_id,
2621 &count, &info, NULL);
2622 if (status != PJ_SUCCESS)
2623 return status;
2624
2625 if (count != 1)
Nanang Izzuddin50fae732011-03-22 09:49:23 +00002626 return (count > 1? PJ_ETOOMANY : PJ_ENOTFOUND);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002627
2628 status = pjmedia_codec_mgr_get_default_param( codec_mgr, info, param);
2629 return status;
2630}
2631
2632
2633/*
2634 * Set codec parameters.
2635 */
Nanang Izzuddin06839e72010-01-27 11:48:31 +00002636PJ_DEF(pj_status_t) pjsua_codec_set_param( const pj_str_t *codec_id,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002637 const pjmedia_codec_param *param)
2638{
Nanang Izzuddin06839e72010-01-27 11:48:31 +00002639 const pjmedia_codec_info *info[2];
2640 pjmedia_codec_mgr *codec_mgr;
2641 unsigned count = 2;
2642 pj_status_t status;
2643
2644 codec_mgr = pjmedia_endpt_get_codec_mgr(pjsua_var.med_endpt);
2645
2646 status = pjmedia_codec_mgr_find_codecs_by_id(codec_mgr, codec_id,
2647 &count, info, NULL);
2648 if (status != PJ_SUCCESS)
2649 return status;
2650
2651 /* Codec ID should be specific, except for G.722.1 */
2652 if (count > 1 &&
2653 pj_strnicmp2(codec_id, "G7221/16", 8) != 0 &&
2654 pj_strnicmp2(codec_id, "G7221/32", 8) != 0)
2655 {
2656 pj_assert(!"Codec ID is not specific");
2657 return PJ_ETOOMANY;
2658 }
2659
2660 status = pjmedia_codec_mgr_set_default_param(codec_mgr, info[0], param);
2661 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002662}
Nanang Izzuddin50fae732011-03-22 09:49:23 +00002663
2664
Nanang Izzuddin3da4ad82011-12-07 10:43:28 +00002665pj_status_t pjsua_media_apply_xml_control(pjsua_call_id call_id,
2666 const pj_str_t *xml_st)
2667{
Sauw Ming2a044602012-04-26 03:39:24 +00002668#if PJMEDIA_HAS_VIDEO
Nanang Izzuddin3da4ad82011-12-07 10:43:28 +00002669 pjsua_call *call = &pjsua_var.calls[call_id];
2670 const pj_str_t PICT_FAST_UPDATE = {"picture_fast_update", 19};
2671
Nanang Izzuddin3da4ad82011-12-07 10:43:28 +00002672 if (pj_strstr(xml_st, &PICT_FAST_UPDATE)) {
2673 unsigned i;
2674
2675 PJ_LOG(4,(THIS_FILE, "Received keyframe request via SIP INFO"));
2676
2677 for (i = 0; i < call->med_cnt; ++i) {
2678 pjsua_call_media *cm = &call->media[i];
2679 if (cm->type != PJMEDIA_TYPE_VIDEO || !cm->strm.v.stream)
2680 continue;
2681
2682 pjmedia_vid_stream_send_keyframe(cm->strm.v.stream);
2683 }
2684
2685 return PJ_SUCCESS;
2686 }
2687#endif
2688
2689 /* Just to avoid compiler warning of unused var */
Sauw Ming2a044602012-04-26 03:39:24 +00002690 PJ_UNUSED_ARG(call_id);
Nanang Izzuddin3da4ad82011-12-07 10:43:28 +00002691 PJ_UNUSED_ARG(xml_st);
2692
2693 return PJ_ENOTSUP;
2694}
2695