~noskcaj/ubuntu/saucy/sflphone/merge-1.2.3-2

« back to all changes in this revision

Viewing changes to daemon/libs/pjproject-2.0.1/pjsip/src/pjsua-lib/pjsua_media.c

  • Committer: Jackson Doak
  • Date: 2013-07-10 21:04:46 UTC
  • mfrom: (20.1.3 sid)
  • Revision ID: noskcaj@ubuntu.com-20130710210446-y8f587vza807icr9
Properly merged from upstream.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* $Id: pjsua_media.c 4182 2012-06-27 07:12:23Z ming $ */
 
2
/*
 
3
 * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
 
4
 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
 
5
 *
 
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
 
 
26
#define DEFAULT_RTP_PORT        4000
 
27
 
 
28
#ifndef PJSUA_REQUIRE_CONSECUTIVE_RTCP_PORT
 
29
#   define PJSUA_REQUIRE_CONSECUTIVE_RTCP_PORT  0
 
30
#endif
 
31
 
 
32
/* Next RTP port to be used */
 
33
static pj_uint16_t next_rtp_port;
 
34
 
 
35
static 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
 
 
44
 
 
45
/**
 
46
 * Init media subsystems.
 
47
 */
 
48
pj_status_t pjsua_media_subsys_init(const pjsua_media_config *cfg)
 
49
{
 
50
    pj_status_t status;
 
51
 
 
52
    pj_log_push_indent();
 
53
 
 
54
    /* 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);
 
60
    /* EC settings use different API */
 
61
    pjsua_var.aud_svmask &= ~(PJMEDIA_AUD_DEV_CAP_EC |
 
62
                              PJMEDIA_AUD_DEV_CAP_EC_TAIL);
 
63
 
 
64
    /* Copy configuration */
 
65
    pjsua_media_config_dup(pjsua_var.pool, &pjsua_var.media_cfg, cfg);
 
66
 
 
67
    /* Normalize configuration */
 
68
    if (pjsua_var.media_cfg.snd_clock_rate == 0) {
 
69
        pjsua_var.media_cfg.snd_clock_rate = pjsua_var.media_cfg.clock_rate;
 
70
    }
 
71
 
 
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);
 
92
        goto on_error;
 
93
    }
 
94
 
 
95
    status = pjsua_aud_subsys_init();
 
96
    if (status != PJ_SUCCESS)
 
97
        goto on_error;
 
98
 
 
99
#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
 
100
    /* Initialize SRTP library (ticket #788). */
 
101
    status = pjmedia_srtp_init_lib(pjsua_var.med_endpt);
 
102
    if (status != PJ_SUCCESS) {
 
103
        pjsua_perror(THIS_FILE, "Error initializing SRTP library",
 
104
                     status);
 
105
        goto on_error;
 
106
    }
 
107
#endif
 
108
 
 
109
    /* Video */
 
110
#if PJMEDIA_HAS_VIDEO
 
111
    status = pjsua_vid_subsys_init();
 
112
    if (status != PJ_SUCCESS)
 
113
        goto on_error;
 
114
#endif
 
115
 
 
116
    pj_log_pop_indent();
 
117
    return PJ_SUCCESS;
 
118
 
 
119
on_error:
 
120
    pj_log_pop_indent();
 
121
    return status;
 
122
}
 
123
 
 
124
/*
 
125
 * Start pjsua media subsystem.
 
126
 */
 
127
pj_status_t pjsua_media_subsys_start(void)
 
128
{
 
129
    pj_status_t status;
 
130
 
 
131
    pj_log_push_indent();
 
132
 
 
133
#if DISABLED_FOR_TICKET_1185
 
134
    /* Create media for calls, if none is specified */
 
135
    if (pjsua_var.calls[0].media[0].tp == NULL) {
 
136
        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);
 
143
        if (status != PJ_SUCCESS) {
 
144
            pj_log_pop_indent();
 
145
            return status;
 
146
        }
 
147
    }
 
148
#endif
 
149
 
 
150
    /* Audio */
 
151
    status = pjsua_aud_subsys_start();
 
152
    if (status != PJ_SUCCESS) {
 
153
        pj_log_pop_indent();
 
154
        return status;
 
155
    }
 
156
 
 
157
    /* Video */
 
158
#if PJMEDIA_HAS_VIDEO
 
159
    status = pjsua_vid_subsys_start();
 
160
    if (status != PJ_SUCCESS) {
 
161
        pjsua_aud_subsys_destroy();
 
162
        pj_log_pop_indent();
 
163
        return status;
 
164
    }
 
165
#endif
 
166
 
 
167
    /* Perform NAT detection */
 
168
    status = pjsua_detect_nat_type();
 
169
    if (status != PJ_SUCCESS) {
 
170
        PJ_PERROR(1,(THIS_FILE, status, "NAT type detection failed"));
 
171
    }
 
172
 
 
173
    pj_log_pop_indent();
 
174
    return PJ_SUCCESS;
 
175
}
 
176
 
 
177
 
 
178
/*
 
179
 * Destroy pjsua media subsystem.
 
180
 */
 
181
pj_status_t pjsua_media_subsys_destroy(unsigned flags)
 
182
{
 
183
    unsigned i;
 
184
 
 
185
    PJ_LOG(4,(THIS_FILE, "Shutting down media.."));
 
186
    pj_log_push_indent();
 
187
 
 
188
    if (pjsua_var.med_endpt) {
 
189
        pjsua_aud_subsys_destroy();
 
190
    }
 
191
 
 
192
    /* Close media transports */
 
193
    for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
 
194
        /* 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);
 
199
    }
 
200
 
 
201
    /* Destroy media endpoint. */
 
202
    if (pjsua_var.med_endpt) {
 
203
 
 
204
#       if PJMEDIA_HAS_VIDEO
 
205
            pjsua_vid_subsys_destroy();
 
206
#       endif
 
207
 
 
208
        pjmedia_endpt_destroy(pjsua_var.med_endpt);
 
209
        pjsua_var.med_endpt = NULL;
 
210
 
 
211
        /* Deinitialize sound subsystem */
 
212
        // Not necessary, as pjmedia_snd_deinit() should have been called
 
213
        // in pjmedia_endpt_destroy().
 
214
        //pjmedia_snd_deinit();
 
215
    }
 
216
 
 
217
    /* Reset RTP port */
 
218
    next_rtp_port = 0;
 
219
 
 
220
    pj_log_pop_indent();
 
221
 
 
222
    return PJ_SUCCESS;
 
223
}
 
224
 
 
225
/*
 
226
 * Create RTP and RTCP socket pair, and possibly resolve their public
 
227
 * address via STUN.
 
228
 */
 
229
static pj_status_t create_rtp_rtcp_sock(const pjsua_transport_config *cfg,
 
230
                                        pjmedia_sock_info *skinfo)
 
