1
/* $Id: pjsua_media.c 4750 2014-02-19 04:11:43Z bennylp $ */
3
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
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.
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.
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
20
#include <pjsua-lib/pjsua.h>
21
#include <pjsua-lib/pjsua_internal.h>
24
#define THIS_FILE "pjsua_media.c"
26
#define DEFAULT_RTP_PORT 4000
28
#ifndef PJSUA_REQUIRE_CONSECUTIVE_RTCP_PORT
29
# define PJSUA_REQUIRE_CONSECUTIVE_RTCP_PORT 0
32
static void pjsua_media_config_dup(pj_pool_t *pool,
33
pjsua_media_config *dst,
34
const pjsua_media_config *src)
36
pj_memcpy(dst, src, sizeof(*src));
37
pj_strdup(pool, &dst->turn_server, &src->turn_server);
38
pj_stun_auth_cred_dup(pool, &dst->turn_auth_cred, &src->turn_auth_cred);
43
* Init media subsystems.
45
pj_status_t pjsua_media_subsys_init(const pjsua_media_config *cfg)
51
/* Specify which audio device settings are save-able */
52
pjsua_var.aud_svmask = 0xFFFFFFFF;
53
/* These are not-settable */
54
pjsua_var.aud_svmask &= ~(PJMEDIA_AUD_DEV_CAP_EXT_FORMAT |
55
PJMEDIA_AUD_DEV_CAP_INPUT_SIGNAL_METER |
56
PJMEDIA_AUD_DEV_CAP_OUTPUT_SIGNAL_METER);
57
/* EC settings use different API */
58
pjsua_var.aud_svmask &= ~(PJMEDIA_AUD_DEV_CAP_EC |
59
PJMEDIA_AUD_DEV_CAP_EC_TAIL);
61
/* Copy configuration */
62
pjsua_media_config_dup(pjsua_var.pool, &pjsua_var.media_cfg, cfg);
64
/* Normalize configuration */
65
if (pjsua_var.media_cfg.snd_clock_rate == 0) {
66
pjsua_var.media_cfg.snd_clock_rate = pjsua_var.media_cfg.clock_rate;
69
if (pjsua_var.media_cfg.has_ioqueue &&
70
pjsua_var.media_cfg.thread_cnt == 0)
72
pjsua_var.media_cfg.thread_cnt = 1;
75
if (pjsua_var.media_cfg.max_media_ports < pjsua_var.ua_cfg.max_calls) {
76
pjsua_var.media_cfg.max_media_ports = pjsua_var.ua_cfg.max_calls + 2;
79
/* Create media endpoint. */
80
status = pjmedia_endpt_create(&pjsua_var.cp.factory,
81
pjsua_var.media_cfg.has_ioqueue? NULL :
82
pjsip_endpt_get_ioqueue(pjsua_var.endpt),
83
pjsua_var.media_cfg.thread_cnt,
84
&pjsua_var.med_endpt);
85
if (status != PJ_SUCCESS) {
86
pjsua_perror(THIS_FILE,
87
"Media stack initialization has returned error",
92
status = pjsua_aud_subsys_init();
93
if (status != PJ_SUCCESS)
96
#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
97
/* Initialize SRTP library (ticket #788). */
98
status = pjmedia_srtp_init_lib(pjsua_var.med_endpt);
99
if (status != PJ_SUCCESS) {
100
pjsua_perror(THIS_FILE, "Error initializing SRTP library",
107
#if PJMEDIA_HAS_VIDEO
108
status = pjsua_vid_subsys_init();
109
if (status != PJ_SUCCESS)
122
* Start pjsua media subsystem.
124
pj_status_t pjsua_media_subsys_start(void)
128
pj_log_push_indent();
130
#if DISABLED_FOR_TICKET_1185
131
/* Create media for calls, if none is specified */
132
if (pjsua_var.calls[0].media[0].tp == NULL) {
133
pjsua_transport_config transport_cfg;
135
/* Create default transport config */
136
pjsua_transport_config_default(&transport_cfg);
137
transport_cfg.port = DEFAULT_RTP_PORT;
139
status = pjsua_media_transports_create(&transport_cfg);
140
if (status != PJ_SUCCESS) {
148
status = pjsua_aud_subsys_start();
149
if (status != PJ_SUCCESS) {
155
#if PJMEDIA_HAS_VIDEO
156
status = pjsua_vid_subsys_start();
157
if (status != PJ_SUCCESS) {
158
pjsua_aud_subsys_destroy();
164
/* Perform NAT detection */
165
if (pjsua_var.ua_cfg.stun_srv_cnt) {
166
status = pjsua_detect_nat_type();
167
if (status != PJ_SUCCESS) {
168
PJ_PERROR(1,(THIS_FILE, status, "NAT type detection failed"));
178
* Destroy pjsua media subsystem.
180
pj_status_t pjsua_media_subsys_destroy(unsigned flags)
182
PJ_UNUSED_ARG(flags);
184
PJ_LOG(4,(THIS_FILE, "Shutting down media.."));
185
pj_log_push_indent();
187
if (pjsua_var.med_endpt) {
188
/* Wait for media endpoint's worker threads to quit. */
189
pjmedia_endpt_stop_threads(pjsua_var.med_endpt);
191
pjsua_aud_subsys_destroy();
195
// This part has been moved out to pjsua_destroy() (see also #1717).
196
/* Close media transports */
197
for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
198
/* TODO: check if we're not allowed to send to network in the
199
* "flags", and if so do not do TURN allocation...
201
PJ_UNUSED_ARG(flags);
202
pjsua_media_channel_deinit(i);
206
/* Destroy media endpoint. */
207
if (pjsua_var.med_endpt) {
209
# if PJMEDIA_HAS_VIDEO
210
pjsua_vid_subsys_destroy();
213
pjmedia_endpt_destroy(pjsua_var.med_endpt);
214
pjsua_var.med_endpt = NULL;
216
/* Deinitialize sound subsystem */
217
// Not necessary, as pjmedia_snd_deinit() should have been called
218
// in pjmedia_endpt_destroy().
219
//pjmedia_snd_deinit();
228
* Create RTP and RTCP socket pair, and possibly resolve their public
231
static pj_status_t create_rtp_rtcp_sock(pjsua_call_media *call_med,
232
const pjsua_transport_config *cfg,
233
pjmedia_sock_info *skinfo)
241
pj_sockaddr bound_addr;
242
pj_sockaddr mapped_addr[2];
243
pj_status_t status = PJ_SUCCESS;
244
char addr_buf[PJ_INET6_ADDRSTRLEN+10];
245
pjsua_acc *acc = &pjsua_var.acc[call_med->call->acc_id];
248
use_ipv6 = (acc->cfg.ipv6_media_use != PJSUA_IPV6_DISABLED);
249
af = use_ipv6 ? pj_AF_INET6() : pj_AF_INET();
251
/* Make sure STUN server resolution has completed */
252
if (!use_ipv6 && pjsua_sip_acc_is_using_stun(call_med->call->acc_id)) {
253
status = resolve_stun_server(PJ_TRUE);
254
if (status != PJ_SUCCESS) {
255
pjsua_perror(THIS_FILE, "Error resolving STUN server", status);
260
if (acc->next_rtp_port == 0)
261
acc->next_rtp_port = (pj_uint16_t)cfg->port;
263
if (acc->next_rtp_port == 0)
264
acc->next_rtp_port = (pj_uint16_t)DEFAULT_RTP_PORT;
267
sock[i] = PJ_INVALID_SOCKET;
269
pj_sockaddr_init(af, &bound_addr, NULL, 0);
270
if (cfg->bound_addr.slen) {
271
status = pj_sockaddr_set_str_addr(af, &bound_addr, &cfg->bound_addr);
272
if (status != PJ_SUCCESS) {
273
pjsua_perror(THIS_FILE, "Unable to resolve transport bind address",
279
/* Loop retry to bind RTP and RTCP sockets. */
280
for (i=0; i<RTP_RETRY; ++i, acc->next_rtp_port += 2) {
282
if (cfg->port > 0 && cfg->port_range > 0 &&
283
(acc->next_rtp_port > cfg->port + cfg->port_range ||
284
acc->next_rtp_port < cfg->port))
286
acc->next_rtp_port = (pj_uint16_t)cfg->port;
289
/* Create RTP socket. */
290
status = pj_sock_socket(af, pj_SOCK_DGRAM(), 0, &sock[0]);
291
if (status != PJ_SUCCESS) {
292
pjsua_perror(THIS_FILE, "socket() error", status);
296
/* Apply QoS to RTP socket, if specified */
297
status = pj_sock_apply_qos2(sock[0], cfg->qos_type,
299
2, THIS_FILE, "RTP socket");
301
/* Bind RTP socket */
302
pj_sockaddr_set_port(&bound_addr, acc->next_rtp_port);
303
status=pj_sock_bind(sock[0], &bound_addr,
304
pj_sockaddr_get_len(&bound_addr));
305
if (status != PJ_SUCCESS) {
306
pj_sock_close(sock[0]);
307
sock[0] = PJ_INVALID_SOCKET;
311
/* Create RTCP socket. */
312
status = pj_sock_socket(af, pj_SOCK_DGRAM(), 0, &sock[1]);
313
if (status != PJ_SUCCESS) {
314
pjsua_perror(THIS_FILE, "socket() error", status);
315
pj_sock_close(sock[0]);
319
/* Apply QoS to RTCP socket, if specified */
320
status = pj_sock_apply_qos2(sock[1], cfg->qos_type,
322
2, THIS_FILE, "RTCP socket");
324
/* Bind RTCP socket */
325
pj_sockaddr_set_port(&bound_addr, (pj_uint16_t)(acc->next_rtp_port+1));
326
status=pj_sock_bind(sock[1], &bound_addr,
327
pj_sockaddr_get_len(&bound_addr));
328
if (status != PJ_SUCCESS) {
329
pj_sock_close(sock[0]);
330
sock[0] = PJ_INVALID_SOCKET;
332
pj_sock_close(sock[1]);
333
sock[1] = PJ_INVALID_SOCKET;
338
* If we're configured to use STUN, then find out the mapped address,
339
* and make sure that the mapped RTCP port is adjacent with the RTP.
341
if (!use_ipv6 && pjsua_sip_acc_is_using_stun(call_med->call->acc_id) &&
342
pjsua_var.stun_srv.addr.sa_family != 0)
346
pj_sockaddr_in resolved_addr[2];
347
pjstun_setting stun_opt;
349
pj_ansi_strcpy(ip_addr,
350
pj_inet_ntoa(pjsua_var.stun_srv.ipv4.sin_addr));
351
stun_srv = pj_str(ip_addr);
353
pj_bzero(&stun_opt, sizeof(stun_opt));
354
stun_opt.use_stun2 = pjsua_var.ua_cfg.stun_map_use_stun2;
355
stun_opt.srv1 = stun_opt.srv2 = stun_srv;
356
stun_opt.port1 = stun_opt.port2 =
357
pj_ntohs(pjsua_var.stun_srv.ipv4.sin_port);
358
status=pjstun_get_mapped_addr2(&pjsua_var.cp.factory, &stun_opt,
359
2, sock, resolved_addr);
360
#if defined(PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT) && \
361
PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT!=0
362
/* Handle EPIPE (Broken Pipe) error, which happens on UDP socket
363
* after app wakes up from suspended state. In this case, simply
365
* P.S.: The magic status is PJ_STATUS_FROM_OS(EPIPE)
367
if (status == 120032) {
368
PJ_LOG(4,(THIS_FILE, "Got EPIPE error, retrying.."));
369
pj_sock_close(sock[0]);
370
sock[0] = PJ_INVALID_SOCKET;
372
pj_sock_close(sock[1]);
373
sock[1] = PJ_INVALID_SOCKET;
379
if (status != PJ_SUCCESS) {
380
pjsua_perror(THIS_FILE, "STUN resolve error", status);
384
pj_sockaddr_cp(&mapped_addr[0], &resolved_addr[0]);
385
pj_sockaddr_cp(&mapped_addr[1], &resolved_addr[1]);
387
#if PJSUA_REQUIRE_CONSECUTIVE_RTCP_PORT
388
if (pj_sockaddr_get_port(&mapped_addr[1]) ==
389
pj_sockaddr_get_port(&mapped_addr[0])+1)
395
pj_sock_close(sock[0]);
396
sock[0] = PJ_INVALID_SOCKET;
398
pj_sock_close(sock[1]);
399
sock[1] = PJ_INVALID_SOCKET;
401
if (pj_sockaddr_get_port(&mapped_addr[1]) !=
402
pj_sockaddr_get_port(&mapped_addr[0])+1)
405
"Note: STUN mapped RTCP port %d is not adjacent"
407
pj_sockaddr_get_port(&mapped_addr[1]),
408
pj_sockaddr_get_port(&mapped_addr[0])));
414
} else if (cfg->public_addr.slen) {
416
status = pj_sockaddr_init(af, &mapped_addr[0], &cfg->public_addr,
417
(pj_uint16_t)acc->next_rtp_port);
418
if (status != PJ_SUCCESS)
421
status = pj_sockaddr_init(af, &mapped_addr[1], &cfg->public_addr,
422
(pj_uint16_t)(acc->next_rtp_port+1));
423
if (status != PJ_SUCCESS)
429
if (acc->cfg.allow_sdp_nat_rewrite && acc->reg_mapped_addr.slen) {
432
/* Take the address from mapped addr as seen by registrar */
433
status = pj_sockaddr_set_str_addr(af, &bound_addr,
434
&acc->reg_mapped_addr);
435
if (status != PJ_SUCCESS) {
436
/* just leave bound_addr with whatever it was
437
pj_bzero(pj_sockaddr_get_addr(&bound_addr),
438
pj_sockaddr_get_addr_len(&bound_addr));
443
if (!pj_sockaddr_has_addr(&bound_addr)) {
446
/* Get local IP address. */
447
status = pj_gethostip(af, &addr);
448
if (status != PJ_SUCCESS)
451
pj_sockaddr_copy_addr(&bound_addr, &addr);
454
for (i=0; i<2; ++i) {
455
pj_sockaddr_init(af, &mapped_addr[i], NULL, 0);
456
pj_sockaddr_copy_addr(&mapped_addr[i], &bound_addr);
457
pj_sockaddr_set_port(&mapped_addr[i],
458
(pj_uint16_t)(acc->next_rtp_port+i));
465
if (sock[0] == PJ_INVALID_SOCKET) {
467
"Unable to find appropriate RTP/RTCP ports combination"));
472
skinfo->rtp_sock = sock[0];
473
pj_sockaddr_cp(&skinfo->rtp_addr_name, &mapped_addr[0]);
475
skinfo->rtcp_sock = sock[1];
476
pj_sockaddr_cp(&skinfo->rtcp_addr_name, &mapped_addr[1]);
478
PJ_LOG(4,(THIS_FILE, "RTP socket reachable at %s",
479
pj_sockaddr_print(&skinfo->rtp_addr_name, addr_buf,
480
sizeof(addr_buf), 3)));
481
PJ_LOG(4,(THIS_FILE, "RTCP socket reachable at %s",
482
pj_sockaddr_print(&skinfo->rtcp_addr_name, addr_buf,
483
sizeof(addr_buf), 3)));
485
acc->next_rtp_port += 2;
489
for (i=0; i<2; ++i) {
490
if (sock[i] != PJ_INVALID_SOCKET)
491
pj_sock_close(sock[i]);
496
/* Create normal UDP media transports */
497
static pj_status_t create_udp_media_transport(const pjsua_transport_config *cfg,
498
pjsua_call_media *call_med)
500
pjmedia_sock_info skinfo;
503
status = create_rtp_rtcp_sock(call_med, cfg, &skinfo);
504
if (status != PJ_SUCCESS) {
505
pjsua_perror(THIS_FILE, "Unable to create RTP/RTCP socket",
510
status = pjmedia_transport_udp_attach(pjsua_var.med_endpt, NULL,
511
&skinfo, 0, &call_med->tp);
512
if (status != PJ_SUCCESS) {
513
pjsua_perror(THIS_FILE, "Unable to create media transport",
518
pjmedia_transport_simulate_lost(call_med->tp, PJMEDIA_DIR_ENCODING,
519
pjsua_var.media_cfg.tx_drop_pct);
521
pjmedia_transport_simulate_lost(call_med->tp, PJMEDIA_DIR_DECODING,
522
pjsua_var.media_cfg.rx_drop_pct);
524
call_med->tp_ready = PJ_SUCCESS;
530
pjmedia_transport_close(call_med->tp);
535
#if DISABLED_FOR_TICKET_1185
536
/* Create normal UDP media transports */
537
static pj_status_t create_udp_media_transports(pjsua_transport_config *cfg)
542
for (i=0; i < pjsua_var.ua_cfg.max_calls; ++i) {
543
pjsua_call *call = &pjsua_var.calls[i];
546
for (strm_idx=0; strm_idx < call->med_cnt; ++strm_idx) {
547
pjsua_call_media *call_med = &call->media[strm_idx];
549
status = create_udp_media_transport(cfg, &call_med->tp);
550
if (status != PJ_SUCCESS)
558
for (i=0; i < pjsua_var.ua_cfg.max_calls; ++i) {
559
pjsua_call *call = &pjsua_var.calls[i];
562
for (strm_idx=0; strm_idx < call->med_cnt; ++strm_idx) {
563
pjsua_call_media *call_med = &call->media[strm_idx];
566
pjmedia_transport_close(call_med->tp);
575
/* Deferred callback to notify ICE init complete */
576
static void ice_init_complete_cb(void *user_data)
578
pjsua_call_media *call_med = (pjsua_call_media*)user_data;
580
if (call_med->call == NULL)
583
/* No need to acquire_call() if we only change the tp_ready flag
584
* (i.e. transport is being created synchronously). Otherwise
585
* calling acquire_call() here may cause deadlock. See
586
* https://trac.pjsip.org/repos/ticket/1578
588
call_med->tp_ready = call_med->tp_result;
590
if (call_med->med_create_cb) {
591
pjsua_call *call = NULL;
592
pjsip_dialog *dlg = NULL;
594
if (acquire_call("ice_init_complete_cb", call_med->call->index,
595
&call, &dlg) != PJ_SUCCESS)
597
/* Call have been terminated */
601
(*call_med->med_create_cb)(call_med, call_med->tp_ready,
602
call_med->call->secure_level, NULL);
605
pjsip_dlg_dec_lock(dlg);
609
/* Deferred callback to notify ICE negotiation failure */
610
static void ice_failed_nego_cb(void *user_data)
612
int call_id = (int)(pj_ssize_t)user_data;
613
pjsua_call *call = NULL;
614
pjsip_dialog *dlg = NULL;
616
if (acquire_call("ice_failed_nego_cb", call_id,
617
&call, &dlg) != PJ_SUCCESS)
619
/* Call have been terminated */
623
pjsua_var.ua_cfg.cb.on_call_media_state(call_id);
626
pjsip_dlg_dec_lock(dlg);
630
/* This callback is called when ICE negotiation completes */
631
static void on_ice_complete(pjmedia_transport *tp,
635
pjsua_call_media *call_med = (pjsua_call_media*)tp->user_data;
641
call = call_med->call;
644
case PJ_ICE_STRANS_OP_INIT:
645
call_med->tp_result = result;
646
pjsua_schedule_timer2(&ice_init_complete_cb, call_med, 1);
648
case PJ_ICE_STRANS_OP_NEGOTIATION:
649
if (result == PJ_SUCCESS) {
650
/* Update RTP address */
651
pjmedia_transport_info tpinfo;
652
pjmedia_transport_info_init(&tpinfo);
653
pjmedia_transport_get_info(call_med->tp, &tpinfo);
654
pj_sockaddr_cp(&call_med->rtp_addr, &tpinfo.sock_info.rtp_addr_name);
656
call_med->state = PJSUA_CALL_MEDIA_ERROR;
657
call_med->dir = PJMEDIA_DIR_NONE;
658
if (call && pjsua_var.ua_cfg.cb.on_call_media_state) {
659
/* Defer the callback to a timer */
660
pjsua_schedule_timer2(&ice_failed_nego_cb,
661
(void*)(pj_ssize_t)call->index, 1);
664
/* Check if default ICE transport address is changed */
665
call->reinv_ice_sent = PJ_FALSE;
666
pjsua_call_schedule_reinvite_check(call, 0);
668
case PJ_ICE_STRANS_OP_KEEP_ALIVE:
669
if (result != PJ_SUCCESS) {
670
PJ_PERROR(4,(THIS_FILE, result,
671
"ICE keep alive failure for transport %d:%d",
672
call->index, call_med->idx));
674
if (pjsua_var.ua_cfg.cb.on_call_media_transport_state) {
675
pjsua_med_tp_state_info info;
677
pj_bzero(&info, sizeof(info));
678
info.med_idx = call_med->idx;
679
info.state = call_med->tp_st;
680
info.status = result;
682
(*pjsua_var.ua_cfg.cb.on_call_media_transport_state)(
685
if (pjsua_var.ua_cfg.cb.on_ice_transport_error) {
686
pjsua_call_id id = call->index;
687
(*pjsua_var.ua_cfg.cb.on_ice_transport_error)(id, op, result,
695
/* Parse "HOST:PORT" format */
696
static pj_status_t parse_host_port(const pj_str_t *host_port,
697
pj_str_t *host, pj_uint16_t *port)
701
str_port.ptr = pj_strchr(host_port, ':');
702
if (str_port.ptr != NULL) {
705
host->ptr = host_port->ptr;
706
host->slen = (str_port.ptr - host->ptr);
708
str_port.slen = host_port->slen - host->slen - 1;
709
iport = (int)pj_strtoul(&str_port);
710
if (iport < 1 || iport > 65535)
712
*port = (pj_uint16_t)iport;
721
/* Create ICE media transports (when ice is enabled) */
722
static pj_status_t create_ice_media_transport(
723
const pjsua_transport_config *cfg,
724
pjsua_call_media *call_med,
727
char stunip[PJ_INET6_ADDRSTRLEN];
728
pjsua_acc_config *acc_cfg;
729
pj_ice_strans_cfg ice_cfg;
730
pjmedia_ice_cb ice_cb;
735
acc_cfg = &pjsua_var.acc[call_med->call->acc_id].cfg;
737
/* Make sure STUN server resolution has completed */
738
status = resolve_stun_server(PJ_TRUE);
739
if (status != PJ_SUCCESS) {
740
pjsua_perror(THIS_FILE, "Error resolving STUN server", status);
744
/* Create ICE stream transport configuration */
745
pj_ice_strans_cfg_default(&ice_cfg);
746
pj_stun_config_init(&ice_cfg.stun_cfg, &pjsua_var.cp.factory, 0,
747
pjsip_endpt_get_ioqueue(pjsua_var.endpt),
748
pjsip_endpt_get_timer_heap(pjsua_var.endpt));
750
ice_cfg.af = pj_AF_INET();
751
ice_cfg.resolver = pjsua_var.resolver;
753
ice_cfg.opt = acc_cfg->ice_cfg.ice_opt;
755
/* Configure STUN settings */
756
if (pj_sockaddr_has_addr(&pjsua_var.stun_srv)) {
757
pj_sockaddr_print(&pjsua_var.stun_srv, stunip, sizeof(stunip), 0);
758
ice_cfg.stun.server = pj_str(stunip);
759
ice_cfg.stun.port = pj_sockaddr_get_port(&pjsua_var.stun_srv);
761
if (acc_cfg->ice_cfg.ice_max_host_cands >= 0)
762
ice_cfg.stun.max_host_cands = acc_cfg->ice_cfg.ice_max_host_cands;
764
/* Copy binding port setting to STUN setting */
765
pj_sockaddr_init(ice_cfg.af, &ice_cfg.stun.cfg.bound_addr,
766
&cfg->bound_addr, (pj_uint16_t)cfg->port);
767
ice_cfg.stun.cfg.port_range = (pj_uint16_t)cfg->port_range;
768
if (cfg->port != 0 && ice_cfg.stun.cfg.port_range == 0)
769
ice_cfg.stun.cfg.port_range =
770
(pj_uint16_t)(pjsua_var.ua_cfg.max_calls * 10);
772
/* Copy QoS setting to STUN setting */
773
ice_cfg.stun.cfg.qos_type = cfg->qos_type;
774
pj_memcpy(&ice_cfg.stun.cfg.qos_params, &cfg->qos_params,
775
sizeof(cfg->qos_params));
777
/* Configure TURN settings */
778
if (acc_cfg->turn_cfg.enable_turn) {
779
status = parse_host_port(&acc_cfg->turn_cfg.turn_server,
780
&ice_cfg.turn.server,
782
if (status != PJ_SUCCESS || ice_cfg.turn.server.slen == 0) {
783
PJ_LOG(1,(THIS_FILE, "Invalid TURN server setting"));
786
if (ice_cfg.turn.port == 0)
787
ice_cfg.turn.port = 3479;
788
ice_cfg.turn.conn_type = acc_cfg->turn_cfg.turn_conn_type;
789
pj_memcpy(&ice_cfg.turn.auth_cred,
790
&acc_cfg->turn_cfg.turn_auth_cred,
791
sizeof(ice_cfg.turn.auth_cred));
793
/* Copy QoS setting to TURN setting */
794
ice_cfg.turn.cfg.qos_type = cfg->qos_type;
795
pj_memcpy(&ice_cfg.turn.cfg.qos_params, &cfg->qos_params,
796
sizeof(cfg->qos_params));
798
/* Copy binding port setting to TURN setting */
799
pj_sockaddr_init(ice_cfg.af, &ice_cfg.turn.cfg.bound_addr,
800
&cfg->bound_addr, (pj_uint16_t)cfg->port);
801
ice_cfg.turn.cfg.port_range = (pj_uint16_t)cfg->port_range;
802
if (cfg->port != 0 && ice_cfg.turn.cfg.port_range == 0)
803
ice_cfg.turn.cfg.port_range =
804
(pj_uint16_t)(pjsua_var.ua_cfg.max_calls * 10);
807
/* Configure packet size for STUN and TURN sockets */
808
ice_cfg.stun.cfg.max_pkt_size = PJMEDIA_MAX_MRU;
809
ice_cfg.turn.cfg.max_pkt_size = PJMEDIA_MAX_MRU;
811
pj_bzero(&ice_cb, sizeof(pjmedia_ice_cb));
812
ice_cb.on_ice_complete = &on_ice_complete;
813
pj_ansi_snprintf(name, sizeof(name), "icetp%02d", call_med->idx);
814
call_med->tp_ready = PJ_EPENDING;
817
if (PJMEDIA_ADVERTISE_RTCP && !acc_cfg->ice_cfg.ice_no_rtcp)
820
status = pjmedia_ice_create3(pjsua_var.med_endpt, name, comp_cnt,
821
&ice_cfg, &ice_cb, 0, call_med,
823
if (status != PJ_SUCCESS) {
824
pjsua_perror(THIS_FILE, "Unable to create ICE media transport",
829
/* Wait until transport is initialized, or time out */
831
pj_bool_t has_pjsua_lock = PJSUA_LOCK_IS_LOCKED();
832
pjsip_dialog *dlg = call_med->call->inv ?
833
call_med->call->inv->dlg : NULL;
837
/* Don't lock otherwise deadlock:
838
* https://trac.pjsip.org/repos/ticket/1737
841
pjsip_dlg_dec_lock(dlg);
843
while (call_med->tp_ready == PJ_EPENDING) {
844
pjsua_handle_events(100);
847
pjsip_dlg_inc_lock(dlg);
854
if (async && call_med->tp_ready == PJ_EPENDING) {
856
} else if (call_med->tp_ready != PJ_SUCCESS) {
857
pjsua_perror(THIS_FILE, "Error initializing ICE media transport",
859
status = call_med->tp_ready;
863
pjmedia_transport_simulate_lost(call_med->tp, PJMEDIA_DIR_ENCODING,
864
pjsua_var.media_cfg.tx_drop_pct);
866
pjmedia_transport_simulate_lost(call_med->tp, PJMEDIA_DIR_DECODING,
867
pjsua_var.media_cfg.rx_drop_pct);
872
if (call_med->tp != NULL) {
873
pjmedia_transport_close(call_med->tp);
880
#if DISABLED_FOR_TICKET_1185
881
/* Create ICE media transports (when ice is enabled) */
882
static pj_status_t create_ice_media_transports(pjsua_transport_config *cfg)
887
for (i=0; i < pjsua_var.ua_cfg.max_calls; ++i) {
888
pjsua_call *call = &pjsua_var.calls[i];
891
for (strm_idx=0; strm_idx < call->med_cnt; ++strm_idx) {
892
pjsua_call_media *call_med = &call->media[strm_idx];
894
status = create_ice_media_transport(cfg, call_med);
895
if (status != PJ_SUCCESS)
903
for (i=0; i < pjsua_var.ua_cfg.max_calls; ++i) {
904
pjsua_call *call = &pjsua_var.calls[i];
907
for (strm_idx=0; strm_idx < call->med_cnt; ++strm_idx) {
908
pjsua_call_media *call_med = &call->media[strm_idx];
911
pjmedia_transport_close(call_med->tp);
920
#if DISABLED_FOR_TICKET_1185
922
* Create media transports for all the calls. This function creates
923
* one UDP media transport for each call.
925
PJ_DEF(pj_status_t) pjsua_media_transports_create(
926
const pjsua_transport_config *app_cfg)
928
pjsua_transport_config cfg;
933
/* Make sure pjsua_init() has been called */
934
PJ_ASSERT_RETURN(pjsua_var.ua_cfg.max_calls>0, PJ_EINVALIDOP);
938
/* Delete existing media transports */
939
for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
940
pjsua_call *call = &pjsua_var.calls[i];
943
for (strm_idx=0; strm_idx < call->med_cnt; ++strm_idx) {
944
pjsua_call_media *call_med = &call->media[strm_idx];
946
if (call_med->tp && call_med->tp_auto_del) {
947
pjmedia_transport_close(call_med->tp);
949
call_med->tp_orig = NULL;
955
pjsua_transport_config_dup(pjsua_var.pool, &cfg, app_cfg);
957
/* Create the transports */
958
if (pjsua_var.ice_cfg.enable_ice) {
959
status = create_ice_media_transports(&cfg);
961
status = create_udp_media_transports(&cfg);
964
/* Set media transport auto_delete to True */
965
for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
966
pjsua_call *call = &pjsua_var.calls[i];
969
for (strm_idx=0; strm_idx < call->med_cnt; ++strm_idx) {
970
pjsua_call_media *call_med = &call->media[strm_idx];
972
call_med->tp_auto_del = PJ_TRUE;
982
* Attach application's created media transports.
984
PJ_DEF(pj_status_t) pjsua_media_transports_attach(pjsua_media_transport tp[],
986
pj_bool_t auto_delete)
990
PJ_ASSERT_RETURN(tp && count==pjsua_var.ua_cfg.max_calls, PJ_EINVAL);
992
/* Assign the media transports */
993
for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
994
pjsua_call *call = &pjsua_var.calls[i];
997
for (strm_idx=0; strm_idx < call->med_cnt; ++strm_idx) {
998
pjsua_call_media *call_med = &call->media[strm_idx];
1000
if (call_med->tp && call_med->tp_auto_del) {
1001
pjmedia_transport_close(call_med->tp);
1002
call_med->tp = NULL;
1003
call_med->tp_orig = NULL;
1007
PJ_TODO(remove_pjsua_media_transports_attach);
1009
call->media[0].tp = tp[i].transport;
1010
call->media[0].tp_auto_del = auto_delete;
1017
/* Go through the list of media in the SDP, find acceptable media, and
1018
* sort them based on the "quality" of the media, and store the indexes
1019
* in the specified array. Media with the best quality will be listed
1020
* first in the array. The quality factors considered currently is
1023
static void sort_media(const pjmedia_sdp_session *sdp,
1024
const pj_str_t *type,
1025
pjmedia_srtp_use use_srtp,
1028
unsigned *p_total_count)
1032
int score[PJSUA_MAX_CALL_MEDIA];
1034
pj_assert(*p_count >= PJSUA_MAX_CALL_MEDIA);
1035
pj_assert(*p_total_count >= PJSUA_MAX_CALL_MEDIA);
1039
for (i=0; i<PJSUA_MAX_CALL_MEDIA; ++i)
1042
/* Score each media */
1043
for (i=0; i<sdp->media_count && count<PJSUA_MAX_CALL_MEDIA; ++i) {
1044
const pjmedia_sdp_media *m = sdp->media[i];
1045
const pjmedia_sdp_conn *c;
1047
/* Skip different media */
1048
if (pj_stricmp(&m->desc.media, type) != 0) {
1049
score[count++] = -22000;
1053
c = m->conn? m->conn : sdp->conn;
1055
/* Supported transports */
1056
if (pj_stricmp2(&m->desc.transport, "RTP/SAVP")==0) {
1058
case PJMEDIA_SRTP_MANDATORY:
1059
case PJMEDIA_SRTP_OPTIONAL:
1062
case PJMEDIA_SRTP_DISABLED:
1067
} else if (pj_stricmp2(&m->desc.transport, "RTP/AVP")==0) {
1069
case PJMEDIA_SRTP_MANDATORY:
1073
case PJMEDIA_SRTP_OPTIONAL:
1074
/* No change in score */
1076
case PJMEDIA_SRTP_DISABLED:
1084
/* Is media disabled? */
1085
if (m->desc.port == 0)
1088
/* Is media inactive? */
1089
if (pjmedia_sdp_media_find_attr2(m, "inactive", NULL) ||
1090
pj_strcmp2(&c->addr, "0.0.0.0") == 0)
1099
/* Created sorted list based on quality */
1100
for (i=0; i<count; ++i) {
1104
for (j=1; j<count; ++j) {
1105
if (score[j] > score[best])
1108
/* Don't put media with negative score, that media is unacceptable
1111
midx[i] = (pj_uint8_t)best;
1112
if (score[best] >= 0)
1114
if (score[best] > -22000)
1117
score[best] = -22000;
1122
/* Callback to receive media events */
1123
pj_status_t call_media_on_event(pjmedia_event *event,
1126
pjsua_call_media *call_med = (pjsua_call_media*)user_data;
1127
pjsua_call *call = call_med->call;
1128
pj_status_t status = PJ_SUCCESS;
1130
switch(event->type) {
1131
case PJMEDIA_EVENT_KEYFRAME_MISSING:
1132
if (call->opt.req_keyframe_method & PJSUA_VID_REQ_KEYFRAME_SIP_INFO)
1136
pj_get_timestamp(&now);
1137
if (pj_elapsed_msec(&call_med->last_req_keyframe, &now) >=
1138
PJSUA_VID_REQ_KEYFRAME_INTERVAL)
1140
pjsua_msg_data msg_data;
1141
const pj_str_t SIP_INFO = {"INFO", 4};
1142
const char *BODY_TYPE = "application/media_control+xml";
1144
"<?xml version=\"1.0\" encoding=\"utf-8\" ?>"
1145
"<media_control><vc_primitive><to_encoder>"
1146
"<picture_fast_update/>"
1147
"</to_encoder></vc_primitive></media_control>";
1149
PJ_LOG(4,(THIS_FILE,
1150
"Sending video keyframe request via SIP INFO"));
1152
pjsua_msg_data_init(&msg_data);
1153
pj_cstr(&msg_data.content_type, BODY_TYPE);
1154
pj_cstr(&msg_data.msg_body, BODY);
1155
status = pjsua_call_send_request(call->index, &SIP_INFO,
1157
if (status != PJ_SUCCESS) {
1158
pj_perror(3, THIS_FILE, status,
1159
"Failed requesting keyframe via SIP INFO");
1161
call_med->last_req_keyframe = now;
1171
if (pjsua_var.ua_cfg.cb.on_call_media_event && call) {
1172
(*pjsua_var.ua_cfg.cb.on_call_media_event)(call->index,
1173
call_med->idx, event);
1179
/* Set media transport state and notify the application via the callback. */
1180
void pjsua_set_media_tp_state(pjsua_call_media *call_med,
1181
pjsua_med_tp_st tp_st)
1183
if (pjsua_var.ua_cfg.cb.on_call_media_transport_state &&
1184
call_med->tp_st != tp_st)
1186
pjsua_med_tp_state_info info;
1188
pj_bzero(&info, sizeof(info));
1189
info.med_idx = call_med->idx;
1191
info.status = call_med->tp_ready;
1192
(*pjsua_var.ua_cfg.cb.on_call_media_transport_state)(
1193
call_med->call->index, &info);
1196
call_med->tp_st = tp_st;
1199
/* Callback to resume pjsua_call_media_init() after media transport
1200
* creation is completed.
1202
static pj_status_t call_media_init_cb(pjsua_call_media *call_med,
1207
pjsua_acc *acc = &pjsua_var.acc[call_med->call->acc_id];
1208
pjmedia_transport_info tpinfo;
1211
if (status != PJ_SUCCESS)
1214
pjmedia_transport_simulate_lost(call_med->tp, PJMEDIA_DIR_ENCODING,
1215
pjsua_var.media_cfg.tx_drop_pct);
1217
pjmedia_transport_simulate_lost(call_med->tp, PJMEDIA_DIR_DECODING,
1218
pjsua_var.media_cfg.rx_drop_pct);
1220
if (call_med->tp_st == PJSUA_MED_TP_CREATING)
1221
pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_IDLE);
1223
if (!call_med->tp_orig &&
1224
pjsua_var.ua_cfg.cb.on_create_media_transport)
1226
call_med->use_custom_med_tp = PJ_TRUE;
1228
call_med->use_custom_med_tp = PJ_FALSE;
1230
#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
1231
/* This function may be called when SRTP transport already exists
1232
* (e.g: in re-invite, update), don't need to destroy/re-create.
1234
if (!call_med->tp_orig) {
1235
pjmedia_srtp_setting srtp_opt;
1236
pjmedia_transport *srtp = NULL;
1238
/* Check if SRTP requires secure signaling */
1239
if (acc->cfg.use_srtp != PJMEDIA_SRTP_DISABLED) {
1240
if (security_level < acc->cfg.srtp_secure_signaling) {
1241
err_code = PJSIP_SC_NOT_ACCEPTABLE;
1242
status = PJSIP_ESESSIONINSECURE;
1247
/* Always create SRTP adapter */
1248
pjmedia_srtp_setting_default(&srtp_opt);
1249
srtp_opt.close_member_tp = PJ_TRUE;
1251
/* If media session has been ever established, let's use remote's
1252
* preference in SRTP usage policy, especially when it is stricter.
1254
if (call_med->rem_srtp_use > acc->cfg.use_srtp)
1255
srtp_opt.use = call_med->rem_srtp_use;
1257
srtp_opt.use = acc->cfg.use_srtp;
1259
status = pjmedia_transport_srtp_create(pjsua_var.med_endpt,
1262
if (status != PJ_SUCCESS) {
1263
err_code = PJSIP_SC_INTERNAL_SERVER_ERROR;
1267
/* Set SRTP as current media transport */
1268
call_med->tp_orig = call_med->tp;
1269
call_med->tp = srtp;
1272
call_med->tp_orig = call_med->tp;
1273
PJ_UNUSED_ARG(security_level);
1277
pjmedia_transport_info_init(&tpinfo);
1278
pjmedia_transport_get_info(call_med->tp, &tpinfo);
1280
pj_sockaddr_cp(&call_med->rtp_addr, &tpinfo.sock_info.rtp_addr_name);
1284
if (status != PJ_SUCCESS && call_med->tp) {
1285
pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_NULL);
1286
pjmedia_transport_close(call_med->tp);
1287
call_med->tp = NULL;
1291
*sip_err_code = err_code;
1293
if (call_med->med_init_cb) {
1294
pjsua_med_tp_state_info info;
1296
pj_bzero(&info, sizeof(info));
1297
info.status = status;
1298
info.state = call_med->tp_st;
1299
info.med_idx = call_med->idx;
1300
info.sip_err_code = err_code;
1301
(*call_med->med_init_cb)(call_med->call->index, &info);
1307
/* Determine if call's media is being changed, for example when video is being
1308
* added. Then we can reject incoming re-INVITE, for example. This is the
1309
* solution for https://trac.pjsip.org/repos/ticket/1738
1311
pj_bool_t pjsua_call_media_is_changing(pjsua_call *call)
1313
/* The problem in #1738 occurs because we do handle_events() loop while
1314
* adding media, which could cause incoming re-INVITE to be processed and
1315
* cause havoc. Since the handle_events() loop only happens while adding
1316
* media, it is sufficient to only check if "prov > cnt" for now.
1318
return call->med_prov_cnt > call->med_cnt;
1321
/* Initialize the media line */
1322
pj_status_t pjsua_call_media_init(pjsua_call_media *call_med,
1324
const pjsua_transport_config *tcfg,
1328
pjsua_med_tp_state_cb cb)
1330
pj_status_t status = PJ_SUCCESS;
1333
* Note: this function may be called when the media already exists
1334
* (e.g. in reinvites, updates, etc.)
1336
call_med->type = type;
1338
/* Create the media transport for initial call. Here are the possible
1339
* media transport state and the action needed:
1340
* - PJSUA_MED_TP_NULL or call_med->tp==NULL, create one.
1341
* - PJSUA_MED_TP_RUNNING, do nothing.
1342
* - PJSUA_MED_TP_DISABLED, re-init (media_create(), etc). Currently,
1343
* this won't happen as media_channel_update() will always clean up
1344
* the unused transport of a disabled media.
1346
if (call_med->tp == NULL) {
1347
#if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0)
1348
/* While in initial call, set default video devices */
1349
if (type == PJMEDIA_TYPE_VIDEO) {
1350
status = pjsua_vid_channel_init(call_med);
1351
if (status != PJ_SUCCESS)
1356
pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_CREATING);
1358
if (pjsua_var.acc[call_med->call->acc_id].cfg.ice_cfg.enable_ice) {
1359
status = create_ice_media_transport(tcfg, call_med, async);
1360
if (async && status == PJ_EPENDING) {
1361
/* We will resume call media initialization in the
1362
* on_ice_complete() callback.
1364
call_med->med_create_cb = &call_media_init_cb;
1365
call_med->med_init_cb = cb;
1370
status = create_udp_media_transport(tcfg, call_med);
1373
if (status != PJ_SUCCESS) {
1374
PJ_PERROR(1,(THIS_FILE, status, "Error creating media transport"));
1378
/* Media transport creation completed immediately, so
1379
* we don't need to call the callback.
1381
call_med->med_init_cb = NULL;
1383
} else if (call_med->tp_st == PJSUA_MED_TP_DISABLED) {
1384
/* Media is being reenabled. */
1385
//pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_IDLE);
1387
pj_assert(!"Currently no media transport reuse");
1390
return call_media_init_cb(call_med, status, security_level,
1394
/* Callback to resume pjsua_media_channel_init() after media transport
1395
* initialization is completed.
1397
static pj_status_t media_channel_init_cb(pjsua_call_id call_id,
1398
const pjsua_med_tp_state_info *info)
1400
pjsua_call *call = &pjsua_var.calls[call_id];
1401
pj_status_t status = (info? info->status : PJ_SUCCESS);
1405
pj_mutex_lock(call->med_ch_mutex);
1407
/* Set the callback to NULL to indicate that the async operation
1410
call->media_prov[info->med_idx].med_init_cb = NULL;
1412
/* In case of failure, save the information to be returned
1413
* by the last media transport to finish.
1415
if (info->status != PJ_SUCCESS)
1416
pj_memcpy(&call->med_ch_info, info, sizeof(*info));
1418
/* Check whether all the call's medias have finished calling their
1421
for (mi=0; mi < call->med_prov_cnt; ++mi) {
1422
pjsua_call_media *call_med = &call->media_prov[mi];
1424
if (call_med->med_init_cb) {
1425
pj_mutex_unlock(call->med_ch_mutex);
1429
if (call_med->tp_ready != PJ_SUCCESS)
1430
status = call_med->tp_ready;
1433
/* OK, we are called by the last media transport finished. */
1434
pj_mutex_unlock(call->med_ch_mutex);
1437
if (call->med_ch_mutex) {
1438
pj_mutex_destroy(call->med_ch_mutex);
1439
call->med_ch_mutex = NULL;
1442
if (status != PJ_SUCCESS) {
1443
if (call->med_ch_info.status == PJ_SUCCESS) {
1444
call->med_ch_info.status = status;
1445
call->med_ch_info.sip_err_code = PJSIP_SC_TEMPORARILY_UNAVAILABLE;
1447
pjsua_media_prov_clean_up(call_id);
1451
/* Tell the media transport of a new offer/answer session */
1452
for (mi=0; mi < call->med_prov_cnt; ++mi) {
1453
pjsua_call_media *call_med = &call->media_prov[mi];
1455
/* Note: tp may be NULL if this media line is disabled */
1456
if (call_med->tp && call_med->tp_st == PJSUA_MED_TP_IDLE) {
1457
pj_pool_t *tmp_pool = call->async_call.pool_prov;
1460
tmp_pool = (call->inv? call->inv->pool_prov:
1461
call->async_call.dlg->pool);
1464
if (call_med->use_custom_med_tp) {
1465
unsigned custom_med_tp_flags = PJSUA_MED_TP_CLOSE_MEMBER;
1467
/* Use custom media transport returned by the application */
1469
(*pjsua_var.ua_cfg.cb.on_create_media_transport)
1470
(call_id, mi, call_med->tp,
1471
custom_med_tp_flags);
1472
if (!call_med->tp) {
1474
PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_TEMPORARILY_UNAVAILABLE);
1479
status = pjmedia_transport_media_create(
1480
call_med->tp, tmp_pool,
1481
0, call->async_call.rem_sdp, mi);
1483
if (status != PJ_SUCCESS) {
1484
call->med_ch_info.status = status;
1485
call->med_ch_info.med_idx = mi;
1486
call->med_ch_info.state = call_med->tp_st;
1487
call->med_ch_info.sip_err_code = PJSIP_SC_TEMPORARILY_UNAVAILABLE;
1488
pjsua_media_prov_clean_up(call_id);
1492
pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_INIT);
1496
call->med_ch_info.status = PJ_SUCCESS;
1499
if (call->med_ch_cb)
1500
(*call->med_ch_cb)(call->index, &call->med_ch_info);
1506
/* Clean up media transports in provisional media that is not used
1509
static void media_prov_clean_up(pjsua_call_id call_id, int idx)
1511
pjsua_call *call = &pjsua_var.calls[call_id];
1514
for (i = idx; i < call->med_prov_cnt; ++i) {
1515
pjsua_call_media *call_med = &call->media_prov[i];
1517
pj_bool_t used = PJ_FALSE;
1519
if (call_med->tp == NULL)
1522
for (j = 0; j < call->med_cnt; ++j) {
1523
if (call->media[j].tp == call_med->tp) {
1530
if (call_med->tp_st > PJSUA_MED_TP_IDLE) {
1531
pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_IDLE);
1532
pjmedia_transport_media_stop(call_med->tp);
1534
pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_NULL);
1535
pjmedia_transport_close(call_med->tp);
1536
call_med->tp = call_med->tp_orig = NULL;
1541
void pjsua_media_prov_clean_up(pjsua_call_id call_id)
1543
media_prov_clean_up(call_id, 0);
1547
pj_status_t pjsua_media_channel_init(pjsua_call_id call_id,
1550
pj_pool_t *tmp_pool,
1551
const pjmedia_sdp_session *rem_sdp,
1554
pjsua_med_tp_state_cb cb)
1556
const pj_str_t STR_AUDIO = { "audio", 5 };
1557
const pj_str_t STR_VIDEO = { "video", 5 };
1558
pjsua_call *call = &pjsua_var.calls[call_id];
1559
pjsua_acc *acc = &pjsua_var.acc[call->acc_id];
1560
pj_uint8_t maudidx[PJSUA_MAX_CALL_MEDIA];
1561
unsigned maudcnt = PJ_ARRAY_SIZE(maudidx);
1562
unsigned mtotaudcnt = PJ_ARRAY_SIZE(maudidx);
1563
pj_uint8_t mvididx[PJSUA_MAX_CALL_MEDIA];
1564
unsigned mvidcnt = PJ_ARRAY_SIZE(mvididx);
1565
unsigned mtotvidcnt = PJ_ARRAY_SIZE(mvididx);
1567
pj_bool_t pending_med_tp = PJ_FALSE;
1568
pj_bool_t reinit = PJ_FALSE;
1571
PJ_UNUSED_ARG(role);
1574
* Note: this function may be called when the media already exists
1575
* (e.g. in reinvites, updates, etc).
1578
if (pjsua_get_state() != PJSUA_STATE_RUNNING) {
1579
if (sip_err_code) *sip_err_code = PJSIP_SC_SERVICE_UNAVAILABLE;
1584
pj_pool_t *tmppool = (call->inv? call->inv->pool_prov:
1585
call->async_call.dlg->pool);
1587
status = pj_mutex_create_simple(tmppool, NULL, &call->med_ch_mutex);
1588
if (status != PJ_SUCCESS)
1592
if (call->inv && call->inv->state == PJSIP_INV_STATE_CONFIRMED)
1595
PJ_LOG(4,(THIS_FILE, "Call %d: %sinitializing media..",
1596
call_id, (reinit?"re-":"") ));
1598
pj_log_push_indent();
1600
/* Init provisional media state */
1601
if (call->med_cnt == 0) {
1602
/* New media session, just copy whole from call media state. */
1603
pj_memcpy(call->media_prov, call->media, sizeof(call->media));
1605
/* Clean up any unused transports. Note that when local SDP reoffer
1606
* is rejected by remote, there may be any initialized transports that
1607
* are not used by call media and currently there is no notification
1608
* from PJSIP level regarding the reoffer rejection.
1610
pjsua_media_prov_clean_up(call_id);
1612
/* Updating media session, copy from call media state. */
1613
pj_memcpy(call->media_prov, call->media,
1614
sizeof(call->media[0]) * call->med_cnt);
1616
call->med_prov_cnt = call->med_cnt;
1618
#if DISABLED_FOR_TICKET_1185
1619
/* Return error if media transport has not been created yet
1620
* (e.g. application is starting)
1622
for (i=0; i<call->med_cnt; ++i) {
1623
if (call->media[i].tp == NULL) {
1630
/* Get media count for each media type */
1632
sort_media(rem_sdp, &STR_AUDIO, acc->cfg.use_srtp,
1633
maudidx, &maudcnt, &mtotaudcnt);
1635
/* Expecting audio in the offer */
1636
if (sip_err_code) *sip_err_code = PJSIP_SC_NOT_ACCEPTABLE_HERE;
1637
status = PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_NOT_ACCEPTABLE_HERE);
1641
#if PJMEDIA_HAS_VIDEO
1642
sort_media(rem_sdp, &STR_VIDEO, acc->cfg.use_srtp,
1643
mvididx, &mvidcnt, &mtotvidcnt);
1645
mvidcnt = mtotvidcnt = 0;
1646
PJ_UNUSED_ARG(STR_VIDEO);
1649
/* Update media count only when remote add any media, this media count
1650
* must never decrease. Also note that we shouldn't apply the media
1651
* count setting (of the call setting) before the SDP negotiation.
1653
if (call->med_prov_cnt < rem_sdp->media_count)
1654
call->med_prov_cnt = PJ_MIN(rem_sdp->media_count,
1655
PJSUA_MAX_CALL_MEDIA);
1657
call->rem_offerer = PJ_TRUE;
1658
call->rem_aud_cnt = maudcnt;
1659
call->rem_vid_cnt = mvidcnt;
1663
/* If call already established, calculate media count from current
1664
* local active SDP and call setting. Otherwise, calculate media
1665
* count from the call setting only.
1668
const pjmedia_sdp_session *sdp;
1670
status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &sdp);
1671
pj_assert(status == PJ_SUCCESS);
1673
sort_media(sdp, &STR_AUDIO, acc->cfg.use_srtp,
1674
maudidx, &maudcnt, &mtotaudcnt);
1675
pj_assert(maudcnt > 0);
1677
sort_media(sdp, &STR_VIDEO, acc->cfg.use_srtp,
1678
mvididx, &mvidcnt, &mtotvidcnt);
1680
/* Call setting may add or remove media. Adding media is done by
1681
* enabling any disabled/port-zeroed media first, then adding new
1682
* media whenever needed. Removing media is done by disabling
1683
* media with the lowest 'quality'.
1686
/* Check if we need to add new audio */
1687
if (maudcnt < call->opt.aud_cnt &&
1688
mtotaudcnt < call->opt.aud_cnt)
1690
for (mi = 0; mi < call->opt.aud_cnt - mtotaudcnt; ++mi)
1691
maudidx[maudcnt++] = (pj_uint8_t)call->med_prov_cnt++;
1693
mtotaudcnt = call->opt.aud_cnt;
1695
maudcnt = call->opt.aud_cnt;
1697
/* Check if we need to add new video */
1698
if (mvidcnt < call->opt.vid_cnt &&
1699
mtotvidcnt < call->opt.vid_cnt)
1701
for (mi = 0; mi < call->opt.vid_cnt - mtotvidcnt; ++mi)
1702
mvididx[mvidcnt++] = (pj_uint8_t)call->med_prov_cnt++;
1704
mtotvidcnt = call->opt.vid_cnt;
1706
mvidcnt = call->opt.vid_cnt;
1710
maudcnt = mtotaudcnt = call->opt.aud_cnt;
1711
for (mi=0; mi<maudcnt; ++mi) {
1712
maudidx[mi] = (pj_uint8_t)mi;
1714
mvidcnt = mtotvidcnt = call->opt.vid_cnt;
1715
for (mi=0; mi<mvidcnt; ++mi) {
1716
mvididx[mi] = (pj_uint8_t)(maudcnt + mi);
1718
call->med_prov_cnt = maudcnt + mvidcnt;
1720
/* Need to publish supported media? */
1721
if (call->opt.flag & PJSUA_CALL_INCLUDE_DISABLED_MEDIA) {
1722
if (mtotaudcnt == 0) {
1724
maudidx[0] = (pj_uint8_t)call->med_prov_cnt++;
1726
#if PJMEDIA_HAS_VIDEO
1727
if (mtotvidcnt == 0) {
1729
mvididx[0] = (pj_uint8_t)call->med_prov_cnt++;
1735
call->rem_offerer = PJ_FALSE;
1738
if (call->med_prov_cnt == 0) {
1739
/* Expecting at least one media */
1740
if (sip_err_code) *sip_err_code = PJSIP_SC_NOT_ACCEPTABLE_HERE;
1741
status = PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_NOT_ACCEPTABLE_HERE);
1746
call->med_ch_cb = cb;
1750
call->async_call.rem_sdp =
1751
pjmedia_sdp_session_clone(call->inv->pool_prov, rem_sdp);
1753
call->async_call.rem_sdp = NULL;
1756
call->async_call.pool_prov = tmp_pool;
1758
/* Initialize each media line */
1759
for (mi=0; mi < call->med_prov_cnt; ++mi) {
1760
pjsua_call_media *call_med = &call->media_prov[mi];
1761
pj_bool_t enabled = PJ_FALSE;
1762
pjmedia_type media_type = PJMEDIA_TYPE_UNKNOWN;
1764
if (pj_memchr(maudidx, mi, mtotaudcnt * sizeof(maudidx[0]))) {
1765
media_type = PJMEDIA_TYPE_AUDIO;
1766
if (call->opt.aud_cnt &&
1767
pj_memchr(maudidx, mi, maudcnt * sizeof(maudidx[0])))
1771
} else if (pj_memchr(mvididx, mi, mtotvidcnt * sizeof(mvididx[0]))) {
1772
media_type = PJMEDIA_TYPE_VIDEO;
1773
if (call->opt.vid_cnt &&
1774
pj_memchr(mvididx, mi, mvidcnt * sizeof(mvididx[0])))
1781
status = pjsua_call_media_init(call_med, media_type,
1783
security_level, sip_err_code,
1785
(async? &media_channel_init_cb:
1787
if (status == PJ_EPENDING) {
1788
pending_med_tp = PJ_TRUE;
1789
} else if (status != PJ_SUCCESS) {
1790
if (pending_med_tp) {
1791
/* Save failure information. */
1792
call_med->tp_ready = status;
1793
pj_bzero(&call->med_ch_info, sizeof(call->med_ch_info));
1794
call->med_ch_info.status = status;
1795
call->med_ch_info.state = call_med->tp_st;
1796
call->med_ch_info.med_idx = call_med->idx;
1798
call->med_ch_info.sip_err_code = *sip_err_code;
1800
/* We will return failure in the callback later. */
1804
pjsua_media_prov_clean_up(call_id);
1808
/* By convention, the media is disabled if transport is NULL
1809
* or transport state is PJSUA_MED_TP_DISABLED.
1812
// Don't close transport here, as SDP negotiation has not been
1813
// done and stream may be still active. Once SDP negotiation
1814
// is done (channel_update() invoked), this transport will be
1816
//pjmedia_transport_close(call_med->tp);
1817
//call_med->tp = NULL;
1818
pj_assert(call_med->tp_st == PJSUA_MED_TP_INIT ||
1819
call_med->tp_st == PJSUA_MED_TP_RUNNING);
1820
pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_DISABLED);
1823
/* Put media type just for info */
1824
call_med->type = media_type;
1828
call->audio_idx = maudidx[0];
1830
PJ_LOG(4,(THIS_FILE, "Media index %d selected for audio call %d",
1831
call->audio_idx, call->index));
1833
if (pending_med_tp) {
1834
/* We shouldn't use temporary pool anymore. */
1835
call->async_call.pool_prov = NULL;
1836
/* We have a pending media transport initialization. */
1837
pj_log_pop_indent();
1841
/* Media transport initialization completed immediately, so
1842
* we don't need to call the callback.
1844
call->med_ch_cb = NULL;
1846
status = media_channel_init_cb(call_id, NULL);
1847
if (status != PJ_SUCCESS && sip_err_code)
1848
*sip_err_code = call->med_ch_info.sip_err_code;
1850
pj_log_pop_indent();
1854
if (call->med_ch_mutex) {
1855
pj_mutex_destroy(call->med_ch_mutex);
1856
call->med_ch_mutex = NULL;
1859
pj_log_pop_indent();
1864
/* Create SDP based on the current media channel. Note that, this function
1865
* will not modify the media channel, so when receiving new offer or
1866
* updating media count (via call setting), media channel must be reinit'd
1867
* (using pjsua_media_channel_init()) first before calling this function.
1869
pj_status_t pjsua_media_channel_create_sdp(pjsua_call_id call_id,
1871
const pjmedia_sdp_session *rem_sdp,
1872
pjmedia_sdp_session **p_sdp,
1875
enum { MAX_MEDIA = PJSUA_MAX_CALL_MEDIA };
1876
pjmedia_sdp_session *sdp;
1878
pjsua_call *call = &pjsua_var.calls[call_id];
1879
pjmedia_sdp_neg_state sdp_neg_state = PJMEDIA_SDP_NEG_STATE_NULL;
1881
unsigned tot_bandw_tias = 0;
1884
if (pjsua_get_state() != PJSUA_STATE_RUNNING)
1888
// This function should not really change the media channel.
1890
/* If this is a re-offer, let's re-initialize media as remote may
1891
* add or remove media
1893
if (call->inv && call->inv->state == PJSIP_INV_STATE_CONFIRMED) {
1894
status = pjsua_media_channel_init(call_id, PJSIP_ROLE_UAS,
1895
call->secure_level, pool,
1896
rem_sdp, sip_err_code,
1898
if (status != PJ_SUCCESS)
1902
/* Audio is first in our offer, by convention */
1903
// The audio_idx should not be changed here, as this function may be
1904
// called in generating re-offer and the current active audio index
1906
//call->audio_idx = 0;
1911
// Since r3512, old-style hold should have got transport, created by
1912
// pjsua_media_channel_init() in initial offer/answer or remote reoffer.
1913
/* Create media if it's not created. This could happen when call is
1914
* currently on-hold (with the old style hold)
1916
if (call->media[call->audio_idx].tp == NULL) {
1918
role = (rem_sdp ? PJSIP_ROLE_UAS : PJSIP_ROLE_UAC);
1919
status = pjsua_media_channel_init(call_id, role, call->secure_level,
1920
pool, rem_sdp, sip_err_code);
1921
if (status != PJ_SUCCESS)
1926
/* Get SDP negotiator state */
1927
if (call->inv && call->inv->neg)
1928
sdp_neg_state = pjmedia_sdp_neg_get_state(call->inv->neg);
1930
/* Get one address to use in the origin field */
1931
pj_bzero(&origin, sizeof(origin));
1932
for (mi=0; mi<call->med_prov_cnt; ++mi) {
1933
pjmedia_transport_info tpinfo;
1935
if (call->media_prov[mi].tp == NULL)
1938
pjmedia_transport_info_init(&tpinfo);
1939
pjmedia_transport_get_info(call->media_prov[mi].tp, &tpinfo);
1940
pj_sockaddr_cp(&origin, &tpinfo.sock_info.rtp_addr_name);
1944
/* Create the base (blank) SDP */
1945
status = pjmedia_endpt_create_base_sdp(pjsua_var.med_endpt, pool, NULL,
1947
if (status != PJ_SUCCESS)
1950
/* Process each media line */
1951
for (mi=0; mi<call->med_prov_cnt; ++mi) {
1952
pjsua_call_media *call_med = &call->media_prov[mi];
1953
pjmedia_sdp_media *m = NULL;
1954
pjmedia_transport_info tpinfo;
1957
if (rem_sdp && mi >= rem_sdp->media_count) {
1958
/* Remote might have removed some media lines. */
1959
media_prov_clean_up(call->index, rem_sdp->media_count);
1960
call->med_prov_cnt = rem_sdp->media_count;
1964
if (call_med->tp == NULL || call_med->tp_st == PJSUA_MED_TP_DISABLED)
1967
* This media is disabled. Just create a valid SDP with zero
1971
/* Just clone the remote media and deactivate it */
1972
m = pjmedia_sdp_media_clone_deactivate(pool,
1973
rem_sdp->media[mi]);
1975
m = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_media);
1976
m->desc.transport = pj_str("RTP/AVP");
1977
m->desc.fmt_count = 1;
1979
switch (call_med->type) {
1980
case PJMEDIA_TYPE_AUDIO:
1981
m->desc.media = pj_str("audio");
1982
m->desc.fmt[0] = pj_str("0");
1984
case PJMEDIA_TYPE_VIDEO:
1985
m->desc.media = pj_str("video");
1986
m->desc.fmt[0] = pj_str("31");
1989
/* This must be us generating re-offer, and some unknown
1990
* media may exist, so just clone from active local SDP
1991
* (and it should have been deactivated already).
1993
pj_assert(call->inv && call->inv->neg &&
1994
sdp_neg_state == PJMEDIA_SDP_NEG_STATE_DONE);
1996
const pjmedia_sdp_session *s_;
1997
pjmedia_sdp_neg_get_active_local(call->inv->neg, &s_);
1999
pj_assert(mi < s_->media_count);
2000
m = pjmedia_sdp_media_clone(pool, s_->media[mi]);
2007
/* Add connection line, if none */
2008
if (m->conn == NULL && sdp->conn == NULL) {
2011
use_ipv6 = (pjsua_var.acc[call->acc_id].cfg.ipv6_media_use !=
2012
PJSUA_IPV6_DISABLED);
2014
m->conn = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_conn);
2015
m->conn->net_type = pj_str("IN");
2017
m->conn->addr_type = pj_str("IP6");
2018
m->conn->addr = pj_str("::1");
2020
m->conn->addr_type = pj_str("IP4");
2021
m->conn->addr = pj_str("127.0.0.1");
2025
sdp->media[sdp->media_count++] = m;
2029
/* Get transport address info */
2030
pjmedia_transport_info_init(&tpinfo);
2031
pjmedia_transport_get_info(call_med->tp, &tpinfo);
2033
/* Ask pjmedia endpoint to create SDP media line */
2034
switch (call_med->type) {
2035
case PJMEDIA_TYPE_AUDIO:
2036
status = pjmedia_endpt_create_audio_sdp(pjsua_var.med_endpt, pool,
2037
&tpinfo.sock_info, 0, &m);
2039
#if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0)
2040
case PJMEDIA_TYPE_VIDEO:
2041
status = pjmedia_endpt_create_video_sdp(pjsua_var.med_endpt, pool,
2042
&tpinfo.sock_info, 0, &m);
2046
pj_assert(!"Invalid call_med media type");
2050
if (status != PJ_SUCCESS)
2053
sdp->media[sdp->media_count++] = m;
2055
/* Give to transport */
2056
status = pjmedia_transport_encode_sdp(call_med->tp, pool,
2058
if (status != PJ_SUCCESS) {
2059
if (sip_err_code) *sip_err_code = PJSIP_SC_NOT_ACCEPTABLE;
2063
#if PJSUA_SDP_SESS_HAS_CONN
2064
/* Copy c= line of the first media to session level,
2067
if (sdp->conn == NULL) {
2068
sdp->conn = pjmedia_sdp_conn_clone(pool, m->conn);
2073
/* Find media bandwidth info */
2074
for (i = 0; i < m->bandw_count; ++i) {
2075
const pj_str_t STR_BANDW_MODIFIER_TIAS = { "TIAS", 4 };
2076
if (!pj_stricmp(&m->bandw[i]->modifier, &STR_BANDW_MODIFIER_TIAS))
2078
tot_bandw_tias += m->bandw[i]->value;
2084
/* Add NAT info in the SDP */
2085
if (pjsua_var.ua_cfg.nat_type_in_sdp) {
2086
pjmedia_sdp_attr *a;
2090
value.ptr = nat_info;
2091
if (pjsua_var.ua_cfg.nat_type_in_sdp == 1) {
2092
value.slen = pj_ansi_snprintf(nat_info, sizeof(nat_info),
2093
"%d", pjsua_var.nat_type);
2095
const char *type_name = pj_stun_get_nat_name(pjsua_var.nat_type);
2096
value.slen = pj_ansi_snprintf(nat_info, sizeof(nat_info),
2102
a = pjmedia_sdp_attr_create(pool, "X-nat", &value);
2104
pjmedia_sdp_attr_add(&sdp->attr_count, sdp->attr, a);
2109
/* Add bandwidth info in session level using bandwidth modifier "AS". */
2110
if (tot_bandw_tias) {
2112
const pj_str_t STR_BANDW_MODIFIER_AS = { "AS", 2 };
2113
pjmedia_sdp_bandw *b;
2115
/* AS bandwidth = RTP bitrate + RTCP bitrate.
2116
* RTP bitrate = payload bitrate (total TIAS) + overheads (~16kbps).
2117
* RTCP bitrate = est. 5% of RTP bitrate.
2118
* Note that AS bandwidth is in kbps.
2120
bandw = tot_bandw_tias + 16000;
2121
bandw += bandw * 5 / 100;
2122
b = PJ_POOL_ALLOC_T(pool, pjmedia_sdp_bandw);
2123
b->modifier = STR_BANDW_MODIFIER_AS;
2124
b->value = bandw / 1000;
2125
sdp->bandw[sdp->bandw_count++] = b;
2129
#if DISABLED_FOR_TICKET_1185 && defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
2130
/* Check if SRTP is in optional mode and configured to use duplicated
2131
* media, i.e: secured and unsecured version, in the SDP offer.
2134
pjsua_var.acc[call->acc_id].cfg.use_srtp == PJMEDIA_SRTP_OPTIONAL &&
2135
pjsua_var.acc[call->acc_id].cfg.srtp_optional_dup_offer)
2139
for (i = 0; i < sdp->media_count; ++i) {
2140
pjmedia_sdp_media *m = sdp->media[i];
2142
/* Check if this media is unsecured but has SDP "crypto"
2145
if (pj_stricmp2(&m->desc.transport, "RTP/AVP") == 0 &&
2146
pjmedia_sdp_media_find_attr2(m, "crypto", NULL) != NULL)
2148
if (i == (unsigned)call->audio_idx &&
2149
sdp_neg_state == PJMEDIA_SDP_NEG_STATE_DONE)
2151
/* This is a session update, and peer has chosen the
2152
* unsecured version, so let's make this unsecured too.
2154
pjmedia_sdp_media_remove_all_attr(m, "crypto");
2156
/* This is new offer, duplicate media so we'll have
2157
* secured (with "RTP/SAVP" transport) and and unsecured
2160
pjmedia_sdp_media *new_m;
2162
/* Duplicate this media and apply secured transport */
2163
new_m = pjmedia_sdp_media_clone(pool, m);
2164
pj_strdup2(pool, &new_m->desc.transport, "RTP/SAVP");
2166
/* Remove the "crypto" attribute in the unsecured media */
2167
pjmedia_sdp_media_remove_all_attr(m, "crypto");
2169
/* Insert the new media before the unsecured media */
2170
if (sdp->media_count < PJMEDIA_MAX_SDP_MEDIA) {
2171
pj_array_insert(sdp->media, sizeof(new_m),
2172
sdp->media_count, i, &new_m);
2182
call->rem_offerer = (rem_sdp != NULL);
2184
/* Notify application */
2185
if (pjsua_var.ua_cfg.cb.on_call_sdp_created) {
2186
(*pjsua_var.ua_cfg.cb.on_call_sdp_created)(call_id, sdp,
2195
static void stop_media_stream(pjsua_call *call, unsigned med_idx)
2197
pjsua_call_media *call_med = &call->media[med_idx];
2199
/* Check if stream does not exist */
2200
if (med_idx >= call->med_cnt)
2203
pj_log_push_indent();
2205
if (call_med->type == PJMEDIA_TYPE_AUDIO) {
2206
pjsua_aud_stop_stream(call_med);
2209
#if PJMEDIA_HAS_VIDEO
2210
else if (call_med->type == PJMEDIA_TYPE_VIDEO) {
2211
pjsua_vid_stop_stream(call_med);
2215
PJ_LOG(4,(THIS_FILE, "Media stream call%02d:%d is destroyed",
2216
call->index, med_idx));
2217
call_med->prev_state = call_med->state;
2218
call_med->state = PJSUA_CALL_MEDIA_NONE;
2220
/* Try to sync recent changes to provisional media */
2221
if (med_idx < call->med_prov_cnt &&
2222
call->media_prov[med_idx].tp == call_med->tp)
2224
pjsua_call_media *prov_med = &call->media_prov[med_idx];
2227
prov_med->prev_state = call_med->prev_state;
2228
prov_med->state = call_med->state;
2231
prov_med->rtp_tx_seq_ts_set = call_med->rtp_tx_seq_ts_set;
2232
prov_med->rtp_tx_seq = call_med->rtp_tx_seq;
2233
prov_med->rtp_tx_ts = call_med->rtp_tx_ts;
2236
if (call_med->type == PJMEDIA_TYPE_AUDIO) {
2237
prov_med->strm.a.conf_slot = call_med->strm.a.conf_slot;
2238
prov_med->strm.a.stream = call_med->strm.a.stream;
2240
#if PJMEDIA_HAS_VIDEO
2241
else if (call_med->type == PJMEDIA_TYPE_VIDEO) {
2242
prov_med->strm.v.cap_win_id = call_med->strm.v.cap_win_id;
2243
prov_med->strm.v.rdr_win_id = call_med->strm.v.rdr_win_id;
2244
prov_med->strm.v.stream = call_med->strm.v.stream;
2249
pj_log_pop_indent();
2252
static void stop_media_session(pjsua_call_id call_id)
2254
pjsua_call *call = &pjsua_var.calls[call_id];
2257
for (mi=0; mi<call->med_cnt; ++mi) {
2258
stop_media_stream(call, mi);
2262
pj_status_t pjsua_media_channel_deinit(pjsua_call_id call_id)
2264
pjsua_call *call = &pjsua_var.calls[call_id];
2267
for (mi=0; mi<call->med_cnt; ++mi) {
2268
pjsua_call_media *call_med = &call->media[mi];
2270
if (call_med->tp_st == PJSUA_MED_TP_CREATING) {
2271
/* We will do the deinitialization after media transport
2272
* creation is completed.
2274
call->async_call.med_ch_deinit = PJ_TRUE;
2279
PJ_LOG(4,(THIS_FILE, "Call %d: deinitializing media..", call_id));
2280
pj_log_push_indent();
2282
stop_media_session(call_id);
2284
/* Clean up media transports */
2285
pjsua_media_prov_clean_up(call_id);
2286
call->med_prov_cnt = 0;
2287
for (mi=0; mi<call->med_cnt; ++mi) {
2288
pjsua_call_media *call_med = &call->media[mi];
2290
if (call_med->tp_st > PJSUA_MED_TP_IDLE) {
2291
pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_IDLE);
2292
pjmedia_transport_media_stop(call_med->tp);
2296
pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_NULL);
2297
pjmedia_transport_close(call_med->tp);
2298
call_med->tp = call_med->tp_orig = NULL;
2300
call_med->tp_orig = NULL;
2303
pj_log_pop_indent();
2309
/* Match codec fmtp. This will compare the values and the order. */
2310
static pj_bool_t match_codec_fmtp(const pjmedia_codec_fmtp *fmtp1,
2311
const pjmedia_codec_fmtp *fmtp2)
2315
if (fmtp1->cnt != fmtp2->cnt)
2318
for (i = 0; i < fmtp1->cnt; ++i) {
2319
if (pj_stricmp(&fmtp1->param[i].name, &fmtp2->param[i].name))
2321
if (pj_stricmp(&fmtp1->param[i].val, &fmtp2->param[i].val))
2328
#if PJSUA_MEDIA_HAS_PJMEDIA || PJSUA_THIRD_PARTY_STREAM_HAS_GET_INFO
2330
static pj_bool_t is_ice_running(pjmedia_transport *tp)
2332
pjmedia_transport_info tpinfo;
2333
pjmedia_ice_transport_info *ice_info;
2335
pjmedia_transport_info_init(&tpinfo);
2336
pjmedia_transport_get_info(tp, &tpinfo);
2337
ice_info = (pjmedia_ice_transport_info*)
2338
pjmedia_transport_info_get_spc_info(&tpinfo,
2339
PJMEDIA_TRANSPORT_TYPE_ICE);
2340
return (ice_info && ice_info->sess_state == PJ_ICE_STRANS_STATE_RUNNING);
2344
static pj_bool_t is_media_changed(const pjsua_call *call,
2346
const pjsua_stream_info *new_si_)
2348
const pjsua_call_media *call_med = &call->media[med_idx];
2350
/* Check for newly added media */
2351
if (med_idx >= call->med_cnt)
2354
/* Compare media type */
2355
if (call_med->type != new_si_->type)
2358
/* Audio update checks */
2359
if (call_med->type == PJMEDIA_TYPE_AUDIO) {
2360
pjmedia_stream_info the_old_si;
2361
const pjmedia_stream_info *old_si = NULL;
2362
const pjmedia_stream_info *new_si = &new_si_->info.aud;
2363
const pjmedia_codec_info *old_ci = NULL;
2364
const pjmedia_codec_info *new_ci = &new_si->fmt;
2365
const pjmedia_codec_param *old_cp = NULL;
2366
const pjmedia_codec_param *new_cp = new_si->param;
2368
/* Compare media direction */
2369
if (call_med->dir != new_si->dir)
2372
/* Get current active stream info */
2373
if (call_med->strm.a.stream) {
2374
pjmedia_stream_get_info(call_med->strm.a.stream, &the_old_si);
2375
old_si = &the_old_si;
2376
old_ci = &old_si->fmt;
2377
old_cp = old_si->param;
2379
/* The stream is inactive. */
2380
return (new_si->dir != PJMEDIA_DIR_NONE);
2383
/* Compare remote RTP address. If ICE is running, change in default
2384
* address can happen after negotiation, this can be handled
2385
* internally by ICE and does not need to cause media restart.
2387
if (!is_ice_running(call_med->tp) &&
2388
pj_sockaddr_cmp(&old_si->rem_addr, &new_si->rem_addr))
2393
/* Compare codec info */
2394
if (pj_stricmp(&old_ci->encoding_name, &new_ci->encoding_name) ||
2395
old_ci->clock_rate != new_ci->clock_rate ||
2396
old_ci->channel_cnt != new_ci->channel_cnt ||
2397
old_si->rx_pt != new_si->rx_pt ||
2398
old_si->tx_pt != new_si->tx_pt ||
2399
old_si->rx_event_pt != new_si->tx_event_pt ||
2400
old_si->tx_event_pt != new_si->tx_event_pt)
2405
/* Compare codec param */
2406
if (old_cp->setting.frm_per_pkt != new_cp->setting.frm_per_pkt ||
2407
old_cp->setting.vad != new_cp->setting.vad ||
2408
old_cp->setting.cng != new_cp->setting.cng ||
2409
old_cp->setting.plc != new_cp->setting.plc ||
2410
old_cp->setting.penh != new_cp->setting.penh ||
2411
!match_codec_fmtp(&old_cp->setting.dec_fmtp,
2412
&new_cp->setting.dec_fmtp) ||
2413
!match_codec_fmtp(&old_cp->setting.enc_fmtp,
2414
&new_cp->setting.enc_fmtp))
2420
#if PJMEDIA_HAS_VIDEO
2421
else if (call_med->type == PJMEDIA_TYPE_VIDEO) {
2422
pjmedia_vid_stream_info the_old_si;
2423
const pjmedia_vid_stream_info *old_si = NULL;
2424
const pjmedia_vid_stream_info *new_si = &new_si_->info.vid;
2425
const pjmedia_vid_codec_info *old_ci = NULL;
2426
const pjmedia_vid_codec_info *new_ci = &new_si->codec_info;
2427
const pjmedia_vid_codec_param *old_cp = NULL;
2428
const pjmedia_vid_codec_param *new_cp = new_si->codec_param;
2430
/* Compare media direction */
2431
if (call_med->dir != new_si->dir)
2434
/* Get current active stream info */
2435
if (call_med->strm.v.stream) {
2436
pjmedia_vid_stream_get_info(call_med->strm.v.stream, &the_old_si);
2437
old_si = &the_old_si;
2438
old_ci = &old_si->codec_info;
2439
old_cp = old_si->codec_param;
2441
/* The stream is inactive. */
2442
return (new_si->dir != PJMEDIA_DIR_NONE);
2445
/* Compare remote RTP address. If ICE is running, change in default
2446
* address can happen after negotiation, this can be handled
2447
* internally by ICE and does not need to cause media restart.
2449
if (!is_ice_running(call_med->tp) &&
2450
pj_sockaddr_cmp(&old_si->rem_addr, &new_si->rem_addr))
2455
/* Compare codec info */
2456
if (pj_stricmp(&old_ci->encoding_name, &new_ci->encoding_name) ||
2457
old_si->rx_pt != new_si->rx_pt ||
2458
old_si->tx_pt != new_si->tx_pt)
2463
/* Compare codec param */
2464
if (/* old_cp->enc_mtu != new_cp->enc_mtu || */
2465
pj_memcmp(&old_cp->enc_fmt.det, &new_cp->enc_fmt.det,
2466
sizeof(pjmedia_video_format_detail)) ||
2467
!match_codec_fmtp(&old_cp->dec_fmtp, &new_cp->dec_fmtp) ||
2468
!match_codec_fmtp(&old_cp->enc_fmtp, &new_cp->enc_fmtp))
2477
/* Just return PJ_TRUE for other media type */
2484
#else /* PJSUA_MEDIA_HAS_PJMEDIA || PJSUA_THIRD_PARTY_STREAM_HAS_GET_INFO */
2486
static pj_bool_t is_media_changed(const pjsua_call *call,
2488
const pjsua_stream_info *new_si_)
2490
PJ_UNUSED_ARG(call);
2491
PJ_UNUSED_ARG(med_idx);
2492
PJ_UNUSED_ARG(new_si_);
2493
/* Always assume that media has been changed */
2497
#endif /* PJSUA_MEDIA_HAS_PJMEDIA || PJSUA_THIRD_PARTY_STREAM_HAS_GET_INFO */
2500
pj_status_t pjsua_media_channel_update(pjsua_call_id call_id,
2501
const pjmedia_sdp_session *local_sdp,
2502
const pjmedia_sdp_session *remote_sdp)
2504
pjsua_call *call = &pjsua_var.calls[call_id];
2505
pjsua_acc *acc = &pjsua_var.acc[call->acc_id];
2506
pj_pool_t *tmp_pool = call->inv->pool_prov;
2508
pj_bool_t got_media = PJ_FALSE;
2509
pj_status_t status = PJ_SUCCESS;
2511
const pj_str_t STR_AUDIO = { "audio", 5 };
2512
const pj_str_t STR_VIDEO = { "video", 5 };
2513
pj_uint8_t maudidx[PJSUA_MAX_CALL_MEDIA];
2514
unsigned maudcnt = PJ_ARRAY_SIZE(maudidx);
2515
unsigned mtotaudcnt = PJ_ARRAY_SIZE(maudidx);
2516
pj_uint8_t mvididx[PJSUA_MAX_CALL_MEDIA];
2517
unsigned mvidcnt = PJ_ARRAY_SIZE(mvididx);
2518
unsigned mtotvidcnt = PJ_ARRAY_SIZE(mvididx);
2519
pj_bool_t need_renego_sdp = PJ_FALSE;
2521
if (pjsua_get_state() != PJSUA_STATE_RUNNING)
2524
PJ_LOG(4,(THIS_FILE, "Call %d: updating media..", call_id));
2525
pj_log_push_indent();
2527
/* Destroy existing media session, if any. */
2528
//stop_media_session(call->index);
2530
/* Call media count must be at least equal to SDP media. Note that
2531
* it may not be equal when remote removed any SDP media line.
2533
pj_assert(call->med_prov_cnt >= local_sdp->media_count);
2535
/* Reset audio_idx first */
2536
call->audio_idx = -1;
2538
/* Sort audio/video based on "quality" */
2539
sort_media(local_sdp, &STR_AUDIO, acc->cfg.use_srtp,
2540
maudidx, &maudcnt, &mtotaudcnt);
2541
#if PJMEDIA_HAS_VIDEO
2542
sort_media(local_sdp, &STR_VIDEO, acc->cfg.use_srtp,
2543
mvididx, &mvidcnt, &mtotvidcnt);
2545
PJ_UNUSED_ARG(STR_VIDEO);
2546
mvidcnt = mtotvidcnt = 0;
2549
/* Applying media count limitation. Note that in generating SDP answer,
2550
* no media count limitation applied, as we didn't know yet which media
2551
* would pass the SDP negotiation.
2553
if (maudcnt > call->opt.aud_cnt || mvidcnt > call->opt.vid_cnt)
2555
pjmedia_sdp_session *local_sdp2;
2557
maudcnt = PJ_MIN(maudcnt, call->opt.aud_cnt);
2558
mvidcnt = PJ_MIN(mvidcnt, call->opt.vid_cnt);
2559
local_sdp2 = pjmedia_sdp_session_clone(tmp_pool, local_sdp);
2561
for (mi=0; mi < local_sdp2->media_count; ++mi) {
2562
pjmedia_sdp_media *m = local_sdp2->media[mi];
2564
if (m->desc.port == 0 ||
2565
pj_memchr(maudidx, mi, maudcnt*sizeof(maudidx[0])) ||
2566
pj_memchr(mvididx, mi, mvidcnt*sizeof(mvididx[0])))
2571
/* Deactivate this media */
2572
pjmedia_sdp_media_deactivate(tmp_pool, m);
2575
local_sdp = local_sdp2;
2576
need_renego_sdp = PJ_TRUE;
2579
/* Process each media stream */
2580
for (mi=0; mi < call->med_prov_cnt; ++mi) {
2581
pjsua_call_media *call_med = &call->media_prov[mi];
2582
pj_bool_t media_changed = PJ_FALSE;
2584
if (mi >= local_sdp->media_count ||
2585
mi >= remote_sdp->media_count)
2587
/* This may happen when remote removed any SDP media lines in
2592
stop_media_stream(call, mi);
2594
/* Close the media transport */
2596
pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_NULL);
2597
pjmedia_transport_close(call_med->tp);
2598
call_med->tp = call_med->tp_orig = NULL;
2602
/* Something is wrong */
2603
PJ_LOG(1,(THIS_FILE, "Error updating media for call %d: "
2604
"invalid media index %d in SDP", call_id, mi));
2605
status = PJMEDIA_SDP_EINSDP;
2610
/* Apply media update action */
2611
if (call_med->type==PJMEDIA_TYPE_AUDIO) {
2612
pjmedia_stream_info the_si, *si = &the_si;
2613
pjsua_stream_info stream_info;
2615
status = pjmedia_stream_info_from_sdp(
2616
si, tmp_pool, pjsua_var.med_endpt,
2617
local_sdp, remote_sdp, mi);
2618
if (status != PJ_SUCCESS) {
2619
PJ_PERROR(1,(THIS_FILE, status,
2620
"pjmedia_stream_info_from_sdp() failed "
2621
"for call_id %d media %d",
2626
/* Codec parameter of stream info (si->param) can be NULL if
2627
* the stream is rejected or disabled.
2629
/* Override ptime, if this option is specified. */
2630
if (pjsua_var.media_cfg.ptime != 0 && si->param) {
2631
si->param->setting.frm_per_pkt = (pj_uint8_t)
2632
(pjsua_var.media_cfg.ptime / si->param->info.frm_ptime);
2633
if (si->param->setting.frm_per_pkt == 0)
2634
si->param->setting.frm_per_pkt = 1;
2637
/* Disable VAD, if this option is specified. */
2638
if (pjsua_var.media_cfg.no_vad && si->param) {
2639
si->param->setting.vad = 0;
2642
/* Check if this media is changed */
2643
stream_info.type = PJMEDIA_TYPE_AUDIO;
2644
stream_info.info.aud = the_si;
2645
if (pjsua_var.media_cfg.no_smart_media_update ||
2646
is_media_changed(call, mi, &stream_info))
2648
media_changed = PJ_TRUE;
2649
/* Stop the media */
2650
stop_media_stream(call, mi);
2652
PJ_LOG(4,(THIS_FILE, "Call %d: stream #%d (audio) unchanged.",
2656
/* Check if no media is active */
2657
if (si->dir == PJMEDIA_DIR_NONE) {
2659
/* Update call media state and direction */
2660
call_med->state = PJSUA_CALL_MEDIA_NONE;
2661
call_med->dir = PJMEDIA_DIR_NONE;
2664
pjmedia_transport_info tp_info;
2665
pjmedia_srtp_info *srtp_info;
2667
/* Start/restart media transport based on info in SDP */
2668
status = pjmedia_transport_media_start(call_med->tp,
2669
tmp_pool, local_sdp,
2671
if (status != PJ_SUCCESS) {
2672
PJ_PERROR(1,(THIS_FILE, status,
2673
"pjmedia_transport_media_start() failed "
2674
"for call_id %d media %d",
2679
pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_RUNNING);
2681
/* Get remote SRTP usage policy */
2682
pjmedia_transport_info_init(&tp_info);
2683
pjmedia_transport_get_info(call_med->tp, &tp_info);
2684
srtp_info = (pjmedia_srtp_info*)
2685
pjmedia_transport_info_get_spc_info(
2686
&tp_info, PJMEDIA_TRANSPORT_TYPE_SRTP);
2688
call_med->rem_srtp_use = srtp_info->peer_use;
2691
/* Update audio channel */
2692
if (media_changed) {
2693
status = pjsua_aud_channel_update(call_med,
2694
call->inv->pool, si,
2695
local_sdp, remote_sdp);
2696
if (status != PJ_SUCCESS) {
2697
PJ_PERROR(1,(THIS_FILE, status,
2698
"pjsua_aud_channel_update() failed "
2699
"for call_id %d media %d",
2705
/* Call media direction */
2706
call_med->dir = si->dir;
2708
/* Call media state */
2709
if (call->local_hold)
2710
call_med->state = PJSUA_CALL_MEDIA_LOCAL_HOLD;
2711
else if (call_med->dir == PJMEDIA_DIR_DECODING)
2712
call_med->state = PJSUA_CALL_MEDIA_REMOTE_HOLD;
2714
call_med->state = PJSUA_CALL_MEDIA_ACTIVE;
2718
if (status == PJ_SUCCESS) {
2725
case PJMEDIA_DIR_NONE:
2728
case PJMEDIA_DIR_ENCODING:
2731
case PJMEDIA_DIR_DECODING:
2734
case PJMEDIA_DIR_ENCODING_DECODING:
2741
len = pj_ansi_sprintf( info+info_len,
2742
", stream #%d: %.*s (%s)", mi,
2743
(int)si->fmt.encoding_name.slen,
2744
si->fmt.encoding_name.ptr,
2748
PJ_LOG(4,(THIS_FILE,"Audio updated%s", info));
2752
if (call->audio_idx==-1 && status==PJ_SUCCESS &&
2753
si->dir != PJMEDIA_DIR_NONE)
2755
call->audio_idx = mi;
2758
#if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0)
2759
} else if (call_med->type==PJMEDIA_TYPE_VIDEO) {
2760
pjmedia_vid_stream_info the_si, *si = &the_si;
2761
pjsua_stream_info stream_info;
2763
status = pjmedia_vid_stream_info_from_sdp(
2764
si, tmp_pool, pjsua_var.med_endpt,
2765
local_sdp, remote_sdp, mi);
2766
if (status != PJ_SUCCESS) {
2767
PJ_PERROR(1,(THIS_FILE, status,
2768
"pjmedia_vid_stream_info_from_sdp() failed "
2769
"for call_id %d media %d",
2774
/* Check if this media is changed */
2775
stream_info.type = PJMEDIA_TYPE_VIDEO;
2776
stream_info.info.vid = the_si;
2777
if (is_media_changed(call, mi, &stream_info)) {
2778
media_changed = PJ_TRUE;
2779
/* Stop the media */
2780
stop_media_stream(call, mi);
2782
PJ_LOG(4,(THIS_FILE, "Call %d: stream #%d (video) unchanged.",
2786
/* Check if no media is active */
2787
if (si->dir == PJMEDIA_DIR_NONE) {
2789
/* Update call media state and direction */
2790
call_med->state = PJSUA_CALL_MEDIA_NONE;
2791
call_med->dir = PJMEDIA_DIR_NONE;
2794
pjmedia_transport_info tp_info;
2795
pjmedia_srtp_info *srtp_info;
2797
/* Start/restart media transport */
2798
status = pjmedia_transport_media_start(call_med->tp,
2799
tmp_pool, local_sdp,
2801
if (status != PJ_SUCCESS) {
2802
PJ_PERROR(1,(THIS_FILE, status,
2803
"pjmedia_transport_media_start() failed "
2804
"for call_id %d media %d",
2809
pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_RUNNING);
2811
/* Get remote SRTP usage policy */
2812
pjmedia_transport_info_init(&tp_info);
2813
pjmedia_transport_get_info(call_med->tp, &tp_info);
2814
srtp_info = (pjmedia_srtp_info*)
2815
pjmedia_transport_info_get_spc_info(
2816
&tp_info, PJMEDIA_TRANSPORT_TYPE_SRTP);
2818
call_med->rem_srtp_use = srtp_info->peer_use;
2821
/* Update audio channel */
2822
if (media_changed) {
2823
status = pjsua_vid_channel_update(call_med,
2824
call->inv->pool, si,
2825
local_sdp, remote_sdp);
2826
if (status != PJ_SUCCESS) {
2827
PJ_PERROR(1,(THIS_FILE, status,
2828
"pjsua_vid_channel_update() failed "
2829
"for call_id %d media %d",
2835
/* Call media direction */
2836
call_med->dir = si->dir;
2838
/* Call media state */
2839
if (call->local_hold)
2840
call_med->state = PJSUA_CALL_MEDIA_LOCAL_HOLD;
2841
else if (call_med->dir == PJMEDIA_DIR_DECODING)
2842
call_med->state = PJSUA_CALL_MEDIA_REMOTE_HOLD;
2844
call_med->state = PJSUA_CALL_MEDIA_ACTIVE;
2855
case PJMEDIA_DIR_NONE:
2858
case PJMEDIA_DIR_ENCODING:
2861
case PJMEDIA_DIR_DECODING:
2864
case PJMEDIA_DIR_ENCODING_DECODING:
2871
len = pj_ansi_sprintf( info+info_len,
2872
", stream #%d: %.*s (%s)", mi,
2873
(int)si->codec_info.encoding_name.slen,
2874
si->codec_info.encoding_name.ptr,
2878
PJ_LOG(4,(THIS_FILE,"Video updated%s", info));
2883
status = PJMEDIA_EINVALIMEDIATYPE;
2886
/* Close the transport of deactivated media, need this here as media
2887
* can be deactivated by the SDP negotiation and the max media count
2888
* (account) setting.
2890
if (local_sdp->media[mi]->desc.port==0 && call_med->tp) {
2891
pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_NULL);
2892
pjmedia_transport_close(call_med->tp);
2893
call_med->tp = call_med->tp_orig = NULL;
2896
if (status != PJ_SUCCESS) {
2897
PJ_PERROR(1,(THIS_FILE, status, "Error updating media call%02d:%d",
2900
got_media = PJ_TRUE;
2904
/* Update call media from provisional media */
2905
call->med_cnt = call->med_prov_cnt;
2906
pj_memcpy(call->media, call->media_prov,
2907
sizeof(call->media_prov[0]) * call->med_prov_cnt);
2909
/* Perform SDP re-negotiation if needed. */
2910
if (got_media && need_renego_sdp) {
2911
pjmedia_sdp_neg *neg = call->inv->neg;
2913
/* This should only happen when we are the answerer. */
2914
PJ_ASSERT_RETURN(neg && !pjmedia_sdp_neg_was_answer_remote(neg),
2915
PJMEDIA_SDPNEG_EINSTATE);
2917
status = pjmedia_sdp_neg_set_remote_offer(tmp_pool, neg, remote_sdp);
2918
if (status != PJ_SUCCESS)
2921
status = pjmedia_sdp_neg_set_local_answer(tmp_pool, neg, local_sdp);
2922
if (status != PJ_SUCCESS)
2925
status = pjmedia_sdp_neg_negotiate(tmp_pool, neg, 0);
2926
if (status != PJ_SUCCESS)
2930
pj_log_pop_indent();
2931
return (got_media? PJ_SUCCESS : PJMEDIA_SDPNEG_ENOMEDIA);
2934
pj_log_pop_indent();
2938
/*****************************************************************************
2943
* Enum all supported codecs in the system.
2945
PJ_DEF(pj_status_t) pjsua_enum_codecs( pjsua_codec_info id[],
2948
pjmedia_codec_mgr *codec_mgr;
2949
pjmedia_codec_info info[32];
2950
unsigned i, count, prio[32];
2953
codec_mgr = pjmedia_endpt_get_codec_mgr(pjsua_var.med_endpt);
2954
count = PJ_ARRAY_SIZE(info);
2955
status = pjmedia_codec_mgr_enum_codecs( codec_mgr, &count, info, prio);
2956
if (status != PJ_SUCCESS) {
2961
if (count > *p_count) count = *p_count;
2963
for (i=0; i<count; ++i) {
2964
pj_bzero(&id[i], sizeof(pjsua_codec_info));
2966
pjmedia_codec_info_to_id(&info[i], id[i].buf_, sizeof(id[i].buf_));
2967
id[i].codec_id = pj_str(id[i].buf_);
2968
id[i].priority = (pj_uint8_t) prio[i];
2978
* Change codec priority.
2980
PJ_DEF(pj_status_t) pjsua_codec_set_priority( const pj_str_t *codec_id,
2981
pj_uint8_t priority )
2983
const pj_str_t all = { NULL, 0 };
2984
pjmedia_codec_mgr *codec_mgr;
2986
codec_mgr = pjmedia_endpt_get_codec_mgr(pjsua_var.med_endpt);
2988
if (codec_id->slen==1 && *codec_id->ptr=='*')
2991
return pjmedia_codec_mgr_set_codec_priority(codec_mgr, codec_id,
2997
* Get codec parameters.
2999
PJ_DEF(pj_status_t) pjsua_codec_get_param( const pj_str_t *codec_id,
3000
pjmedia_codec_param *param )
3002
const pj_str_t all = { NULL, 0 };
3003
const pjmedia_codec_info *info;
3004
pjmedia_codec_mgr *codec_mgr;
3008
codec_mgr = pjmedia_endpt_get_codec_mgr(pjsua_var.med_endpt);
3010
if (codec_id->slen==1 && *codec_id->ptr=='*')
3013
status = pjmedia_codec_mgr_find_codecs_by_id(codec_mgr, codec_id,
3014
&count, &info, NULL);
3015
if (status != PJ_SUCCESS)
3019
return (count > 1? PJ_ETOOMANY : PJ_ENOTFOUND);
3021
status = pjmedia_codec_mgr_get_default_param( codec_mgr, info, param);
3027
* Set codec parameters.
3029
PJ_DEF(pj_status_t) pjsua_codec_set_param( const pj_str_t *codec_id,
3030
const pjmedia_codec_param *param)
3032
const pjmedia_codec_info *info[2];
3033
pjmedia_codec_mgr *codec_mgr;
3037
codec_mgr = pjmedia_endpt_get_codec_mgr(pjsua_var.med_endpt);
3039
status = pjmedia_codec_mgr_find_codecs_by_id(codec_mgr, codec_id,
3040
&count, info, NULL);
3041
if (status != PJ_SUCCESS)
3044
/* Codec ID should be specific, except for G.722.1 */
3046
pj_strnicmp2(codec_id, "G7221/16", 8) != 0 &&
3047
pj_strnicmp2(codec_id, "G7221/32", 8) != 0)
3049
pj_assert(!"Codec ID is not specific");
3053
status = pjmedia_codec_mgr_set_default_param(codec_mgr, info[0], param);
3058
pj_status_t pjsua_media_apply_xml_control(pjsua_call_id call_id,
3059
const pj_str_t *xml_st)
3061
#if PJMEDIA_HAS_VIDEO
3062
pjsua_call *call = &pjsua_var.calls[call_id];
3063
const pj_str_t PICT_FAST_UPDATE = {"picture_fast_update", 19};
3065
if (pj_strstr(xml_st, &PICT_FAST_UPDATE)) {
3068
PJ_LOG(4,(THIS_FILE, "Received keyframe request via SIP INFO"));
3070
for (i = 0; i < call->med_cnt; ++i) {
3071
pjsua_call_media *cm = &call->media[i];
3072
if (cm->type != PJMEDIA_TYPE_VIDEO || !cm->strm.v.stream)
3075
pjmedia_vid_stream_send_keyframe(cm->strm.v.stream);
3082
/* Just to avoid compiler warning of unused var */
3083
PJ_UNUSED_ARG(call_id);
3084
PJ_UNUSED_ARG(xml_st);