blob: 9e2c4c2385723c1b3a7f718c694c91b2b97f3422 [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;
518
519 PJSUA_LOCK();
520
521 call_med->tp_ready = call_med->tp_result;
522 if (call_med->med_create_cb)
523 (*call_med->med_create_cb)(call_med, call_med->tp_ready,
524 call_med->call->secure_level, NULL);
525
526 PJSUA_UNLOCK();
527}
528
Benny Prijono096c56c2007-09-15 08:30:16 +0000529/* This callback is called when ICE negotiation completes */
Benny Prijonof76e1392008-06-06 14:51:48 +0000530static void on_ice_complete(pjmedia_transport *tp,
531 pj_ice_strans_op op,
532 pj_status_t result)
Benny Prijono096c56c2007-09-15 08:30:16 +0000533{
Benny Prijono0bc99a92011-03-17 04:34:43 +0000534 pjsua_call_media *call_med = (pjsua_call_media*)tp->user_data;
Benny Prijono096c56c2007-09-15 08:30:16 +0000535
Benny Prijono0bc99a92011-03-17 04:34:43 +0000536 if (!call_med)
Benny Prijonof76e1392008-06-06 14:51:48 +0000537 return;
538
539 switch (op) {
540 case PJ_ICE_STRANS_OP_INIT:
Sauw Ming2a899232012-01-09 11:51:56 +0000541 call_med->tp_result = result;
542 pjsua_schedule_timer2(&med_tp_timer_cb, call_med, 1);
Benny Prijonof76e1392008-06-06 14:51:48 +0000543 break;
544 case PJ_ICE_STRANS_OP_NEGOTIATION:
545 if (result != PJ_SUCCESS) {
Benny Prijono0bc99a92011-03-17 04:34:43 +0000546 call_med->state = PJSUA_CALL_MEDIA_ERROR;
547 call_med->dir = PJMEDIA_DIR_NONE;
Benny Prijonof76e1392008-06-06 14:51:48 +0000548
Benny Prijono0bc99a92011-03-17 04:34:43 +0000549 if (call_med->call && pjsua_var.ua_cfg.cb.on_call_media_state) {
550 pjsua_var.ua_cfg.cb.on_call_media_state(call_med->call->index);
Benny Prijonof76e1392008-06-06 14:51:48 +0000551 }
Benny Prijono0bc99a92011-03-17 04:34:43 +0000552 } else if (call_med->call) {
Benny Prijonof5d9f1f2009-10-14 13:13:18 +0000553 /* Send UPDATE if default transport address is different than
554 * what was advertised (ticket #881)
555 */
556 pjmedia_transport_info tpinfo;
557 pjmedia_ice_transport_info *ii = NULL;
558 unsigned i;
559
560 pjmedia_transport_info_init(&tpinfo);
561 pjmedia_transport_get_info(tp, &tpinfo);
562 for (i=0; i<tpinfo.specific_info_cnt; ++i) {
563 if (tpinfo.spc_info[i].type==PJMEDIA_TRANSPORT_TYPE_ICE) {
564 ii = (pjmedia_ice_transport_info*)
565 tpinfo.spc_info[i].buffer;
566 break;
567 }
568 }
569
570 if (ii && ii->role==PJ_ICE_SESS_ROLE_CONTROLLING &&
571 pj_sockaddr_cmp(&tpinfo.sock_info.rtp_addr_name,
Benny Prijono0bc99a92011-03-17 04:34:43 +0000572 &call_med->rtp_addr))
Benny Prijonof5d9f1f2009-10-14 13:13:18 +0000573 {
Benny Prijonoa8f9e622010-06-21 13:28:55 +0000574 pj_bool_t use_update;
575 const pj_str_t STR_UPDATE = { "UPDATE", 6 };
576 pjsip_dialog_cap_status support_update;
577 pjsip_dialog *dlg;
578
Benny Prijono0bc99a92011-03-17 04:34:43 +0000579 dlg = call_med->call->inv->dlg;
Benny Prijonoa8f9e622010-06-21 13:28:55 +0000580 support_update = pjsip_dlg_remote_has_cap(dlg, PJSIP_H_ALLOW,
581 NULL, &STR_UPDATE);
582 use_update = (support_update == PJSIP_DIALOG_CAP_SUPPORTED);
583
Benny Prijonof5d9f1f2009-10-14 13:13:18 +0000584 PJ_LOG(4,(THIS_FILE,
585 "ICE default transport address has changed for "
Benny Prijonodb5d89d2011-10-25 13:39:06 +0000586 "call %d, sending %s",
587 call_med->call->index,
Benny Prijonoa8f9e622010-06-21 13:28:55 +0000588 (use_update ? "UPDATE" : "re-INVITE")));
589
590 if (use_update)
Benny Prijono0bc99a92011-03-17 04:34:43 +0000591 pjsua_call_update(call_med->call->index, 0, NULL);
Benny Prijonoa8f9e622010-06-21 13:28:55 +0000592 else
Benny Prijono0bc99a92011-03-17 04:34:43 +0000593 pjsua_call_reinvite(call_med->call->index, 0, NULL);
Benny Prijonof5d9f1f2009-10-14 13:13:18 +0000594 }
Benny Prijonof76e1392008-06-06 14:51:48 +0000595 }
596 break;
Benny Prijono4d6ff4d2010-06-19 12:35:33 +0000597 case PJ_ICE_STRANS_OP_KEEP_ALIVE:
598 if (result != PJ_SUCCESS) {
599 PJ_PERROR(4,(THIS_FILE, result,
Benny Prijono0bc99a92011-03-17 04:34:43 +0000600 "ICE keep alive failure for transport %d:%d",
601 call_med->call->index, call_med->idx));
Benny Prijono4d6ff4d2010-06-19 12:35:33 +0000602 }
Sauw Ming73ecfe82011-09-21 10:20:01 +0000603 if (pjsua_var.ua_cfg.cb.on_call_media_transport_state) {
604 pjsua_med_tp_state_info info;
605
606 pj_bzero(&info, sizeof(info));
607 info.med_idx = call_med->idx;
608 info.state = call_med->tp_st;
609 info.status = result;
610 info.ext_info = &op;
611 (*pjsua_var.ua_cfg.cb.on_call_media_transport_state)(
612 call_med->call->index, &info);
613 }
Benny Prijono4d6ff4d2010-06-19 12:35:33 +0000614 if (pjsua_var.ua_cfg.cb.on_ice_transport_error) {
Benny Prijono0bc99a92011-03-17 04:34:43 +0000615 pjsua_call_id id = call_med->call->index;
Benny Prijono4d6ff4d2010-06-19 12:35:33 +0000616 (*pjsua_var.ua_cfg.cb.on_ice_transport_error)(id, op, result,
617 NULL);
618 }
619 break;
Benny Prijono096c56c2007-09-15 08:30:16 +0000620 }
621}
622
623
Benny Prijonof76e1392008-06-06 14:51:48 +0000624/* Parse "HOST:PORT" format */
625static pj_status_t parse_host_port(const pj_str_t *host_port,
626 pj_str_t *host, pj_uint16_t *port)
Benny Prijonoc97608e2007-03-23 16:34:20 +0000627{
Benny Prijonof76e1392008-06-06 14:51:48 +0000628 pj_str_t str_port;
629
630 str_port.ptr = pj_strchr(host_port, ':');
631 if (str_port.ptr != NULL) {
632 int iport;
633
634 host->ptr = host_port->ptr;
635 host->slen = (str_port.ptr - host->ptr);
636 str_port.ptr++;
637 str_port.slen = host_port->slen - host->slen - 1;
638 iport = (int)pj_strtoul(&str_port);
639 if (iport < 1 || iport > 65535)
640 return PJ_EINVAL;
641 *port = (pj_uint16_t)iport;
642 } else {
643 *host = *host_port;
644 *port = 0;
645 }
646
647 return PJ_SUCCESS;
648}
649
650/* Create ICE media transports (when ice is enabled) */
Benny Prijono0bc99a92011-03-17 04:34:43 +0000651static pj_status_t create_ice_media_transport(
652 const pjsua_transport_config *cfg,
Sauw Ming73ecfe82011-09-21 10:20:01 +0000653 pjsua_call_media *call_med,
654 pj_bool_t async)
Benny Prijonof76e1392008-06-06 14:51:48 +0000655{
656 char stunip[PJ_INET6_ADDRSTRLEN];
657 pj_ice_strans_cfg ice_cfg;
Benny Prijono0bc99a92011-03-17 04:34:43 +0000658 pjmedia_ice_cb ice_cb;
659 char name[32];
660 unsigned comp_cnt;
Benny Prijonoc97608e2007-03-23 16:34:20 +0000661 pj_status_t status;
662
Benny Prijonoda9785b2007-04-02 20:43:06 +0000663 /* Make sure STUN server resolution has completed */
Benny Prijonobb995fd2009-08-12 11:03:23 +0000664 status = resolve_stun_server(PJ_TRUE);
Benny Prijonoda9785b2007-04-02 20:43:06 +0000665 if (status != PJ_SUCCESS) {
666 pjsua_perror(THIS_FILE, "Error resolving STUN server", status);
667 return status;
668 }
669
Benny Prijonof76e1392008-06-06 14:51:48 +0000670 /* Create ICE stream transport configuration */
671 pj_ice_strans_cfg_default(&ice_cfg);
672 pj_stun_config_init(&ice_cfg.stun_cfg, &pjsua_var.cp.factory, 0,
673 pjsip_endpt_get_ioqueue(pjsua_var.endpt),
674 pjsip_endpt_get_timer_heap(pjsua_var.endpt));
675
676 ice_cfg.af = pj_AF_INET();
677 ice_cfg.resolver = pjsua_var.resolver;
678
Benny Prijono329d6382009-05-29 13:04:03 +0000679 ice_cfg.opt = pjsua_var.media_cfg.ice_opt;
680
Benny Prijonof76e1392008-06-06 14:51:48 +0000681 /* Configure STUN settings */
682 if (pj_sockaddr_has_addr(&pjsua_var.stun_srv)) {
683 pj_sockaddr_print(&pjsua_var.stun_srv, stunip, sizeof(stunip), 0);
684 ice_cfg.stun.server = pj_str(stunip);
685 ice_cfg.stun.port = pj_sockaddr_get_port(&pjsua_var.stun_srv);
686 }
Benny Prijono329d6382009-05-29 13:04:03 +0000687 if (pjsua_var.media_cfg.ice_max_host_cands >= 0)
688 ice_cfg.stun.max_host_cands = pjsua_var.media_cfg.ice_max_host_cands;
Benny Prijonof76e1392008-06-06 14:51:48 +0000689
Benny Prijono4d79b0f2009-10-25 09:02:07 +0000690 /* Copy QoS setting to STUN setting */
691 ice_cfg.stun.cfg.qos_type = cfg->qos_type;
692 pj_memcpy(&ice_cfg.stun.cfg.qos_params, &cfg->qos_params,
693 sizeof(cfg->qos_params));
694
Benny Prijonof76e1392008-06-06 14:51:48 +0000695 /* Configure TURN settings */
696 if (pjsua_var.media_cfg.enable_turn) {
697 status = parse_host_port(&pjsua_var.media_cfg.turn_server,
698 &ice_cfg.turn.server,
699 &ice_cfg.turn.port);
700 if (status != PJ_SUCCESS || ice_cfg.turn.server.slen == 0) {
701 PJ_LOG(1,(THIS_FILE, "Invalid TURN server setting"));
702 return PJ_EINVAL;
703 }
704 if (ice_cfg.turn.port == 0)
705 ice_cfg.turn.port = 3479;
706 ice_cfg.turn.conn_type = pjsua_var.media_cfg.turn_conn_type;
707 pj_memcpy(&ice_cfg.turn.auth_cred,
708 &pjsua_var.media_cfg.turn_auth_cred,
709 sizeof(ice_cfg.turn.auth_cred));
Benny Prijono4d79b0f2009-10-25 09:02:07 +0000710
711 /* Copy QoS setting to TURN setting */
712 ice_cfg.turn.cfg.qos_type = cfg->qos_type;
713 pj_memcpy(&ice_cfg.turn.cfg.qos_params, &cfg->qos_params,
714 sizeof(cfg->qos_params));
Benny Prijonof76e1392008-06-06 14:51:48 +0000715 }
Benny Prijonob681a2f2007-03-25 18:44:51 +0000716
Benny Prijono0bc99a92011-03-17 04:34:43 +0000717 pj_bzero(&ice_cb, sizeof(pjmedia_ice_cb));
718 ice_cb.on_ice_complete = &on_ice_complete;
719 pj_ansi_snprintf(name, sizeof(name), "icetp%02d", call_med->idx);
720 call_med->tp_ready = PJ_EPENDING;
Benny Prijonoc97608e2007-03-23 16:34:20 +0000721
Benny Prijono0bc99a92011-03-17 04:34:43 +0000722 comp_cnt = 1;
723 if (PJMEDIA_ADVERTISE_RTCP && !pjsua_var.media_cfg.ice_no_rtcp)
724 ++comp_cnt;
Benny Prijonof76e1392008-06-06 14:51:48 +0000725
Benny Prijonobd6613f2011-04-11 17:27:14 +0000726 status = pjmedia_ice_create3(pjsua_var.med_endpt, name, comp_cnt,
727 &ice_cfg, &ice_cb, 0, call_med,
728 &call_med->tp);
Benny Prijono0bc99a92011-03-17 04:34:43 +0000729 if (status != PJ_SUCCESS) {
730 pjsua_perror(THIS_FILE, "Unable to create ICE media transport",
731 status);
732 goto on_error;
Benny Prijonoc97608e2007-03-23 16:34:20 +0000733 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000734
Benny Prijono0bc99a92011-03-17 04:34:43 +0000735 /* Wait until transport is initialized, or time out */
Sauw Ming73ecfe82011-09-21 10:20:01 +0000736 if (!async) {
Nanang Izzuddin1c565422011-12-28 09:52:07 +0000737 pj_bool_t has_pjsua_lock = PJSUA_LOCK_IS_LOCKED();
738 if (has_pjsua_lock)
739 PJSUA_UNLOCK();
Sauw Ming73ecfe82011-09-21 10:20:01 +0000740 while (call_med->tp_ready == PJ_EPENDING) {
741 pjsua_handle_events(100);
742 }
Nanang Izzuddin1c565422011-12-28 09:52:07 +0000743 if (has_pjsua_lock)
744 PJSUA_LOCK();
Benny Prijono0bc99a92011-03-17 04:34:43 +0000745 }
Sauw Ming73ecfe82011-09-21 10:20:01 +0000746
747 if (async && call_med->tp_ready == PJ_EPENDING) {
748 return PJ_EPENDING;
749 } else if (call_med->tp_ready != PJ_SUCCESS) {
Benny Prijono0bc99a92011-03-17 04:34:43 +0000750 pjsua_perror(THIS_FILE, "Error initializing ICE media transport",
751 call_med->tp_ready);
752 status = call_med->tp_ready;
753 goto on_error;
754 }
755
756 pjmedia_transport_simulate_lost(call_med->tp, PJMEDIA_DIR_ENCODING,
757 pjsua_var.media_cfg.tx_drop_pct);
758
759 pjmedia_transport_simulate_lost(call_med->tp, PJMEDIA_DIR_DECODING,
760 pjsua_var.media_cfg.rx_drop_pct);
761
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000762 return PJ_SUCCESS;
763
764on_error:
Benny Prijono0bc99a92011-03-17 04:34:43 +0000765 if (call_med->tp != NULL) {
766 pjmedia_transport_close(call_med->tp);
767 call_med->tp = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000768 }
769
Benny Prijonoc97608e2007-03-23 16:34:20 +0000770 return status;
771}
772
Benny Prijono0bc99a92011-03-17 04:34:43 +0000773#if DISABLED_FOR_TICKET_1185
774/* Create ICE media transports (when ice is enabled) */
775static pj_status_t create_ice_media_transports(pjsua_transport_config *cfg)
776{
777 unsigned i;
778 pj_status_t status;
Benny Prijonoc97608e2007-03-23 16:34:20 +0000779
Benny Prijono0bc99a92011-03-17 04:34:43 +0000780 for (i=0; i < pjsua_var.ua_cfg.max_calls; ++i) {
781 pjsua_call *call = &pjsua_var.calls[i];
782 unsigned strm_idx;
783
784 for (strm_idx=0; strm_idx < call->med_cnt; ++strm_idx) {
785 pjsua_call_media *call_med = &call->media[strm_idx];
786
787 status = create_ice_media_transport(cfg, call_med);
788 if (status != PJ_SUCCESS)
789 goto on_error;
790 }
791 }
792
793 return PJ_SUCCESS;
794
795on_error:
796 for (i=0; i < pjsua_var.ua_cfg.max_calls; ++i) {
797 pjsua_call *call = &pjsua_var.calls[i];
798 unsigned strm_idx;
799
800 for (strm_idx=0; strm_idx < call->med_cnt; ++strm_idx) {
801 pjsua_call_media *call_med = &call->media[strm_idx];
802
803 if (call_med->tp) {
804 pjmedia_transport_close(call_med->tp);
805 call_med->tp = NULL;
806 }
807 }
808 }
809 return status;
810}
811#endif
812
813#if DISABLED_FOR_TICKET_1185
Benny Prijonoc97608e2007-03-23 16:34:20 +0000814/*
Benny Prijono0bc99a92011-03-17 04:34:43 +0000815 * Create media transports for all the calls. This function creates
Benny Prijonoc97608e2007-03-23 16:34:20 +0000816 * one UDP media transport for each call.
817 */
Benny Prijono1f61a8f2007-08-16 10:11:44 +0000818PJ_DEF(pj_status_t) pjsua_media_transports_create(
819 const pjsua_transport_config *app_cfg)
Benny Prijonoc97608e2007-03-23 16:34:20 +0000820{
821 pjsua_transport_config cfg;
822 unsigned i;
823 pj_status_t status;
824
825
826 /* Make sure pjsua_init() has been called */
827 PJ_ASSERT_RETURN(pjsua_var.ua_cfg.max_calls>0, PJ_EINVALIDOP);
828
829 PJSUA_LOCK();
830
831 /* Delete existing media transports */
832 for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
Benny Prijono0bc99a92011-03-17 04:34:43 +0000833 pjsua_call *call = &pjsua_var.calls[i];
834 unsigned strm_idx;
835
836 for (strm_idx=0; strm_idx < call->med_cnt; ++strm_idx) {
837 pjsua_call_media *call_med = &call->media[strm_idx];
838
839 if (call_med->tp && call_med->tp_auto_del) {
840 pjmedia_transport_close(call_med->tp);
841 call_med->tp = NULL;
842 call_med->tp_orig = NULL;
843 }
Benny Prijonoc97608e2007-03-23 16:34:20 +0000844 }
845 }
846
847 /* Copy config */
848 pjsua_transport_config_dup(pjsua_var.pool, &cfg, app_cfg);
849
Benny Prijono40860c32008-09-04 13:55:33 +0000850 /* Create the transports */
Benny Prijonoc97608e2007-03-23 16:34:20 +0000851 if (pjsua_var.media_cfg.enable_ice) {
Benny Prijono4d79b0f2009-10-25 09:02:07 +0000852 status = create_ice_media_transports(&cfg);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000853 } else {
854 status = create_udp_media_transports(&cfg);
855 }
856
Benny Prijono40860c32008-09-04 13:55:33 +0000857 /* Set media transport auto_delete to True */
858 for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
Benny Prijono0bc99a92011-03-17 04:34:43 +0000859 pjsua_call *call = &pjsua_var.calls[i];
860 unsigned strm_idx;
861
862 for (strm_idx=0; strm_idx < call->med_cnt; ++strm_idx) {
863 pjsua_call_media *call_med = &call->media[strm_idx];
864
865 call_med->tp_auto_del = PJ_TRUE;
866 }
Benny Prijono40860c32008-09-04 13:55:33 +0000867 }
Benny Prijonoc97608e2007-03-23 16:34:20 +0000868
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000869 PJSUA_UNLOCK();
870
871 return status;
872}
873
Benny Prijono40860c32008-09-04 13:55:33 +0000874/*
875 * Attach application's created media transports.
876 */
877PJ_DEF(pj_status_t) pjsua_media_transports_attach(pjsua_media_transport tp[],
878 unsigned count,
879 pj_bool_t auto_delete)
880{
881 unsigned i;
882
883 PJ_ASSERT_RETURN(tp && count==pjsua_var.ua_cfg.max_calls, PJ_EINVAL);
884
885 /* Assign the media transports */
886 for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
Benny Prijono0bc99a92011-03-17 04:34:43 +0000887 pjsua_call *call = &pjsua_var.calls[i];
888 unsigned strm_idx;
889
890 for (strm_idx=0; strm_idx < call->med_cnt; ++strm_idx) {
891 pjsua_call_media *call_med = &call->media[strm_idx];
892
893 if (call_med->tp && call_med->tp_auto_del) {
894 pjmedia_transport_close(call_med->tp);
895 call_med->tp = NULL;
896 call_med->tp_orig = NULL;
897 }
Benny Prijono40860c32008-09-04 13:55:33 +0000898 }
899
Benny Prijono0bc99a92011-03-17 04:34:43 +0000900 PJ_TODO(remove_pjsua_media_transports_attach);
901
902 call->media[0].tp = tp[i].transport;
903 call->media[0].tp_auto_del = auto_delete;
Benny Prijono40860c32008-09-04 13:55:33 +0000904 }
905
906 return PJ_SUCCESS;
907}
Benny Prijono0bc99a92011-03-17 04:34:43 +0000908#endif
Benny Prijono40860c32008-09-04 13:55:33 +0000909
Benny Prijono0bc99a92011-03-17 04:34:43 +0000910/* Go through the list of media in the SDP, find acceptable media, and
911 * sort them based on the "quality" of the media, and store the indexes
912 * in the specified array. Media with the best quality will be listed
913 * first in the array. The quality factors considered currently is
914 * encryption.
915 */
916static void sort_media(const pjmedia_sdp_session *sdp,
917 const pj_str_t *type,
918 pjmedia_srtp_use use_srtp,
919 pj_uint8_t midx[],
Nanang Izzuddindebd48a2011-12-01 09:06:14 +0000920 unsigned *p_count,
921 unsigned *p_total_count)
Nanang Izzuddin1e952a82010-10-05 16:32:04 +0000922{
923 unsigned i;
Benny Prijono0bc99a92011-03-17 04:34:43 +0000924 unsigned count = 0;
925 int score[PJSUA_MAX_CALL_MEDIA];
Nanang Izzuddin1e952a82010-10-05 16:32:04 +0000926
Benny Prijono0bc99a92011-03-17 04:34:43 +0000927 pj_assert(*p_count >= PJSUA_MAX_CALL_MEDIA);
Nanang Izzuddindebd48a2011-12-01 09:06:14 +0000928 pj_assert(*p_total_count >= PJSUA_MAX_CALL_MEDIA);
Benny Prijono0bc99a92011-03-17 04:34:43 +0000929
930 *p_count = 0;
Nanang Izzuddindebd48a2011-12-01 09:06:14 +0000931 *p_total_count = 0;
Benny Prijono09c0d672011-04-11 05:03:24 +0000932 for (i=0; i<PJSUA_MAX_CALL_MEDIA; ++i)
933 score[i] = 1;
Benny Prijono0bc99a92011-03-17 04:34:43 +0000934
935 /* Score each media */
936 for (i=0; i<sdp->media_count && count<PJSUA_MAX_CALL_MEDIA; ++i) {
Nanang Izzuddin1e952a82010-10-05 16:32:04 +0000937 const pjmedia_sdp_media *m = sdp->media[i];
Nanang Izzuddina6414292011-04-08 04:26:18 +0000938 const pjmedia_sdp_conn *c;
Nanang Izzuddin1e952a82010-10-05 16:32:04 +0000939
Benny Prijono0bc99a92011-03-17 04:34:43 +0000940 /* Skip different media */
941 if (pj_stricmp(&m->desc.media, type) != 0) {
942 score[count++] = -22000;
Nanang Izzuddin1e952a82010-10-05 16:32:04 +0000943 continue;
944 }
945
Nanang Izzuddina6414292011-04-08 04:26:18 +0000946 c = m->conn? m->conn : sdp->conn;
947
Benny Prijono0bc99a92011-03-17 04:34:43 +0000948 /* Supported transports */
949 if (pj_stricmp2(&m->desc.transport, "RTP/SAVP")==0) {
950 switch (use_srtp) {
951 case PJMEDIA_SRTP_MANDATORY:
952 case PJMEDIA_SRTP_OPTIONAL:
953 ++score[i];
Nanang Izzuddin1e952a82010-10-05 16:32:04 +0000954 break;
Benny Prijono0bc99a92011-03-17 04:34:43 +0000955 case PJMEDIA_SRTP_DISABLED:
Nanang Izzuddind8c33d82011-08-18 18:30:55 +0000956 //--score[i];
957 score[i] -= 5;
Benny Prijono0bc99a92011-03-17 04:34:43 +0000958 break;
Nanang Izzuddin1e952a82010-10-05 16:32:04 +0000959 }
Benny Prijono0bc99a92011-03-17 04:34:43 +0000960 } else if (pj_stricmp2(&m->desc.transport, "RTP/AVP")==0) {
961 switch (use_srtp) {
962 case PJMEDIA_SRTP_MANDATORY:
Nanang Izzuddind8c33d82011-08-18 18:30:55 +0000963 //--score[i];
964 score[i] -= 5;
Benny Prijono0bc99a92011-03-17 04:34:43 +0000965 break;
966 case PJMEDIA_SRTP_OPTIONAL:
967 /* No change in score */
968 break;
969 case PJMEDIA_SRTP_DISABLED:
970 ++score[i];
971 break;
972 }
973 } else {
974 score[i] -= 10;
975 }
976
977 /* Is media disabled? */
978 if (m->desc.port == 0)
979 score[i] -= 10;
980
981 /* Is media inactive? */
Nanang Izzuddina6414292011-04-08 04:26:18 +0000982 if (pjmedia_sdp_media_find_attr2(m, "inactive", NULL) ||
983 pj_strcmp2(&c->addr, "0.0.0.0") == 0)
984 {
985 //score[i] -= 10;
986 score[i] -= 1;
987 }
Benny Prijono0bc99a92011-03-17 04:34:43 +0000988
989 ++count;
990 }
991
992 /* Created sorted list based on quality */
993 for (i=0; i<count; ++i) {
994 unsigned j;
995 int best = 0;
996
997 for (j=1; j<count; ++j) {
998 if (score[j] > score[best])
999 best = j;
1000 }
1001 /* Don't put media with negative score, that media is unacceptable
1002 * for us.
1003 */
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001004 midx[i] = (pj_uint8_t)best;
1005 if (score[best] >= 0)
Benny Prijono0bc99a92011-03-17 04:34:43 +00001006 (*p_count)++;
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001007 if (score[best] > -22000)
1008 (*p_total_count)++;
Benny Prijono0bc99a92011-03-17 04:34:43 +00001009
1010 score[best] = -22000;
1011
1012 }
1013}
1014
Benny Prijonoee0ba182011-07-15 06:18:29 +00001015/* Callback to receive media events */
Sauw Ming0fabe1b2011-12-01 10:49:07 +00001016pj_status_t call_media_on_event(pjmedia_event *event,
1017 void *user_data)
Benny Prijonoee0ba182011-07-15 06:18:29 +00001018{
Sauw Ming0fabe1b2011-12-01 10:49:07 +00001019 pjsua_call_media *call_med = (pjsua_call_media*)user_data;
Benny Prijonoee0ba182011-07-15 06:18:29 +00001020 pjsua_call *call = call_med->call;
Nanang Izzuddin3da4ad82011-12-07 10:43:28 +00001021 pj_status_t status = PJ_SUCCESS;
1022
1023 switch(event->type) {
1024 case PJMEDIA_EVENT_KEYFRAME_MISSING:
1025 if (call->opt.req_keyframe_method & PJSUA_VID_REQ_KEYFRAME_SIP_INFO)
1026 {
1027 pj_timestamp now;
1028
1029 pj_get_timestamp(&now);
1030 if (pj_elapsed_msec(&call_med->last_req_keyframe, &now) >=
1031 PJSUA_VID_REQ_KEYFRAME_INTERVAL)
1032 {
1033 pjsua_msg_data msg_data;
1034 const pj_str_t SIP_INFO = {"INFO", 4};
1035 const char *BODY_TYPE = "application/media_control+xml";
1036 const char *BODY =
1037 "<?xml version=\"1.0\" encoding=\"utf-8\" ?>"
1038 "<media_control><vc_primitive><to_encoder>"
1039 "<picture_fast_update/>"
1040 "</to_encoder></vc_primitive></media_control>";
1041
1042 PJ_LOG(4,(THIS_FILE,
1043 "Sending video keyframe request via SIP INFO"));
1044
1045 pjsua_msg_data_init(&msg_data);
1046 pj_cstr(&msg_data.content_type, BODY_TYPE);
1047 pj_cstr(&msg_data.msg_body, BODY);
1048 status = pjsua_call_send_request(call->index, &SIP_INFO,
1049 &msg_data);
1050 if (status != PJ_SUCCESS) {
1051 pj_perror(3, THIS_FILE, status,
1052 "Failed requesting keyframe via SIP INFO");
1053 } else {
1054 call_med->last_req_keyframe = now;
1055 }
1056 }
1057 }
1058 break;
1059
1060 default:
1061 break;
1062 }
Benny Prijonoee0ba182011-07-15 06:18:29 +00001063
1064 if (pjsua_var.ua_cfg.cb.on_call_media_event && call) {
Benny Prijonoee0ba182011-07-15 06:18:29 +00001065 (*pjsua_var.ua_cfg.cb.on_call_media_event)(call->index,
1066 call_med->idx, event);
Benny Prijono53a7c702008-04-14 02:57:29 +00001067 }
1068
Nanang Izzuddin3da4ad82011-12-07 10:43:28 +00001069 return status;
Benny Prijono53a7c702008-04-14 02:57:29 +00001070}
1071
Sauw Ming73ecfe82011-09-21 10:20:01 +00001072/* Set media transport state and notify the application via the callback. */
Benny Prijono1312e752012-03-22 09:56:52 +00001073void pjsua_set_media_tp_state(pjsua_call_media *call_med,
1074 pjsua_med_tp_st tp_st)
Sauw Ming73ecfe82011-09-21 10:20:01 +00001075{
1076 if (pjsua_var.ua_cfg.cb.on_call_media_transport_state &&
1077 call_med->tp_st != tp_st)
1078 {
1079 pjsua_med_tp_state_info info;
1080
1081 pj_bzero(&info, sizeof(info));
1082 info.med_idx = call_med->idx;
1083 info.state = tp_st;
1084 info.status = call_med->tp_ready;
1085 (*pjsua_var.ua_cfg.cb.on_call_media_transport_state)(
1086 call_med->call->index, &info);
1087 }
1088
1089 call_med->tp_st = tp_st;
1090}
1091
1092/* Callback to resume pjsua_call_media_init() after media transport
1093 * creation is completed.
1094 */
1095static pj_status_t call_media_init_cb(pjsua_call_media *call_med,
1096 pj_status_t status,
1097 int security_level,
1098 int *sip_err_code)
Nanang Izzuddin1e952a82010-10-05 16:32:04 +00001099{
Benny Prijono0bc99a92011-03-17 04:34:43 +00001100 pjsua_acc *acc = &pjsua_var.acc[call_med->call->acc_id];
Benny Prijonodb5d89d2011-10-25 13:39:06 +00001101 pjmedia_transport_info tpinfo;
Sauw Ming73ecfe82011-09-21 10:20:01 +00001102 int err_code = 0;
Nanang Izzuddin1e952a82010-10-05 16:32:04 +00001103
Sauw Ming73ecfe82011-09-21 10:20:01 +00001104 if (status != PJ_SUCCESS)
Benny Prijono0ec6cfb2011-12-26 09:00:42 +00001105 goto on_return;
Nanang Izzuddin1e952a82010-10-05 16:32:04 +00001106
Sauw Ming73ecfe82011-09-21 10:20:01 +00001107 if (call_med->tp_st == PJSUA_MED_TP_CREATING)
Benny Prijono1312e752012-03-22 09:56:52 +00001108 pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_IDLE);
Nanang Izzuddin1e952a82010-10-05 16:32:04 +00001109
Sauw Minge7dbbc82011-10-24 09:28:13 +00001110 if (!call_med->tp_orig &&
1111 pjsua_var.ua_cfg.cb.on_create_media_transport)
1112 {
1113 call_med->use_custom_med_tp = PJ_TRUE;
1114 } else
1115 call_med->use_custom_med_tp = PJ_FALSE;
1116
Benny Prijono0bc99a92011-03-17 04:34:43 +00001117#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
1118 /* This function may be called when SRTP transport already exists
1119 * (e.g: in re-invite, update), don't need to destroy/re-create.
1120 */
Sauw Minge7dbbc82011-10-24 09:28:13 +00001121 if (!call_med->tp_orig) {
Benny Prijono0bc99a92011-03-17 04:34:43 +00001122 pjmedia_srtp_setting srtp_opt;
1123 pjmedia_transport *srtp = NULL;
Nanang Izzuddin1e952a82010-10-05 16:32:04 +00001124
Benny Prijono0bc99a92011-03-17 04:34:43 +00001125 /* Check if SRTP requires secure signaling */
1126 if (acc->cfg.use_srtp != PJMEDIA_SRTP_DISABLED) {
1127 if (security_level < acc->cfg.srtp_secure_signaling) {
Sauw Ming73ecfe82011-09-21 10:20:01 +00001128 err_code = PJSIP_SC_NOT_ACCEPTABLE;
Benny Prijono0bc99a92011-03-17 04:34:43 +00001129 status = PJSIP_ESESSIONINSECURE;
Benny Prijono0ec6cfb2011-12-26 09:00:42 +00001130 goto on_return;
Benny Prijono0bc99a92011-03-17 04:34:43 +00001131 }
1132 }
1133
1134 /* Always create SRTP adapter */
1135 pjmedia_srtp_setting_default(&srtp_opt);
Nanang Izzuddinb6c239c2011-05-10 05:42:28 +00001136 srtp_opt.close_member_tp = PJ_TRUE;
Nanang Izzuddin1d568722012-04-24 05:40:32 +00001137
Sauw Minge7dbbc82011-10-24 09:28:13 +00001138 /* If media session has been ever established, let's use remote's
Benny Prijono0bc99a92011-03-17 04:34:43 +00001139 * preference in SRTP usage policy, especially when it is stricter.
1140 */
1141 if (call_med->rem_srtp_use > acc->cfg.use_srtp)
1142 srtp_opt.use = call_med->rem_srtp_use;
1143 else
1144 srtp_opt.use = acc->cfg.use_srtp;
1145
1146 status = pjmedia_transport_srtp_create(pjsua_var.med_endpt,
1147 call_med->tp,
1148 &srtp_opt, &srtp);
1149 if (status != PJ_SUCCESS) {
Sauw Ming73ecfe82011-09-21 10:20:01 +00001150 err_code = PJSIP_SC_INTERNAL_SERVER_ERROR;
Benny Prijono0ec6cfb2011-12-26 09:00:42 +00001151 goto on_return;
Benny Prijono0bc99a92011-03-17 04:34:43 +00001152 }
1153
1154 /* Set SRTP as current media transport */
1155 call_med->tp_orig = call_med->tp;
1156 call_med->tp = srtp;
1157 }
1158#else
Benny Prijono7df19342011-07-23 02:54:03 +00001159 call_med->tp_orig = call_med->tp;
Benny Prijono0bc99a92011-03-17 04:34:43 +00001160 PJ_UNUSED_ARG(security_level);
1161#endif
1162
Benny Prijonodb5d89d2011-10-25 13:39:06 +00001163
1164 pjmedia_transport_info_init(&tpinfo);
1165 pjmedia_transport_get_info(call_med->tp, &tpinfo);
1166
1167 pj_sockaddr_cp(&call_med->rtp_addr, &tpinfo.sock_info.rtp_addr_name);
1168
1169
Benny Prijono0ec6cfb2011-12-26 09:00:42 +00001170on_return:
Sauw Ming73ecfe82011-09-21 10:20:01 +00001171 if (status != PJ_SUCCESS && call_med->tp) {
Nanang Izzuddin1d568722012-04-24 05:40:32 +00001172 pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_NULL);
Benny Prijono0bc99a92011-03-17 04:34:43 +00001173 pjmedia_transport_close(call_med->tp);
1174 call_med->tp = NULL;
1175 }
Sauw Ming73ecfe82011-09-21 10:20:01 +00001176
1177 if (sip_err_code)
1178 *sip_err_code = err_code;
1179
1180 if (call_med->med_init_cb) {
1181 pjsua_med_tp_state_info info;
1182
1183 pj_bzero(&info, sizeof(info));
1184 info.status = status;
1185 info.state = call_med->tp_st;
1186 info.med_idx = call_med->idx;
1187 info.sip_err_code = err_code;
1188 (*call_med->med_init_cb)(call_med->call->index, &info);
1189 }
1190
1191 return status;
1192}
1193
1194/* Initialize the media line */
1195pj_status_t pjsua_call_media_init(pjsua_call_media *call_med,
1196 pjmedia_type type,
1197 const pjsua_transport_config *tcfg,
1198 int security_level,
1199 int *sip_err_code,
1200 pj_bool_t async,
1201 pjsua_med_tp_state_cb cb)
1202{
Sauw Ming73ecfe82011-09-21 10:20:01 +00001203 pj_status_t status = PJ_SUCCESS;
1204
1205 /*
1206 * Note: this function may be called when the media already exists
1207 * (e.g. in reinvites, updates, etc.)
1208 */
1209 call_med->type = type;
1210
Nanang Izzuddin1d568722012-04-24 05:40:32 +00001211 /* Create the media transport for initial call. Here are the possible
1212 * media transport state and the action needed:
1213 * - PJSUA_MED_TP_NULL or call_med->tp==NULL, create one.
1214 * - PJSUA_MED_TP_RUNNING, do nothing.
1215 * - PJSUA_MED_TP_DISABLED, re-init (media_create(), etc). Currently,
1216 * this won't happen as media_channel_update() will always clean up
1217 * the unused transport of a disabled media.
1218 */
Sauw Ming73ecfe82011-09-21 10:20:01 +00001219 if (call_med->tp == NULL) {
1220#if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0)
1221 /* While in initial call, set default video devices */
1222 if (type == PJMEDIA_TYPE_VIDEO) {
Benny Prijono1312e752012-03-22 09:56:52 +00001223 status = pjsua_vid_channel_init(call_med);
1224 if (status != PJ_SUCCESS)
1225 return status;
Sauw Ming73ecfe82011-09-21 10:20:01 +00001226 }
1227#endif
1228
Benny Prijono1312e752012-03-22 09:56:52 +00001229 pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_CREATING);
Sauw Ming73ecfe82011-09-21 10:20:01 +00001230
Sauw Ming73ecfe82011-09-21 10:20:01 +00001231 if (pjsua_var.media_cfg.enable_ice) {
1232 status = create_ice_media_transport(tcfg, call_med, async);
Sauw Mingc8e12942011-10-25 08:51:02 +00001233 if (async && status == PJ_EPENDING) {
Sauw Ming848742f2011-09-28 04:20:30 +00001234 /* We will resume call media initialization in the
1235 * on_ice_complete() callback.
1236 */
Sauw Mingc8e12942011-10-25 08:51:02 +00001237 call_med->med_create_cb = &call_media_init_cb;
1238 call_med->med_init_cb = cb;
1239
Sauw Ming848742f2011-09-28 04:20:30 +00001240 return PJ_EPENDING;
1241 }
Sauw Ming73ecfe82011-09-21 10:20:01 +00001242 } else {
1243 status = create_udp_media_transport(tcfg, call_med);
1244 }
1245
Sauw Ming848742f2011-09-28 04:20:30 +00001246 if (status != PJ_SUCCESS) {
Sauw Ming73ecfe82011-09-21 10:20:01 +00001247 PJ_PERROR(1,(THIS_FILE, status, "Error creating media transport"));
1248 return status;
1249 }
1250
1251 /* Media transport creation completed immediately, so
1252 * we don't need to call the callback.
1253 */
1254 call_med->med_init_cb = NULL;
1255
1256 } else if (call_med->tp_st == PJSUA_MED_TP_DISABLED) {
1257 /* Media is being reenabled. */
Nanang Izzuddin1d568722012-04-24 05:40:32 +00001258 //pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_IDLE);
1259
1260 pj_assert(!"Currently no media transport reuse");
Sauw Ming73ecfe82011-09-21 10:20:01 +00001261 }
1262
1263 return call_media_init_cb(call_med, status, security_level,
1264 sip_err_code);
1265}
1266
1267/* Callback to resume pjsua_media_channel_init() after media transport
1268 * initialization is completed.
1269 */
1270static pj_status_t media_channel_init_cb(pjsua_call_id call_id,
1271 const pjsua_med_tp_state_info *info)
1272{
1273 pjsua_call *call = &pjsua_var.calls[call_id];
1274 pj_status_t status = (info? info->status : PJ_SUCCESS);
1275 unsigned mi;
1276
1277 if (info) {
1278 pj_mutex_lock(call->med_ch_mutex);
1279
1280 /* Set the callback to NULL to indicate that the async operation
1281 * has completed.
1282 */
Nanang Izzuddin1d568722012-04-24 05:40:32 +00001283 call->media_prov[info->med_idx].med_init_cb = NULL;
Sauw Ming73ecfe82011-09-21 10:20:01 +00001284
1285 /* In case of failure, save the information to be returned
1286 * by the last media transport to finish.
1287 */
1288 if (info->status != PJ_SUCCESS)
1289 pj_memcpy(&call->med_ch_info, info, sizeof(info));
1290
1291 /* Check whether all the call's medias have finished calling their
1292 * callbacks.
1293 */
Nanang Izzuddin1d568722012-04-24 05:40:32 +00001294 for (mi=0; mi < call->med_prov_cnt; ++mi) {
1295 pjsua_call_media *call_med = &call->media_prov[mi];
Sauw Ming73ecfe82011-09-21 10:20:01 +00001296
Sauw Ming2a899232012-01-09 11:51:56 +00001297 if (call_med->med_init_cb) {
Sauw Ming73ecfe82011-09-21 10:20:01 +00001298 pj_mutex_unlock(call->med_ch_mutex);
1299 return PJ_SUCCESS;
1300 }
1301
1302 if (call_med->tp_ready != PJ_SUCCESS)
1303 status = call_med->tp_ready;
1304 }
1305
1306 /* OK, we are called by the last media transport finished. */
1307 pj_mutex_unlock(call->med_ch_mutex);
1308 }
1309
1310 if (call->med_ch_mutex) {
1311 pj_mutex_destroy(call->med_ch_mutex);
1312 call->med_ch_mutex = NULL;
1313 }
1314
1315 if (status != PJ_SUCCESS) {
Nanang Izzuddin1d568722012-04-24 05:40:32 +00001316 pjsua_media_prov_clean_up(call_id);
Benny Prijono0ec6cfb2011-12-26 09:00:42 +00001317 goto on_return;
Sauw Ming73ecfe82011-09-21 10:20:01 +00001318 }
1319
1320 /* Tell the media transport of a new offer/answer session */
Nanang Izzuddin1d568722012-04-24 05:40:32 +00001321 for (mi=0; mi < call->med_prov_cnt; ++mi) {
1322 pjsua_call_media *call_med = &call->media_prov[mi];
Sauw Ming73ecfe82011-09-21 10:20:01 +00001323
1324 /* Note: tp may be NULL if this media line is disabled */
1325 if (call_med->tp && call_med->tp_st == PJSUA_MED_TP_IDLE) {
Sauw Ming99cc8ff2011-09-22 04:24:56 +00001326 pj_pool_t *tmp_pool = call->async_call.pool_prov;
1327
1328 if (!tmp_pool) {
1329 tmp_pool = (call->inv? call->inv->pool_prov:
1330 call->async_call.dlg->pool);
1331 }
Sauw Ming73ecfe82011-09-21 10:20:01 +00001332
Sauw Minge7dbbc82011-10-24 09:28:13 +00001333 if (call_med->use_custom_med_tp) {
1334 unsigned custom_med_tp_flags = 0;
1335
1336 /* Use custom media transport returned by the application */
1337 call_med->tp =
1338 (*pjsua_var.ua_cfg.cb.on_create_media_transport)
1339 (call_id, mi, call_med->tp,
1340 custom_med_tp_flags);
1341 if (!call_med->tp) {
1342 status =
1343 PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_NOT_ACCEPTABLE);
1344 }
1345 }
1346
1347 if (call_med->tp) {
1348 status = pjmedia_transport_media_create(
1349 call_med->tp, tmp_pool,
1350 0, call->async_call.rem_sdp, mi);
1351 }
Sauw Ming73ecfe82011-09-21 10:20:01 +00001352 if (status != PJ_SUCCESS) {
1353 call->med_ch_info.status = status;
1354 call->med_ch_info.med_idx = mi;
1355 call->med_ch_info.state = call_med->tp_st;
1356 call->med_ch_info.sip_err_code = PJSIP_SC_NOT_ACCEPTABLE;
Nanang Izzuddin1d568722012-04-24 05:40:32 +00001357 pjsua_media_prov_clean_up(call_id);
Benny Prijono0ec6cfb2011-12-26 09:00:42 +00001358 goto on_return;
Sauw Ming73ecfe82011-09-21 10:20:01 +00001359 }
1360
Benny Prijono1312e752012-03-22 09:56:52 +00001361 pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_INIT);
Sauw Ming73ecfe82011-09-21 10:20:01 +00001362 }
1363 }
1364
1365 call->med_ch_info.status = PJ_SUCCESS;
1366
Benny Prijono0ec6cfb2011-12-26 09:00:42 +00001367on_return:
Sauw Ming73ecfe82011-09-21 10:20:01 +00001368 if (call->med_ch_cb)
1369 (*call->med_ch_cb)(call->index, &call->med_ch_info);
1370
Benny Prijono0bc99a92011-03-17 04:34:43 +00001371 return status;
1372}
Nanang Izzuddin1e952a82010-10-05 16:32:04 +00001373
Nanang Izzuddin1d568722012-04-24 05:40:32 +00001374
1375/* Clean up media transports in provisional media that is not used
1376 * by call media.
1377 */
1378void pjsua_media_prov_clean_up(pjsua_call_id call_id)
1379{
1380 pjsua_call *call = &pjsua_var.calls[call_id];
1381 unsigned i;
1382
1383 for (i = 0; i < call->med_prov_cnt; ++i) {
1384 pjsua_call_media *call_med = &call->media_prov[i];
1385 unsigned j;
1386 pj_bool_t used = PJ_FALSE;
1387
1388 if (call_med->tp == NULL)
1389 continue;
1390
1391 for (j = 0; j < call->med_cnt; ++j) {
1392 if (call->media[j].tp == call_med->tp) {
1393 used = PJ_TRUE;
1394 break;
1395 }
1396 }
1397
1398 if (!used) {
1399 if (call_med->tp_st > PJSUA_MED_TP_IDLE) {
1400 pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_IDLE);
1401 pjmedia_transport_media_stop(call_med->tp);
1402 }
1403 pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_NULL);
1404 pjmedia_transport_close(call_med->tp);
1405 call_med->tp = call_med->tp_orig = NULL;
1406 }
1407 }
1408}
1409
1410
Benny Prijonod8179652008-01-23 20:39:07 +00001411pj_status_t pjsua_media_channel_init(pjsua_call_id call_id,
1412 pjsip_role_e role,
1413 int security_level,
1414 pj_pool_t *tmp_pool,
1415 const pjmedia_sdp_session *rem_sdp,
Sauw Ming73ecfe82011-09-21 10:20:01 +00001416 int *sip_err_code,
1417 pj_bool_t async,
1418 pjsua_med_tp_state_cb cb)
Benny Prijonoc97608e2007-03-23 16:34:20 +00001419{
Benny Prijono0bc99a92011-03-17 04:34:43 +00001420 const pj_str_t STR_AUDIO = { "audio", 5 };
1421 const pj_str_t STR_VIDEO = { "video", 5 };
Benny Prijonod8179652008-01-23 20:39:07 +00001422 pjsua_call *call = &pjsua_var.calls[call_id];
Benny Prijonod8179652008-01-23 20:39:07 +00001423 pjsua_acc *acc = &pjsua_var.acc[call->acc_id];
Benny Prijono0bc99a92011-03-17 04:34:43 +00001424 pj_uint8_t maudidx[PJSUA_MAX_CALL_MEDIA];
1425 unsigned maudcnt = PJ_ARRAY_SIZE(maudidx);
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001426 unsigned mtotaudcnt = PJ_ARRAY_SIZE(maudidx);
Benny Prijono0bc99a92011-03-17 04:34:43 +00001427 pj_uint8_t mvididx[PJSUA_MAX_CALL_MEDIA];
1428 unsigned mvidcnt = PJ_ARRAY_SIZE(mvididx);
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001429 unsigned mtotvidcnt = PJ_ARRAY_SIZE(mvididx);
Benny Prijono0bc99a92011-03-17 04:34:43 +00001430 unsigned mi;
Sauw Ming73ecfe82011-09-21 10:20:01 +00001431 pj_bool_t pending_med_tp = PJ_FALSE;
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001432 pj_bool_t reinit = PJ_FALSE;
Benny Prijono0bc99a92011-03-17 04:34:43 +00001433 pj_status_t status;
Benny Prijonoc97608e2007-03-23 16:34:20 +00001434
Benny Prijonod8179652008-01-23 20:39:07 +00001435 PJ_UNUSED_ARG(role);
1436
Nanang Izzuddinb6c239c2011-05-10 05:42:28 +00001437 /*
1438 * Note: this function may be called when the media already exists
1439 * (e.g. in reinvites, updates, etc).
1440 */
1441
Benny Prijono0bc99a92011-03-17 04:34:43 +00001442 if (pjsua_get_state() != PJSUA_STATE_RUNNING)
1443 return PJ_EBUSY;
1444
Sauw Ming73ecfe82011-09-21 10:20:01 +00001445 if (async) {
1446 pj_pool_t *tmppool = (call->inv? call->inv->pool_prov:
1447 call->async_call.dlg->pool);
1448
1449 status = pj_mutex_create_simple(tmppool, NULL, &call->med_ch_mutex);
1450 if (status != PJ_SUCCESS)
1451 return status;
1452 }
1453
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001454 if (call->inv && call->inv->state == PJSIP_INV_STATE_CONFIRMED)
1455 reinit = PJ_TRUE;
1456
1457 PJ_LOG(4,(THIS_FILE, "Call %d: %sinitializing media..",
1458 call_id, (reinit?"re-":"") ));
1459
Benny Prijonob90fd382011-09-18 14:59:56 +00001460 pj_log_push_indent();
1461
Nanang Izzuddin1d568722012-04-24 05:40:32 +00001462 /* Init provisional media state */
1463 if (call->med_cnt == 0) {
1464 /* New media session, just copy whole from call media state. */
1465 pj_memcpy(call->media_prov, call->media, sizeof(call->media));
1466 } else {
1467 /* Clean up any unused transports. Note that when local SDP reoffer
1468 * is rejected by remote, there may be any initialized transports that
1469 * are not used by call media and currently there is no notification
1470 * from PJSIP level regarding the reoffer rejection.
1471 */
1472 pjsua_media_prov_clean_up(call_id);
1473
1474 /* Updating media session, copy from call media state. */
1475 pj_memcpy(call->media_prov, call->media,
1476 sizeof(call->media[0]) * call->med_cnt);
1477 }
1478 call->med_prov_cnt = call->med_cnt;
1479
Benny Prijono0bc99a92011-03-17 04:34:43 +00001480#if DISABLED_FOR_TICKET_1185
Benny Prijonod8179652008-01-23 20:39:07 +00001481 /* Return error if media transport has not been created yet
Benny Prijono68f9e4f2008-03-21 08:56:02 +00001482 * (e.g. application is starting)
1483 */
Benny Prijono0bc99a92011-03-17 04:34:43 +00001484 for (i=0; i<call->med_cnt; ++i) {
1485 if (call->media[i].tp == NULL) {
Benny Prijonob90fd382011-09-18 14:59:56 +00001486 status = PJ_EBUSY;
1487 goto on_error;
Benny Prijonod8179652008-01-23 20:39:07 +00001488 }
Benny Prijonoc97608e2007-03-23 16:34:20 +00001489 }
Benny Prijonod8179652008-01-23 20:39:07 +00001490#endif
Benny Prijonoc97608e2007-03-23 16:34:20 +00001491
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001492 /* Get media count for each media type */
Benny Prijono0bc99a92011-03-17 04:34:43 +00001493 if (rem_sdp) {
1494 sort_media(rem_sdp, &STR_AUDIO, acc->cfg.use_srtp,
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001495 maudidx, &maudcnt, &mtotaudcnt);
Benny Prijono0bc99a92011-03-17 04:34:43 +00001496 if (maudcnt==0) {
1497 /* Expecting audio in the offer */
1498 if (sip_err_code) *sip_err_code = PJSIP_SC_NOT_ACCEPTABLE_HERE;
Benny Prijonob90fd382011-09-18 14:59:56 +00001499 status = PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_NOT_ACCEPTABLE_HERE);
1500 goto on_error;
Benny Prijonoc97608e2007-03-23 16:34:20 +00001501 }
1502
Nanang Izzuddind8c33d82011-08-18 18:30:55 +00001503#if PJMEDIA_HAS_VIDEO
Benny Prijono0bc99a92011-03-17 04:34:43 +00001504 sort_media(rem_sdp, &STR_VIDEO, acc->cfg.use_srtp,
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001505 mvididx, &mvidcnt, &mtotvidcnt);
Nanang Izzuddind8c33d82011-08-18 18:30:55 +00001506#else
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001507 mvidcnt = mtotvidcnt = 0;
1508 PJ_UNUSED_ARG(STR_VIDEO);
Nanang Izzuddind8c33d82011-08-18 18:30:55 +00001509#endif
Nanang Izzuddinb6c239c2011-05-10 05:42:28 +00001510
1511 /* Update media count only when remote add any media, this media count
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001512 * must never decrease. Also note that we shouldn't apply the media
1513 * count setting (of the call setting) before the SDP negotiation.
Benny Prijonod8179652008-01-23 20:39:07 +00001514 */
Nanang Izzuddin1d568722012-04-24 05:40:32 +00001515 if (call->med_prov_cnt < rem_sdp->media_count)
1516 call->med_prov_cnt = PJ_MIN(rem_sdp->media_count,
1517 PJSUA_MAX_CALL_MEDIA);
Benny Prijonod8179652008-01-23 20:39:07 +00001518
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001519 call->rem_offerer = PJ_TRUE;
1520 call->rem_aud_cnt = maudcnt;
1521 call->rem_vid_cnt = mvidcnt;
1522
Benny Prijono0bc99a92011-03-17 04:34:43 +00001523 } else {
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001524
1525 /* If call already established, calculate media count from current
1526 * local active SDP and call setting. Otherwise, calculate media
1527 * count from the call setting only.
1528 */
1529 if (reinit) {
Benny Prijono0ec6cfb2011-12-26 09:00:42 +00001530 const pjmedia_sdp_session *sdp;
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001531
1532 status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &sdp);
1533 pj_assert(status == PJ_SUCCESS);
1534
1535 sort_media(sdp, &STR_AUDIO, acc->cfg.use_srtp,
1536 maudidx, &maudcnt, &mtotaudcnt);
1537 pj_assert(maudcnt > 0);
1538
1539 sort_media(sdp, &STR_VIDEO, acc->cfg.use_srtp,
1540 mvididx, &mvidcnt, &mtotvidcnt);
1541
1542 /* Call setting may add or remove media. Adding media is done by
1543 * enabling any disabled/port-zeroed media first, then adding new
1544 * media whenever needed. Removing media is done by disabling
1545 * media with the lowest 'quality'.
1546 */
1547
1548 /* Check if we need to add new audio */
Benny Prijonoae86e6a2011-12-27 12:47:52 +00001549 if (maudcnt < call->opt.aud_cnt &&
1550 mtotaudcnt < call->opt.aud_cnt)
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001551 {
Benny Prijonoae86e6a2011-12-27 12:47:52 +00001552 for (mi = 0; mi < call->opt.aud_cnt - mtotaudcnt; ++mi)
Nanang Izzuddin1d568722012-04-24 05:40:32 +00001553 maudidx[maudcnt++] = (pj_uint8_t)call->med_prov_cnt++;
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001554
Benny Prijonoae86e6a2011-12-27 12:47:52 +00001555 mtotaudcnt = call->opt.aud_cnt;
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001556 }
Benny Prijonoae86e6a2011-12-27 12:47:52 +00001557 maudcnt = call->opt.aud_cnt;
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001558
1559 /* Check if we need to add new video */
Benny Prijonoae86e6a2011-12-27 12:47:52 +00001560 if (mvidcnt < call->opt.vid_cnt &&
1561 mtotvidcnt < call->opt.vid_cnt)
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001562 {
Benny Prijonoae86e6a2011-12-27 12:47:52 +00001563 for (mi = 0; mi < call->opt.vid_cnt - mtotvidcnt; ++mi)
Nanang Izzuddin1d568722012-04-24 05:40:32 +00001564 mvididx[mvidcnt++] = (pj_uint8_t)call->med_prov_cnt++;
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001565
Benny Prijonoae86e6a2011-12-27 12:47:52 +00001566 mtotvidcnt = call->opt.vid_cnt;
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001567 }
Benny Prijonoae86e6a2011-12-27 12:47:52 +00001568 mvidcnt = call->opt.vid_cnt;
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001569
1570 } else {
1571
Benny Prijonoae86e6a2011-12-27 12:47:52 +00001572 maudcnt = mtotaudcnt = call->opt.aud_cnt;
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001573 for (mi=0; mi<maudcnt; ++mi) {
1574 maudidx[mi] = (pj_uint8_t)mi;
1575 }
Benny Prijonoae86e6a2011-12-27 12:47:52 +00001576 mvidcnt = mtotvidcnt = call->opt.vid_cnt;
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001577 for (mi=0; mi<mvidcnt; ++mi) {
1578 mvididx[mi] = (pj_uint8_t)(maudcnt + mi);
1579 }
Nanang Izzuddin1d568722012-04-24 05:40:32 +00001580 call->med_prov_cnt = maudcnt + mvidcnt;
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001581
1582 /* Need to publish supported media? */
1583 if (call->opt.flag & PJSUA_CALL_INCLUDE_DISABLED_MEDIA) {
1584 if (mtotaudcnt == 0) {
1585 mtotaudcnt = 1;
Nanang Izzuddin1d568722012-04-24 05:40:32 +00001586 maudidx[0] = (pj_uint8_t)call->med_prov_cnt++;
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001587 }
Nanang Izzuddind8c33d82011-08-18 18:30:55 +00001588#if PJMEDIA_HAS_VIDEO
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001589 if (mtotvidcnt == 0) {
1590 mtotvidcnt = 1;
Nanang Izzuddin1d568722012-04-24 05:40:32 +00001591 mvididx[0] = (pj_uint8_t)call->med_prov_cnt++;
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001592 }
1593#endif
1594 }
Benny Prijono0bc99a92011-03-17 04:34:43 +00001595 }
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001596
1597 call->rem_offerer = PJ_FALSE;
Benny Prijonoa310bd22008-06-27 21:19:44 +00001598 }
1599
Nanang Izzuddin1d568722012-04-24 05:40:32 +00001600 if (call->med_prov_cnt == 0) {
Benny Prijono0bc99a92011-03-17 04:34:43 +00001601 /* Expecting at least one media */
Benny Prijonoab8dba92008-06-27 21:59:15 +00001602 if (sip_err_code) *sip_err_code = PJSIP_SC_NOT_ACCEPTABLE_HERE;
Benny Prijonob90fd382011-09-18 14:59:56 +00001603 status = PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_NOT_ACCEPTABLE_HERE);
1604 goto on_error;
Benny Prijonoa310bd22008-06-27 21:19:44 +00001605 }
1606
Sauw Ming73ecfe82011-09-21 10:20:01 +00001607 if (async) {
1608 call->med_ch_cb = cb;
Benny Prijono7eff5ef2011-10-26 06:53:30 +00001609 }
1610
1611 if (rem_sdp) {
1612 call->async_call.rem_sdp =
1613 pjmedia_sdp_session_clone(call->inv->pool_prov, rem_sdp);
1614 } else {
1615 call->async_call.rem_sdp = NULL;
Sauw Ming73ecfe82011-09-21 10:20:01 +00001616 }
1617
Sauw Ming99cc8ff2011-09-22 04:24:56 +00001618 call->async_call.pool_prov = tmp_pool;
1619
Benny Prijono0bc99a92011-03-17 04:34:43 +00001620 /* Initialize each media line */
Nanang Izzuddin1d568722012-04-24 05:40:32 +00001621 for (mi=0; mi < call->med_prov_cnt; ++mi) {
1622 pjsua_call_media *call_med = &call->media_prov[mi];
Benny Prijono0bc99a92011-03-17 04:34:43 +00001623 pj_bool_t enabled = PJ_FALSE;
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001624 pjmedia_type media_type = PJMEDIA_TYPE_UNKNOWN;
Benny Prijonoa310bd22008-06-27 21:19:44 +00001625
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001626 if (pj_memchr(maudidx, mi, mtotaudcnt * sizeof(maudidx[0]))) {
1627 media_type = PJMEDIA_TYPE_AUDIO;
Benny Prijonoae86e6a2011-12-27 12:47:52 +00001628 if (call->opt.aud_cnt &&
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001629 pj_memchr(maudidx, mi, maudcnt * sizeof(maudidx[0])))
1630 {
1631 enabled = PJ_TRUE;
Benny Prijono0bc99a92011-03-17 04:34:43 +00001632 }
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001633 } else if (pj_memchr(mvididx, mi, mtotvidcnt * sizeof(mvididx[0]))) {
1634 media_type = PJMEDIA_TYPE_VIDEO;
Benny Prijonoae86e6a2011-12-27 12:47:52 +00001635 if (call->opt.vid_cnt &&
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001636 pj_memchr(mvididx, mi, mvidcnt * sizeof(mvididx[0])))
1637 {
1638 enabled = PJ_TRUE;
Benny Prijono0bc99a92011-03-17 04:34:43 +00001639 }
Benny Prijono0bc99a92011-03-17 04:34:43 +00001640 }
1641
1642 if (enabled) {
1643 status = pjsua_call_media_init(call_med, media_type,
1644 &acc->cfg.rtp_cfg,
Sauw Ming73ecfe82011-09-21 10:20:01 +00001645 security_level, sip_err_code,
1646 async,
Sauw Ming3a55bb92011-10-06 06:49:09 +00001647 (async? &media_channel_init_cb:
1648 NULL));
Sauw Ming73ecfe82011-09-21 10:20:01 +00001649 if (status == PJ_EPENDING) {
1650 pending_med_tp = PJ_TRUE;
1651 } else if (status != PJ_SUCCESS) {
1652 if (pending_med_tp) {
1653 /* Save failure information. */
1654 call_med->tp_ready = status;
1655 pj_bzero(&call->med_ch_info, sizeof(call->med_ch_info));
1656 call->med_ch_info.status = status;
1657 call->med_ch_info.state = call_med->tp_st;
1658 call->med_ch_info.med_idx = call_med->idx;
1659 if (sip_err_code)
1660 call->med_ch_info.sip_err_code = *sip_err_code;
1661
1662 /* We will return failure in the callback later. */
1663 return PJ_EPENDING;
1664 }
1665
Nanang Izzuddin1d568722012-04-24 05:40:32 +00001666 pjsua_media_prov_clean_up(call_id);
Benny Prijonob90fd382011-09-18 14:59:56 +00001667 goto on_error;
Benny Prijono0bc99a92011-03-17 04:34:43 +00001668 }
1669 } else {
Nanang Izzuddinb6c239c2011-05-10 05:42:28 +00001670 /* By convention, the media is disabled if transport is NULL
1671 * or transport state is PJSUA_MED_TP_DISABLED.
1672 */
Benny Prijono0bc99a92011-03-17 04:34:43 +00001673 if (call_med->tp) {
Nanang Izzuddinb6c239c2011-05-10 05:42:28 +00001674 // Don't close transport here, as SDP negotiation has not been
Nanang Izzuddin1d568722012-04-24 05:40:32 +00001675 // done and stream may be still active. Once SDP negotiation
1676 // is done (channel_update() invoked), this transport will be
1677 // closed there.
Nanang Izzuddinb6c239c2011-05-10 05:42:28 +00001678 //pjmedia_transport_close(call_med->tp);
1679 //call_med->tp = NULL;
1680 pj_assert(call_med->tp_st == PJSUA_MED_TP_INIT ||
1681 call_med->tp_st == PJSUA_MED_TP_RUNNING);
Benny Prijono1312e752012-03-22 09:56:52 +00001682 pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_DISABLED);
Benny Prijono0bc99a92011-03-17 04:34:43 +00001683 }
Nanang Izzuddinb6c239c2011-05-10 05:42:28 +00001684
1685 /* Put media type just for info */
1686 call_med->type = media_type;
Benny Prijono0bc99a92011-03-17 04:34:43 +00001687 }
Benny Prijono224b4e22008-06-19 14:10:28 +00001688 }
1689
Benny Prijono0bc99a92011-03-17 04:34:43 +00001690 call->audio_idx = maudidx[0];
1691
1692 PJ_LOG(4,(THIS_FILE, "Media index %d selected for audio call %d",
1693 call->audio_idx, call->index));
1694
Sauw Ming73ecfe82011-09-21 10:20:01 +00001695 if (pending_med_tp) {
Sauw Ming99cc8ff2011-09-22 04:24:56 +00001696 /* We shouldn't use temporary pool anymore. */
1697 call->async_call.pool_prov = NULL;
Sauw Ming73ecfe82011-09-21 10:20:01 +00001698 /* We have a pending media transport initialization. */
1699 pj_log_pop_indent();
1700 return PJ_EPENDING;
Benny Prijono0bc99a92011-03-17 04:34:43 +00001701 }
1702
Sauw Ming73ecfe82011-09-21 10:20:01 +00001703 /* Media transport initialization completed immediately, so
1704 * we don't need to call the callback.
1705 */
1706 call->med_ch_cb = NULL;
1707
1708 status = media_channel_init_cb(call_id, NULL);
1709 if (status != PJ_SUCCESS && sip_err_code)
1710 *sip_err_code = call->med_ch_info.sip_err_code;
1711
Benny Prijonob90fd382011-09-18 14:59:56 +00001712 pj_log_pop_indent();
Sauw Ming73ecfe82011-09-21 10:20:01 +00001713 return status;
Benny Prijonob90fd382011-09-18 14:59:56 +00001714
1715on_error:
Sauw Ming73ecfe82011-09-21 10:20:01 +00001716 if (call->med_ch_mutex) {
1717 pj_mutex_destroy(call->med_ch_mutex);
1718 call->med_ch_mutex = NULL;
1719 }
1720
Benny Prijonob90fd382011-09-18 14:59:56 +00001721 pj_log_pop_indent();
1722 return status;
Benny Prijonoc97608e2007-03-23 16:34:20 +00001723}
1724
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001725
1726/* Create SDP based on the current media channel. Note that, this function
1727 * will not modify the media channel, so when receiving new offer or
1728 * updating media count (via call setting), media channel must be reinit'd
1729 * (using pjsua_media_channel_init()) first before calling this function.
1730 */
Benny Prijonoc97608e2007-03-23 16:34:20 +00001731pj_status_t pjsua_media_channel_create_sdp(pjsua_call_id call_id,
1732 pj_pool_t *pool,
Benny Prijonod8179652008-01-23 20:39:07 +00001733 const pjmedia_sdp_session *rem_sdp,
Benny Prijono25b2ea12008-01-24 19:20:54 +00001734 pjmedia_sdp_session **p_sdp,
Benny Prijono0bc99a92011-03-17 04:34:43 +00001735 int *sip_err_code)
Benny Prijonoc97608e2007-03-23 16:34:20 +00001736{
Benny Prijono0bc99a92011-03-17 04:34:43 +00001737 enum { MAX_MEDIA = PJSUA_MAX_CALL_MEDIA };
Benny Prijonoc97608e2007-03-23 16:34:20 +00001738 pjmedia_sdp_session *sdp;
Benny Prijono0bc99a92011-03-17 04:34:43 +00001739 pj_sockaddr origin;
Benny Prijonoc97608e2007-03-23 16:34:20 +00001740 pjsua_call *call = &pjsua_var.calls[call_id];
Nanang Izzuddin1e952a82010-10-05 16:32:04 +00001741 pjmedia_sdp_neg_state sdp_neg_state = PJMEDIA_SDP_NEG_STATE_NULL;
Benny Prijono0bc99a92011-03-17 04:34:43 +00001742 unsigned mi;
Nanang Izzuddin2f296352012-01-27 09:12:59 +00001743 unsigned tot_bandw_tias = 0;
Benny Prijonoc97608e2007-03-23 16:34:20 +00001744 pj_status_t status;
1745
Benny Prijono0bc99a92011-03-17 04:34:43 +00001746 if (pjsua_get_state() != PJSUA_STATE_RUNNING)
Benny Prijono55e82352007-05-10 20:49:08 +00001747 return PJ_EBUSY;
Benny Prijono55e82352007-05-10 20:49:08 +00001748
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001749#if 0
1750 // This function should not really change the media channel.
Benny Prijono0bc99a92011-03-17 04:34:43 +00001751 if (rem_sdp) {
Nanang Izzuddinb6c239c2011-05-10 05:42:28 +00001752 /* If this is a re-offer, let's re-initialize media as remote may
1753 * add or remove media
1754 */
1755 if (call->inv && call->inv->state == PJSIP_INV_STATE_CONFIRMED) {
1756 status = pjsua_media_channel_init(call_id, PJSIP_ROLE_UAS,
1757 call->secure_level, pool,
Sauw Ming73ecfe82011-09-21 10:20:01 +00001758 rem_sdp, sip_err_code,
1759 PJ_FALSE, NULL);
Nanang Izzuddinb6c239c2011-05-10 05:42:28 +00001760 if (status != PJ_SUCCESS)
1761 return status;
Benny Prijono0324ba52010-12-02 10:41:46 +00001762 }
Benny Prijono0bc99a92011-03-17 04:34:43 +00001763 } else {
1764 /* Audio is first in our offer, by convention */
Nanang Izzuddinb6c239c2011-05-10 05:42:28 +00001765 // The audio_idx should not be changed here, as this function may be
1766 // called in generating re-offer and the current active audio index
1767 // can be anywhere.
1768 //call->audio_idx = 0;
Nanang Izzuddin3150d8b2010-12-01 08:20:28 +00001769 }
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00001770#endif
Nanang Izzuddin3150d8b2010-12-01 08:20:28 +00001771
Nanang Izzuddinb6c239c2011-05-10 05:42:28 +00001772#if 0
1773 // Since r3512, old-style hold should have got transport, created by
1774 // pjsua_media_channel_init() in initial offer/answer or remote reoffer.
Benny Prijono224b4e22008-06-19 14:10:28 +00001775 /* Create media if it's not created. This could happen when call is
Benny Prijono0bc99a92011-03-17 04:34:43 +00001776 * currently on-hold (with the old style hold)
Benny Prijono224b4e22008-06-19 14:10:28 +00001777 */
Benny Prijono0bc99a92011-03-17 04:34:43 +00001778 if (call->media[call->audio_idx].tp == NULL) {
Benny Prijono224b4e22008-06-19 14:10:28 +00001779 pjsip_role_e role;
1780 role = (rem_sdp ? PJSIP_ROLE_UAS : PJSIP_ROLE_UAC);
1781 status = pjsua_media_channel_init(call_id, role, call->secure_level,
Benny Prijono0bc99a92011-03-17 04:34:43 +00001782 pool, rem_sdp, sip_err_code);
Benny Prijono224b4e22008-06-19 14:10:28 +00001783 if (status != PJ_SUCCESS)
1784 return status;
1785 }
Nanang Izzuddinb6c239c2011-05-10 05:42:28 +00001786#endif
Benny Prijono224b4e22008-06-19 14:10:28 +00001787
Nanang Izzuddin1e952a82010-10-05 16:32:04 +00001788 /* Get SDP negotiator state */
1789 if (call->inv && call->inv->neg)
1790 sdp_neg_state = pjmedia_sdp_neg_get_state(call->inv->neg);
1791
Benny Prijono0bc99a92011-03-17 04:34:43 +00001792 /* Get one address to use in the origin field */
1793 pj_bzero(&origin, sizeof(origin));
Nanang Izzuddin1d568722012-04-24 05:40:32 +00001794 for (mi=0; mi<call->med_prov_cnt; ++mi) {
Benny Prijono0bc99a92011-03-17 04:34:43 +00001795 pjmedia_transport_info tpinfo;
Benny Prijono617c5bc2007-04-02 19:51:21 +00001796
Nanang Izzuddin1d568722012-04-24 05:40:32 +00001797 if (call->media_prov[mi].tp == NULL)
Benny Prijono0bc99a92011-03-17 04:34:43 +00001798 continue;
1799
1800 pjmedia_transport_info_init(&tpinfo);
Nanang Izzuddin1d568722012-04-24 05:40:32 +00001801 pjmedia_transport_get_info(call->media_prov[mi].tp, &tpinfo);
Benny Prijono0bc99a92011-03-17 04:34:43 +00001802 pj_sockaddr_cp(&origin, &tpinfo.sock_info.rtp_addr_name);
1803 break;
Benny Prijono25b2ea12008-01-24 19:20:54 +00001804 }
Benny Prijonoc97608e2007-03-23 16:34:20 +00001805
Benny Prijono0bc99a92011-03-17 04:34:43 +00001806 /* Create the base (blank) SDP */
1807 status = pjmedia_endpt_create_base_sdp(pjsua_var.med_endpt, pool, NULL,
1808 &origin, &sdp);
1809 if (status != PJ_SUCCESS)
1810 return status;
Benny Prijonoa310bd22008-06-27 21:19:44 +00001811
Benny Prijono0bc99a92011-03-17 04:34:43 +00001812 /* Process each media line */
Nanang Izzuddin1d568722012-04-24 05:40:32 +00001813 for (mi=0; mi<call->med_prov_cnt; ++mi) {
1814 pjsua_call_media *call_med = &call->media_prov[mi];
Benny Prijono0bc99a92011-03-17 04:34:43 +00001815 pjmedia_sdp_media *m = NULL;
1816 pjmedia_transport_info tpinfo;
Nanang Izzuddin2f296352012-01-27 09:12:59 +00001817 unsigned i;
Benny Prijono0bc99a92011-03-17 04:34:43 +00001818
Nanang Izzuddinb6c239c2011-05-10 05:42:28 +00001819 if (rem_sdp && mi >= rem_sdp->media_count) {
1820 /* Remote might have removed some media lines. */
1821 break;
Nanang Izzuddin1e952a82010-10-05 16:32:04 +00001822 }
1823
Nanang Izzuddinb6c239c2011-05-10 05:42:28 +00001824 if (call_med->tp == NULL || call_med->tp_st == PJSUA_MED_TP_DISABLED)
1825 {
Benny Prijono0bc99a92011-03-17 04:34:43 +00001826 /*
Nanang Izzuddinb6c239c2011-05-10 05:42:28 +00001827 * This media is disabled. Just create a valid SDP with zero
Benny Prijono0bc99a92011-03-17 04:34:43 +00001828 * port.
1829 */
Nanang Izzuddind8c33d82011-08-18 18:30:55 +00001830 if (rem_sdp) {
1831 /* Just clone the remote media and deactivate it */
1832 m = pjmedia_sdp_media_clone_deactivate(pool,
1833 rem_sdp->media[mi]);
1834 } else {
1835 m = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_media);
1836 m->desc.transport = pj_str("RTP/AVP");
1837 m->desc.fmt_count = 1;
1838 m->conn = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_conn);
1839 m->conn->net_type = pj_str("IN");
1840 m->conn->addr_type = pj_str("IP4");
1841 m->conn->addr = pj_str("127.0.0.1");
Benny Prijonoa310bd22008-06-27 21:19:44 +00001842
Nanang Izzuddind8c33d82011-08-18 18:30:55 +00001843 switch (call_med->type) {
1844 case PJMEDIA_TYPE_AUDIO:
1845 m->desc.media = pj_str("audio");
1846 m->desc.fmt[0] = pj_str("0");
1847 break;
1848 case PJMEDIA_TYPE_VIDEO:
1849 m->desc.media = pj_str("video");
1850 m->desc.fmt[0] = pj_str("31");
1851 break;
1852 default:
Nanang Izzuddincfa312c2011-10-26 16:57:05 +00001853 /* This must be us generating re-offer, and some unknown
1854 * media may exist, so just clone from active local SDP
1855 * (and it should have been deactivated already).
1856 */
1857 pj_assert(call->inv && call->inv->neg &&
1858 sdp_neg_state == PJMEDIA_SDP_NEG_STATE_DONE);
1859 {
1860 const pjmedia_sdp_session *s_;
1861 pjmedia_sdp_neg_get_active_local(call->inv->neg, &s_);
1862
1863 pj_assert(mi < s_->media_count);
1864 m = pjmedia_sdp_media_clone(pool, s_->media[mi]);
1865 m->desc.port = 0;
Nanang Izzuddind8c33d82011-08-18 18:30:55 +00001866 }
Nanang Izzuddincfa312c2011-10-26 16:57:05 +00001867 break;
Benny Prijono0bc99a92011-03-17 04:34:43 +00001868 }
Benny Prijonoa310bd22008-06-27 21:19:44 +00001869 }
Benny Prijono0bc99a92011-03-17 04:34:43 +00001870
1871 sdp->media[sdp->media_count++] = m;
1872 continue;
Benny Prijonoa310bd22008-06-27 21:19:44 +00001873 }
1874
Benny Prijono0bc99a92011-03-17 04:34:43 +00001875 /* Get transport address info */
1876 pjmedia_transport_info_init(&tpinfo);
1877 pjmedia_transport_get_info(call_med->tp, &tpinfo);
Benny Prijonoa310bd22008-06-27 21:19:44 +00001878
Benny Prijono0bc99a92011-03-17 04:34:43 +00001879 /* Ask pjmedia endpoint to create SDP media line */
1880 switch (call_med->type) {
1881 case PJMEDIA_TYPE_AUDIO:
1882 status = pjmedia_endpt_create_audio_sdp(pjsua_var.med_endpt, pool,
1883 &tpinfo.sock_info, 0, &m);
1884 break;
Nanang Izzuddin63b3c132011-07-19 11:11:07 +00001885#if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0)
Benny Prijono0bc99a92011-03-17 04:34:43 +00001886 case PJMEDIA_TYPE_VIDEO:
1887 status = pjmedia_endpt_create_video_sdp(pjsua_var.med_endpt, pool,
1888 &tpinfo.sock_info, 0, &m);
1889 break;
Nanang Izzuddin63b3c132011-07-19 11:11:07 +00001890#endif
Benny Prijono0bc99a92011-03-17 04:34:43 +00001891 default:
1892 pj_assert(!"Invalid call_med media type");
1893 return PJ_EBUG;
1894 }
Benny Prijonoa310bd22008-06-27 21:19:44 +00001895
Benny Prijono0bc99a92011-03-17 04:34:43 +00001896 if (status != PJ_SUCCESS)
1897 return status;
1898
1899 sdp->media[sdp->media_count++] = m;
1900
1901 /* Give to transport */
1902 status = pjmedia_transport_encode_sdp(call_med->tp, pool,
1903 sdp, rem_sdp, mi);
1904 if (status != PJ_SUCCESS) {
1905 if (sip_err_code) *sip_err_code = PJSIP_SC_NOT_ACCEPTABLE;
1906 return status;
Benny Prijonoa310bd22008-06-27 21:19:44 +00001907 }
Nanang Izzuddin91ba2a22011-04-11 19:59:09 +00001908
1909 /* Copy c= line of the first media to session level,
1910 * if there's none.
1911 */
1912 if (sdp->conn == NULL) {
1913 sdp->conn = pjmedia_sdp_conn_clone(pool, m->conn);
Benny Prijonoa310bd22008-06-27 21:19:44 +00001914 }
Nanang Izzuddin2f296352012-01-27 09:12:59 +00001915
1916
1917 /* Find media bandwidth info */
1918 for (i = 0; i < m->bandw_count; ++i) {
1919 const pj_str_t STR_BANDW_MODIFIER_TIAS = { "TIAS", 4 };
1920 if (!pj_stricmp(&m->bandw[i]->modifier, &STR_BANDW_MODIFIER_TIAS))
1921 {
1922 tot_bandw_tias += m->bandw[i]->value;
1923 break;
1924 }
1925 }
Benny Prijonoa310bd22008-06-27 21:19:44 +00001926 }
1927
Benny Prijono6ba8c542007-10-16 01:34:14 +00001928 /* Add NAT info in the SDP */
1929 if (pjsua_var.ua_cfg.nat_type_in_sdp) {
1930 pjmedia_sdp_attr *a;
1931 pj_str_t value;
1932 char nat_info[80];
1933
1934 value.ptr = nat_info;
1935 if (pjsua_var.ua_cfg.nat_type_in_sdp == 1) {
1936 value.slen = pj_ansi_snprintf(nat_info, sizeof(nat_info),
1937 "%d", pjsua_var.nat_type);
1938 } else {
1939 const char *type_name = pj_stun_get_nat_name(pjsua_var.nat_type);
1940 value.slen = pj_ansi_snprintf(nat_info, sizeof(nat_info),
1941 "%d %s",
1942 pjsua_var.nat_type,
1943 type_name);
1944 }
1945
1946 a = pjmedia_sdp_attr_create(pool, "X-nat", &value);
1947
1948 pjmedia_sdp_attr_add(&sdp->attr_count, sdp->attr, a);
1949
1950 }
1951
Benny Prijonoc97608e2007-03-23 16:34:20 +00001952
Nanang Izzuddin2f296352012-01-27 09:12:59 +00001953 /* Add bandwidth info in session level using bandwidth modifier "AS". */
1954 if (tot_bandw_tias) {
1955 unsigned bandw;
1956 const pj_str_t STR_BANDW_MODIFIER_AS = { "AS", 2 };
1957 pjmedia_sdp_bandw *b;
1958
1959 /* AS bandwidth = RTP bitrate + RTCP bitrate.
1960 * RTP bitrate = payload bitrate (total TIAS) + overheads (~16kbps).
1961 * RTCP bitrate = est. 5% of RTP bitrate.
1962 * Note that AS bandwidth is in kbps.
1963 */
1964 bandw = tot_bandw_tias + 16000;
1965 bandw += bandw * 5 / 100;
1966 b = PJ_POOL_ALLOC_T(pool, pjmedia_sdp_bandw);
1967 b->modifier = STR_BANDW_MODIFIER_AS;
1968 b->value = bandw / 1000;
1969 sdp->bandw[sdp->bandw_count++] = b;
1970 }
1971
1972
Benny Prijono0bc99a92011-03-17 04:34:43 +00001973#if DISABLED_FOR_TICKET_1185 && defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
Nanang Izzuddind89cc3a2010-05-13 05:22:51 +00001974 /* Check if SRTP is in optional mode and configured to use duplicated
1975 * media, i.e: secured and unsecured version, in the SDP offer.
1976 */
1977 if (!rem_sdp &&
1978 pjsua_var.acc[call->acc_id].cfg.use_srtp == PJMEDIA_SRTP_OPTIONAL &&
1979 pjsua_var.acc[call->acc_id].cfg.srtp_optional_dup_offer)
1980 {
1981 unsigned i;
1982
1983 for (i = 0; i < sdp->media_count; ++i) {
1984 pjmedia_sdp_media *m = sdp->media[i];
1985
Benny Prijono0bc99a92011-03-17 04:34:43 +00001986 /* Check if this media is unsecured but has SDP "crypto"
Nanang Izzuddind89cc3a2010-05-13 05:22:51 +00001987 * attribute.
1988 */
1989 if (pj_stricmp2(&m->desc.transport, "RTP/AVP") == 0 &&
1990 pjmedia_sdp_media_find_attr2(m, "crypto", NULL) != NULL)
1991 {
Benny Prijono0bc99a92011-03-17 04:34:43 +00001992 if (i == (unsigned)call->audio_idx &&
Nanang Izzuddin1e952a82010-10-05 16:32:04 +00001993 sdp_neg_state == PJMEDIA_SDP_NEG_STATE_DONE)
1994 {
1995 /* This is a session update, and peer has chosen the
1996 * unsecured version, so let's make this unsecured too.
1997 */
1998 pjmedia_sdp_media_remove_all_attr(m, "crypto");
1999 } else {
2000 /* This is new offer, duplicate media so we'll have
2001 * secured (with "RTP/SAVP" transport) and and unsecured
2002 * versions.
2003 */
2004 pjmedia_sdp_media *new_m;
Nanang Izzuddind89cc3a2010-05-13 05:22:51 +00002005
Nanang Izzuddin1e952a82010-10-05 16:32:04 +00002006 /* Duplicate this media and apply secured transport */
2007 new_m = pjmedia_sdp_media_clone(pool, m);
2008 pj_strdup2(pool, &new_m->desc.transport, "RTP/SAVP");
Nanang Izzuddind89cc3a2010-05-13 05:22:51 +00002009
Nanang Izzuddin1e952a82010-10-05 16:32:04 +00002010 /* Remove the "crypto" attribute in the unsecured media */
2011 pjmedia_sdp_media_remove_all_attr(m, "crypto");
Nanang Izzuddind89cc3a2010-05-13 05:22:51 +00002012
Nanang Izzuddin1e952a82010-10-05 16:32:04 +00002013 /* Insert the new media before the unsecured media */
2014 if (sdp->media_count < PJMEDIA_MAX_SDP_MEDIA) {
Benny Prijono0bc99a92011-03-17 04:34:43 +00002015 pj_array_insert(sdp->media, sizeof(new_m),
Nanang Izzuddin1e952a82010-10-05 16:32:04 +00002016 sdp->media_count, i, &new_m);
2017 ++sdp->media_count;
2018 ++i;
2019 }
Nanang Izzuddind89cc3a2010-05-13 05:22:51 +00002020 }
2021 }
2022 }
2023 }
2024#endif
2025
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00002026 call->rem_offerer = (rem_sdp != NULL);
2027
Nanang Izzuddinac23f002012-05-17 08:14:05 +00002028 /* Notify application */
2029 if (pjsua_var.ua_cfg.cb.on_call_sdp_created) {
2030 (*pjsua_var.ua_cfg.cb.on_call_sdp_created)(call_id, sdp,
2031 pool, rem_sdp);
2032 }
2033
Benny Prijonoc97608e2007-03-23 16:34:20 +00002034 *p_sdp = sdp;
2035 return PJ_SUCCESS;
Benny Prijonoc97608e2007-03-23 16:34:20 +00002036}
2037
2038
2039static void stop_media_session(pjsua_call_id call_id)
2040{
2041 pjsua_call *call = &pjsua_var.calls[call_id];
Benny Prijono0bc99a92011-03-17 04:34:43 +00002042 unsigned mi;
Benny Prijonoc97608e2007-03-23 16:34:20 +00002043
Benny Prijonob90fd382011-09-18 14:59:56 +00002044 pj_log_push_indent();
2045
Benny Prijono0bc99a92011-03-17 04:34:43 +00002046 for (mi=0; mi<call->med_cnt; ++mi) {
2047 pjsua_call_media *call_med = &call->media[mi];
2048
2049 if (call_med->type == PJMEDIA_TYPE_AUDIO) {
Benny Prijono1312e752012-03-22 09:56:52 +00002050 pjsua_aud_stop_stream(call_med);
Nanang Izzuddinfd461eb2008-06-09 09:35:59 +00002051 }
Nanang Izzuddin50fae732011-03-22 09:49:23 +00002052
2053#if PJMEDIA_HAS_VIDEO
2054 else if (call_med->type == PJMEDIA_TYPE_VIDEO) {
Benny Prijono1312e752012-03-22 09:56:52 +00002055 pjsua_vid_stop_stream(call_med);
Benny Prijonoc97608e2007-03-23 16:34:20 +00002056 }
Nanang Izzuddin50fae732011-03-22 09:49:23 +00002057#endif
Nanang Izzuddinbf26db12011-03-18 07:54:50 +00002058
2059 PJ_LOG(4,(THIS_FILE, "Media session call%02d:%d is destroyed",
2060 call_id, mi));
Sauw Minge7dbbc82011-10-24 09:28:13 +00002061 call_med->prev_state = call_med->state;
Benny Prijono0bc99a92011-03-17 04:34:43 +00002062 call_med->state = PJSUA_CALL_MEDIA_NONE;
Nanang Izzuddin1d568722012-04-24 05:40:32 +00002063
2064 /* Try to sync recent changes to provisional media */
2065 if (mi<call->med_prov_cnt && call->media_prov[mi].tp==call_med->tp)
2066 {
2067 pjsua_call_media *prov_med = &call->media_prov[mi];
2068
2069 /* Media state */
2070 prov_med->prev_state = call_med->prev_state;
2071 prov_med->state = call_med->state;
2072
2073 /* RTP seq/ts */
2074 prov_med->rtp_tx_seq_ts_set = call_med->rtp_tx_seq_ts_set;
2075 prov_med->rtp_tx_seq = call_med->rtp_tx_seq;
2076 prov_med->rtp_tx_ts = call_med->rtp_tx_ts;
2077
2078 /* Stream */
2079 if (call_med->type == PJMEDIA_TYPE_AUDIO) {
2080 prov_med->strm.a.conf_slot = call_med->strm.a.conf_slot;
2081 prov_med->strm.a.stream = call_med->strm.a.stream;
2082 }
2083#if PJMEDIA_HAS_VIDEO
2084 else if (call_med->type == PJMEDIA_TYPE_VIDEO) {
2085 prov_med->strm.v.cap_win_id = call_med->strm.v.cap_win_id;
2086 prov_med->strm.v.rdr_win_id = call_med->strm.v.rdr_win_id;
2087 prov_med->strm.v.stream = call_med->strm.v.stream;
2088 }
2089#endif
2090 }
Benny Prijonoc97608e2007-03-23 16:34:20 +00002091 }
Benny Prijonob90fd382011-09-18 14:59:56 +00002092
2093 pj_log_pop_indent();
Benny Prijonoc97608e2007-03-23 16:34:20 +00002094}
2095
2096pj_status_t pjsua_media_channel_deinit(pjsua_call_id call_id)
2097{
2098 pjsua_call *call = &pjsua_var.calls[call_id];
Benny Prijono0bc99a92011-03-17 04:34:43 +00002099 unsigned mi;
Benny Prijonoc97608e2007-03-23 16:34:20 +00002100
Sauw Ming903154f2011-10-03 08:22:48 +00002101 PJSUA_LOCK();
Sauw Mingec765352011-10-03 02:04:36 +00002102 for (mi=0; mi<call->med_cnt; ++mi) {
2103 pjsua_call_media *call_med = &call->media[mi];
2104
Sauw Ming903154f2011-10-03 08:22:48 +00002105 if (call_med->tp_st == PJSUA_MED_TP_CREATING) {
2106 /* We will do the deinitialization after media transport
2107 * creation is completed.
2108 */
2109 call->async_call.med_ch_deinit = PJ_TRUE;
2110 PJSUA_UNLOCK();
2111 return PJ_SUCCESS;
2112 }
Sauw Mingec765352011-10-03 02:04:36 +00002113 }
Sauw Ming903154f2011-10-03 08:22:48 +00002114 PJSUA_UNLOCK();
Sauw Mingec765352011-10-03 02:04:36 +00002115
Benny Prijonob90fd382011-09-18 14:59:56 +00002116 PJ_LOG(4,(THIS_FILE, "Call %d: deinitializing media..", call_id));
2117 pj_log_push_indent();
2118
Benny Prijonoc97608e2007-03-23 16:34:20 +00002119 stop_media_session(call_id);
2120
Nanang Izzuddin1d568722012-04-24 05:40:32 +00002121 /* Clean up media transports */
2122 pjsua_media_prov_clean_up(call_id);
2123 call->med_prov_cnt = 0;
Benny Prijono0bc99a92011-03-17 04:34:43 +00002124 for (mi=0; mi<call->med_cnt; ++mi) {
2125 pjsua_call_media *call_med = &call->media[mi];
Benny Prijonoc97608e2007-03-23 16:34:20 +00002126
Sauw Minge7dbbc82011-10-24 09:28:13 +00002127 if (call_med->tp_st > PJSUA_MED_TP_IDLE) {
Benny Prijono1312e752012-03-22 09:56:52 +00002128 pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_IDLE);
Nanang Izzuddin1d568722012-04-24 05:40:32 +00002129 pjmedia_transport_media_stop(call_med->tp);
Benny Prijono0bc99a92011-03-17 04:34:43 +00002130 }
2131
Nanang Izzuddinb6c239c2011-05-10 05:42:28 +00002132 if (call_med->tp) {
Nanang Izzuddin1d568722012-04-24 05:40:32 +00002133 pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_NULL);
Benny Prijono0bc99a92011-03-17 04:34:43 +00002134 pjmedia_transport_close(call_med->tp);
Nanang Izzuddinb6c239c2011-05-10 05:42:28 +00002135 call_med->tp = call_med->tp_orig = NULL;
Benny Prijono0bc99a92011-03-17 04:34:43 +00002136 }
Sauw Minge7dbbc82011-10-24 09:28:13 +00002137 call_med->tp_orig = NULL;
Benny Prijonod8179652008-01-23 20:39:07 +00002138 }
Nanang Izzuddin670f71b2009-01-20 14:05:54 +00002139
Benny Prijonob90fd382011-09-18 14:59:56 +00002140 pj_log_pop_indent();
Nanang Izzuddin670f71b2009-01-20 14:05:54 +00002141
Benny Prijonoc97608e2007-03-23 16:34:20 +00002142 return PJ_SUCCESS;
2143}
2144
2145
Benny Prijono0bc99a92011-03-17 04:34:43 +00002146pj_status_t pjsua_media_channel_update(pjsua_call_id call_id,
2147 const pjmedia_sdp_session *local_sdp,
2148 const pjmedia_sdp_session *remote_sdp)
2149{
2150 pjsua_call *call = &pjsua_var.calls[call_id];
Nanang Izzuddind8c33d82011-08-18 18:30:55 +00002151 pjsua_acc *acc = &pjsua_var.acc[call->acc_id];
Benny Prijono0bc99a92011-03-17 04:34:43 +00002152 pj_pool_t *tmp_pool = call->inv->pool_prov;
2153 unsigned mi;
Nanang Izzuddinb6c239c2011-05-10 05:42:28 +00002154 pj_bool_t got_media = PJ_FALSE;
Nanang Izzuddinbf26db12011-03-18 07:54:50 +00002155 pj_status_t status = PJ_SUCCESS;
Benny Prijono0bc99a92011-03-17 04:34:43 +00002156
Nanang Izzuddind8c33d82011-08-18 18:30:55 +00002157 const pj_str_t STR_AUDIO = { "audio", 5 };
2158 const pj_str_t STR_VIDEO = { "video", 5 };
2159 pj_uint8_t maudidx[PJSUA_MAX_CALL_MEDIA];
2160 unsigned maudcnt = PJ_ARRAY_SIZE(maudidx);
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00002161 unsigned mtotaudcnt = PJ_ARRAY_SIZE(maudidx);
Nanang Izzuddind8c33d82011-08-18 18:30:55 +00002162 pj_uint8_t mvididx[PJSUA_MAX_CALL_MEDIA];
2163 unsigned mvidcnt = PJ_ARRAY_SIZE(mvididx);
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00002164 unsigned mtotvidcnt = PJ_ARRAY_SIZE(mvididx);
Nanang Izzuddind8c33d82011-08-18 18:30:55 +00002165 pj_bool_t need_renego_sdp = PJ_FALSE;
2166
Benny Prijono0bc99a92011-03-17 04:34:43 +00002167 if (pjsua_get_state() != PJSUA_STATE_RUNNING)
2168 return PJ_EBUSY;
2169
Benny Prijonob90fd382011-09-18 14:59:56 +00002170 PJ_LOG(4,(THIS_FILE, "Call %d: updating media..", call_id));
2171 pj_log_push_indent();
2172
Benny Prijono0bc99a92011-03-17 04:34:43 +00002173 /* Destroy existing media session, if any. */
2174 stop_media_session(call->index);
2175
Nanang Izzuddind8c33d82011-08-18 18:30:55 +00002176 /* Call media count must be at least equal to SDP media. Note that
2177 * it may not be equal when remote removed any SDP media line.
2178 */
Nanang Izzuddin1d568722012-04-24 05:40:32 +00002179 pj_assert(call->med_prov_cnt >= local_sdp->media_count);
Nanang Izzuddind8c33d82011-08-18 18:30:55 +00002180
Benny Prijono0bc99a92011-03-17 04:34:43 +00002181 /* Reset audio_idx first */
2182 call->audio_idx = -1;
2183
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00002184 /* Sort audio/video based on "quality" */
Nanang Izzuddind8c33d82011-08-18 18:30:55 +00002185 sort_media(local_sdp, &STR_AUDIO, acc->cfg.use_srtp,
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00002186 maudidx, &maudcnt, &mtotaudcnt);
Nanang Izzuddind8c33d82011-08-18 18:30:55 +00002187#if PJMEDIA_HAS_VIDEO
2188 sort_media(local_sdp, &STR_VIDEO, acc->cfg.use_srtp,
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00002189 mvididx, &mvidcnt, &mtotvidcnt);
Nanang Izzuddind8c33d82011-08-18 18:30:55 +00002190#else
2191 PJ_UNUSED_ARG(STR_VIDEO);
Nanang Izzuddin3d7385c2011-12-01 10:02:54 +00002192 mvidcnt = mtotvidcnt = 0;
Nanang Izzuddind8c33d82011-08-18 18:30:55 +00002193#endif
Nanang Izzuddindebd48a2011-12-01 09:06:14 +00002194
2195 /* Applying media count limitation. Note that in generating SDP answer,
2196 * no media count limitation applied, as we didn't know yet which media
2197 * would pass the SDP negotiation.
2198 */
Benny Prijonoae86e6a2011-12-27 12:47:52 +00002199 if (maudcnt > call->opt.aud_cnt || mvidcnt > call->opt.vid_cnt)
Nanang Izzuddind8c33d82011-08-18 18:30:55 +00002200 {
2201 pjmedia_sdp_session *local_sdp2;
2202
Benny Prijonoae86e6a2011-12-27 12:47:52 +00002203 maudcnt = PJ_MIN(maudcnt, call->opt.aud_cnt);
2204 mvidcnt = PJ_MIN(mvidcnt, call->opt.vid_cnt);
Nanang Izzuddind8c33d82011-08-18 18:30:55 +00002205 local_sdp2 = pjmedia_sdp_session_clone(tmp_pool, local_sdp);
2206
2207 for (mi=0; mi < local_sdp2->media_count; ++mi) {
2208 pjmedia_sdp_media *m = local_sdp2->media[mi];
2209
2210 if (m->desc.port == 0 ||
2211 pj_memchr(maudidx, mi, maudcnt*sizeof(maudidx[0])) ||
2212 pj_memchr(mvididx, mi, mvidcnt*sizeof(mvididx[0])))
2213 {
2214 continue;
2215 }
2216
2217 /* Deactivate this media */
2218 pjmedia_sdp_media_deactivate(tmp_pool, m);
2219 }
2220
2221 local_sdp = local_sdp2;
2222 need_renego_sdp = PJ_TRUE;
2223 }
2224
Benny Prijono0bc99a92011-03-17 04:34:43 +00002225 /* Process each media stream */
Nanang Izzuddin1d568722012-04-24 05:40:32 +00002226 for (mi=0; mi < call->med_prov_cnt; ++mi) {
2227 pjsua_call_media *call_med = &call->media_prov[mi];
Benny Prijono0bc99a92011-03-17 04:34:43 +00002228
Nanang Izzuddinb6c239c2011-05-10 05:42:28 +00002229 if (mi >= local_sdp->media_count ||
2230 mi >= remote_sdp->media_count)
Benny Prijono0bc99a92011-03-17 04:34:43 +00002231 {
Nanang Izzuddinb6c239c2011-05-10 05:42:28 +00002232 /* This may happen when remote removed any SDP media lines in
2233 * its re-offer.
2234 */
2235 continue;
2236#if 0
Benny Prijono0bc99a92011-03-17 04:34:43 +00002237 /* Something is wrong */
2238 PJ_LOG(1,(THIS_FILE, "Error updating media for call %d: "
2239 "invalid media index %d in SDP", call_id, mi));
Benny Prijonob90fd382011-09-18 14:59:56 +00002240 status = PJMEDIA_SDP_EINSDP;
2241 goto on_error;
Nanang Izzuddinb6c239c2011-05-10 05:42:28 +00002242#endif
Benny Prijono0bc99a92011-03-17 04:34:43 +00002243 }
2244
Benny Prijono1312e752012-03-22 09:56:52 +00002245 if (call_med->type==PJMEDIA_TYPE_AUDIO) {
2246 pjmedia_stream_info the_si, *si = &the_si;
2247
2248 status = pjmedia_stream_info_from_sdp(si, tmp_pool, pjsua_var.med_endpt,
2249 local_sdp, remote_sdp, mi);
2250 if (status != PJ_SUCCESS) {
2251 PJ_PERROR(1,(THIS_FILE, status,
2252 "pjmedia_stream_info_from_sdp() failed "
2253 "for call_id %d media %d",
2254 call_id, mi));
2255 continue;
2256 }
2257
2258 /* Check if no media is active */
2259 if (si->dir == PJMEDIA_DIR_NONE) {
2260 /* Update call media state and direction */
2261 call_med->state = PJSUA_CALL_MEDIA_NONE;
2262 call_med->dir = PJMEDIA_DIR_NONE;
2263
2264 } else {
2265 pjmedia_transport_info tp_info;
2266
2267 /* Start/restart media transport based on info in SDP */
2268 status = pjmedia_transport_media_start(call_med->tp,
2269 tmp_pool, local_sdp,
2270 remote_sdp, mi);
2271 if (status != PJ_SUCCESS) {
2272 PJ_PERROR(1,(THIS_FILE, status,
2273 "pjmedia_transport_media_start() failed "
2274 "for call_id %d media %d",
2275 call_id, mi));
2276 continue;
2277 }
2278
2279 pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_RUNNING);
2280
2281 /* Get remote SRTP usage policy */
2282 pjmedia_transport_info_init(&tp_info);
2283 pjmedia_transport_get_info(call_med->tp, &tp_info);
2284 if (tp_info.specific_info_cnt > 0) {
2285 unsigned i;
2286 for (i = 0; i < tp_info.specific_info_cnt; ++i) {
2287 if (tp_info.spc_info[i].type == PJMEDIA_TRANSPORT_TYPE_SRTP)
2288 {
2289 pjmedia_srtp_info *srtp_info =
2290 (pjmedia_srtp_info*) tp_info.spc_info[i].buffer;
2291
2292 call_med->rem_srtp_use = srtp_info->peer_use;
2293 break;
2294 }
2295 }
2296 }
2297
2298 /* Call media direction */
2299 call_med->dir = si->dir;
2300
2301 /* Call media state */
2302 if (call->local_hold)
2303 call_med->state = PJSUA_CALL_MEDIA_LOCAL_HOLD;
2304 else if (call_med->dir == PJMEDIA_DIR_DECODING)
2305 call_med->state = PJSUA_CALL_MEDIA_REMOTE_HOLD;
2306 else
2307 call_med->state = PJSUA_CALL_MEDIA_ACTIVE;
2308 }
2309
2310 /* Call implementation */
2311 status = pjsua_aud_channel_update(call_med, tmp_pool, si,
2312 local_sdp, remote_sdp);
2313 if (status != PJ_SUCCESS) {
2314 PJ_PERROR(1,(THIS_FILE, status,
2315 "pjsua_aud_channel_update() failed "
2316 "for call_id %d media %d",
2317 call_id, mi));
2318 continue;
2319 }
2320
2321 /* Print info. */
2322 if (status == PJ_SUCCESS) {
2323 char info[80];
2324 int info_len = 0;
2325 int len;
2326 const char *dir;
2327
2328 switch (si->dir) {
2329 case PJMEDIA_DIR_NONE:
2330 dir = "inactive";
2331 break;
2332 case PJMEDIA_DIR_ENCODING:
2333 dir = "sendonly";
2334 break;
2335 case PJMEDIA_DIR_DECODING:
2336 dir = "recvonly";
2337 break;
2338 case PJMEDIA_DIR_ENCODING_DECODING:
2339 dir = "sendrecv";
2340 break;
2341 default:
2342 dir = "unknown";
2343 break;
2344 }
2345 len = pj_ansi_sprintf( info+info_len,
2346 ", stream #%d: %.*s (%s)", mi,
2347 (int)si->fmt.encoding_name.slen,
2348 si->fmt.encoding_name.ptr,
2349 dir);
2350 if (len > 0)
2351 info_len += len;
2352 PJ_LOG(4,(THIS_FILE,"Audio updated%s", info));
2353 }
2354
2355
Benny Prijono0bc99a92011-03-17 04:34:43 +00002356 if (call->audio_idx==-1 && status==PJ_SUCCESS &&
Benny Prijono1312e752012-03-22 09:56:52 +00002357 si->dir != PJMEDIA_DIR_NONE)
Benny Prijono0bc99a92011-03-17 04:34:43 +00002358 {
2359 call->audio_idx = mi;
2360 }
Benny Prijono1312e752012-03-22 09:56:52 +00002361
Nanang Izzuddin63b3c132011-07-19 11:11:07 +00002362#if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0)
Benny Prijono1312e752012-03-22 09:56:52 +00002363 } else if (call_med->type==PJMEDIA_TYPE_VIDEO) {
2364 pjmedia_vid_stream_info the_si, *si = &the_si;
2365
2366 status = pjmedia_vid_stream_info_from_sdp(si, tmp_pool, pjsua_var.med_endpt,
2367 local_sdp, remote_sdp, mi);
2368 if (status != PJ_SUCCESS) {
2369 PJ_PERROR(1,(THIS_FILE, status,
2370 "pjmedia_vid_stream_info_from_sdp() failed "
2371 "for call_id %d media %d",
2372 call_id, mi));
2373 continue;
2374 }
2375
2376 /* Check if no media is active */
2377 if (si->dir == PJMEDIA_DIR_NONE) {
Nanang Izzuddin1d568722012-04-24 05:40:32 +00002378 /* Update call media state and direction */
Benny Prijono1312e752012-03-22 09:56:52 +00002379 call_med->state = PJSUA_CALL_MEDIA_NONE;
Benny Prijono1312e752012-03-22 09:56:52 +00002380 call_med->dir = PJMEDIA_DIR_NONE;
2381
2382 } else {
2383 pjmedia_transport_info tp_info;
2384
2385 /* Start/restart media transport */
2386 status = pjmedia_transport_media_start(call_med->tp,
2387 tmp_pool, local_sdp,
2388 remote_sdp, mi);
2389 if (status != PJ_SUCCESS) {
2390 PJ_PERROR(1,(THIS_FILE, status,
2391 "pjmedia_transport_media_start() failed "
2392 "for call_id %d media %d",
2393 call_id, mi));
2394 continue;
2395 }
2396
2397 pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_RUNNING);
2398
2399 /* Get remote SRTP usage policy */
2400 pjmedia_transport_info_init(&tp_info);
2401 pjmedia_transport_get_info(call_med->tp, &tp_info);
2402 if (tp_info.specific_info_cnt > 0) {
2403 unsigned i;
2404 for (i = 0; i < tp_info.specific_info_cnt; ++i) {
2405 if (tp_info.spc_info[i].type ==
2406 PJMEDIA_TRANSPORT_TYPE_SRTP)
2407 {
2408 pjmedia_srtp_info *sri;
2409 sri=(pjmedia_srtp_info*)tp_info.spc_info[i].buffer;
2410 call_med->rem_srtp_use = sri->peer_use;
2411 break;
2412 }
2413 }
2414 }
2415
2416 /* Call media direction */
2417 call_med->dir = si->dir;
2418
2419 /* Call media state */
2420 if (call->local_hold)
2421 call_med->state = PJSUA_CALL_MEDIA_LOCAL_HOLD;
2422 else if (call_med->dir == PJMEDIA_DIR_DECODING)
2423 call_med->state = PJSUA_CALL_MEDIA_REMOTE_HOLD;
2424 else
2425 call_med->state = PJSUA_CALL_MEDIA_ACTIVE;
2426 }
2427
2428 status = pjsua_vid_channel_update(call_med, tmp_pool, si,
2429 local_sdp, remote_sdp);
2430 if (status != PJ_SUCCESS) {
2431 PJ_PERROR(1,(THIS_FILE, status,
Nanang Izzuddin1dc6d212012-04-03 08:02:12 +00002432 "pjsua_vid_channel_update() failed "
Benny Prijono1312e752012-03-22 09:56:52 +00002433 "for call_id %d media %d",
2434 call_id, mi));
2435 continue;
2436 }
2437
2438 /* Print info. */
2439 {
2440 char info[80];
2441 int info_len = 0;
2442 int len;
2443 const char *dir;
2444
2445 switch (si->dir) {
2446 case PJMEDIA_DIR_NONE:
2447 dir = "inactive";
2448 break;
2449 case PJMEDIA_DIR_ENCODING:
2450 dir = "sendonly";
2451 break;
2452 case PJMEDIA_DIR_DECODING:
2453 dir = "recvonly";
2454 break;
2455 case PJMEDIA_DIR_ENCODING_DECODING:
2456 dir = "sendrecv";
2457 break;
2458 default:
2459 dir = "unknown";
2460 break;
2461 }
2462 len = pj_ansi_sprintf( info+info_len,
2463 ", stream #%d: %.*s (%s)", mi,
2464 (int)si->codec_info.encoding_name.slen,
2465 si->codec_info.encoding_name.ptr,
2466 dir);
2467 if (len > 0)
2468 info_len += len;
2469 PJ_LOG(4,(THIS_FILE,"Video updated%s", info));
2470 }
2471
Nanang Izzuddin50fae732011-03-22 09:49:23 +00002472#endif
Benny Prijono1312e752012-03-22 09:56:52 +00002473 } else {
Nanang Izzuddinb6c239c2011-05-10 05:42:28 +00002474 status = PJMEDIA_EINVALIMEDIATYPE;
Benny Prijono0bc99a92011-03-17 04:34:43 +00002475 }
2476
Nanang Izzuddind8c33d82011-08-18 18:30:55 +00002477 /* Close the transport of deactivated media, need this here as media
2478 * can be deactivated by the SDP negotiation and the max media count
2479 * (account) setting.
2480 */
2481 if (local_sdp->media[mi]->desc.port==0 && call_med->tp) {
Nanang Izzuddin1d568722012-04-24 05:40:32 +00002482 pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_NULL);
Nanang Izzuddind8c33d82011-08-18 18:30:55 +00002483 pjmedia_transport_close(call_med->tp);
2484 call_med->tp = call_med->tp_orig = NULL;
Nanang Izzuddind8c33d82011-08-18 18:30:55 +00002485 }
2486
Benny Prijono0bc99a92011-03-17 04:34:43 +00002487 if (status != PJ_SUCCESS) {
Nanang Izzuddinb6c239c2011-05-10 05:42:28 +00002488 PJ_PERROR(1,(THIS_FILE, status, "Error updating media call%02d:%d",
Benny Prijono0bc99a92011-03-17 04:34:43 +00002489 call_id, mi));
Nanang Izzuddinb6c239c2011-05-10 05:42:28 +00002490 } else {
2491 got_media = PJ_TRUE;
Benny Prijono0bc99a92011-03-17 04:34:43 +00002492 }
2493 }
2494
Nanang Izzuddin1d568722012-04-24 05:40:32 +00002495 /* Update call media from provisional media */
2496 call->med_cnt = call->med_prov_cnt;
2497 pj_memcpy(call->media, call->media_prov,
2498 sizeof(call->media_prov[0]) * call->med_prov_cnt);
2499
Nanang Izzuddind8c33d82011-08-18 18:30:55 +00002500 /* Perform SDP re-negotiation if needed. */
2501 if (got_media && need_renego_sdp) {
2502 pjmedia_sdp_neg *neg = call->inv->neg;
2503
2504 /* This should only happen when we are the answerer. */
2505 PJ_ASSERT_RETURN(neg && !pjmedia_sdp_neg_was_answer_remote(neg),
2506 PJMEDIA_SDPNEG_EINSTATE);
2507
2508 status = pjmedia_sdp_neg_set_remote_offer(tmp_pool, neg, remote_sdp);
2509 if (status != PJ_SUCCESS)
Benny Prijonob90fd382011-09-18 14:59:56 +00002510 goto on_error;
Nanang Izzuddind8c33d82011-08-18 18:30:55 +00002511
2512 status = pjmedia_sdp_neg_set_local_answer(tmp_pool, neg, local_sdp);
2513 if (status != PJ_SUCCESS)
Benny Prijonob90fd382011-09-18 14:59:56 +00002514 goto on_error;
Nanang Izzuddind8c33d82011-08-18 18:30:55 +00002515
2516 status = pjmedia_sdp_neg_negotiate(tmp_pool, neg, 0);
2517 if (status != PJ_SUCCESS)
Benny Prijonob90fd382011-09-18 14:59:56 +00002518 goto on_error;
Nanang Izzuddind8c33d82011-08-18 18:30:55 +00002519 }
2520
Benny Prijonob90fd382011-09-18 14:59:56 +00002521 pj_log_pop_indent();
Nanang Izzuddinb6c239c2011-05-10 05:42:28 +00002522 return (got_media? PJ_SUCCESS : PJMEDIA_SDPNEG_ENOMEDIA);
Benny Prijonob90fd382011-09-18 14:59:56 +00002523
2524on_error:
2525 pj_log_pop_indent();
2526 return status;
Benny Prijono0bc99a92011-03-17 04:34:43 +00002527}
2528
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002529/*****************************************************************************
2530 * Codecs.
2531 */
2532
2533/*
2534 * Enum all supported codecs in the system.
2535 */
2536PJ_DEF(pj_status_t) pjsua_enum_codecs( pjsua_codec_info id[],
2537 unsigned *p_count )
2538{
2539 pjmedia_codec_mgr *codec_mgr;
2540 pjmedia_codec_info info[32];
2541 unsigned i, count, prio[32];
2542 pj_status_t status;
2543
2544 codec_mgr = pjmedia_endpt_get_codec_mgr(pjsua_var.med_endpt);
2545 count = PJ_ARRAY_SIZE(info);
2546 status = pjmedia_codec_mgr_enum_codecs( codec_mgr, &count, info, prio);
2547 if (status != PJ_SUCCESS) {
2548 *p_count = 0;
2549 return status;
2550 }
2551
2552 if (count > *p_count) count = *p_count;
2553
2554 for (i=0; i<count; ++i) {
Nanang Izzuddin56b2ce42011-04-06 13:55:01 +00002555 pj_bzero(&id[i], sizeof(pjsua_codec_info));
2556
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002557 pjmedia_codec_info_to_id(&info[i], id[i].buf_, sizeof(id[i].buf_));
2558 id[i].codec_id = pj_str(id[i].buf_);
2559 id[i].priority = (pj_uint8_t) prio[i];
2560 }
2561
2562 *p_count = count;
2563
2564 return PJ_SUCCESS;
2565}
2566
2567
2568/*
2569 * Change codec priority.
2570 */
2571PJ_DEF(pj_status_t) pjsua_codec_set_priority( const pj_str_t *codec_id,
2572 pj_uint8_t priority )
2573{
Benny Prijono88accae2008-06-26 15:48:14 +00002574 const pj_str_t all = { NULL, 0 };
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002575 pjmedia_codec_mgr *codec_mgr;
2576
2577 codec_mgr = pjmedia_endpt_get_codec_mgr(pjsua_var.med_endpt);
2578
Benny Prijono88accae2008-06-26 15:48:14 +00002579 if (codec_id->slen==1 && *codec_id->ptr=='*')
2580 codec_id = &all;
2581
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002582 return pjmedia_codec_mgr_set_codec_priority(codec_mgr, codec_id,
2583 priority);
2584}
2585
2586
2587/*
2588 * Get codec parameters.
2589 */
2590PJ_DEF(pj_status_t) pjsua_codec_get_param( const pj_str_t *codec_id,
2591 pjmedia_codec_param *param )
2592{
Benny Prijono88accae2008-06-26 15:48:14 +00002593 const pj_str_t all = { NULL, 0 };
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002594 const pjmedia_codec_info *info;
2595 pjmedia_codec_mgr *codec_mgr;
2596 unsigned count = 1;
2597 pj_status_t status;
2598
2599 codec_mgr = pjmedia_endpt_get_codec_mgr(pjsua_var.med_endpt);
2600
Benny Prijono88accae2008-06-26 15:48:14 +00002601 if (codec_id->slen==1 && *codec_id->ptr=='*')
2602 codec_id = &all;
2603
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002604 status = pjmedia_codec_mgr_find_codecs_by_id(codec_mgr, codec_id,
2605 &count, &info, NULL);
2606 if (status != PJ_SUCCESS)
2607 return status;
2608
2609 if (count != 1)
Nanang Izzuddin50fae732011-03-22 09:49:23 +00002610 return (count > 1? PJ_ETOOMANY : PJ_ENOTFOUND);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002611
2612 status = pjmedia_codec_mgr_get_default_param( codec_mgr, info, param);
2613 return status;
2614}
2615
2616
2617/*
2618 * Set codec parameters.
2619 */
Nanang Izzuddin06839e72010-01-27 11:48:31 +00002620PJ_DEF(pj_status_t) pjsua_codec_set_param( const pj_str_t *codec_id,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002621 const pjmedia_codec_param *param)
2622{
Nanang Izzuddin06839e72010-01-27 11:48:31 +00002623 const pjmedia_codec_info *info[2];
2624 pjmedia_codec_mgr *codec_mgr;
2625 unsigned count = 2;
2626 pj_status_t status;
2627
2628 codec_mgr = pjmedia_endpt_get_codec_mgr(pjsua_var.med_endpt);
2629
2630 status = pjmedia_codec_mgr_find_codecs_by_id(codec_mgr, codec_id,
2631 &count, info, NULL);
2632 if (status != PJ_SUCCESS)
2633 return status;
2634
2635 /* Codec ID should be specific, except for G.722.1 */
2636 if (count > 1 &&
2637 pj_strnicmp2(codec_id, "G7221/16", 8) != 0 &&
2638 pj_strnicmp2(codec_id, "G7221/32", 8) != 0)
2639 {
2640 pj_assert(!"Codec ID is not specific");
2641 return PJ_ETOOMANY;
2642 }
2643
2644 status = pjmedia_codec_mgr_set_default_param(codec_mgr, info[0], param);
2645 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002646}
Nanang Izzuddin50fae732011-03-22 09:49:23 +00002647
2648
Nanang Izzuddin3da4ad82011-12-07 10:43:28 +00002649pj_status_t pjsua_media_apply_xml_control(pjsua_call_id call_id,
2650 const pj_str_t *xml_st)
2651{
Sauw Ming2a044602012-04-26 03:39:24 +00002652#if PJMEDIA_HAS_VIDEO
Nanang Izzuddin3da4ad82011-12-07 10:43:28 +00002653 pjsua_call *call = &pjsua_var.calls[call_id];
2654 const pj_str_t PICT_FAST_UPDATE = {"picture_fast_update", 19};
2655
Nanang Izzuddin3da4ad82011-12-07 10:43:28 +00002656 if (pj_strstr(xml_st, &PICT_FAST_UPDATE)) {
2657 unsigned i;
2658
2659 PJ_LOG(4,(THIS_FILE, "Received keyframe request via SIP INFO"));
2660
2661 for (i = 0; i < call->med_cnt; ++i) {
2662 pjsua_call_media *cm = &call->media[i];
2663 if (cm->type != PJMEDIA_TYPE_VIDEO || !cm->strm.v.stream)
2664 continue;
2665
2666 pjmedia_vid_stream_send_keyframe(cm->strm.v.stream);
2667 }
2668
2669 return PJ_SUCCESS;
2670 }
2671#endif
2672
2673 /* Just to avoid compiler warning of unused var */
Sauw Ming2a044602012-04-26 03:39:24 +00002674 PJ_UNUSED_ARG(call_id);
Nanang Izzuddin3da4ad82011-12-07 10:43:28 +00002675 PJ_UNUSED_ARG(xml_st);
2676
2677 return PJ_ENOTSUP;
2678}
2679