231
{
 
232
    enum {
 
233
        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
 
 
252
    if (next_rtp_port == 0)
 
253
        next_rtp_port = (pj_uint16_t)40000;
 
254
 
 
255
    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 */
 
279
        status = pj_sock_apply_qos2(sock[0], cfg->qos_type,
 
280
                                    &cfg->qos_params,
 
281
                                    2, THIS_FILE, "RTP socket");
 
282
 
 
283
        /* Bind RTP socket */
 
284
        status=pj_sock_bind_in(sock[0], pj_ntohl(bound_addr.sin_addr.s_addr),
 
285
                               next_rtp_port);
 
286
        if (status != PJ_SUCCESS) {
 
287
            pj_sock_close(sock[0]);
 
288
            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 */
 
301
        status = pj_sock_apply_qos2(sock[1], cfg->qos_type,
 
302
                                    &cfg->qos_params,
 
303
                                    2, THIS_FILE, "RTCP socket");
 
304
 
 
305
        /* Bind RTCP socket */
 
306
        status=pj_sock_bind_in(sock[1], pj_ntohl(bound_addr.sin_addr.s_addr),
 
307
                               (pj_uint16_t)(next_rtp_port+1));
 
308
        if (status != PJ_SUCCESS) {
 
309
            pj_sock_close(sock[0]);
 
310
            sock[0] = PJ_INVALID_SOCKET;
 
311
 
 
312
            pj_sock_close(sock[1]);
 
313
            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
 
 
325
            pj_ansi_strcpy(ip_addr,
 
326
                           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
 
339
            if (pj_ntohs(mapped_addr[1].sin_port) ==
 
340
                pj_ntohs(mapped_addr[0].sin_port)+1)
 
341
            {
 
342
                /* Success! */
 
343
                break;
 
344
            }
 
345
 
 
346
            pj_sock_close(sock[0]);
 
347
            sock[0] = PJ_INVALID_SOCKET;
 
348
 
 
349
            pj_sock_close(sock[1]);
 
350
            sock[1] = PJ_INVALID_SOCKET;
 
351
#else
 
352
            if (pj_ntohs(mapped_addr[1].sin_port) !=
 
353
                pj_ntohs(mapped_addr[0].sin_port)+1)
 
354
            {
 
355
                PJ_LOG(4,(THIS_FILE,
 
356
                          "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) {
 
404
        PJ_LOG(1,(THIS_FILE,
 
405
                  "Unable to find appropriate RTP/RTCP ports combination"));
 
406
        goto on_error;
 
407
    }
 
408
 
 
409
 
 
410
    skinfo->rtp_sock = sock[0];
 
411
    pj_memcpy(&skinfo->rtp_addr_name,
 
412
              &mapped_addr[0], sizeof(pj_sockaddr_in));
 
413
 
 
414
    skinfo->rtcp_sock = sock[1];
 
415
    pj_memcpy(&skinfo->rtcp_addr_name,
 
416
              &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
 
 
428
on_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
 
 
436
/* Create normal UDP media transports */
 
437
static pj_status_t create_udp_media_transport(const pjsua_transport_config *cfg,
 
438
                                              pjsua_call_media *call_med)
 
439
{
 
440
    pjmedia_sock_info skinfo;
 
441
    pj_status_t status;
 
442
 
 
443
    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;
 
448
    }
 
449
 
 
450
    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
    }
 
457
 
 
458
    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);
 
463
 
 
464
    call_med->tp_ready = PJ_SUCCESS;
 
465
 
 
466
    return PJ_SUCCESS;
 
467
 
 
468
on_error:
 
469
    if (call_med->tp)
 
470
        pjmedia_transport_close(call_med->tp);
 
471
 
 
472
    return status;
 
473
}
 
474
 
 
475
#if DISABLED_FOR_TICKET_1185
 
476
/* Create normal UDP media transports */
 
477
static pj_status_t create_udp_media_transports(pjsua_transport_config *cfg)
 
478
{
 
479
    unsigned i;
 
480
    pj_status_t status;
 
481
 
 
482
    for (i=0; i < pjsua_var.ua_cfg.max_calls; ++i) {
 
483
        pjsua_call *call = &pjsua_var.calls[i];
 
484
        unsigned strm_idx;
 
485
 
 
486
        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;
 
492
        }
 
493
    }
 
494
 
 
495
    return PJ_SUCCESS;
 
496
 
 
497
on_error:
 
498
    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
            }
 
509
        }
 
510
    }
 
511
    return status;
 
512
}
 
513
#endif
 
514
 
 
515
static void med_tp_timer_cb(void *user_data)
 
516
{
 
517
    pjsua_call_media *call_med = (pjsua_call_media*)user_data;
 
518
    pjsua_call *call = NULL;
 
519
    pjsip_dialog *dlg = NULL;
 
520
 
 
521
    acquire_call("med_tp_timer_cb", call_med->call->index, &call, &dlg);
 
522
 
 
523
    call_med->tp_ready = call_med->tp_result;
 
524
    if (call_med->med_create_cb)
 
525
        (*call_med->med_create_cb)(call_med, call_med->tp_ready,
 
526
                                   call_med->call->secure_level, NULL);
 
527
 
 
528
    if (dlg)
 
529
        pjsip_dlg_dec_lock(dlg);
 
530
}
 
531
 
 
532
/* This callback is called when ICE negotiation completes */
 
533
static void on_ice_complete(pjmedia_transport *tp,
 
534
                            pj_ice_strans_op op,
 
535
                            pj_status_t result)
 
536
{
 
537
    pjsua_call_media *call_med = (pjsua_call_media*)tp->user_data;
 
538
 
 
539
    if (!call_med)
 
540
        return;
 
541
 
 
542
    switch (op) {
 
543
    case PJ_ICE_STRANS_OP_INIT:
 
544
        call_med->tp_result = result;
 
545
        pjsua_schedule_timer2(&med_tp_timer_cb, call_med, 1);
 
546
        break;
 
547
    case PJ_ICE_STRANS_OP_NEGOTIATION:
 
548
        if (result != PJ_SUCCESS) {
 
549
            call_med->state = PJSUA_CALL_MEDIA_ERROR;
 
550
            call_med->dir = PJMEDIA_DIR_NONE;
 
551
 
 
552
            if (call_med->call && pjsua_var.ua_cfg.cb.on_call_media_state) {
 
553
                pjsua_var.ua_cfg.cb.on_call_media_state(call_med->call->index);
 
554
            }
 
555
        } else if (call_med->call) {
 
556
            /* Send UPDATE if default transport address is different than
 
557
             * what was advertised (ticket #881)
 
558
             */
 
559
            pjmedia_transport_info tpinfo;
 
560
            pjmedia_ice_transport_info *ii = NULL;
 
561
            unsigned i;
 
562
 
 
563
            pjmedia_transport_info_init(&tpinfo);
 
564
            pjmedia_transport_get_info(tp, &tpinfo);
 
565
            for (i=0; i<tpinfo.specific_info_cnt; ++i) {
 
566
                if (tpinfo.spc_info[i].type==PJMEDIA_TRANSPORT_TYPE_ICE) {
 
567
                    ii = (pjmedia_ice_transport_info*)
 
568
                         tpinfo.spc_info[i].buffer;
 
569
                    break;
 
570
                }
 
571
            }
 
572
 
 
573
            if (ii && ii->role==PJ_ICE_SESS_ROLE_CONTROLLING &&
 
574
                pj_sockaddr_cmp(&tpinfo.sock_info.rtp_addr_name,
 
575
                                &call_med->rtp_addr))
 
576
            {
 
577
                pj_bool_t use_update;
 
578
                const pj_str_t STR_UPDATE = { "UPDATE", 6 };
 
579
                pjsip_dialog_cap_status support_update;
 
580
                pjsip_dialog *dlg;
 
581
 
 
582
                dlg = call_med->call->inv->dlg;
 
583
                support_update = pjsip_dlg_remote_has_cap(dlg, PJSIP_H_ALLOW,
 
584
                                                          NULL, &STR_UPDATE);
 
585
                use_update = (support_update == PJSIP_DIALOG_CAP_SUPPORTED);
 
586
 
 
587
                PJ_LOG(4,(THIS_FILE,
 
588
                          "ICE default transport address has changed for "
 
589
                          "call %d, sending %s",
 
590
                          call_med->call->index,
 
591
                          (use_update ? "UPDATE" : "re-INVITE")));
 
592
 
 
593
                if (use_update)
 
594
                    pjsua_call_update(call_med->call->index, 0, NULL);
 
595
                else
 
596
                    pjsua_call_reinvite(call_med->call->index, 0, NULL);
 
597
            }
 
598
        }
 
599
        break;
 
600
    case PJ_ICE_STRANS_OP_KEEP_ALIVE:
 
601
        if (result != PJ_SUCCESS) {
 
602
            PJ_PERROR(4,(THIS_FILE, result,
 
603
                         "ICE keep alive failure for transport %d:%d",
 
604
                         call_med->call->index, call_med->idx));
 
605
        }
 
606
        if (pjsua_var.ua_cfg.cb.on_call_media_transport_state) {
 
607
            pjsua_med_tp_state_info info;
 
608
 
 
609
            pj_bzero(&info, sizeof(info));
 
610
            info.med_idx = call_med->idx;
 
611
            info.state = call_med->tp_st;
 
612
            info.status = result;
 
613
            info.ext_info = &op;
 
614
            (*pjsua_var.ua_cfg.cb.on_call_media_transport_state)(
 
615
                call_med->call->index, &info);
 
616
        }
 
617
        if (pjsua_var.ua_cfg.cb.on_ice_transport_error) {
 
618
            pjsua_call_id id = call_med->call->index;
 
619
            (*pjsua_var.ua_cfg.cb.on_ice_transport_error)(id, op, result,
 
620
                                                          NULL);
 
621
        }
 
622
        break;
 
623
    }
 
624
}
 
625
 
 
626
 
 
627
/* Parse "HOST:PORT" format */
 
628
static pj_status_t parse_host_port(const pj_str_t *host_port,
 
629
                                   pj_str_t *host, pj_uint16_t *port)
 
630
{
 
631
    pj_str_t str_port;
 
632
 
 
633
    str_port.ptr = pj_strchr(host_port, ':');
 
634
    if (str_port.ptr != NULL) {
 
635
        int iport;
 
636
 
 
637
        host->ptr = host_port->ptr;
 
638
        host->slen = (str_port.ptr - host->ptr);
 
639
        str_port.ptr++;
 
640
        str_port.slen = host_port->slen - host->slen - 1;
 
641
        iport = (int)pj_strtoul(&str_port);
 
642
        if (iport < 1 || iport > 65535)
 
643
            return PJ_EINVAL;
 
644
        *port = (pj_uint16_t)iport;
 
645
    } else {
 
646
        *host = *host_port;
 
647
        *port = 0;
 
648
    }
 
649
 
 
650
    return PJ_SUCCESS;
 
651
}
 
652
 
 
653
/* Create ICE media transports (when ice is enabled) */
 
654
static pj_status_t create_ice_media_transport(
 
655
                                const pjsua_transport_config *cfg,
 
656
                                pjsua_call_media *call_med,
 
657
                                pj_bool_t async)
 
658
{
 
659
    char stunip[PJ_INET6_ADDRSTRLEN];
 
660
    pj_ice_strans_cfg ice_cfg;
 
661
    pjmedia_ice_cb ice_cb;
 
662
    char name[32];
 
663
    unsigned comp_cnt;
 
664
    pj_status_t status;
 
665
 
 
666
    /* Make sure STUN server resolution has completed */
 
667
    status = resolve_stun_server(PJ_TRUE);
 
668
    if (status != PJ_SUCCESS) {
 
669
        pjsua_perror(THIS_FILE, "Error resolving STUN server", status);
 
670
        return status;
 
671
    }
 
672
 
 
673
    /* Create ICE stream transport configuration */
 
674
    pj_ice_strans_cfg_default(&ice_cfg);
 
675
    pj_stun_config_init(&ice_cfg.stun_cfg, &pjsua_var.cp.factory, 0,
 
676
                        pjsip_endpt_get_ioqueue(pjsua_var.endpt),
 
677
                        pjsip_endpt_get_timer_heap(pjsua_var.endpt));
 
678
 
 
679
    ice_cfg.af = pj_AF_INET();
 
680
    ice_cfg.resolver = pjsua_var.resolver;
 
681
 
 
682
    ice_cfg.opt = pjsua_var.media_cfg.ice_opt;
 
683
 
 
684
    /* Configure STUN settings */
 
685
    if (pj_sockaddr_has_addr(&pjsua_var.stun_srv)) {
 
686
        pj_sockaddr_print(&pjsua_var.stun_srv, stunip, sizeof(stunip), 0);
 
687
        ice_cfg.stun.server = pj_str(stunip);
 
688
        ice_cfg.stun.port = pj_sockaddr_get_port(&pjsua_var.stun_srv);
 
689
    }
 
690
    if (pjsua_var.media_cfg.ice_max_host_cands >= 0)
 
691
        ice_cfg.stun.max_host_cands = pjsua_var.media_cfg.ice_max_host_cands;
 
692
 
 
693
    /* Copy QoS setting to STUN setting */
 
694
    ice_cfg.stun.cfg.qos_type = cfg->qos_type;
 
695
    pj_memcpy(&ice_cfg.stun.cfg.qos_params, &cfg->qos_params,
 
696
              sizeof(cfg->qos_params));
 
697
 
 
698
    /* Configure TURN settings */
 
699
    if (pjsua_var.media_cfg.enable_turn) {
 
700
        status = parse_host_port(&pjsua_var.media_cfg.turn_server,
 
701
                                 &ice_cfg.turn.server,
 
702
                                 &ice_cfg.turn.port);
 
703
        if (status != PJ_SUCCESS || ice_cfg.turn.server.slen == 0) {
 
704
            PJ_LOG(1,(THIS_FILE, "Invalid TURN server setting"));
 
705
            return PJ_EINVAL;
 
706
        }
 
707
        if (ice_cfg.turn.port == 0)
 
708
            ice_cfg.turn.port = 3479;
 
709
        ice_cfg.turn.conn_type = pjsua_var.media_cfg.turn_conn_type;
 
710
        pj_memcpy(&ice_cfg.turn.auth_cred,
 
711
                  &pjsua_var.media_cfg.turn_auth_cred,
 
712
                  sizeof(ice_cfg.turn.auth_cred));
 
713
 
 
714
        /* Copy QoS setting to TURN setting */
 
715
        ice_cfg.turn.cfg.qos_type = cfg->qos_type;
 
716
        pj_memcpy(&ice_cfg.turn.cfg.qos_params, &cfg->qos_params,
 
717
                  sizeof(cfg->qos_params));
 
718
    }
 
719
 
 
720
    pj_bzero(&ice_cb, sizeof(pjmedia_ice_cb));
 
721
    ice_cb.on_ice_complete = &on_ice_complete;
 
722
    pj_ansi_snprintf(name, sizeof(name), "icetp%02d", call_med->idx);
 
723
    call_med->tp_ready = PJ_EPENDING;
 
724
 
 
725
    comp_cnt = 1;
 
726
    if (PJMEDIA_ADVERTISE_RTCP && !pjsua_var.media_cfg.ice_no_rtcp)
 
727
        ++comp_cnt;
 
728
 
 
729
    status = pjmedia_ice_create3(pjsua_var.med_endpt, name, comp_cnt,
 
730
                                 &ice_cfg, &ice_cb, 0, call_med,
 
731
                                 &call_med->tp);
 
732
    if (status != PJ_SUCCESS) {
 
733
        pjsua_perror(THIS_FILE, "Unable to create ICE media transport",
 
734
                     status);
 
735
        goto on_error;
 
736
    }
 
737
 
 
738
    /* Wait until transport is initialized, or time out */
 
739
    if (!async) {
 
740
        pj_bool_t has_pjsua_lock = PJSUA_LOCK_IS_LOCKED();
 
741
        if (has_pjsua_lock)
 
742
            PJSUA_UNLOCK();
 
743
        while (call_med->tp_ready == PJ_EPENDING) {
 
744
            pjsua_handle_events(100);
 
745
        }
 
746
        if (has_pjsua_lock)
 
747
            PJSUA_LOCK();
 
748
    }
 
749
 
 
750
    if (async && call_med->tp_ready == PJ_EPENDING) {
 
751
        return PJ_EPENDING;
 
752
    } else if (call_med->tp_ready != PJ_SUCCESS) {
 
753
        pjsua_perror(THIS_FILE, "Error initializing ICE media transport",
 
754
                     call_med->tp_ready);
 
755
        status = call_med->tp_ready;
 
756
        goto on_error;
 
757
    }
 
758
 
 
759
    pjmedia_transport_simulate_lost(call_med->tp, PJMEDIA_DIR_ENCODING,
 
760
                                    pjsua_var.media_cfg.tx_drop_pct);
 
761
 
 
762
    pjmedia_transport_simulate_lost(call_med->tp, PJMEDIA_DIR_DECODING,
 
763
                                    pjsua_var.media_cfg.rx_drop_pct);
 
764
 
 
765
    return PJ_SUCCESS;
 
766
 
 
767
on_error:
 
768
    if (call_med->tp != NULL) {
 
769
        pjmedia_transport_close(call_med->tp);
 
770
        call_med->tp = NULL;
 
771
    }
 
772
 
 
773
    return status;
 
774
}
 
775
 
 
776
#if DISABLED_FOR_TICKET_1185
 
777
/* Create ICE media transports (when ice is enabled) */
 
778
static pj_status_t create_ice_media_transports(pjsua_transport_config *cfg)
 
779
{
 
780
    unsigned i;
 
781
    pj_status_t status;
 
782
 
 
783
    for (i=0; i < pjsua_var.ua_cfg.max_calls; ++i) {
 
784
        pjsua_call *call = &pjsua_var.calls[i];
 
785
        unsigned strm_idx;
 
786
 
 
787
        for (strm_idx=0; strm_idx < call->med_cnt; ++strm_idx) {
 
788
            pjsua_call_media *call_med = &call->media[strm_idx];
 
789
 
 
790
            status = create_ice_media_transport(cfg, call_med);
 
791
            if (status != PJ_SUCCESS)
 
792
                goto on_error;
 
793
        }
 
794
    }
 
795
 
 
796
    return PJ_SUCCESS;
 
797
 
 
798
on_error:
 
799
    for (i=0; i < pjsua_var.ua_cfg.max_calls; ++i) {
 
800
        pjsua_call *call = &pjsua_var.calls[i];
 
801
        unsigned strm_idx;
 
802
 
 
803
        for (strm_idx=0; strm_idx < call->med_cnt; ++strm_idx) {
 
804
            pjsua_call_media *call_med = &call->media[strm_idx];
 
805
 
 
806
            if (call_med->tp) {
 
807
                pjmedia_transport_close(call_med->tp);
 
808
                call_med->tp = NULL;
 
809
            }
 
810
        }
 
811
    }
 
812
    return status;
 
813
}
 
814
#endif
 
815
 
 
816
#if DISABLED_FOR_TICKET_1185
 
817
/*
 
818
 * Create media transports for all the calls. This function creates
 
819
 * one UDP media transport for each call.
 
820
 */
 
821
PJ_DEF(pj_status_t) pjsua_media_transports_create(
 
822
                        const pjsua_transport_config *app_cfg)
 
823
{
 
824
    pjsua_transport_config cfg;
 
825
    unsigned i;
 
826
    pj_status_t status;
 
827
 
 
828
 
 
829
    /* Make sure pjsua_init() has been called */
 
830
    PJ_ASSERT_RETURN(pjsua_var.ua_cfg.max_calls>0, PJ_EINVALIDOP);
 
831
 
 
832
    PJSUA_LOCK();
 
833
 
 
834
    /* Delete existing media transports */
 
835
    for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
 
836
        pjsua_call *call = &pjsua_var.calls[i];
 
837
        unsigned strm_idx;
 
838
 
 
839
        for (strm_idx=0; strm_idx < call->med_cnt; ++strm_idx) {
 
840
            pjsua_call_media *call_med = &call->media[strm_idx];
 
841
 
 
842
            if (call_med->tp && call_med->tp_auto_del) {
 
843
                pjmedia_transport_close(call_med->tp);
 
844
                call_med->tp = NULL;
 
845
                call_med->tp_orig = NULL;
 
846
            }
 
847
        }
 
848
    }
 
849
 
 
850
    /* Copy config */
 
851
    pjsua_transport_config_dup(pjsua_var.pool, &cfg, app_cfg);
 
852
 
 
853
    /* Create the transports */
 
854
    if (pjsua_var.media_cfg.enable_ice) {
 
855
        status = create_ice_media_transports(&cfg);
 
856
    } else {
 
857
        status = create_udp_media_transports(&cfg);
 
858
    }
 
859
 
 
860
    /* Set media transport auto_delete to True */
 
861
    for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
 
862
        pjsua_call *call = &pjsua_var.calls[i];
 
863
        unsigned strm_idx;
 
864
 
 
865
        for (strm_idx=0; strm_idx < call->med_cnt; ++strm_idx) {
 
866
            pjsua_call_media *call_med = &call->media[strm_idx];
 
867
 
 
868
            call_med->tp_auto_del = PJ_TRUE;
 
869
        }
 
870
    }
 
871
 
 
872
    PJSUA_UNLOCK();
 
873
 
 
874
    return status;
 
875
}
 
876
 
 
877
/*
 
878
 * Attach application's created media transports.
 
879
 */
 
880
PJ_DEF(pj_status_t) pjsua_media_transports_attach(pjsua_media_transport tp[],
 
881
                                                  unsigned count,
 
882
                                                  pj_bool_t auto_delete)
 
883
{
 
884
    unsigned i;
 
885
 
 
886
    PJ_ASSERT_RETURN(tp && count==pjsua_var.ua_cfg.max_calls, PJ_EINVAL);
 
887
 
 
888
    /* Assign the media transports */
 
889
    for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
 
890
        pjsua_call *call = &pjsua_var.calls[i];
 
891
        unsigned strm_idx;
 
892
 
 
893
        for (strm_idx=0; strm_idx < call->med_cnt; ++strm_idx) {
 
894
            pjsua_call_media *call_med = &call->media[strm_idx];
 
895
 
 
896
            if (call_med->tp && call_med->tp_auto_del) {
 
897
                pjmedia_transport_close(call_med->tp);
 
898
                call_med->tp = NULL;
 
899
                call_med->tp_orig = NULL;
 
900
            }
 
901
        }
 
902
 
 
903
        PJ_TODO(remove_pjsua_media_transports_attach);
 
904
 
 
905
        call->media[0].tp = tp[i].transport;
 
906
        call->media[0].tp_auto_del = auto_delete;
 
907
    }
 
908
 
 
909
    return PJ_SUCCESS;
 
910
}
 
911
#endif
 
912
 
 
913
/* Go through the list of media in the SDP, find acceptable media, and
 
914
 * sort them based on the "quality" of the media, and store the indexes
 
915
 * in the specified array. Media with the best quality will be listed
 
916
 * first in the array. The quality factors considered currently is
 
917
 * encryption.
 
918
 */
 
919
static void sort_media(const pjmedia_sdp_session *sdp,
 
920
                       const pj_str_t *type,
 
921
                       pjmedia_srtp_use use_srtp,
 
922
                       pj_uint8_t midx[],
 
923
                       unsigned *p_count,
 
924
                       unsigned *p_total_count)
 
925
{
 
926
    unsigned i;
 
927
    unsigned count = 0;
 
928
    int score[PJSUA_MAX_CALL_MEDIA];
 
929
 
 
930
    pj_assert(*p_count >= PJSUA_MAX_CALL_MEDIA);
 
931
    pj_assert(*p_total_count >= PJSUA_MAX_CALL_MEDIA);
 
932
 
 
933
    *p_count = 0;
 
934
    *p_total_count = 0;
 
935
    for (i=0; i<PJSUA_MAX_CALL_MEDIA; ++i)
 
936
        score[i] = 1;
 
937
 
 
938
    /* Score each media */
 
939
    for (i=0; i<sdp->media_count && count<PJSUA_MAX_CALL_MEDIA; ++i) {
 
940
        const pjmedia_sdp_media *m = sdp->media[i];
 
941
        const pjmedia_sdp_conn *c;
 
942
 
 
943
        /* Skip different media */
 
944
        if (pj_stricmp(&m->desc.media, type) != 0) {
 
945
            score[count++] = -22000;
 
946
            continue;
 
947
        }
 
948
 
 
949
        c = m->conn? m->conn : sdp->conn;
 
950
 
 
951
        /* Supported transports */
 
952
        if (pj_stricmp2(&m->desc.transport, "RTP/SAVP")==0) {
 
953
            switch (use_srtp) {
 
954
            case PJMEDIA_SRTP_MANDATORY:
 
955
            case PJMEDIA_SRTP_OPTIONAL:
 
956
                ++score[i];
 
957
                break;
 
958
            case PJMEDIA_SRTP_DISABLED:
 
959
                //--score[i];
 
960
                score[i] -= 5;
 
961
                break;
 
962
            }
 
963
        } else if (pj_stricmp2(&m->desc.transport, "RTP/AVP")==0) {
 
964
            switch (use_srtp) {
 
965
            case PJMEDIA_SRTP_MANDATORY:
 
966
                //--score[i];
 
967
                score[i] -= 5;
 
968
                break;
 
969
            case PJMEDIA_SRTP_OPTIONAL:
 
970
                /* No change in score */
 
971
                break;
 
972
            case PJMEDIA_SRTP_DISABLED:
 
973
                ++score[i];
 
974
                break;
 
975
            }
 
976
        } else {
 
977
            score[i] -= 10;
 
978
        }
 
979
 
 
980
        /* Is media disabled? */
 
981
        if (m->desc.port == 0)
 
982
            score[i] -= 10;
 
983
 
 
984
        /* Is media inactive? */
 
985
        if (pjmedia_sdp_media_find_attr2(m, "inactive", NULL) ||
 
986
            pj_strcmp2(&c->addr, "0.0.0.0") == 0)
 
987
        {
 
988
            //score[i] -= 10;
 
989
            score[i] -= 1;
 
990
        }
 
991
 
 
992
        ++count;
 
993
    }
 
994
 
 
995
    /* Created sorted list based on quality */
 
996
    for (i=0; i<count; ++i) {
 
997
        unsigned j;
 
998
        int best = 0;
 
999
 
 
1000
        for (j=1; j<count; ++j) {
 
1001
            if (score[j] > score[best])
 
1002
                best = j;
 
1003
        }
 
1004
        /* Don't put media with negative score, that media is unacceptable
 
1005
         * for us.
 
1006
         */
 
1007
        midx[i] = (pj_uint8_t)best;
 
1008
        if (score[best] >= 0)
 
1009
            (*p_count)++;
 
1010
        if (score[best] > -22000)
 
1011
            (*p_total_count)++;
 
1012
 
 
1013
        score[best] = -22000;
 
1014
 
 
1015
    }
 
1016
}
 
1017
 
 
1018
/* Callback to receive media events */
 
1019
pj_status_t call_media_on_event(pjmedia_event *event,
 
1020
                                void *user_data)
 
1021
{
 
1022
    pjsua_call_media *call_med = (pjsua_call_media*)user_data;
 
1023
    pjsua_call *call = call_med->call;
 
1024
    pj_status_t status = PJ_SUCCESS;
 
1025
 
 
1026
    switch(event->type) {
 
1027
        case PJMEDIA_EVENT_KEYFRAME_MISSING:
 
1028
            if (call->opt.req_keyframe_method & PJSUA_VID_REQ_KEYFRAME_SIP_INFO)
 
1029
            {
 
1030
                pj_timestamp now;
 
1031
 
 
1032
                pj_get_timestamp(&now);
 
1033
                if (pj_elapsed_msec(&call_med->last_req_keyframe, &now) >=
 
1034
                    PJSUA_VID_REQ_KEYFRAME_INTERVAL)
 
1035
                {
 
1036
                    pjsua_msg_data msg_data;
 
1037
                    const pj_str_t SIP_INFO = {"INFO", 4};
 
1038
                    const char *BODY_TYPE = "application/media_control+xml";
 
1039
                    const char *BODY =
 
1040
                        "<?xml version=\"1.0\" encoding=\"utf-8\" ?>"
 
1041
                        "<media_control><vc_primitive><to_encoder>"
 
1042
                        "<picture_fast_update/>"
 
1043
                        "</to_encoder></vc_primitive></media_control>";
 
1044
 
 
1045
                    PJ_LOG(4,(THIS_FILE,
 
1046
                              "Sending video keyframe request via SIP INFO"));
 
1047
 
 
1048
                    pjsua_msg_data_init(&msg_data);
 
1049
                    pj_cstr(&msg_data.content_type, BODY_TYPE);
 
1050
                    pj_cstr(&msg_data.msg_body, BODY);
 
1051
                    status = pjsua_call_send_request(call->index, &SIP_INFO,
 
1052
                                                     &msg_data);
 
1053
                    if (status != PJ_SUCCESS) {
 
1054
                        pj_perror(3, THIS_FILE, status,
 
1055
                                  "Failed requesting keyframe via SIP INFO");
 
1056
                    } else {
 
1057
                        call_med->last_req_keyframe = now;
 
1058
                    }
 
1059
                }
 
1060
            }
 
1061
            break;
 
1062
 
 
1063
        default:
 
1064
            break;
 
1065
    }
 
1066
 
 
1067
    if (pjsua_var.ua_cfg.cb.on_call_media_event && call) {
 
1068
        (*pjsua_var.ua_cfg.cb.on_call_media_event)(call->index,
 
1069
                                                   call_med->idx, event);
 
1070
    }
 
1071
 
 
1072
    return status;
 
1073
}
 
1074
 
 
1075
/* Set media transport state and notify the application via the callback. */
 
1076
void pjsua_set_media_tp_state(pjsua_call_media *call_med,
 
1077
                              pjsua_med_tp_st tp_st)
 
1078
{
 
1079
    if (pjsua_var.ua_cfg.cb.on_call_media_transport_state &&
 
1080
        call_med->tp_st != tp_st)
 
1081
    {
 
1082
        pjsua_med_tp_state_info info;
 
1083
 
 
1084
        pj_bzero(&info, sizeof(info));
 
1085
        info.med_idx = call_med->idx;
 
1086
        info.state = tp_st;
 
1087
        info.status = call_med->tp_ready;
 
1088
        (*pjsua_var.ua_cfg.cb.on_call_media_transport_state)(
 
1089
            call_med->call->index, &info);
 
1090
    }
 
1091
 
 
1092
    call_med->tp_st = tp_st;
 
1093
}
 
1094
 
 
1095
/* Callback to resume pjsua_call_media_init() after media transport
 
1096
 * creation is completed.
 
1097
 */
 
1098
static pj_status_t call_media_init_cb(pjsua_call_media *call_med,
 
1099
                                      pj_status_t status,
 
1100
                                      int security_level,
 
1101
                                      int *sip_err_code)
 
1102
{
 
1103
    pjsua_acc *acc = &pjsua_var.acc[call_med->call->acc_id];
 
1104
    pjmedia_transport_info tpinfo;
 
1105
    int err_code = 0;
 
1106
 
 
1107
    if (status != PJ_SUCCESS)
 
1108
        goto on_return;
 
1109
 
 
1110
    pjmedia_transport_simulate_lost(call_med->tp, PJMEDIA_DIR_ENCODING,
 
1111
                                    pjsua_var.media_cfg.tx_drop_pct);
 
1112
 
 
1113
    pjmedia_transport_simulate_lost(call_med->tp, PJMEDIA_DIR_DECODING,
 
1114
                                    pjsua_var.media_cfg.rx_drop_pct);
 
1115
 
 
1116
    if (call_med->tp_st == PJSUA_MED_TP_CREATING)
 
1117
        pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_IDLE);
 
1118
 
 
1119
    if (!call_med->tp_orig &&
 
1120
        pjsua_var.ua_cfg.cb.on_create_media_transport)
 
1121
    {
 
1122
        call_med->use_custom_med_tp = PJ_TRUE;
 
1123
    } else
 
1124
        call_med->use_custom_med_tp = PJ_FALSE;
 
1125
 
 
1126
#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
 
1127
    /* This function may be called when SRTP transport already exists
 
1128
     * (e.g: in re-invite, update), don't need to destroy/re-create.
 
1129
     */
 
1130
    if (!call_med->tp_orig) {
 
1131
        pjmedia_srtp_setting srtp_opt;
 
1132
        pjmedia_transport *srtp = NULL;
 
1133
 
 
1134
        /* Check if SRTP requires secure signaling */
 
1135
        if (acc->cfg.use_srtp != PJMEDIA_SRTP_DISABLED) {
 
1136
            if (security_level < acc->cfg.srtp_secure_signaling) {
 
1137
                err_code = PJSIP_SC_NOT_ACCEPTABLE;
 
1138
                status = PJSIP_ESESSIONINSECURE;
 
1139
                goto on_return;
 
1140
            }
 
1141
        }
 
1142
 
 
1143
        /* Always create SRTP adapter */
 
1144
        pjmedia_srtp_setting_default(&srtp_opt);
 
1145
        srtp_opt.close_member_tp = PJ_TRUE;
 
1146
 
 
1147
        /* If media session has been ever established, let's use remote's
 
1148
         * preference in SRTP usage policy, especially when it is stricter.
 
1149
         */
 
1150
        if (call_med->rem_srtp_use > acc->cfg.use_srtp)
 
1151
            srtp_opt.use = call_med->rem_srtp_use;
 
1152
        else
 
1153
            srtp_opt.use = acc->cfg.use_srtp;
 
1154
 
 
1155
        status = pjmedia_transport_srtp_create(pjsua_var.med_endpt,
 
1156
                                               call_med->tp,
 
1157
                                               &srtp_opt, &srtp);
 
1158
        if (status != PJ_SUCCESS) {
 
1159
            err_code = PJSIP_SC_INTERNAL_SERVER_ERROR;
 
1160
            goto on_return;
 
1161
        }
 
1162
 
 
1163
        /* Set SRTP as current media transport */
 
1164
        call_med->tp_orig = call_med->tp;
 
1165
        call_med->tp = srtp;
 
1166
    }
 
1167
#else
 
1168
    call_med->tp_orig = call_med->tp;
 
1169
    PJ_UNUSED_ARG(security_level);
 
1170
#endif
 
1171
 
 
1172
 
 
1173
    pjmedia_transport_info_init(&tpinfo);
 
1174
    pjmedia_transport_get_info(call_med->tp, &tpinfo);
 
1175
 
 
1176
    pj_sockaddr_cp(&call_med->rtp_addr, &tpinfo.sock_info.rtp_addr_name);
 
1177
 
 
1178
 
 
1179
on_return:
 
1180
    if (status != PJ_SUCCESS && call_med->tp) {
 
1181
        pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_NULL);
 
1182
        pjmedia_transport_close(call_med->tp);
 
1183
        call_med->tp = NULL;
 
1184
    }
 
1185
 
 
1186
    if (sip_err_code)
 
1187
        *sip_err_code = err_code;
 
1188
 
 
1189
    if (call_med->med_init_cb) {
 
1190
        pjsua_med_tp_state_info info;
 
1191
 
 
1192
        pj_bzero(&info, sizeof(info));
 
1193
        info.status = status;
 
1194
        info.state = call_med->tp_st;
 
1195
        info.med_idx = call_med->idx;
 
1196
        info.sip_err_code = err_code;
 
1197
        (*call_med->med_init_cb)(call_med->call->index, &info);
 
1198
    }
 
1199
 
 
1200
    return status;
 
1201
}
 
1202
 
 
1203
/* Initialize the media line */
 
1204
pj_status_t pjsua_call_media_init(pjsua_call_media *call_med,
 
1205
                                  pjmedia_type type,
 
1206
                                  const pjsua_transport_config *tcfg,
 
1207
                                  int security_level,
 
1208
                                  int *sip_err_code,
 
1209
                                  pj_bool_t async,
 
1210
                                  pjsua_med_tp_state_cb cb)
 
1211
{
 
1212
    pj_status_t status = PJ_SUCCESS;
 
1213
 
 
1214
    /*
 
1215
     * Note: this function may be called when the media already exists
 
1216
     * (e.g. in reinvites, updates, etc.)
 
1217
     */
 
1218
    call_med->type = type;
 
1219
 
 
1220
    /* Create the media transport for initial call. Here are the possible
 
1221
     * media transport state and the action needed:
 
1222
     * - PJSUA_MED_TP_NULL or call_med->tp==NULL, create one.
 
1223
     * - PJSUA_MED_TP_RUNNING, do nothing.
 
1224
     * - PJSUA_MED_TP_DISABLED, re-init (media_create(), etc). Currently,
 
1225
     *   this won't happen as media_channel_update() will always clean up
 
1226
     *   the unused transport of a disabled media.
 
1227
     */
 
1228
    if (call_med->tp == NULL) {
 
1229
#if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0)
 
1230
        /* While in initial call, set default video devices */
 
1231
        if (type == PJMEDIA_TYPE_VIDEO) {
 
1232
            status = pjsua_vid_channel_init(call_med);
 
1233
            if (status != PJ_SUCCESS)
 
1234
                return status;
 
1235
        }
 
1236
#endif
 
1237
 
 
1238
        pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_CREATING);
 
1239
 
 
1240
        if (pjsua_var.media_cfg.enable_ice) {
 
1241
            status = create_ice_media_transport(tcfg, call_med, async);
 
1242
            if (async && status == PJ_EPENDING) {
 
1243
                /* We will resume call media initialization in the
 
1244
                 * on_ice_complete() callback.
 
1245
                 */
 
1246
                call_med->med_create_cb = &call_media_init_cb;
 
1247
                call_med->med_init_cb = cb;
 
1248
 
 
1249
                return PJ_EPENDING;
 
1250
            }
 
1251
        } else {
 
1252
            status = create_udp_media_transport(tcfg, call_med);
 
1253
        }
 
1254
 
 
1255
        if (status != PJ_SUCCESS) {
 
1256
            PJ_PERROR(1,(THIS_FILE, status, "Error creating media transport"));
 
1257
            return status;
 
1258
        }
 
1259
 
 
1260
        /* Media transport creation completed immediately, so
 
1261
         * we don't need to call the callback.
 
1262
         */
 
1263
        call_med->med_init_cb = NULL;
 
1264
 
 
1265
    } else if (call_med->tp_st == PJSUA_MED_TP_DISABLED) {
 
1266
        /* Media is being reenabled. */
 
1267
        //pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_IDLE);
 
1268
 
 
1269
        pj_assert(!"Currently no media transport reuse");
 
1270
    }
 
1271
 
 
1272
    return call_media_init_cb(call_med, status, security_level,
 
1273
                              sip_err_code);
 
1274
}
 
1275
 
 
1276
/* Callback to resume pjsua_media_channel_init() after media transport
 
1277
 * initialization is completed.
 
1278
 */
 
1279
static pj_status_t media_channel_init_cb(pjsua_call_id call_id,
 
1280
                                         const pjsua_med_tp_state_info *info)
 
1281
{
 
1282
    pjsua_call *call = &pjsua_var.calls[call_id];
 
1283
    pj_status_t status = (info? info->status : PJ_SUCCESS);
 
1284
    unsigned mi;
 
1285
 
 
1286
    if (info) {
 
1287
        pj_mutex_lock(call->med_ch_mutex);
 
1288
 
 
1289
        /* Set the callback to NULL to indicate that the async operation
 
1290
         * has completed.
 
1291
         */
 
1292
        call->media_prov[info->med_idx].med_init_cb = NULL;
 
1293
 
 
1294
        /* In case of failure, save the information to be returned
 
1295
         * by the last media transport to finish.
 
1296
         */
 
1297
        if (info->status != PJ_SUCCESS)
 
1298
            pj_memcpy(&call->med_ch_info, info, sizeof(info));
 
1299
 
 
1300
        /* Check whether all the call's medias have finished calling their
 
1301
         * callbacks.
 
1302
         */
 
1303
        for (mi=0; mi < call->med_prov_cnt; ++mi) {
 
1304
            pjsua_call_media *call_med = &call->media_prov[mi];
 
1305
 
 
1306
            if (call_med->med_init_cb) {
 
1307
                pj_mutex_unlock(call->med_ch_mutex);
 
1308
                return PJ_SUCCESS;
 
1309
            }
 
1310
 
 
1311
            if (call_med->tp_ready != PJ_SUCCESS)
 
1312
                status = call_med->tp_ready;
 
1313
        }
 
1314
 
 
1315
        /* OK, we are called by the last media transport finished. */
 
1316
        pj_mutex_unlock(call->med_ch_mutex);
 
1317
    }
 
1318
 
 
1319
    if (call->med_ch_mutex) {
 
1320
        pj_mutex_destroy(call->med_ch_mutex);
 
1321
        call->med_ch_mutex = NULL;
 
1322
    }
 
1323
 
 
1324
    if (status != PJ_SUCCESS) {
 
1325
        if (call->med_ch_info.status == PJ_SUCCESS) {
 
1326
            call->med_ch_info.status = status;
 
1327
            call->med_ch_info.sip_err_code = PJSIP_SC_TEMPORARILY_UNAVAILABLE;
 
1328
        }
 
1329
        pjsua_media_prov_clean_up(call_id);
 
1330
        goto on_return;
 
1331
    }
 
1332
 
 
1333
    /* Tell the media transport of a new offer/answer session */
 
1334
    for (mi=0; mi < call->med_prov_cnt; ++mi) {
 
1335
        pjsua_call_media *call_med = &call->media_prov[mi];
 
1336
 
 
1337
        /* Note: tp may be NULL if this media line is disabled */
 
1338
        if (call_med->tp && call_med->tp_st == PJSUA_MED_TP_IDLE) {
 
1339
            pj_pool_t *tmp_pool = call->async_call.pool_prov;
 
1340
 
 
1341
            if (!tmp_pool) {
 
1342
                tmp_pool = (call->inv? call->inv->pool_prov:
 
1343
                            call->async_call.dlg->pool);
 
1344
            }
 
1345
 
 
1346
            if (call_med->use_custom_med_tp) {
 
1347
                unsigned custom_med_tp_flags = 0;
 
1348
 
 
1349
                /* Use custom media transport returned by the application */
 
1350
                call_med->tp =
 
1351
                    (*pjsua_var.ua_cfg.cb.on_create_media_transport)
 
1352
                        (call_id, mi, call_med->tp,
 
1353
                         custom_med_tp_flags);
 
1354
                if (!call_med->tp) {
 
1355
                    status =
 
1356
                        PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_TEMPORARILY_UNAVAILABLE);
 
1357
                }
 
1358
            }
 
1359
 
 
1360
            if (call_med->tp) {
 
1361
                status = pjmedia_transport_media_create(
 
1362
                             call_med->tp, tmp_pool,
 
1363
                             0, call->async_call.rem_sdp, mi);
 
1364
            }
 
1365
            if (status != PJ_SUCCESS) {
 
1366
                call->med_ch_info.status = status;
 
1367
                call->med_ch_info.med_idx = mi;
 
1368
                call->med_ch_info.state = call_med->tp_st;
 
1369
                call->med_ch_info.sip_err_code = PJSIP_SC_TEMPORARILY_UNAVAILABLE;
 
1370
                pjsua_media_prov_clean_up(call_id);
 
1371
                goto on_return;
 
1372
            }
 
1373
 
 
1374
            pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_INIT);
 
1375
        }
 
1376
    }
 
1377
 
 
1378
    call->med_ch_info.status = PJ_SUCCESS;
 
1379
 
 
1380
on_return:
 
1381
    if (call->med_ch_cb)
 
1382
        (*call->med_ch_cb)(call->index, &call->med_ch_info);
 
1383
 
 
1384
    return status;
 
1385
}
 
1386
 
 
1387
 
 
1388
/* Clean up media transports in provisional media that is not used
 
1389
 * by call media.
 
1390
 */
 
1391
void pjsua_media_prov_clean_up(pjsua_call_id call_id)
 
1392
{
 
1393
    pjsua_call *call = &pjsua_var.calls[call_id];
 
1394
    unsigned i;
 
1395
 
 
1396
    for (i = 0; i < call->med_prov_cnt; ++i) {
 
1397
        pjsua_call_media *call_med = &call->media_prov[i];
 
1398
        unsigned j;
 
1399
        pj_bool_t used = PJ_FALSE;
 
1400
 
 
1401
        if (call_med->tp == NULL)
 
1402
            continue;
 
1403
 
 
1404
        for (j = 0; j < call->med_cnt; ++j) {
 
1405
            if (call->media[j].tp == call_med->tp) {
 
1406
                used = PJ_TRUE;
 
1407
                break;
 
1408
            }
 
1409
        }
 
1410
 
 
1411
        if (!used) {
 
1412
            if (call_med->tp_st > PJSUA_MED_TP_IDLE) {
 
1413
                pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_IDLE);
 
1414
                pjmedia_transport_media_stop(call_med->tp);
 
1415
            }
 
1416
            pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_NULL);
 
1417
            pjmedia_transport_close(call_med->tp);
 
1418
            call_med->tp = call_med->tp_orig = NULL;
 
1419
        }
 
1420
    }
 
1421
}
 
1422
 
 
1423
 
 
1424
pj_status_t pjsua_media_channel_init(pjsua_call_id call_id,
 
1425
                                     pjsip_role_e role,
 
1426
                                     int security_level,
 
1427
                                     pj_pool_t *tmp_pool,
 
1428
                                     const pjmedia_sdp_session *rem_sdp,
 
1429
                                     int *sip_err_code,
 
1430
                                     pj_bool_t async,
 
1431
                                     pjsua_med_tp_state_cb cb)
 
1432
{
 
1433
    const pj_str_t STR_AUDIO = { "audio", 5 };
 
1434
    const pj_str_t STR_VIDEO = { "video", 5 };
 
1435
    pjsua_call *call = &pjsua_var.calls[call_id];
 
1436
    pjsua_acc *acc = &pjsua_var.acc[call->acc_id];
 
1437
    pj_uint8_t maudidx[PJSUA_MAX_CALL_MEDIA];
 
1438
    unsigned maudcnt = PJ_ARRAY_SIZE(maudidx);
 
1439
    unsigned mtotaudcnt = PJ_ARRAY_SIZE(maudidx);
 
1440
    pj_uint8_t mvididx[PJSUA_MAX_CALL_MEDIA];
 
1441
    unsigned mvidcnt = PJ_ARRAY_SIZE(mvididx);
 
1442
    unsigned mtotvidcnt = PJ_ARRAY_SIZE(mvididx);
 
1443
    unsigned mi;
 
1444
    pj_bool_t pending_med_tp = PJ_FALSE;
 
1445
    pj_bool_t reinit = PJ_FALSE;
 
1446
    pj_status_t status;
 
1447
 
 
1448
    PJ_UNUSED_ARG(role);
 
1449
 
 
1450
    /*
 
1451
     * Note: this function may be called when the media already exists
 
1452
     * (e.g. in reinvites, updates, etc).
 
1453
     */
 
1454
 
 
1455
    if (pjsua_get_state() != PJSUA_STATE_RUNNING)
 
1456
        return PJ_EBUSY;
 
1457
 
 
1458
    if (async) {
 
1459
        pj_pool_t *tmppool = (call->inv? call->inv->pool_prov:
 
1460
                              call->async_call.dlg->pool);
 
1461
 
 
1462
        status = pj_mutex_create_simple(tmppool, NULL, &call->med_ch_mutex);
 
1463
        if (status != PJ_SUCCESS)
 
1464
            return status;
 
1465
    }
 
1466
 
 
1467
    if (call->inv && call->inv->state == PJSIP_INV_STATE_CONFIRMED)
 
1468
        reinit = PJ_TRUE;
 
1469
 
 
1470
    PJ_LOG(4,(THIS_FILE, "Call %d: %sinitializing media..",
 
1471
                         call_id, (reinit?"re-":"") ));
 
1472
 
 
1473
    pj_log_push_indent();
 
1474
 
 
1475
    /* Init provisional media state */
 
1476
    if (call->med_cnt == 0) {
 
1477
        /* New media session, just copy whole from call media state. */
 
1478
        pj_memcpy(call->media_prov, call->media, sizeof(call->media));
 
1479
    } else {
 
1480
        /* Clean up any unused transports. Note that when local SDP reoffer
 
1481
         * is rejected by remote, there may be any initialized transports that
 
1482
         * are not used by call media and currently there is no notification
 
1483
         * from PJSIP level regarding the reoffer rejection.
 
1484
         */
 
1485
        pjsua_media_prov_clean_up(call_id);
 
1486
 
 
1487
        /* Updating media session, copy from call media state. */
 
1488
        pj_memcpy(call->media_prov, call->media,
 
1489
                  sizeof(call->media[0]) * call->med_cnt);
 
1490
    }
 
1491
    call->med_prov_cnt = call->med_cnt;
 
1492
 
 
1493
#if DISABLED_FOR_TICKET_1185
 
1494
    /* Return error if media transport has not been created yet
 
1495
     * (e.g. application is starting)
 
1496
     */
 
1497
    for (i=0; i<call->med_cnt; ++i) {
 
1498
        if (call->media[i].tp == NULL) {
 
1499
            status = PJ_EBUSY;
 
1500
            goto on_error;
 
1501
        }
 
1502
    }
 
1503
#endif
 
1504
 
 
1505
    /* Get media count for each media type */
 
1506
    if (rem_sdp) {
 
1507
        sort_media(rem_sdp, &STR_AUDIO, acc->cfg.use_srtp,
 
1508
                   maudidx, &maudcnt, &mtotaudcnt);
 
1509
        if (maudcnt==0) {
 
1510
            /* Expecting audio in the offer */
 
1511
            if (sip_err_code) *sip_err_code = PJSIP_SC_NOT_ACCEPTABLE_HERE;
 
1512
            status = PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_NOT_ACCEPTABLE_HERE);
 
1513
            goto on_error;
 
1514
        }
 
1515
 
 
1516
#if PJMEDIA_HAS_VIDEO
 
1517
        sort_media(rem_sdp, &STR_VIDEO, acc->cfg.use_srtp,
 
1518
                   mvididx, &mvidcnt, &mtotvidcnt);
 
1519
#else
 
1520
        mvidcnt = mtotvidcnt = 0;
 
1521
        PJ_UNUSED_ARG(STR_VIDEO);
 
1522
#endif
 
1523
 
 
1524
        /* Update media count only when remote add any media, this media count
 
1525
         * must never decrease. Also note that we shouldn't apply the media
 
1526
         * count setting (of the call setting) before the SDP negotiation.
 
1527
         */
 
1528
        if (call->med_prov_cnt < rem_sdp->media_count)
 
1529
            call->med_prov_cnt = PJ_MIN(rem_sdp->media_count,
 
1530
                                        PJSUA_MAX_CALL_MEDIA);
 
1531
 
 
1532
        call->rem_offerer = PJ_TRUE;
 
1533
        call->rem_aud_cnt = maudcnt;
 
1534
        call->rem_vid_cnt = mvidcnt;
 
1535
 
 
1536
    } else {
 
1537
 
 
1538
        /* If call already established, calculate media count from current
 
1539
         * local active SDP and call setting. Otherwise, calculate media
 
1540
         * count from the call setting only.
 
1541
         */
 
1542
        if (reinit) {
 
1543
            const pjmedia_sdp_session *sdp;
 
1544
 
 
1545
            status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &sdp);
 
1546
            pj_assert(status == PJ_SUCCESS);
 
1547
 
 
1548
            sort_media(sdp, &STR_AUDIO, acc->cfg.use_srtp,
 
1549
                       maudidx, &maudcnt, &mtotaudcnt);
 
1550
            pj_assert(maudcnt > 0);
 
1551
 
 
1552
            sort_media(sdp, &STR_VIDEO, acc->cfg.use_srtp,
 
1553
                       mvididx, &mvidcnt, &mtotvidcnt);
 
1554
 
 
1555
            /* Call setting may add or remove media. Adding media is done by
 
1556
             * enabling any disabled/port-zeroed media first, then adding new
 
1557
             * media whenever needed. Removing media is done by disabling
 
1558
             * media with the lowest 'quality'.
 
1559
             */
 
1560
 
 
1561
            /* Check if we need to add new audio */
 
1562
            if (maudcnt < call->opt.aud_cnt &&
 
1563
                mtotaudcnt < call->opt.aud_cnt)
 
1564
            {
 
1565
                for (mi = 0; mi < call->opt.aud_cnt - mtotaudcnt; ++mi)
 
1566
                    maudidx[maudcnt++] = (pj_uint8_t)call->med_prov_cnt++;
 
1567
 
 
1568
                mtotaudcnt = call->opt.aud_cnt;
 
1569
            }
 
1570
            maudcnt = call->opt.aud_cnt;
 
1571
 
 
1572
            /* Check if we need to add new video */
 
1573
            if (mvidcnt < call->opt.vid_cnt &&
 
1574
                mtotvidcnt < call->opt.vid_cnt)
 
1575
            {
 
1576
                for (mi = 0; mi < call->opt.vid_cnt - mtotvidcnt; ++mi)
 
1577
                    mvididx[mvidcnt++] = (pj_uint8_t)call->med_prov_cnt++;
 
1578
 
 
1579
                mtotvidcnt = call->opt.vid_cnt;
 
1580
            }
 
1581
            mvidcnt = call->opt.vid_cnt;
 
1582
 
 
1583
        } else {
 
1584
 
 
1585
            maudcnt = mtotaudcnt = call->opt.aud_cnt;
 
1586
            for (mi=0; mi<maudcnt; ++mi) {
 
1587
                maudidx[mi] = (pj_uint8_t)mi;
 
1588
            }
 
1589
            mvidcnt = mtotvidcnt = call->opt.vid_cnt;
 
1590
            for (mi=0; mi<mvidcnt; ++mi) {
 
1591
                mvididx[mi] = (pj_uint8_t)(maudcnt + mi);
 
1592
            }
 
1593
            call->med_prov_cnt = maudcnt + mvidcnt;
 
1594
 
 
1595
            /* Need to publish supported media? */
 
1596
            if (call->opt.flag & PJSUA_CALL_INCLUDE_DISABLED_MEDIA) {
 
1597
                if (mtotaudcnt == 0) {
 
1598
                    mtotaudcnt = 1;
 
1599
                    maudidx[0] = (pj_uint8_t)call->med_prov_cnt++;
 
1600
                }
 
1601
#if PJMEDIA_HAS_VIDEO
 
1602
                if (mtotvidcnt == 0) {
 
1603
                    mtotvidcnt = 1;
 
1604
                    mvididx[0] = (pj_uint8_t)call->med_prov_cnt++;
 
1605
                }
 
1606
#endif
 
1607
            }
 
1608
        }
 
1609
 
 
1610
        call->rem_offerer = PJ_FALSE;
 
1611
    }
 
1612
 
 
1613
    if (call->med_prov_cnt == 0) {
 
1614
        /* Expecting at least one media */
 
1615
        if (sip_err_code) *sip_err_code = PJSIP_SC_NOT_ACCEPTABLE_HERE;
 
1616
        status = PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_NOT_ACCEPTABLE_HERE);
 
1617
        goto on_error;
 
1618
    }
 
1619
 
 
1620
    if (async) {
 
1621
        call->med_ch_cb = cb;
 
1622
    }
 
1623
 
 
1624
    if (rem_sdp) {
 
1625
        call->async_call.rem_sdp =
 
1626
            pjmedia_sdp_session_clone(call->inv->pool_prov, rem_sdp);
 
1627
    } else {
 
1628
        call->async_call.rem_sdp = NULL;
 
1629
    }
 
1630
 
 
1631
    call->async_call.pool_prov = tmp_pool;
 
1632
 
 
1633
    /* Initialize each media line */
 
1634
    for (mi=0; mi < call->med_prov_cnt; ++mi) {
 
1635
        pjsua_call_media *call_med = &call->media_prov[mi];
 
1636
        pj_bool_t enabled = PJ_FALSE;
 
1637
        pjmedia_type media_type = PJMEDIA_TYPE_UNKNOWN;
 
1638
 
 
1639
        if (pj_memchr(maudidx, mi, mtotaudcnt * sizeof(maudidx[0]))) {
 
1640
            media_type = PJMEDIA_TYPE_AUDIO;
 
1641
            if (call->opt.aud_cnt &&
 
1642
                pj_memchr(maudidx, mi, maudcnt * sizeof(maudidx[0])))
 
1643
            {
 
1644
                enabled = PJ_TRUE;
 
1645
            }
 
1646
        } else if (pj_memchr(mvididx, mi, mtotvidcnt * sizeof(mvididx[0]))) {
 
1647
            media_type = PJMEDIA_TYPE_VIDEO;
 
1648
            if (call->opt.vid_cnt &&
 
1649
                pj_memchr(mvididx, mi, mvidcnt * sizeof(mvididx[0])))
 
1650
            {
 
1651
                enabled = PJ_TRUE;
 
1652
            }
 
1653
        }
 
1654
 
 
1655
        if (enabled) {
 
1656
            status = pjsua_call_media_init(call_med, media_type,
 
1657
                                           &acc->cfg.rtp_cfg,
 
1658
                                           security_level, sip_err_code,
 
1659
                                           async,
 
1660
                                           (async? &media_channel_init_cb:
 
1661
                                            NULL));
 
1662
            if (status == PJ_EPENDING) {
 
1663
                pending_med_tp = PJ_TRUE;
 
1664
            } else if (status != PJ_SUCCESS) {
 
1665
                if (pending_med_tp) {
 
1666
                    /* Save failure information. */
 
1667
                    call_med->tp_ready = status;
 
1668
                    pj_bzero(&call->med_ch_info, sizeof(call->med_ch_info));
 
1669
                    call->med_ch_info.status = status;
 
1670
                    call->med_ch_info.state = call_med->tp_st;
 
1671
                    call->med_ch_info.med_idx = call_med->idx;
 
1672
                    if (sip_err_code)
 
1673
                        call->med_ch_info.sip_err_code = *sip_err_code;
 
1674
 
 
1675
                    /* We will return failure in the callback later. */
 
1676
                    return PJ_EPENDING;
 
1677
                }
 
1678
 
 
1679
                pjsua_media_prov_clean_up(call_id);
 
1680
                goto on_error;
 
1681
            }
 
1682
        } else {
 
1683
            /* By convention, the media is disabled if transport is NULL
 
1684
             * or transport state is PJSUA_MED_TP_DISABLED.
 
1685
             */
 
1686
            if (call_med->tp) {
 
1687
                // Don't close transport here, as SDP negotiation has not been
 
1688
                // done and stream may be still active. Once SDP negotiation
 
1689
                // is done (channel_update() invoked), this transport will be
 
1690
                // closed there.
 
1691
                //pjmedia_transport_close(call_med->tp);
 
1692
                //call_med->tp = NULL;
 
1693
                pj_assert(call_med->tp_st == PJSUA_MED_TP_INIT ||
 
1694
                          call_med->tp_st == PJSUA_MED_TP_RUNNING);
 
1695
                pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_DISABLED);
 
1696
            }
 
1697
 
 
1698
            /* Put media type just for info */
 
1699
            call_med->type = media_type;
 
1700
        }
 
1701
    }
 
1702
 
 
1703
    call->audio_idx = maudidx[0];
 
1704
 
 
1705
    PJ_LOG(4,(THIS_FILE, "Media index %d selected for audio call %d",
 
1706
              call->audio_idx, call->index));
 
1707
 
 
1708
    if (pending_med_tp) {
 
1709
        /* We shouldn't use temporary pool anymore. */
 
1710
        call->async_call.pool_prov = NULL;
 
1711
        /* We have a pending media transport initialization. */
 
1712
        pj_log_pop_indent();
 
1713
        return PJ_EPENDING;
 
1714
    }
 
1715
 
 
1716
    /* Media transport initialization completed immediately, so
 
1717
     * we don't need to call the callback.
 
1718
     */
 
1719
    call->med_ch_cb = NULL;
 
1720
 
 
1721
    status = media_channel_init_cb(call_id, NULL);
 
1722
    if (status != PJ_SUCCESS && sip_err_code)
 
1723
        *sip_err_code = call->med_ch_info.sip_err_code;
 
1724
 
 
1725
    pj_log_pop_indent();
 
1726
    return status;
 
1727
 
 
1728
on_error:
 
1729
    if (call->med_ch_mutex) {
 
1730
        pj_mutex_destroy(call->med_ch_mutex);
 
1731
        call->med_ch_mutex = NULL;
 
1732
    }
 
1733
 
 
1734
    pj_log_pop_indent();
 
1735
    return status;
 
1736
}
 
1737
 
 
1738
 
 
1739
/* Create SDP based on the current media channel. Note that, this function
 
1740
 * will not modify the media channel, so when receiving new offer or
 
1741
 * updating media count (via call setting), media channel must be reinit'd
 
1742
 * (using pjsua_media_channel_init()) first before calling this function.
 
1743
 */
 
1744
pj_status_t pjsua_media_channel_create_sdp(pjsua_call_id call_id,
 
1745
                                           pj_pool_t *pool,
 
1746
                                           const pjmedia_sdp_session *rem_sdp,
 
1747
                                           pjmedia_sdp_session **p_sdp,
 
1748
                                           int *sip_err_code)
 
1749
{
 
1750
    enum { MAX_MEDIA = PJSUA_MAX_CALL_MEDIA };
 
1751
    pjmedia_sdp_session *sdp;
 
1752
    pj_sockaddr origin;
 
1753
    pjsua_call *call = &pjsua_var.calls[call_id];
 
1754
    pjmedia_sdp_neg_state sdp_neg_state = PJMEDIA_SDP_NEG_STATE_NULL;
 
1755
    unsigned mi;
 
1756
    unsigned tot_bandw_tias = 0;
 
1757
    pj_status_t status;
 
1758
 
 
1759
    if (pjsua_get_state() != PJSUA_STATE_RUNNING)
 
1760
        return PJ_EBUSY;
 
1761
 
 
1762
#if 0
 
1763
    // This function should not really change the media channel.
 
1764
    if (rem_sdp) {
 
1765
        /* If this is a re-offer, let's re-initialize media as remote may
 
1766
         * add or remove media
 
1767
         */
 
1768
        if (call->inv && call->inv->state == PJSIP_INV_STATE_CONFIRMED) {
 
1769
            status = pjsua_media_channel_init(call_id, PJSIP_ROLE_UAS,
 
1770
                                              call->secure_level, pool,
 
1771
                                              rem_sdp, sip_err_code,
 
1772
                                              PJ_FALSE, NULL);
 
1773
            if (status != PJ_SUCCESS)
 
1774
                return status;
 
1775
        }
 
1776
    } else {
 
1777
        /* Audio is first in our offer, by convention */
 
1778
        // The audio_idx should not be changed here, as this function may be
 
1779
        // called in generating re-offer and the current active audio index
 
1780
        // can be anywhere.
 
1781
        //call->audio_idx = 0;
 
1782
    }
 
1783
#endif
 
1784
 
 
1785
#if 0
 
1786
    // Since r3512, old-style hold should have got transport, created by
 
1787
    // pjsua_media_channel_init() in initial offer/answer or remote reoffer.
 
1788
    /* Create media if it's not created. This could happen when call is
 
1789
     * currently on-hold (with the old style hold)
 
1790
     */
 
1791
    if (call->media[call->audio_idx].tp == NULL) {
 
1792
        pjsip_role_e role;
 
1793
        role = (rem_sdp ? PJSIP_ROLE_UAS : PJSIP_ROLE_UAC);
 
1794
        status = pjsua_media_channel_init(call_id, role, call->secure_level,
 
1795
                                          pool, rem_sdp, sip_err_code);
 
1796
        if (status != PJ_SUCCESS)
 
1797
            return status;
 
1798
    }
 
1799
#endif
 
1800
 
 
1801
    /* Get SDP negotiator state */
 
1802
    if (call->inv && call->inv->neg)
 
1803
        sdp_neg_state = pjmedia_sdp_neg_get_state(call->inv->neg);
 
1804
 
 
1805
    /* Get one address to use in the origin field */
 
1806
    pj_bzero(&origin, sizeof(origin));
 
1807
    for (mi=0; mi<call->med_prov_cnt; ++mi) {
 
1808
        pjmedia_transport_info tpinfo;
 
1809
 
 
1810
        if (call->media_prov[mi].tp == NULL)
 
1811
            continue;
 
1812
 
 
1813
        pjmedia_transport_info_init(&tpinfo);
 
1814
        pjmedia_transport_get_info(call->media_prov[mi].tp, &tpinfo);
 
1815
        pj_sockaddr_cp(&origin, &tpinfo.sock_info.rtp_addr_name);
 
1816
        break;
 
1817
    }
 
1818
 
 
1819
    /* Create the base (blank) SDP */
 
1820
    status = pjmedia_endpt_create_base_sdp(pjsua_var.med_endpt, pool, NULL,
 
1821
                                           &origin, &sdp);
 
1822
    if (status != PJ_SUCCESS)
 
1823
        return status;
 
1824
 
 
1825
    /* Process each media line */
 
1826
    for (mi=0; mi<call->med_prov_cnt; ++mi) {
 
1827
        pjsua_call_media *call_med = &call->media_prov[mi];
 
1828
        pjmedia_sdp_media *m = NULL;
 
1829
        pjmedia_transport_info tpinfo;
 
1830
        unsigned i;
 
1831
 
 
1832
        if (rem_sdp && mi >= rem_sdp->media_count) {
 
1833
            /* Remote might have removed some media lines. */
 
1834
            break;
 
1835
        }
 
1836
 
 
1837
        if (call_med->tp == NULL || call_med->tp_st == PJSUA_MED_TP_DISABLED)
 
1838
        {
 
1839
            /*
 
1840
             * This media is disabled. Just create a valid SDP with zero
 
1841
             * port.
 
1842
             */
 
1843
            if (rem_sdp) {
 
1844
                /* Just clone the remote media and deactivate it */
 
1845
                m = pjmedia_sdp_media_clone_deactivate(pool,
 
1846
                                                       rem_sdp->media[mi]);
 
1847
            } else {
 
1848
                m = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_media);
 
1849
                m->desc.transport = pj_str("RTP/AVP");
 
1850
                m->desc.fmt_count = 1;
 
1851
                m->conn = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_conn);
 
1852
                m->conn->net_type = pj_str("IN");
 
1853
                m->conn->addr_type = pj_str("IP4");
 
1854
                m->conn->addr = pj_str("127.0.0.1");
 
1855
 
 
1856
                switch (call_med->type) {
 
1857
                case PJMEDIA_TYPE_AUDIO:
 
1858
                    m->desc.media = pj_str("audio");
 
1859
                    m->desc.fmt[0] = pj_str("0");
 
1860
                    break;
 
1861
                case PJMEDIA_TYPE_VIDEO:
 
1862
                    m->desc.media = pj_str("video");
 
1863
                    m->desc.fmt[0] = pj_str("31");
 
1864
                    break;
 
1865
                default:
 
1866
                    /* This must be us generating re-offer, and some unknown
 
1867
                     * media may exist, so just clone from active local SDP
 
1868
                     * (and it should have been deactivated already).
 
1869
                     */
 
1870
                    pj_assert(call->inv && call->inv->neg &&
 
1871
                              sdp_neg_state == PJMEDIA_SDP_NEG_STATE_DONE);
 
1872
                    {
 
1873
                        const pjmedia_sdp_session *s_;
 
1874
                        pjmedia_sdp_neg_get_active_local(call->inv->neg, &s_);
 
1875
 
 
1876
                        pj_assert(mi < s_->media_count);
 
1877
                        m = pjmedia_sdp_media_clone(pool, s_->media[mi]);
 
1878
                        m->desc.port = 0;
 
1879
                    }
 
1880
                    break;
 
1881
                }
 
1882
            }
 
1883
 
 
1884
            sdp->media[sdp->media_count++] = m;
 
1885
            continue;
 
1886
        }
 
1887
 
 
1888
        /* Get transport address info */
 
1889
        pjmedia_transport_info_init(&tpinfo);
 
1890
        pjmedia_transport_get_info(call_med->tp, &tpinfo);
 
1891
 
 
1892
        /* Ask pjmedia endpoint to create SDP media line */
 
1893
        switch (call_med->type) {
 
1894
        case PJMEDIA_TYPE_AUDIO:
 
1895
            status = pjmedia_endpt_create_audio_sdp(pjsua_var.med_endpt, pool,
 
1896
                                                    &tpinfo.sock_info, 0, &m);
 
1897
            break;
 
1898
#if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0)
 
1899
        case PJMEDIA_TYPE_VIDEO:
 
1900
            status = pjmedia_endpt_create_video_sdp(pjsua_var.med_endpt, pool,
 
1901
                                                    &tpinfo.sock_info, 0, &m);
 
1902
            break;
 
1903
#endif
 
1904
        default:
 
1905
            pj_assert(!"Invalid call_med media type");
 
1906
            return PJ_EBUG;
 
1907
        }
 
1908
 
 
1909
        if (status != PJ_SUCCESS)
 
1910
            return status;
 
1911
 
 
1912
        sdp->media[sdp->media_count++] = m;
 
1913
 
 
1914
        /* Give to transport */
 
1915
        status = pjmedia_transport_encode_sdp(call_med->tp, pool,
 
1916
                                              sdp, rem_sdp, mi);
 
1917
        if (status != PJ_SUCCESS) {
 
1918
            if (sip_err_code) *sip_err_code = PJSIP_SC_NOT_ACCEPTABLE;
 
1919
            return status;
 
1920
        }
 
1921
 
 
1922
        /* Copy c= line of the first media to session level,
 
1923
         * if there's none.
 
1924
         */
 
1925
        if (sdp->conn == NULL) {
 
1926
            sdp->conn = pjmedia_sdp_conn_clone(pool, m->conn);
 
1927
        }
 
1928
 
 
1929
 
 
1930
        /* Find media bandwidth info */
 
1931
        for (i = 0; i < m->bandw_count; ++i) {
 
1932
            const pj_str_t STR_BANDW_MODIFIER_TIAS = { "TIAS", 4 };
 
1933
            if (!pj_stricmp(&m->bandw[i]->modifier, &STR_BANDW_MODIFIER_TIAS))
 
1934
            {
 
1935
                tot_bandw_tias += m->bandw[i]->value;
 
1936
                break;
 
1937
            }
 
1938
        }
 
1939
    }
 
1940
 
 
1941
    /* Add NAT info in the SDP */
 
1942
    if (pjsua_var.ua_cfg.nat_type_in_sdp) {
 
1943
        pjmedia_sdp_attr *a;
 
1944
        pj_str_t value;
 
1945
        char nat_info[80];
 
1946
 
 
1947
        value.ptr = nat_info;
 
1948
        if (pjsua_var.ua_cfg.nat_type_in_sdp == 1) {
 
1949
            value.slen = pj_ansi_snprintf(nat_info, sizeof(nat_info),
 
1950
                                          "%d", pjsua_var.nat_type);
 
1951
        } else {
 
1952
            const char *type_name = pj_stun_get_nat_name(pjsua_var.nat_type);
 
1953
            value.slen = pj_ansi_snprintf(nat_info, sizeof(nat_info),
 
1954
                                          "%d %s",
 
1955
                                          pjsua_var.nat_type,
 
1956
                                          type_name);
 
1957
        }
 
1958
 
 
1959
        a = pjmedia_sdp_attr_create(pool, "X-nat", &value);
 
1960
 
 
1961
        pjmedia_sdp_attr_add(&sdp->attr_count, sdp->attr, a);
 
1962
 
 
1963
    }
 
1964
 
 
1965
 
 
1966
    /* Add bandwidth info in session level using bandwidth modifier "AS". */
 
1967
    if (tot_bandw_tias) {
 
1968
        unsigned bandw;
 
1969
        const pj_str_t STR_BANDW_MODIFIER_AS = { "AS", 2 };
 
1970
        pjmedia_sdp_bandw *b;
 
1971
 
 
1972
        /* AS bandwidth = RTP bitrate + RTCP bitrate.
 
1973
         * RTP bitrate  = payload bitrate (total TIAS) + overheads (~16kbps).
 
1974
         * RTCP bitrate = est. 5% of RTP bitrate.
 
1975
         * Note that AS bandwidth is in kbps.
 
1976
         */
 
1977
        bandw = tot_bandw_tias + 16000;
 
1978
        bandw += bandw * 5 / 100;
 
1979
        b = PJ_POOL_ALLOC_T(pool, pjmedia_sdp_bandw);
 
1980
        b->modifier = STR_BANDW_MODIFIER_AS;
 
1981
        b->value = bandw / 1000;
 
1982
        sdp->bandw[sdp->bandw_count++] = b;
 
1983
    }
 
1984
 
 
1985
 
 
1986
#if DISABLED_FOR_TICKET_1185 && defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
 
1987
    /* Check if SRTP is in optional mode and configured to use duplicated
 
1988
     * media, i.e: secured and unsecured version, in the SDP offer.
 
1989
     */
 
1990
    if (!rem_sdp &&
 
1991
        pjsua_var.acc[call->acc_id].cfg.use_srtp == PJMEDIA_SRTP_OPTIONAL &&
 
1992
        pjsua_var.acc[call->acc_id].cfg.srtp_optional_dup_offer)
 
1993
    {
 
1994
        unsigned i;
 
1995
 
 
1996
        for (i = 0; i < sdp->media_count; ++i) {
 
1997
            pjmedia_sdp_media *m = sdp->media[i];
 
1998
 
 
1999
            /* Check if this media is unsecured but has SDP "crypto"
 
2000
             * attribute.
 
2001
             */
 
2002
            if (pj_stricmp2(&m->desc.transport, "RTP/AVP") == 0 &&
 
2003
                pjmedia_sdp_media_find_attr2(m, "crypto", NULL) != NULL)
 
2004
            {
 
2005
                if (i == (unsigned)call->audio_idx &&
 
2006
                    sdp_neg_state == PJMEDIA_SDP_NEG_STATE_DONE)
 
2007
                {
 
2008
                    /* This is a session update, and peer has chosen the
 
2009
                     * unsecured version, so let's make this unsecured too.
 
2010
                     */
 
2011
                    pjmedia_sdp_media_remove_all_attr(m, "crypto");
 
2012
                } else {
 
2013
                    /* This is new offer, duplicate media so we'll have
 
2014
                     * secured (with "RTP/SAVP" transport) and and unsecured
 
2015
                     * versions.
 
2016
                     */
 
2017
                    pjmedia_sdp_media *new_m;
 
2018
 
 
2019
                    /* Duplicate this media and apply secured transport */
 
2020
                    new_m = pjmedia_sdp_media_clone(pool, m);
 
2021
                    pj_strdup2(pool, &new_m->desc.transport, "RTP/SAVP");
 
2022
 
 
2023
                    /* Remove the "crypto" attribute in the unsecured media */
 
2024
                    pjmedia_sdp_media_remove_all_attr(m, "crypto");
 
2025
 
 
2026
                    /* Insert the new media before the unsecured media */
 
2027
                    if (sdp->media_count < PJMEDIA_MAX_SDP_MEDIA) {
 
2028
                        pj_array_insert(sdp->media, sizeof(new_m),
 
2029
                                        sdp->media_count, i, &new_m);
 
2030
                        ++sdp->media_count;
 
2031
                        ++i;
 
2032
                    }
 
2033
                }
 
2034
            }
 
2035
        }
 
2036
    }
 
2037
#endif
 
2038
 
 
2039
    call->rem_offerer = (rem_sdp != NULL);
 
2040
 
 
2041
    /* Notify application */
 
2042
    if (pjsua_var.ua_cfg.cb.on_call_sdp_created) {
 
2043
        (*pjsua_var.ua_cfg.cb.on_call_sdp_created)(call_id, sdp,
 
2044
                                                   pool, rem_sdp);
 
2045
    }
 
2046
 
 
2047
    *p_sdp = sdp;
 
2048
    return PJ_SUCCESS;
 
2049
}
 
2050
 
 
2051
 
 
2052
static void stop_media_session(pjsua_call_id call_id)
 
2053
{
 
2054
    pjsua_call *call = &pjsua_var.calls[call_id];
 
2055
    unsigned mi;
 
2056
 
 
2057
    pj_log_push_indent();
 
2058
 
 
2059
    for (mi=0; mi<call->med_cnt; ++mi) {
 
2060
        pjsua_call_media *call_med = &call->media[mi];
 
2061
 
 
2062
        if (call_med->type == PJMEDIA_TYPE_AUDIO) {
 
2063
            pjsua_aud_stop_stream(call_med);
 
2064
        }
 
2065
 
 
2066
#if PJMEDIA_HAS_VIDEO
 
2067
        else if (call_med->type == PJMEDIA_TYPE_VIDEO) {
 
2068
            pjsua_vid_stop_stream(call_med);
 
2069
        }
 
2070
#endif
 
2071
 
 
2072
        PJ_LOG(4,(THIS_FILE, "Media session call%02d:%d is destroyed",
 
2073
                             call_id, mi));
 
2074
        call_med->prev_state = call_med->state;
 
2075
        call_med->state = PJSUA_CALL_MEDIA_NONE;
 
2076
 
 
2077
        /* Try to sync recent changes to provisional media */
 
2078
        if (mi<call->med_prov_cnt && call->media_prov[mi].tp==call_med->tp)
 
2079
        {
 
2080
            pjsua_call_media *prov_med = &call->media_prov[mi];
 
2081
 
 
2082
            /* Media state */
 
2083
            prov_med->prev_state = call_med->prev_state;
 
2084
            prov_med->state      = call_med->state;
 
2085
 
 
2086
            /* RTP seq/ts */
 
2087
            prov_med->rtp_tx_seq_ts_set = call_med->rtp_tx_seq_ts_set;
 
2088
            prov_med->rtp_tx_seq        = call_med->rtp_tx_seq;
 
2089
            prov_med->rtp_tx_ts         = call_med->rtp_tx_ts;
 
2090
 
 
2091
            /* Stream */
 
2092
            if (call_med->type == PJMEDIA_TYPE_AUDIO) {
 
2093
                prov_med->strm.a.conf_slot = call_med->strm.a.conf_slot;
 
2094
                prov_med->strm.a.stream    = call_med->strm.a.stream;
 
2095
            }
 
2096
#if PJMEDIA_HAS_VIDEO
 
2097
            else if (call_med->type == PJMEDIA_TYPE_VIDEO) {
 
2098
                prov_med->strm.v.cap_win_id = call_med->strm.v.cap_win_id;
 
2099
                prov_med->strm.v.rdr_win_id = call_med->strm.v.rdr_win_id;
 
2100
                prov_med->strm.v.stream     = call_med->strm.v.stream;
 
2101
            }
 
2102
#endif
 
2103
        }
 
2104
    }
 
2105
 
 
2106
    pj_log_pop_indent();
 
2107
}
 
2108
 
 
2109
pj_status_t pjsua_media_channel_deinit(pjsua_call_id call_id)
 
2110
{
 
2111
    pjsua_call *call = &pjsua_var.calls[call_id];
 
2112
    unsigned mi;
 
2113
 
 
2114
    for (mi=0; mi<call->med_cnt; ++mi) {
 
2115
        pjsua_call_media *call_med = &call->media[mi];
 
2116
 
 
2117
        if (call_med->tp_st == PJSUA_MED_TP_CREATING) {
 
2118
            /* We will do the deinitialization after media transport
 
2119
             * creation is completed.
 
2120
             */
 
2121
            call->async_call.med_ch_deinit = PJ_TRUE;
 
2122
            return PJ_SUCCESS;
 
2123
        }
 
2124
    }
 
2125
 
 
2126
    PJ_LOG(4,(THIS_FILE, "Call %d: deinitializing media..", call_id));
 
2127
    pj_log_push_indent();
 
2128
 
 
2129
    stop_media_session(call_id);
 
2130
 
 
2131
    /* Clean up media transports */
 
2132
    pjsua_media_prov_clean_up(call_id);
 
2133
    call->med_prov_cnt = 0;
 
2134
    for (mi=0; mi<call->med_cnt; ++mi) {
 
2135
        pjsua_call_media *call_med = &call->media[mi];
 
2136
 
 
2137
        if (call_med->tp_st > PJSUA_MED_TP_IDLE) {
 
2138
            pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_IDLE);
 
2139
            pjmedia_transport_media_stop(call_med->tp);
 
2140
        }
 
2141
 
 
2142
        if (call_med->tp) {
 
2143
            pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_NULL);
 
2144
            pjmedia_transport_close(call_med->tp);
 
2145
            call_med->tp = call_med->tp_orig = NULL;
 
2146
        }
 
2147
        call_med->tp_orig = NULL;
 
2148
    }
 
2149
 
 
2150
    pj_log_pop_indent();
 
2151
 
 
2152
    return PJ_SUCCESS;
 
2153
}
 
2154
 
 
2155
 
 
2156
pj_status_t pjsua_media_channel_update(pjsua_call_id call_id,
 
2157
                                       const pjmedia_sdp_session *local_sdp,
 
2158
                                       const pjmedia_sdp_session *remote_sdp)
 
2159
{
 
2160
    pjsua_call *call = &pjsua_var.calls[call_id];
 
2161
    pjsua_acc *acc = &pjsua_var.acc[call->acc_id];
 
2162
    pj_pool_t *tmp_pool = call->inv->pool_prov;
 
2163
    unsigned mi;
 
2164
    pj_bool_t got_media = PJ_FALSE;
 
2165
    pj_status_t status = PJ_SUCCESS;
 
2166
 
 
2167
    const pj_str_t STR_AUDIO = { "audio", 5 };
 
2168
    const pj_str_t STR_VIDEO = { "video", 5 };
 
2169
    pj_uint8_t maudidx[PJSUA_MAX_CALL_MEDIA];
 
2170
    unsigned maudcnt = PJ_ARRAY_SIZE(maudidx);
 
2171
    unsigned mtotaudcnt = PJ_ARRAY_SIZE(maudidx);
 
2172
    pj_uint8_t mvididx[PJSUA_MAX_CALL_MEDIA];
 
2173
    unsigned mvidcnt = PJ_ARRAY_SIZE(mvididx);
 
2174
    unsigned mtotvidcnt = PJ_ARRAY_SIZE(mvididx);
 
2175
    pj_bool_t need_renego_sdp = PJ_FALSE;
 
2176
 
 
2177
    if (pjsua_get_state() != PJSUA_STATE_RUNNING)
 
2178
        return PJ_EBUSY;
 
2179
 
 
2180
    PJ_LOG(4,(THIS_FILE, "Call %d: updating media..", call_id));
 
2181
    pj_log_push_indent();
 
2182
 
 
2183
    /* Destroy existing media session, if any. */
 
2184
    stop_media_session(call->index);
 
2185
 
 
2186
    /* Call media count must be at least equal to SDP media. Note that
 
2187
     * it may not be equal when remote removed any SDP media line.
 
2188
     */
 
2189
    pj_assert(call->med_prov_cnt >= local_sdp->media_count);
 
2190
 
 
2191
    /* Reset audio_idx first */
 
2192
    call->audio_idx = -1;
 
2193
 
 
2194
    /* Sort audio/video based on "quality" */
 
2195
    sort_media(local_sdp, &STR_AUDIO, acc->cfg.use_srtp,
 
2196
               maudidx, &maudcnt, &mtotaudcnt);
 
2197
#if PJMEDIA_HAS_VIDEO
 
2198
    sort_media(local_sdp, &STR_VIDEO, acc->cfg.use_srtp,
 
2199
               mvididx, &mvidcnt, &mtotvidcnt);
 
2200
#else
 
2201
    PJ_UNUSED_ARG(STR_VIDEO);
 
2202
    mvidcnt = mtotvidcnt = 0;
 
2203
#endif
 
2204
 
 
2205
    /* Applying media count limitation. Note that in generating SDP answer,
 
2206
     * no media count limitation applied, as we didn't know yet which media
 
2207
     * would pass the SDP negotiation.
 
2208
     */
 
2209
    if (maudcnt > call->opt.aud_cnt || mvidcnt > call->opt.vid_cnt)
 
2210
    {
 
2211
        pjmedia_sdp_session *local_sdp2;
 
2212
 
 
2213
        maudcnt = PJ_MIN(maudcnt, call->opt.aud_cnt);
 
2214
        mvidcnt = PJ_MIN(mvidcnt, call->opt.vid_cnt);
 
2215
        local_sdp2 = pjmedia_sdp_session_clone(tmp_pool, local_sdp);
 
2216
 
 
2217
        for (mi=0; mi < local_sdp2->media_count; ++mi) {
 
2218
            pjmedia_sdp_media *m = local_sdp2->media[mi];
 
2219
 
 
2220
            if (m->desc.port == 0 ||
 
2221
                pj_memchr(maudidx, mi, maudcnt*sizeof(maudidx[0])) ||
 
2222
                pj_memchr(mvididx, mi, mvidcnt*sizeof(mvididx[0])))
 
2223
            {
 
2224
                continue;
 
2225
            }
 
2226
 
 
2227
            /* Deactivate this media */
 
2228
            pjmedia_sdp_media_deactivate(tmp_pool, m);
 
2229
        }
 
2230
 
 
2231
        local_sdp = local_sdp2;
 
2232
        need_renego_sdp = PJ_TRUE;
 
2233
    }
 
2234
 
 
2235
    /* Process each media stream */
 
2236
    for (mi=0; mi < call->med_prov_cnt; ++mi) {
 
2237
        pjsua_call_media *call_med = &call->media_prov[mi];
 
2238
 
 
2239
        if (mi >= local_sdp->media_count ||
 
2240
            mi >= remote_sdp->media_count)
 
2241
        {
 
2242
            /* This may happen when remote removed any SDP media lines in
 
2243
             * its re-offer.
 
2244
             */
 
2245
            if (call_med->tp) {
 
2246
                /* Close the media transport */
 
2247
                pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_NULL);
 
2248
                pjmedia_transport_close(call_med->tp);
 
2249
                call_med->tp = call_med->tp_orig = NULL;
 
2250
            }
 
2251
            continue;
 
2252
#if 0
 
2253
            /* Something is wrong */
 
2254
            PJ_LOG(1,(THIS_FILE, "Error updating media for call %d: "
 
2255
                      "invalid media index %d in SDP", call_id, mi));
 
2256
            status = PJMEDIA_SDP_EINSDP;
 
2257
            goto on_error;
 
2258
#endif
 
2259
        }
 
2260
 
 
2261
        if (call_med->type==PJMEDIA_TYPE_AUDIO) {
 
2262
            pjmedia_stream_info the_si, *si = &the_si;
 
2263
 
 
2264
            status = pjmedia_stream_info_from_sdp(si, tmp_pool, pjsua_var.med_endpt,
 
2265
                                                  local_sdp, remote_sdp, mi);
 
2266
            if (status != PJ_SUCCESS) {
 
2267
                PJ_PERROR(1,(THIS_FILE, status,
 
2268
                             "pjmedia_stream_info_from_sdp() failed "
 
2269
                                 "for call_id %d media %d",
 
2270
                             call_id, mi));
 
2271
                continue;
 
2272
            }
 
2273
 
 
2274
            /* Check if no media is active */
 
2275
            if (si->dir == PJMEDIA_DIR_NONE) {
 
2276
                /* Update call media state and direction */
 
2277
                call_med->state = PJSUA_CALL_MEDIA_NONE;
 
2278
                call_med->dir = PJMEDIA_DIR_NONE;
 
2279
 
 
2280
            } else {
 
2281
                pjmedia_transport_info tp_info;
 
2282
 
 
2283
                /* Start/restart media transport based on info in SDP */
 
2284
                status = pjmedia_transport_media_start(call_med->tp,
 
2285
                                                       tmp_pool, local_sdp,
 
2286
                                                       remote_sdp, mi);
 
2287
                if (status != PJ_SUCCESS) {
 
2288
                    PJ_PERROR(1,(THIS_FILE, status,
 
2289
                                 "pjmedia_transport_media_start() failed "
 
2290
                                     "for call_id %d media %d",
 
2291
                                 call_id, mi));
 
2292
                    continue;
 
2293
                }
 
2294
 
 
2295
                pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_RUNNING);
 
2296
 
 
2297
                /* Get remote SRTP usage policy */
 
2298
                pjmedia_transport_info_init(&tp_info);
 
2299
                pjmedia_transport_get_info(call_med->tp, &tp_info);
 
2300
                if (tp_info.specific_info_cnt > 0) {
 
2301
                    unsigned i;
 
2302
                    for (i = 0; i < tp_info.specific_info_cnt; ++i) {
 
2303
                        if (tp_info.spc_info[i].type == PJMEDIA_TRANSPORT_TYPE_SRTP)
 
2304
                        {
 
2305
                            pjmedia_srtp_info *srtp_info =
 
2306
                                        (pjmedia_srtp_info*) tp_info.spc_info[i].buffer;
 
2307
 
 
2308
                            call_med->rem_srtp_use = srtp_info->peer_use;
 
2309
                            break;
 
2310
                        }
 
2311
                    }
 
2312
                }
 
2313
 
 
2314
                /* Call media direction */
 
2315
                call_med->dir = si->dir;
 
2316
 
 
2317
                /* Call media state */
 
2318
                if (call->local_hold)
 
2319
                    call_med->state = PJSUA_CALL_MEDIA_LOCAL_HOLD;
 
2320
                else if (call_med->dir == PJMEDIA_DIR_DECODING)
 
2321
                    call_med->state = PJSUA_CALL_MEDIA_REMOTE_HOLD;
 
2322
                else
 
2323
                    call_med->state = PJSUA_CALL_MEDIA_ACTIVE;
 
2324
            }
 
2325
 
 
2326
            /* Call implementation */
 
2327
            status = pjsua_aud_channel_update(call_med, tmp_pool, si,
 
2328
                                              local_sdp, remote_sdp);
 
2329
            if (status != PJ_SUCCESS) {
 
2330
                PJ_PERROR(1,(THIS_FILE, status,
 
2331
                             "pjsua_aud_channel_update() failed "
 
2332
                                 "for call_id %d media %d",
 
2333
                             call_id, mi));
 
2334
                continue;
 
2335
            }
 
2336
 
 
2337
            /* Print info. */
 
2338
            if (status == PJ_SUCCESS) {
 
2339
                char info[80];
 
2340
                int info_len = 0;
 
2341
                int len;
 
2342
                const char *dir;
 
2343
 
 
2344
                switch (si->dir) {
 
2345
                case PJMEDIA_DIR_NONE:
 
2346
                    dir = "inactive";
 
2347
                    break;
 
2348
                case PJMEDIA_DIR_ENCODING:
 
2349
                    dir = "sendonly";
 
2350
                    break;
 
2351
                case PJMEDIA_DIR_DECODING:
 
2352
                    dir = "recvonly";
 
2353
                    break;
 
2354
                case PJMEDIA_DIR_ENCODING_DECODING:
 
2355
                    dir = "sendrecv";
 
2356
                    break;
 
2357
                default:
 
2358
                    dir = "unknown";
 
2359
                    break;
 
2360
                }
 
2361
                len = pj_ansi_sprintf( info+info_len,
 
2362
                                       ", stream #%d: %.*s (%s)", mi,
 
2363
                                       (int)si->fmt.encoding_name.slen,
 
2364
                                       si->fmt.encoding_name.ptr,
 
2365
                                       dir);
 
2366
                if (len > 0)
 
2367
                    info_len += len;
 
2368
                PJ_LOG(4,(THIS_FILE,"Audio updated%s", info));
 
2369
            }
 
2370
 
 
2371
 
 
2372
            if (call->audio_idx==-1 && status==PJ_SUCCESS &&
 
2373
                si->dir != PJMEDIA_DIR_NONE)
 
2374
            {
 
2375
                call->audio_idx = mi;
 
2376
            }
 
2377
 
 
2378
#if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0)
 
2379
        } else if (call_med->type==PJMEDIA_TYPE_VIDEO) {
 
2380
            pjmedia_vid_stream_info the_si, *si = &the_si;
 
2381
 
 
2382
            status = pjmedia_vid_stream_info_from_sdp(si, tmp_pool, pjsua_var.med_endpt,
 
2383
                                                      local_sdp, remote_sdp, mi);
 
2384
            if (status != PJ_SUCCESS) {
 
2385
                PJ_PERROR(1,(THIS_FILE, status,
 
2386
                             "pjmedia_vid_stream_info_from_sdp() failed "
 
2387
                                 "for call_id %d media %d",
 
2388
                             call_id, mi));
 
2389
                continue;
 
2390
            }
 
2391
 
 
2392
            /* Check if no media is active */
 
2393
            if (si->dir == PJMEDIA_DIR_NONE) {
 
2394
                /* Update call media state and direction */
 
2395
                call_med->state = PJSUA_CALL_MEDIA_NONE;
 
2396
                call_med->dir = PJMEDIA_DIR_NONE;
 
2397
 
 
2398
            } else {
 
2399
                pjmedia_transport_info tp_info;
 
2400
 
 
2401
                /* Start/restart media transport */
 
2402
                status = pjmedia_transport_media_start(call_med->tp,
 
2403
                                                       tmp_pool, local_sdp,
 
2404
                                                       remote_sdp, mi);
 
2405
                if (status != PJ_SUCCESS) {
 
2406
                    PJ_PERROR(1,(THIS_FILE, status,
 
2407
                                 "pjmedia_transport_media_start() failed "
 
2408
                                     "for call_id %d media %d",
 
2409
                                 call_id, mi));
 
2410
                    continue;
 
2411
                }
 
2412
 
 
2413
                pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_RUNNING);
 
2414
 
 
2415
                /* Get remote SRTP usage policy */
 
2416
                pjmedia_transport_info_init(&tp_info);
 
2417
                pjmedia_transport_get_info(call_med->tp, &tp_info);
 
2418
                if (tp_info.specific_info_cnt > 0) {
 
2419
                    unsigned i;
 
2420
                    for (i = 0; i < tp_info.specific_info_cnt; ++i) {
 
2421
                        if (tp_info.spc_info[i].type ==
 
2422
                                PJMEDIA_TRANSPORT_TYPE_SRTP)
 
2423
                        {
 
2424
                            pjmedia_srtp_info *sri;
 
2425
                            sri=(pjmedia_srtp_info*)tp_info.spc_info[i].buffer;
 
2426
                            call_med->rem_srtp_use = sri->peer_use;
 
2427
                            break;
 
2428
                        }
 
2429
                    }
 
2430
                }
 
2431
 
 
2432
                /* Call media direction */
 
2433
                call_med->dir = si->dir;
 
2434
 
 
2435
                /* Call media state */
 
2436
                if (call->local_hold)
 
2437
                    call_med->state = PJSUA_CALL_MEDIA_LOCAL_HOLD;
 
2438
                else if (call_med->dir == PJMEDIA_DIR_DECODING)
 
2439
                    call_med->state = PJSUA_CALL_MEDIA_REMOTE_HOLD;
 
2440
                else
 
2441
                    call_med->state = PJSUA_CALL_MEDIA_ACTIVE;
 
2442
            }
 
2443
 
 
2444
            status = pjsua_vid_channel_update(call_med, tmp_pool, si,
 
2445
                                              local_sdp, remote_sdp);
 
2446
            if (status != PJ_SUCCESS) {
 
2447
                PJ_PERROR(1,(THIS_FILE, status,
 
2448
                             "pjsua_vid_channel_update() failed "
 
2449
                                 "for call_id %d media %d",
 
2450
                             call_id, mi));
 
2451
                continue;
 
2452
            }
 
2453
 
 
2454
            /* Print info. */
 
2455
            {
 
2456
                char info[80];
 
2457
                int info_len = 0;
 
2458
                int len;
 
2459
                const char *dir;
 
2460
 
 
2461
                switch (si->dir) {
 
2462
                case PJMEDIA_DIR_NONE:
 
2463
                    dir = "inactive";
 
2464
                    break;
 
2465
                case PJMEDIA_DIR_ENCODING:
 
2466
                    dir = "sendonly";
 
2467
                    break;
 
2468
                case PJMEDIA_DIR_DECODING:
 
2469
                    dir = "recvonly";
 
2470
                    break;
 
2471
                case PJMEDIA_DIR_ENCODING_DECODING:
 
2472
                    dir = "sendrecv";
 
2473
                    break;
 
2474
                default:
 
2475
                    dir = "unknown";
 
2476
                    break;
 
2477
                }
 
2478
                len = pj_ansi_sprintf( info+info_len,
 
2479
                                       ", stream #%d: %.*s (%s)", mi,
 
2480
                                       (int)si->codec_info.encoding_name.slen,
 
2481
                                       si->codec_info.encoding_name.ptr,
 
2482
                                       dir);
 
2483
                if (len > 0)
 
2484
                    info_len += len;
 
2485
                PJ_LOG(4,(THIS_FILE,"Video updated%s", info));
 
2486
            }
 
2487
 
 
2488
#endif
 
2489
        } else {
 
2490
            status = PJMEDIA_EINVALIMEDIATYPE;
 
2491
        }
 
2492
 
 
2493
        /* Close the transport of deactivated media, need this here as media
 
2494
         * can be deactivated by the SDP negotiation and the max media count
 
2495
         * (account) setting.
 
2496
         */
 
2497
        if (local_sdp->media[mi]->desc.port==0 && call_med->tp) {
 
2498
            pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_NULL);
 
2499
            pjmedia_transport_close(call_med->tp);
 
2500
            call_med->tp = call_med->tp_orig = NULL;
 
2501
        }
 
2502
 
 
2503
        if (status != PJ_SUCCESS) {
 
2504
            PJ_PERROR(1,(THIS_FILE, status, "Error updating media call%02d:%d",
 
2505
                         call_id, mi));
 
2506
        } else {
 
2507
            got_media = PJ_TRUE;
 
2508
        }
 
2509
    }
 
2510
 
 
2511
    /* Update call media from provisional media */
 
2512
    call->med_cnt = call->med_prov_cnt;
 
2513
    pj_memcpy(call->media, call->media_prov,
 
2514
              sizeof(call->media_prov[0]) * call->med_prov_cnt);
 
2515
 
 
2516
    /* Perform SDP re-negotiation if needed. */
 
2517
    if (got_media && need_renego_sdp) {
 
2518
        pjmedia_sdp_neg *neg = call->inv->neg;
 
2519
 
 
2520
        /* This should only happen when we are the answerer. */
 
2521
        PJ_ASSERT_RETURN(neg && !pjmedia_sdp_neg_was_answer_remote(neg),
 
2522
                         PJMEDIA_SDPNEG_EINSTATE);
 
2523
 
 
2524
        status = pjmedia_sdp_neg_set_remote_offer(tmp_pool, neg, remote_sdp);
 
2525
        if (status != PJ_SUCCESS)
 
2526
            goto on_error;
 
2527
 
 
2528
        status = pjmedia_sdp_neg_set_local_answer(tmp_pool, neg, local_sdp);
 
2529
        if (status != PJ_SUCCESS)
 
2530
            goto on_error;
 
2531
 
 
2532
        status = pjmedia_sdp_neg_negotiate(tmp_pool, neg, 0);
 
2533
        if (status != PJ_SUCCESS)
 
2534
            goto on_error;
 
2535
    }
 
2536
 
 
2537
    pj_log_pop_indent();
 
2538
    return (got_media? PJ_SUCCESS : PJMEDIA_SDPNEG_ENOMEDIA);
 
2539
 
 
2540
on_error:
 
2541
    pj_log_pop_indent();
 
2542
    return status;
 
2543
}
 
2544
 
 
2545
/*****************************************************************************
 
2546
 * Codecs.
 
2547
 */
 
2548
 
 
2549
/*
 
2550
 * Enum all supported codecs in the system.
 
2551
 */
 
2552
PJ_DEF(pj_status_t) pjsua_enum_codecs( pjsua_codec_info id[],
 
2553
                                       unsigned *p_count )
 
2554
{
 
2555
    pjmedia_codec_mgr *codec_mgr;
 
2556
    pjmedia_codec_info info[32];
 
2557
    unsigned i, count, prio[32];
 
2558
    pj_status_t status;
 
2559
 
 
2560
    codec_mgr = pjmedia_endpt_get_codec_mgr(pjsua_var.med_endpt);
 
2561
    count = PJ_ARRAY_SIZE(info);
 
2562
    status = pjmedia_codec_mgr_enum_codecs( codec_mgr, &count, info, prio);
 
2563
    if (status != PJ_SUCCESS) {
 
2564
        *p_count = 0;
 
2565
        return status;
 
2566
    }
 
2567
 
 
2568
    if (count > *p_count) count = *p_count;
 
2569
 
 
2570
    for (i=0; i<count; ++i) {
 
2571
        pj_bzero(&id[i], sizeof(pjsua_codec_info));
 
2572
 
 
2573
        pjmedia_codec_info_to_id(&info[i], id[i].buf_, sizeof(id[i].buf_));
 
2574
        id[i].codec_id = pj_str(id[i].buf_);
 
2575
        id[i].priority = (pj_uint8_t) prio[i];
 
2576
    }
 
2577
 
 
2578
    *p_count = count;
 
2579
 
 
2580
    return PJ_SUCCESS;
 
2581
}
 
2582
 
 
2583
 
 
2584
/*
 
2585
 * Change codec priority.
 
2586
 */
 
2587
PJ_DEF(pj_status_t) pjsua_codec_set_priority( const pj_str_t *codec_id,
 
2588
                                              pj_uint8_t priority )
 
2589
{
 
2590
    const pj_str_t all = { NULL, 0 };
 
2591
    pjmedia_codec_mgr *codec_mgr;
 
2592
 
 
2593
    codec_mgr = pjmedia_endpt_get_codec_mgr(pjsua_var.med_endpt);
 
2594
 
 
2595
    if (codec_id->slen==1 && *codec_id->ptr=='*')
 
2596
        codec_id = &all;
 
2597
 
 
2598
    return pjmedia_codec_mgr_set_codec_priority(codec_mgr, codec_id,
 
2599
                                                priority);
 
2600
}
 
2601
 
 
2602
 
 
2603
/*
 
2604
 * Get codec parameters.
 
2605
 */
 
2606
PJ_DEF(pj_status_t) pjsua_codec_get_param( const pj_str_t *codec_id,
 
2607
                                           pjmedia_codec_param *param )
 
2608
{
 
2609
    const pj_str_t all = { NULL, 0 };
 
2610
    const pjmedia_codec_info *info;
 
2611
    pjmedia_codec_mgr *codec_mgr;
 
2612
    unsigned count = 1;
 
2613
    pj_status_t status;
 
2614
 
 
2615
    codec_mgr = pjmedia_endpt_get_codec_mgr(pjsua_var.med_endpt);
 
2616
 
 
2617
    if (codec_id->slen==1 && *codec_id->ptr=='*')
 
2618
        codec_id = &all;
 
2619
 
 
2620
    status = pjmedia_codec_mgr_find_codecs_by_id(codec_mgr, codec_id,
 
2621
                                                 &count, &info, NULL);
 
2622
    if (status != PJ_SUCCESS)
 
2623
        return status;
 
2624
 
 
2625
    if (count != 1)
 
2626
        return (count > 1? PJ_ETOOMANY : PJ_ENOTFOUND);
 
2627
 
 
2628
    status = pjmedia_codec_mgr_get_default_param( codec_mgr, info, param);
 
2629
    return status;
 
2630
}
 
2631
 
 
2632
 
 
2633
/*
 
2634
 * Set codec parameters.
 
2635
 */
 
2636
PJ_DEF(pj_status_t) pjsua_codec_set_param( const pj_str_t *codec_id,
 
2637
                                           const pjmedia_codec_param *param)
 
2638
{
 
2639
    const pjmedia_codec_info *info[2];
 
2640
    pjmedia_codec_mgr *codec_mgr;
 
2641
    unsigned count = 2;
 
2642
    pj_status_t status;
 
2643
 
 
2644
    codec_mgr = pjmedia_endpt_get_codec_mgr(pjsua_var.med_endpt);
 
2645
 
 
2646
    status = pjmedia_codec_mgr_find_codecs_by_id(codec_mgr, codec_id,
 
2647
                                                 &count, info, NULL);
 
2648
    if (status != PJ_SUCCESS)
 
2649
        return status;
 
2650
 
 
2651
    /* Codec ID should be specific, except for G.722.1 */
 
2652
    if (count > 1 &&
 
2653
        pj_strnicmp2(codec_id, "G7221/16", 8) != 0 &&
 
2654
        pj_strnicmp2(codec_id, "G7221/32", 8) != 0)
 
2655
    {
 
2656
        pj_assert(!"Codec ID is not specific");
 
2657
        return PJ_ETOOMANY;
 
2658
    }
 
2659
 
 
2660
    status = pjmedia_codec_mgr_set_default_param(codec_mgr, info[0], param);
 
2661
    return status;
 
2662
}
 
2663
 
 
2664
 
 
2665
pj_status_t pjsua_media_apply_xml_control(pjsua_call_id call_id,
 
2666
                                          const pj_str_t *xml_st)
 
2667
{
 
2668
#if PJMEDIA_HAS_VIDEO
 
2669
    pjsua_call *call = &pjsua_var.calls[call_id];
 
2670
    const pj_str_t PICT_FAST_UPDATE = {"picture_fast_update", 19};
 
2671
 
 
2672
    if (pj_strstr(xml_st, &PICT_FAST_UPDATE)) {
 
2673
        unsigned i;
 
2674
 
 
2675
        PJ_LOG(4,(THIS_FILE, "Received keyframe request via SIP INFO"));
 
2676
 
 
2677
        for (i = 0; i < call->med_cnt; ++i) {
 
2678
            pjsua_call_media *cm = &call->media[i];
 
2679
            if (cm->type != PJMEDIA_TYPE_VIDEO || !cm->strm.v.stream)
 
2680
                continue;
 
2681
 
 
2682
            pjmedia_vid_stream_send_keyframe(cm->strm.v.stream);
 
2683
        }
 
2684
 
 
2685
        return PJ_SUCCESS;
 
2686
    }
 
2687
#endif
 
2688
 
 
2689
    /* Just to avoid compiler warning of unused var */
 
2690
    PJ_UNUSED_ARG(call_id);
 
2691
    PJ_UNUSED_ARG(xml_st);
 
2692
 
 
2693
    return PJ_ENOTSUP;
 
2694
}