~ubuntu-branches/ubuntu/wily/sflphone/wily

« back to all changes in this revision

Viewing changes to daemon/libs/pjproject-2.1.0/pjmedia/src/pjmedia/sdp_neg.c

  • Committer: Package Import Robot
  • Author(s): Francois Marier, Francois Marier, Mark Purcell
  • Date: 2014-10-18 15:08:50 UTC
  • mfrom: (1.1.12)
  • mto: This revision was merged to the branch mainline in revision 29.
  • Revision ID: package-import@ubuntu.com-20141018150850-2exfk34ckb15pcwi
Tags: 1.4.1-0.1
[ Francois Marier ]
* Non-maintainer upload
* New upstream release (closes: #759576, #741130)
  - debian/rules +PJPROJECT_VERSION := 2.2.1
  - add upstream patch to fix broken TLS support
  - add patch to fix pjproject regression

[ Mark Purcell ]
* Build-Depends:
  - sflphone-daemon + libavformat-dev, libavcodec-dev, libswscale-dev,
  libavdevice-dev, libavutil-dev
  - sflphone-gnome + libclutter-gtk-1.0-dev

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* $Id: sdp_neg.c 4367 2013-02-21 20:49:19Z nanang $ */
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 <pjmedia/sdp_neg.h>
21
 
#include <pjmedia/sdp.h>
22
 
#include <pjmedia/errno.h>
23
 
#include <pj/assert.h>
24
 
#include <pj/pool.h>
25
 
#include <pj/string.h>
26
 
#include <pj/ctype.h>
27
 
#include <pj/array.h>
28
 
 
29
 
/**
30
 
 * This structure describes SDP media negotiator.
31
 
 */
32
 
struct pjmedia_sdp_neg
33
 
{
34
 
    pjmedia_sdp_neg_state state;            /**< Negotiator state.           */
35
 
    pj_bool_t             prefer_remote_codec_order;
36
 
    pj_bool_t             has_remote_answer;
37
 
    pj_bool_t             answer_was_remote;
38
 
 
39
 
    pjmedia_sdp_session *initial_sdp,       /**< Initial local SDP           */
40
 
                        *active_local_sdp,  /**< Currently active local SDP. */
41
 
                        *active_remote_sdp, /**< Currently active remote's.  */
42
 
                        *neg_local_sdp,     /**< Temporary local SDP.        */
43
 
                        *neg_remote_sdp;    /**< Temporary remote SDP.       */
44
 
};
45
 
 
46
 
static const char *state_str[] = 
47
 
{
48
 
    "STATE_NULL",
49
 
    "STATE_LOCAL_OFFER",
50
 
    "STATE_REMOTE_OFFER",
51
 
    "STATE_WAIT_NEGO",
52
 
    "STATE_DONE",
53
 
};
54
 
 
55
 
/* Definition of customized SDP format negotiation callback */
56
 
struct fmt_match_cb_t
57
 
{
58
 
    pj_str_t                        fmt_name;
59
 
    pjmedia_sdp_neg_fmt_match_cb    cb;
60
 
};
61
 
 
62
 
/* Number of registered customized SDP format negotiation callbacks */
63
 
static unsigned fmt_match_cb_cnt;
64
 
 
65
 
/* The registered customized SDP format negotiation callbacks */
66
 
static struct fmt_match_cb_t 
67
 
              fmt_match_cb[PJMEDIA_SDP_NEG_MAX_CUSTOM_FMT_NEG_CB];
68
 
 
69
 
/* Redefining a very long identifier name, just for convenience */
70
 
#define ALLOW_MODIFY_ANSWER PJMEDIA_SDP_NEG_FMT_MATCH_ALLOW_MODIFY_ANSWER
71
 
 
72
 
static pj_status_t custom_fmt_match( pj_pool_t *pool,
73
 
                                   const pj_str_t *fmt_name,
74
 
                                   pjmedia_sdp_media *offer,
75
 
                                   unsigned o_fmt_idx,
76
 
                                   pjmedia_sdp_media *answer,
77
 
                                   unsigned a_fmt_idx,
78
 
                                   unsigned option);
79
 
 
80
 
 
81
 
/*
82
 
 * Get string representation of negotiator state.
83
 
 */
84
 
PJ_DEF(const char*) pjmedia_sdp_neg_state_str(pjmedia_sdp_neg_state state)
85
 
{
86
 
    if (state >=0 && state < (pjmedia_sdp_neg_state)PJ_ARRAY_SIZE(state_str))
87
 
        return state_str[state];
88
 
 
89
 
    return "<?UNKNOWN?>";
90
 
}
91
 
 
92
 
 
93
 
/*
94
 
 * Create with local offer.
95
 
 */
96
 
PJ_DEF(pj_status_t) pjmedia_sdp_neg_create_w_local_offer( pj_pool_t *pool,
97
 
                                      const pjmedia_sdp_session *local,
98
 
                                      pjmedia_sdp_neg **p_neg)
99
 
{
100
 
    pjmedia_sdp_neg *neg;
101
 
    pj_status_t status;
102
 
 
103
 
    /* Check arguments are valid. */
104
 
    PJ_ASSERT_RETURN(pool && local && p_neg, PJ_EINVAL);
105
 
 
106
 
    *p_neg = NULL;
107
 
 
108
 
    /* Validate local offer. */
109
 
    PJ_ASSERT_RETURN((status=pjmedia_sdp_validate(local))==PJ_SUCCESS, status);
110
 
 
111
 
    /* Create and initialize negotiator. */
112
 
    neg = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_neg);
113
 
    PJ_ASSERT_RETURN(neg != NULL, PJ_ENOMEM);
114
 
 
115
 
    neg->state = PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER;
116
 
    neg->prefer_remote_codec_order = PJMEDIA_SDP_NEG_PREFER_REMOTE_CODEC_ORDER;
117
 
    neg->initial_sdp = pjmedia_sdp_session_clone(pool, local);
118
 
    neg->neg_local_sdp = pjmedia_sdp_session_clone(pool, local);
119
 
 
120
 
    *p_neg = neg;
121
 
    return PJ_SUCCESS;
122
 
}
123
 
 
124
 
/*
125
 
 * Create with remote offer and initial local offer/answer.
126
 
 */
127
 
PJ_DEF(pj_status_t) pjmedia_sdp_neg_create_w_remote_offer(pj_pool_t *pool,
128
 
                                      const pjmedia_sdp_session *initial,
129
 
                                      const pjmedia_sdp_session *remote,
130
 
                                      pjmedia_sdp_neg **p_neg)
131
 
{
132
 
    pjmedia_sdp_neg *neg;
133
 
    pj_status_t status;
134
 
 
135
 
    /* Check arguments are valid. */
136
 
    PJ_ASSERT_RETURN(pool && remote && p_neg, PJ_EINVAL);
137
 
 
138
 
    *p_neg = NULL;
139
 
 
140
 
    /* Validate remote offer and initial answer */
141
 
    status = pjmedia_sdp_validate2(remote, PJ_FALSE);
142
 
    if (status != PJ_SUCCESS)
143
 
        return status;
144
 
 
145
 
    /* Create and initialize negotiator. */
146
 
    neg = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_neg);
147
 
    PJ_ASSERT_RETURN(neg != NULL, PJ_ENOMEM);
148
 
 
149
 
    neg->prefer_remote_codec_order = PJMEDIA_SDP_NEG_PREFER_REMOTE_CODEC_ORDER;
150
 
    neg->neg_remote_sdp = pjmedia_sdp_session_clone(pool, remote);
151
 
 
152
 
    if (initial) {
153
 
        PJ_ASSERT_RETURN((status=pjmedia_sdp_validate(initial))==PJ_SUCCESS, 
154
 
                         status);
155
 
 
156
 
        neg->initial_sdp = pjmedia_sdp_session_clone(pool, initial);
157
 
        neg->neg_local_sdp = pjmedia_sdp_session_clone(pool, initial);
158
 
 
159
 
        neg->state = PJMEDIA_SDP_NEG_STATE_WAIT_NEGO;
160
 
 
161
 
    } else {
162
 
        
163
 
        neg->state = PJMEDIA_SDP_NEG_STATE_REMOTE_OFFER;
164
 
 
165
 
    }
166
 
 
167
 
    *p_neg = neg;
168
 
    return PJ_SUCCESS;
169
 
}
170
 
 
171
 
 
172
 
/*
173
 
 * Set codec order preference.
174
 
 */
175
 
PJ_DEF(pj_status_t) pjmedia_sdp_neg_set_prefer_remote_codec_order(
176
 
                                                pjmedia_sdp_neg *neg,
177
 
                                                pj_bool_t prefer_remote)
178
 
{
179
 
    PJ_ASSERT_RETURN(neg, PJ_EINVAL);
180
 
    neg->prefer_remote_codec_order = prefer_remote;
181
 
    return PJ_SUCCESS;
182
 
}
183
 
 
184
 
 
185
 
/*
186
 
 * Get SDP negotiator state.
187
 
 */
188
 
PJ_DEF(pjmedia_sdp_neg_state) pjmedia_sdp_neg_get_state( pjmedia_sdp_neg *neg )
189
 
{
190
 
    /* Check arguments are valid. */
191
 
    PJ_ASSERT_RETURN(neg != NULL, PJMEDIA_SDP_NEG_STATE_NULL);
192
 
    return neg->state;
193
 
}
194
 
 
195
 
 
196
 
PJ_DEF(pj_status_t) pjmedia_sdp_neg_get_active_local( pjmedia_sdp_neg *neg,
197
 
                                        const pjmedia_sdp_session **local)
198
 
{
199
 
    PJ_ASSERT_RETURN(neg && local, PJ_EINVAL);
200
 
    PJ_ASSERT_RETURN(neg->active_local_sdp, PJMEDIA_SDPNEG_ENOACTIVE);
201
 
 
202
 
    *local = neg->active_local_sdp;
203
 
    return PJ_SUCCESS;
204
 
}
205
 
 
206
 
 
207
 
PJ_DEF(pj_status_t) pjmedia_sdp_neg_get_active_remote( pjmedia_sdp_neg *neg,
208
 
                                   const pjmedia_sdp_session **remote)
209
 
{
210
 
    PJ_ASSERT_RETURN(neg && remote, PJ_EINVAL);
211
 
    PJ_ASSERT_RETURN(neg->active_remote_sdp, PJMEDIA_SDPNEG_ENOACTIVE);
212
 
 
213
 
    *remote = neg->active_remote_sdp;
214
 
    return PJ_SUCCESS;
215
 
}
216
 
 
217
 
 
218
 
PJ_DEF(pj_bool_t) pjmedia_sdp_neg_was_answer_remote(pjmedia_sdp_neg *neg)
219
 
{
220
 
    PJ_ASSERT_RETURN(neg, PJ_FALSE);
221
 
 
222
 
    return neg->answer_was_remote;
223
 
}
224
 
 
225
 
 
226
 
PJ_DEF(pj_status_t) pjmedia_sdp_neg_get_neg_remote( pjmedia_sdp_neg *neg,
227
 
                                const pjmedia_sdp_session **remote)
228
 
{
229
 
    PJ_ASSERT_RETURN(neg && remote, PJ_EINVAL);
230
 
    PJ_ASSERT_RETURN(neg->neg_remote_sdp, PJMEDIA_SDPNEG_ENONEG);
231
 
 
232
 
    *remote = neg->neg_remote_sdp;
233
 
    return PJ_SUCCESS;
234
 
}
235
 
 
236
 
PJ_DEF(pj_status_t) pjmedia_sdp_neg_get_neg_local( pjmedia_sdp_neg *neg,
237
 
                               const pjmedia_sdp_session **local)
238
 
{
239
 
    PJ_ASSERT_RETURN(neg && local, PJ_EINVAL);
240
 
    PJ_ASSERT_RETURN(neg->neg_local_sdp, PJMEDIA_SDPNEG_ENONEG);
241
 
 
242
 
    *local = neg->neg_local_sdp;
243
 
    return PJ_SUCCESS;
244
 
}
245
 
 
246
 
static pjmedia_sdp_media *sdp_media_clone_deactivate(
247
 
                                    pj_pool_t *pool,
248
 
                                    const pjmedia_sdp_media *rem_med,
249
 
                                    const pjmedia_sdp_media *local_med,
250
 
                                    const pjmedia_sdp_session *local_sess)
251
 
{
252
 
    pjmedia_sdp_media *res;
253
 
 
254
 
    res = pjmedia_sdp_media_clone_deactivate(pool, rem_med);
255
 
    if (!res)
256
 
        return NULL;
257
 
 
258
 
    if (!res->conn && (!local_sess || !local_sess->conn)) {
259
 
        if (local_med && local_med->conn)
260
 
            res->conn = pjmedia_sdp_conn_clone(pool, local_med->conn);
261
 
        else {
262
 
            res->conn = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_conn);
263
 
            res->conn->net_type = pj_str("IN");
264
 
            res->conn->addr_type = pj_str("IP4");
265
 
            res->conn->addr = pj_str("127.0.0.1");
266
 
        }
267
 
    }
268
 
 
269
 
    return res;
270
 
}
271
 
 
272
 
/*
273
 
 * Modify local SDP and wait for remote answer.
274
 
 */
275
 
PJ_DEF(pj_status_t) pjmedia_sdp_neg_modify_local_offer( pj_pool_t *pool,
276
 
                                    pjmedia_sdp_neg *neg,
277
 
                                    const pjmedia_sdp_session *local)
278
 
{
279
 
    pjmedia_sdp_session *new_offer;
280
 
    pjmedia_sdp_session *old_offer;
281
 
    char media_used[PJMEDIA_MAX_SDP_MEDIA];
282
 
    unsigned oi; /* old offer media index */
283
 
    pj_status_t status;
284
 
 
285
 
    /* Check arguments are valid. */
286
 
    PJ_ASSERT_RETURN(pool && neg && local, PJ_EINVAL);
287
 
 
288
 
    /* Can only do this in STATE_DONE. */
289
 
    PJ_ASSERT_RETURN(neg->state == PJMEDIA_SDP_NEG_STATE_DONE, 
290
 
                     PJMEDIA_SDPNEG_EINSTATE);
291
 
 
292
 
    /* Validate the new offer */
293
 
    status = pjmedia_sdp_validate(local);
294
 
    if (status != PJ_SUCCESS)
295
 
        return status;
296
 
 
297
 
    /* Change state to STATE_LOCAL_OFFER */
298
 
    neg->state = PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER;
299
 
 
300
 
    /* Init vars */
301
 
    pj_bzero(media_used, sizeof(media_used));
302
 
    old_offer = neg->active_local_sdp;
303
 
    new_offer = pjmedia_sdp_session_clone(pool, local);
304
 
 
305
 
    /* RFC 3264 Section 8: When issuing an offer that modifies the session,
306
 
     * the "o=" line of the new SDP MUST be identical to that in the
307
 
     * previous SDP, except that the version in the origin field MUST
308
 
     * increment by one from the previous SDP.
309
 
     */
310
 
    pj_strdup(pool, &new_offer->origin.user, &old_offer->origin.user);
311
 
    new_offer->origin.id = old_offer->origin.id;
312
 
    new_offer->origin.version = old_offer->origin.version + 1;
313
 
    pj_strdup(pool, &new_offer->origin.net_type, &old_offer->origin.net_type);
314
 
    pj_strdup(pool, &new_offer->origin.addr_type,&old_offer->origin.addr_type);
315
 
    pj_strdup(pool, &new_offer->origin.addr, &old_offer->origin.addr);
316
 
 
317
 
    /* Generating the new offer, in the case media lines doesn't match the
318
 
     * active SDP (e.g. current/active SDP's have m=audio and m=video lines, 
319
 
     * and the new offer only has m=audio line), the negotiator will fix 
320
 
     * the new offer by reordering and adding the missing media line with 
321
 
     * port number set to zero.
322
 
     */
323
 
    for (oi = 0; oi < old_offer->media_count; ++oi) {
324
 
        pjmedia_sdp_media *om;
325
 
        pjmedia_sdp_media *nm;
326
 
        unsigned ni; /* new offer media index */
327
 
        pj_bool_t found = PJ_FALSE;
328
 
 
329
 
        om = old_offer->media[oi];
330
 
        for (ni = oi; ni < new_offer->media_count; ++ni) {
331
 
            nm = new_offer->media[ni];
332
 
            if (pj_strcmp(&nm->desc.media, &om->desc.media) == 0) {
333
 
                if (ni != oi) {
334
 
                    /* The same media found but the position unmatched to the 
335
 
                     * old offer, so let's put this media in the right place, 
336
 
                     * and keep the order of the rest.
337
 
                     */
338
 
                    pj_array_insert(new_offer->media,            /* array    */
339
 
                                    sizeof(new_offer->media[0]), /* elmt size*/
340
 
                                    ni,                          /* count    */
341
 
                                    oi,                          /* pos      */
342
 
                                    &nm);                        /* new elmt */
343
 
                }
344
 
                found = PJ_TRUE;
345
 
                break;
346
 
            }
347
 
        }
348
 
        if (!found) {
349
 
            pjmedia_sdp_media *m;
350
 
 
351
 
            m = sdp_media_clone_deactivate(pool, om, om, local);
352
 
 
353
 
            pj_array_insert(new_offer->media, sizeof(new_offer->media[0]),
354
 
                            new_offer->media_count++, oi, &m);
355
 
        }
356
 
    }
357
 
 
358
 
    /* New_offer fixed */
359
 
    neg->initial_sdp = new_offer;
360
 
    neg->neg_local_sdp = pjmedia_sdp_session_clone(pool, new_offer);
361
 
 
362
 
    return PJ_SUCCESS;
363
 
}
364
 
 
365
 
 
366
 
PJ_DEF(pj_status_t) pjmedia_sdp_neg_send_local_offer( pj_pool_t *pool,
367
 
                                  pjmedia_sdp_neg *neg,
368
 
                                  const pjmedia_sdp_session **offer)
369
 
{
370
 
    /* Check arguments are valid. */
371
 
    PJ_ASSERT_RETURN(neg && offer, PJ_EINVAL);
372
 
 
373
 
    *offer = NULL;
374
 
 
375
 
    /* Can only do this in STATE_DONE or STATE_LOCAL_OFFER. */
376
 
    PJ_ASSERT_RETURN(neg->state == PJMEDIA_SDP_NEG_STATE_DONE ||
377
 
                     neg->state == PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER, 
378
 
                     PJMEDIA_SDPNEG_EINSTATE);
379
 
 
380
 
    if (neg->state == PJMEDIA_SDP_NEG_STATE_DONE) {
381
 
        /* If in STATE_DONE, set the active SDP as the offer. */
382
 
        PJ_ASSERT_RETURN(neg->active_local_sdp, PJMEDIA_SDPNEG_ENOACTIVE);
383
 
 
384
 
        neg->state = PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER;
385
 
        neg->neg_local_sdp = pjmedia_sdp_session_clone(pool, 
386
 
                                                       neg->active_local_sdp);
387
 
        *offer = neg->active_local_sdp;
388
 
 
389
 
    } else {
390
 
        /* We assume that we're in STATE_LOCAL_OFFER.
391
 
         * In this case set the neg_local_sdp as the offer.
392
 
         */
393
 
        *offer = neg->neg_local_sdp;
394
 
    }
395
 
 
396
 
    
397
 
    return PJ_SUCCESS;
398
 
}
399
 
 
400
 
 
401
 
PJ_DEF(pj_status_t) pjmedia_sdp_neg_set_remote_answer( pj_pool_t *pool,
402
 
                                   pjmedia_sdp_neg *neg,
403
 
                                   const pjmedia_sdp_session *remote)
404
 
{
405
 
    /* Check arguments are valid. */
406
 
    PJ_ASSERT_RETURN(pool && neg && remote, PJ_EINVAL);
407
 
 
408
 
    /* Can only do this in STATE_LOCAL_OFFER.
409
 
     * If we haven't provided local offer, then rx_remote_offer() should
410
 
     * be called instead of this function.
411
 
     */
412
 
    PJ_ASSERT_RETURN(neg->state == PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER, 
413
 
                     PJMEDIA_SDPNEG_EINSTATE);
414
 
 
415
 
    /* We're ready to negotiate. */
416
 
    neg->state = PJMEDIA_SDP_NEG_STATE_WAIT_NEGO;
417
 
    neg->has_remote_answer = PJ_TRUE;
418
 
    neg->neg_remote_sdp = pjmedia_sdp_session_clone(pool, remote);
419
 
 
420
 
    return PJ_SUCCESS;
421
 
}
422
 
 
423
 
PJ_DEF(pj_status_t) pjmedia_sdp_neg_set_remote_offer( pj_pool_t *pool,
424
 
                                  pjmedia_sdp_neg *neg,
425
 
                                  const pjmedia_sdp_session *remote)
426
 
{
427
 
    /* Check arguments are valid. */
428
 
    PJ_ASSERT_RETURN(pool && neg && remote, PJ_EINVAL);
429
 
 
430
 
    /* Can only do this in STATE_DONE.
431
 
     * If we already provide local offer, then rx_remote_answer() should
432
 
     * be called instead of this function.
433
 
     */
434
 
    PJ_ASSERT_RETURN(neg->state == PJMEDIA_SDP_NEG_STATE_DONE, 
435
 
                     PJMEDIA_SDPNEG_EINSTATE);
436
 
 
437
 
    /* State now is STATE_REMOTE_OFFER. */
438
 
    neg->state = PJMEDIA_SDP_NEG_STATE_REMOTE_OFFER;
439
 
    neg->neg_remote_sdp = pjmedia_sdp_session_clone(pool, remote);
440
 
 
441
 
    return PJ_SUCCESS;
442
 
}
443
 
 
444
 
PJ_DEF(pj_status_t) pjmedia_sdp_neg_set_local_answer( pj_pool_t *pool,
445
 
                                  pjmedia_sdp_neg *neg,
446
 
                                  const pjmedia_sdp_session *local)
447
 
{
448
 
    /* Check arguments are valid. */
449
 
    PJ_ASSERT_RETURN(pool && neg && local, PJ_EINVAL);
450
 
 
451
 
    /* Can only do this in STATE_REMOTE_OFFER.
452
 
     * If we already provide local offer, then rx_remote_answer() should
453
 
     * be called instead of this function.
454
 
     */
455
 
    PJ_ASSERT_RETURN(neg->state == PJMEDIA_SDP_NEG_STATE_REMOTE_OFFER, 
456
 
                     PJMEDIA_SDPNEG_EINSTATE);
457
 
 
458
 
    /* State now is STATE_WAIT_NEGO. */
459
 
    neg->state = PJMEDIA_SDP_NEG_STATE_WAIT_NEGO;
460
 
    if (local) {
461
 
        neg->neg_local_sdp = pjmedia_sdp_session_clone(pool, local);
462
 
        if (neg->initial_sdp) {
463
 
            /* I don't think there is anything in RFC 3264 that mandates
464
 
             * answerer to place the same origin (and increment version)
465
 
             * in the answer, but probably it won't hurt either.
466
 
             * Note that the version will be incremented in 
467
 
             * pjmedia_sdp_neg_negotiate()
468
 
             */
469
 
            neg->neg_local_sdp->origin.id = neg->initial_sdp->origin.id;
470
 
        } else {
471
 
            neg->initial_sdp = pjmedia_sdp_session_clone(pool, local);
472
 
        }
473
 
    } else {
474
 
        PJ_ASSERT_RETURN(neg->initial_sdp, PJMEDIA_SDPNEG_ENOINITIAL);
475
 
        neg->neg_local_sdp = pjmedia_sdp_session_clone(pool, neg->initial_sdp);
476
 
    }
477
 
 
478
 
    return PJ_SUCCESS;
479
 
}
480
 
 
481
 
PJ_DEF(pj_bool_t) pjmedia_sdp_neg_has_local_answer(pjmedia_sdp_neg *neg)
482
 
{
483
 
    pj_assert(neg && neg->state==PJMEDIA_SDP_NEG_STATE_WAIT_NEGO);
484
 
    return !neg->has_remote_answer;
485
 
}
486
 
 
487
 
 
488
 
/* Swap string. */
489
 
static void str_swap(pj_str_t *str1, pj_str_t *str2)
490
 
{
491
 
    pj_str_t tmp = *str1;
492
 
    *str1 = *str2;
493
 
    *str2 = tmp;
494
 
}
495
 
 
496
 
static void remove_all_media_directions(pjmedia_sdp_media *m)
497
 
{
498
 
    pjmedia_sdp_media_remove_all_attr(m, "inactive");
499
 
    pjmedia_sdp_media_remove_all_attr(m, "sendrecv");
500
 
    pjmedia_sdp_media_remove_all_attr(m, "sendonly");
501
 
    pjmedia_sdp_media_remove_all_attr(m, "recvonly");
502
 
}
503
 
 
504
 
/* Update media direction based on peer's media direction */
505
 
static void update_media_direction(pj_pool_t *pool,
506
 
                                   const pjmedia_sdp_media *remote,
507
 
                                   pjmedia_sdp_media *local)
508
 
{
509
 
    pjmedia_dir old_dir = PJMEDIA_DIR_ENCODING_DECODING,
510
 
                new_dir;
511
 
 
512
 
    /* Get the media direction of local SDP */
513
 
    if (pjmedia_sdp_media_find_attr2(local, "sendonly", NULL))
514
 
        old_dir = PJMEDIA_DIR_ENCODING;
515
 
    else if (pjmedia_sdp_media_find_attr2(local, "recvonly", NULL))
516
 
        old_dir = PJMEDIA_DIR_DECODING;
517
 
    else if (pjmedia_sdp_media_find_attr2(local, "inactive", NULL))
518
 
        old_dir = PJMEDIA_DIR_NONE;
519
 
 
520
 
    new_dir = old_dir;
521
 
 
522
 
    /* Adjust local media direction based on remote media direction */
523
 
    if (pjmedia_sdp_media_find_attr2(remote, "inactive", NULL) != NULL) {
524
 
        /* If remote has "a=inactive", then local is inactive too */
525
 
 
526
 
        new_dir = PJMEDIA_DIR_NONE;
527
 
 
528
 
    } else if(pjmedia_sdp_media_find_attr2(remote, "sendonly", NULL) != NULL) {
529
 
        /* If remote has "a=sendonly", then set local to "recvonly" if
530
 
         * it is currently "sendrecv". Otherwise if local is NOT "recvonly",
531
 
         * then set local direction to "inactive".
532
 
         */
533
 
        switch (old_dir) {
534
 
        case PJMEDIA_DIR_ENCODING_DECODING:
535
 
            new_dir = PJMEDIA_DIR_DECODING;
536
 
            break;
537
 
        case PJMEDIA_DIR_DECODING:
538
 
            /* No change */
539
 
            break;
540
 
        default:
541
 
            new_dir = PJMEDIA_DIR_NONE;
542
 
            break;
543
 
        }
544
 
 
545
 
    } else if(pjmedia_sdp_media_find_attr2(remote, "recvonly", NULL) != NULL) {
546
 
        /* If remote has "a=recvonly", then set local to "sendonly" if
547
 
         * it is currently "sendrecv". Otherwise if local is NOT "sendonly",
548
 
         * then set local direction to "inactive"
549
 
         */
550
 
    
551
 
        switch (old_dir) {
552
 
        case PJMEDIA_DIR_ENCODING_DECODING:
553
 
            new_dir = PJMEDIA_DIR_ENCODING;
554
 
            break;
555
 
        case PJMEDIA_DIR_ENCODING:
556
 
            /* No change */
557
 
            break;
558
 
        default:
559
 
            new_dir = PJMEDIA_DIR_NONE;
560
 
            break;
561
 
        }
562
 
 
563
 
    } else {
564
 
        /* Remote indicates "sendrecv" capability. No change to local 
565
 
         * direction 
566
 
         */
567
 
    }
568
 
 
569
 
    if (new_dir != old_dir) {
570
 
        pjmedia_sdp_attr *a = NULL;
571
 
 
572
 
        remove_all_media_directions(local);
573
 
 
574
 
        switch (new_dir) {
575
 
        case PJMEDIA_DIR_NONE:
576
 
            a = pjmedia_sdp_attr_create(pool, "inactive", NULL);
577
 
            break;
578
 
        case PJMEDIA_DIR_ENCODING:
579
 
            a = pjmedia_sdp_attr_create(pool, "sendonly", NULL);
580
 
            break;
581
 
        case PJMEDIA_DIR_DECODING:
582
 
            a = pjmedia_sdp_attr_create(pool, "recvonly", NULL);
583
 
            break;
584
 
        default:
585
 
            /* sendrecv */
586
 
            break;
587
 
        }
588
 
        
589
 
        if (a) {
590
 
            pjmedia_sdp_media_add_attr(local, a);
591
 
        }
592
 
    }
593
 
}
594
 
 
595
 
 
596
 
/* Update single local media description to after receiving answer
597
 
 * from remote.
598
 
 */
599
 
static pj_status_t process_m_answer( pj_pool_t *pool,
600
 
                                     pjmedia_sdp_media *offer,
601
 
                                     pjmedia_sdp_media *answer,
602
 
                                     pj_bool_t allow_asym)
603
 
{
604
 
    unsigned i;
605
 
 
606
 
    /* Check that the media type match our offer. */
607
 
 
608
 
    if (pj_strcmp(&answer->desc.media, &offer->desc.media)!=0) {
609
 
        /* The media type in the answer is different than the offer! */
610
 
        return PJMEDIA_SDPNEG_EINVANSMEDIA;
611
 
    }
612
 
 
613
 
 
614
 
    /* Check that transport in the answer match our offer. */
615
 
 
616
 
    /* At this point, transport type must be compatible, 
617
 
     * the transport instance will do more validation later.
618
 
     */
619
 
    if (pjmedia_sdp_transport_cmp(&answer->desc.transport, 
620
 
                                  &offer->desc.transport) 
621
 
        != PJ_SUCCESS)
622
 
    {
623
 
        return PJMEDIA_SDPNEG_EINVANSTP;
624
 
    }
625
 
 
626
 
 
627
 
    /* Check if remote has rejected our offer */
628
 
    if (answer->desc.port == 0) {
629
 
        
630
 
        /* Remote has rejected our offer. 
631
 
         * Deactivate our media too.
632
 
         */
633
 
        pjmedia_sdp_media_deactivate(pool, offer);
634
 
 
635
 
        /* Don't need to proceed */
636
 
        return PJ_SUCCESS;
637
 
    }
638
 
 
639
 
    /* Ticket #1148: check if remote answer does not set port to zero when
640
 
     * offered with port zero. Let's just tolerate it.
641
 
     */
642
 
    if (offer->desc.port == 0) {
643
 
        /* Don't need to proceed */
644
 
        return PJ_SUCCESS;
645
 
    }
646
 
 
647
 
    /* Process direction attributes */
648
 
    update_media_direction(pool, answer, offer);
649
 
 
650
 
    /* If asymetric media is allowed, then just check that remote answer has 
651
 
     * codecs that are within the offer. 
652
 
     *
653
 
     * Otherwise if asymetric media is not allowed, then we will choose only
654
 
     * one codec in our initial offer to match the answer.
655
 
     */
656
 
    if (allow_asym) {
657
 
        for (i=0; i<answer->desc.fmt_count; ++i) {
658
 
            unsigned j;
659
 
            pj_str_t *rem_fmt = &answer->desc.fmt[i];
660
 
 
661
 
            for (j=0; j<offer->desc.fmt_count; ++j) {
662
 
                if (pj_strcmp(rem_fmt, &answer->desc.fmt[j])==0)
663
 
                    break;
664
 
            }
665
 
 
666
 
            if (j != offer->desc.fmt_count) {
667
 
                /* Found at least one common codec. */
668
 
                break;
669
 
            }
670
 
        }
671
 
 
672
 
        if (i == answer->desc.fmt_count) {
673
 
            /* No common codec in the answer! */
674
 
            return PJMEDIA_SDPNEG_EANSNOMEDIA;
675
 
        }
676
 
 
677
 
        PJ_TODO(CHECK_SDP_NEGOTIATION_WHEN_ASYMETRIC_MEDIA_IS_ALLOWED);
678
 
 
679
 
    } else {
680
 
        /* Offer format priority based on answer format index/priority */
681
 
        unsigned offer_fmt_prior[PJMEDIA_MAX_SDP_FMT];
682
 
 
683
 
        /* Remove all format in the offer that has no matching answer */
684
 
        for (i=0; i<offer->desc.fmt_count;) {
685
 
            unsigned pt;
686
 
            pj_uint32_t j;
687
 
            pj_str_t *fmt = &offer->desc.fmt[i];
688
 
            
689
 
 
690
 
            /* Find matching answer */
691
 
            pt = pj_strtoul(fmt);
692
 
 
693
 
            if (pt < 96) {
694
 
                for (j=0; j<answer->desc.fmt_count; ++j) {
695
 
                    if (pj_strcmp(fmt, &answer->desc.fmt[j])==0)
696
 
                        break;
697
 
                }
698
 
            } else {
699
 
                /* This is dynamic payload type.
700
 
                 * For dynamic payload type, we must look the rtpmap and
701
 
                 * compare the encoding name.
702
 
                 */
703
 
                const pjmedia_sdp_attr *a;
704
 
                pjmedia_sdp_rtpmap or_;
705
 
 
706
 
                /* Get the rtpmap for the payload type in the offer. */
707
 
                a = pjmedia_sdp_media_find_attr2(offer, "rtpmap", fmt);
708
 
                if (!a) {
709
 
                    pj_assert(!"Bug! Offer should have been validated");
710
 
                    return PJ_EBUG;
711
 
                }
712
 
                pjmedia_sdp_attr_get_rtpmap(a, &or_);
713
 
 
714
 
                /* Find paylaod in answer SDP with matching 
715
 
                 * encoding name and clock rate.
716
 
                 */
717
 
                for (j=0; j<answer->desc.fmt_count; ++j) {
718
 
                    a = pjmedia_sdp_media_find_attr2(answer, "rtpmap", 
719
 
                                                     &answer->desc.fmt[j]);
720
 
                    if (a) {
721
 
                        pjmedia_sdp_rtpmap ar;
722
 
                        pjmedia_sdp_attr_get_rtpmap(a, &ar);
723
 
 
724
 
                        /* See if encoding name, clock rate, and channel
725
 
                         * count match 
726
 
                         */
727
 
                        if (!pj_stricmp(&or_.enc_name, &ar.enc_name) &&
728
 
                            or_.clock_rate == ar.clock_rate &&
729
 
                            (pj_stricmp(&or_.param, &ar.param)==0 ||
730
 
                             (ar.param.slen==1 && *ar.param.ptr=='1')))
731
 
                        {
732
 
                            /* Call custom format matching callbacks */
733
 
                            if (custom_fmt_match(pool, &or_.enc_name,
734
 
                                                 offer, i, answer, j, 0) ==
735
 
                                PJ_SUCCESS)
736
 
                            {
737
 
                                /* Match! */
738
 
                                break;
739
 
                            }
740
 
                        }
741
 
                    }
742
 
                }
743
 
            }
744
 
 
745
 
            if (j == answer->desc.fmt_count) {
746
 
                /* This format has no matching answer.
747
 
                 * Remove it from our offer.
748
 
                 */
749
 
                pjmedia_sdp_attr *a;
750
 
 
751
 
                /* Remove rtpmap associated with this format */
752
 
                a = pjmedia_sdp_media_find_attr2(offer, "rtpmap", fmt);
753
 
                if (a)
754
 
                    pjmedia_sdp_media_remove_attr(offer, a);
755
 
 
756
 
                /* Remove fmtp associated with this format */
757
 
                a = pjmedia_sdp_media_find_attr2(offer, "fmtp", fmt);
758
 
                if (a)
759
 
                    pjmedia_sdp_media_remove_attr(offer, a);
760
 
 
761
 
                /* Remove this format from offer's array */
762
 
                pj_array_erase(offer->desc.fmt, sizeof(offer->desc.fmt[0]),
763
 
                               offer->desc.fmt_count, i);
764
 
                --offer->desc.fmt_count;
765
 
 
766
 
            } else {
767
 
                offer_fmt_prior[i] = j;
768
 
                ++i;
769
 
            }
770
 
        }
771
 
 
772
 
        if (0 == offer->desc.fmt_count) {
773
 
            /* No common codec in the answer! */
774
 
            return PJMEDIA_SDPNEG_EANSNOMEDIA;
775
 
        }
776
 
 
777
 
        /* Post process:
778
 
         * - Resort offer formats so the order match to the answer.
779
 
         * - Remove answer formats that unmatches to the offer.
780
 
         */
781
 
        
782
 
        /* Resort offer formats */
783
 
        for (i=0; i<offer->desc.fmt_count; ++i) {
784
 
            unsigned j;
785
 
            for (j=i+1; j<offer->desc.fmt_count; ++j) {
786
 
                if (offer_fmt_prior[i] > offer_fmt_prior[j]) {
787
 
                    unsigned tmp = offer_fmt_prior[i];
788
 
                    offer_fmt_prior[i] = offer_fmt_prior[j];
789
 
                    offer_fmt_prior[j] = tmp;
790
 
                    str_swap(&offer->desc.fmt[i], &offer->desc.fmt[j]);
791
 
                }
792
 
            }
793
 
        }
794
 
 
795
 
        /* Remove unmatched answer formats */
796
 
        {
797
 
            unsigned del_cnt = 0;
798
 
            for (i=0; i<answer->desc.fmt_count;) {
799
 
                /* The offer is ordered now, also the offer_fmt_prior */
800
 
                if (i >= offer->desc.fmt_count || 
801
 
                    offer_fmt_prior[i]-del_cnt != i)
802
 
                {
803
 
                    pj_str_t *fmt = &answer->desc.fmt[i];
804
 
                    pjmedia_sdp_attr *a;
805
 
 
806
 
                    /* Remove rtpmap associated with this format */
807
 
                    a = pjmedia_sdp_media_find_attr2(answer, "rtpmap", fmt);
808
 
                    if (a)
809
 
                        pjmedia_sdp_media_remove_attr(answer, a);
810
 
 
811
 
                    /* Remove fmtp associated with this format */
812
 
                    a = pjmedia_sdp_media_find_attr2(answer, "fmtp", fmt);
813
 
                    if (a)
814
 
                        pjmedia_sdp_media_remove_attr(answer, a);
815
 
 
816
 
                    /* Remove this format from answer's array */
817
 
                    pj_array_erase(answer->desc.fmt, 
818
 
                                   sizeof(answer->desc.fmt[0]),
819
 
                                   answer->desc.fmt_count, i);
820
 
                    --answer->desc.fmt_count;
821
 
 
822
 
                    ++del_cnt;
823
 
                } else {
824
 
                    ++i;
825
 
                }
826
 
            }
827
 
        }
828
 
    }
829
 
 
830
 
    /* Looks okay */
831
 
    return PJ_SUCCESS;
832
 
}
833
 
 
834
 
 
835
 
/* Update local media session (offer) to create active local session
836
 
 * after receiving remote answer.
837
 
 */
838
 
static pj_status_t process_answer(pj_pool_t *pool,
839
 
                                  pjmedia_sdp_session *offer,
840
 
                                  pjmedia_sdp_session *answer,
841
 
                                  pj_bool_t allow_asym,
842
 
                                  pjmedia_sdp_session **p_active)
843
 
{
844
 
    unsigned omi = 0; /* Offer media index */
845
 
    unsigned ami = 0; /* Answer media index */
846
 
    pj_bool_t has_active = PJ_FALSE;
847
 
    pj_status_t status;
848
 
 
849
 
    /* Check arguments. */
850
 
    PJ_ASSERT_RETURN(pool && offer && answer && p_active, PJ_EINVAL);
851
 
 
852
 
    /* Check that media count match between offer and answer */
853
 
    // Ticket #527, different media count is allowed for more interoperability,
854
 
    // however, the media order must be same between offer and answer.
855
 
    // if (offer->media_count != answer->media_count)
856
 
    //     return PJMEDIA_SDPNEG_EMISMEDIA;
857
 
 
858
 
    /* Now update each media line in the offer with the answer. */
859
 
    for (; omi<offer->media_count; ++omi) {
860
 
        if (ami == answer->media_count) {
861
 
            /* The answer has less media than the offer */
862
 
            pjmedia_sdp_media *am;
863
 
 
864
 
            /* Generate matching-but-disabled-media for the answer */
865
 
            am = sdp_media_clone_deactivate(pool, offer->media[omi],
866
 
                                            offer->media[omi], offer);
867
 
            answer->media[answer->media_count++] = am;
868
 
            ++ami;
869
 
 
870
 
            /* Deactivate our media offer too */
871
 
            pjmedia_sdp_media_deactivate(pool, offer->media[omi]);
872
 
 
873
 
            /* No answer media to be negotiated */
874
 
            continue;
875
 
        }
876
 
 
877
 
        status = process_m_answer(pool, offer->media[omi], answer->media[ami],
878
 
                                  allow_asym);
879
 
 
880
 
        /* If media type is mismatched, just disable the media. */
881
 
        if (status == PJMEDIA_SDPNEG_EINVANSMEDIA) {
882
 
            pjmedia_sdp_media_deactivate(pool, offer->media[omi]);
883
 
            continue;
884
 
        }
885
 
        /* No common format in the answer media. */
886
 
        else if (status == PJMEDIA_SDPNEG_EANSNOMEDIA) {
887
 
            pjmedia_sdp_media_deactivate(pool, offer->media[omi]);
888
 
            pjmedia_sdp_media_deactivate(pool, answer->media[ami]);
889
 
        } 
890
 
        /* Return the error code, for other errors. */
891
 
        else if (status != PJ_SUCCESS) {
892
 
            return status;
893
 
        }
894
 
 
895
 
        if (offer->media[omi]->desc.port != 0)
896
 
            has_active = PJ_TRUE;
897
 
 
898
 
        ++ami;
899
 
    }
900
 
 
901
 
    *p_active = offer;
902
 
 
903
 
    return has_active ? PJ_SUCCESS : PJMEDIA_SDPNEG_ENOMEDIA;
904
 
}
905
 
 
906
 
 
907
 
/* Internal function to rewrite the format string in SDP attribute rtpmap
908
 
 * and fmtp.
909
 
 */
910
 
PJ_INLINE(void) rewrite_pt(pj_pool_t *pool, pj_str_t *attr_val,
911
 
                           const pj_str_t *old_pt, const pj_str_t *new_pt)
912
 
{
913
 
    int len_diff = new_pt->slen - old_pt->slen;
914
 
 
915
 
    /* Note that attribute value should be null-terminated. */
916
 
    if (len_diff > 0) {
917
 
        pj_str_t new_val;
918
 
        new_val.ptr = (char*)pj_pool_alloc(pool, attr_val->slen+len_diff+1);
919
 
        new_val.slen = attr_val->slen + len_diff;
920
 
        pj_memcpy(new_val.ptr + len_diff, attr_val->ptr, attr_val->slen + 1);
921
 
        *attr_val = new_val;
922
 
    } else if (len_diff < 0) {
923
 
        attr_val->slen += len_diff;
924
 
        pj_memmove(attr_val->ptr, attr_val->ptr - len_diff,
925
 
                   attr_val->slen + 1);
926
 
    }
927
 
    pj_memcpy(attr_val->ptr, new_pt->ptr, new_pt->slen);
928
 
}
929
 
 
930
 
 
931
 
/* Internal function to apply symmetric PT for the local answer. */
932
 
static void apply_answer_symmetric_pt(pj_pool_t *pool,
933
 
                                      pjmedia_sdp_media *answer,
934
 
                                      unsigned pt_cnt,
935
 
                                      const pj_str_t pt_offer[],
936
 
                                      const pj_str_t pt_answer[])
937
 
{
938
 
    pjmedia_sdp_attr *a_tmp[PJMEDIA_MAX_SDP_ATTR];
939
 
    unsigned i, a_tmp_cnt = 0;
940
 
 
941
 
    /* Rewrite the payload types in the answer if different to
942
 
     * the ones in the offer.
943
 
     */
944
 
    for (i = 0; i < pt_cnt; ++i) {
945
 
        pjmedia_sdp_attr *a;
946
 
 
947
 
        /* Skip if the PTs are the same already, e.g: static PT. */
948
 
        if (pj_strcmp(&pt_answer[i], &pt_offer[i]) == 0)
949
 
            continue;
950
 
 
951
 
        /* Rewrite payload type in the answer to match to the offer */
952
 
        pj_strdup(pool, &answer->desc.fmt[i], &pt_offer[i]);
953
 
 
954
 
        /* Also update payload type in rtpmap */
955
 
        a = pjmedia_sdp_media_find_attr2(answer, "rtpmap", &pt_answer[i]);
956
 
        if (a) {
957
 
            rewrite_pt(pool, &a->value, &pt_answer[i], &pt_offer[i]);
958
 
            /* Temporarily remove the attribute in case the new payload
959
 
             * type is being used by another format in the media.
960
 
             */
961
 
            pjmedia_sdp_media_remove_attr(answer, a);
962
 
            a_tmp[a_tmp_cnt++] = a;
963
 
        }
964
 
 
965
 
        /* Also update payload type in fmtp */
966
 
        a = pjmedia_sdp_media_find_attr2(answer, "fmtp", &pt_answer[i]);
967
 
        if (a) {
968
 
            rewrite_pt(pool, &a->value, &pt_answer[i], &pt_offer[i]);
969
 
            /* Temporarily remove the attribute in case the new payload
970
 
             * type is being used by another format in the media.
971
 
             */
972
 
            pjmedia_sdp_media_remove_attr(answer, a);
973
 
            a_tmp[a_tmp_cnt++] = a;
974
 
        }
975
 
    }
976
 
 
977
 
    /* Return back 'rtpmap' and 'fmtp' attributes */
978
 
    for (i = 0; i < a_tmp_cnt; ++i)
979
 
        pjmedia_sdp_media_add_attr(answer, a_tmp[i]);
980
 
}
981
 
 
982
 
 
983
 
/* Try to match offer with answer. */
984
 
static pj_status_t match_offer(pj_pool_t *pool,
985
 
                               pj_bool_t prefer_remote_codec_order,
986
 
                               const pjmedia_sdp_media *offer,
987
 
                               const pjmedia_sdp_media *preanswer,
988
 
                               const pjmedia_sdp_session *preanswer_sdp,
989
 
                               pjmedia_sdp_media **p_answer)
990
 
{
991
 
    unsigned i;
992
 
    pj_bool_t master_has_codec = 0,
993
 
              master_has_telephone_event = 0,
994
 
              master_has_other = 0,
995
 
              found_matching_codec = 0,
996
 
              found_matching_telephone_event = 0,
997
 
              found_matching_other = 0;
998
 
    unsigned pt_answer_count = 0;
999
 
    pj_str_t pt_answer[PJMEDIA_MAX_SDP_FMT];
1000
 
    pj_str_t pt_offer[PJMEDIA_MAX_SDP_FMT];
1001
 
    pjmedia_sdp_media *answer;
1002
 
    const pjmedia_sdp_media *master, *slave;
1003
 
 
1004
 
    /* If offer has zero port, just clone the offer */
1005
 
    if (offer->desc.port == 0) {
1006
 
        answer = sdp_media_clone_deactivate(pool, offer, preanswer,
1007
 
                                            preanswer_sdp);
1008
 
        *p_answer = answer;
1009
 
        return PJ_SUCCESS;
1010
 
    }
1011
 
 
1012
 
    /* If the preanswer define zero port, this media is being rejected,
1013
 
     * just clone the preanswer.
1014
 
     */
1015
 
    if (preanswer->desc.port == 0) {
1016
 
        answer = pjmedia_sdp_media_clone(pool, preanswer);
1017
 
        *p_answer = answer;
1018
 
        return PJ_SUCCESS;
1019
 
    }
1020
 
 
1021
 
    /* Set master/slave negotiator based on prefer_remote_codec_order. */
1022
 
    if (prefer_remote_codec_order) {
1023
 
        master = offer;
1024
 
        slave  = preanswer;
1025
 
    } else {
1026
 
        master = preanswer;
1027
 
        slave  = offer;
1028
 
    }
1029
 
    
1030
 
    /* With the addition of telephone-event and dodgy MS RTC SDP, 
1031
 
     * the answer generation algorithm looks really shitty...
1032
 
     */
1033
 
    for (i=0; i<master->desc.fmt_count; ++i) {
1034
 
        unsigned j;
1035
 
        
1036
 
        if (pj_isdigit(*master->desc.fmt[i].ptr)) {
1037
 
            /* This is normal/standard payload type, where it's identified
1038
 
             * by payload number.
1039
 
             */
1040
 
            unsigned pt;
1041
 
 
1042
 
            pt = pj_strtoul(&master->desc.fmt[i]);
1043
 
            
1044
 
            if (pt < 96) {
1045
 
                /* For static payload type, it's enough to compare just
1046
 
                 * the payload number.
1047
 
                 */
1048
 
 
1049
 
                master_has_codec = 1;
1050
 
 
1051
 
                /* We just need to select one codec. 
1052
 
                 * Continue if we have selected matching codec for previous 
1053
 
                 * payload.
1054
 
                 */
1055
 
                if (found_matching_codec)
1056
 
                    continue;
1057
 
 
1058
 
                /* Find matching codec in local descriptor. */
1059
 
                for (j=0; j<slave->desc.fmt_count; ++j) {
1060
 
                    unsigned p;
1061
 
                    p = pj_strtoul(&slave->desc.fmt[j]);
1062
 
                    if (p == pt && pj_isdigit(*slave->desc.fmt[j].ptr)) {
1063
 
                        found_matching_codec = 1;
1064
 
                        pt_offer[pt_answer_count] = slave->desc.fmt[j];
1065
 
                        pt_answer[pt_answer_count++] = slave->desc.fmt[j];
1066
 
                        break;
1067
 
                    }
1068
 
                }
1069
 
 
1070
 
            } else {
1071
 
                /* This is dynamic payload type.
1072
 
                 * For dynamic payload type, we must look the rtpmap and
1073
 
                 * compare the encoding name.
1074
 
                 */
1075
 
                const pjmedia_sdp_attr *a;
1076
 
                pjmedia_sdp_rtpmap or_;
1077
 
                pj_bool_t is_codec;
1078
 
 
1079
 
                /* Get the rtpmap for the payload type in the master. */
1080
 
                a = pjmedia_sdp_media_find_attr2(master, "rtpmap", 
1081
 
                                                 &master->desc.fmt[i]);
1082
 
                if (!a) {
1083
 
                    pj_assert(!"Bug! Offer should have been validated");
1084
 
                    return PJMEDIA_SDP_EMISSINGRTPMAP;
1085
 
                }
1086
 
                pjmedia_sdp_attr_get_rtpmap(a, &or_);
1087
 
 
1088
 
                if (!pj_stricmp2(&or_.enc_name, "telephone-event")) {
1089
 
                    master_has_telephone_event = 1;
1090
 
                    if (found_matching_telephone_event)
1091
 
                        continue;
1092
 
                    is_codec = 0;
1093
 
                } else {
1094
 
                    master_has_codec = 1;
1095
 
                    if (found_matching_codec)
1096
 
                        continue;
1097
 
                    is_codec = 1;
1098
 
                }
1099
 
                
1100
 
                /* Find paylaod in our initial SDP with matching 
1101
 
                 * encoding name and clock rate.
1102
 
                 */
1103
 
                for (j=0; j<slave->desc.fmt_count; ++j) {
1104
 
                    a = pjmedia_sdp_media_find_attr2(slave, "rtpmap", 
1105
 
                                                     &slave->desc.fmt[j]);
1106
 
                    if (a) {
1107
 
                        pjmedia_sdp_rtpmap lr;
1108
 
                        pjmedia_sdp_attr_get_rtpmap(a, &lr);
1109
 
 
1110
 
                        /* See if encoding name, clock rate, and
1111
 
                         * channel count  match 
1112
 
                         */
1113
 
                        if (!pj_stricmp(&or_.enc_name, &lr.enc_name) &&
1114
 
                            or_.clock_rate == lr.clock_rate &&
1115
 
                            (pj_stricmp(&or_.param, &lr.param)==0 ||
1116
 
                             (lr.param.slen==0 && or_.param.slen==1 && 
1117
 
                                                 *or_.param.ptr=='1') || 
1118
 
                             (or_.param.slen==0 && lr.param.slen==1 && 
1119
 
                                                  *lr.param.ptr=='1'))) 
1120
 
                        {
1121
 
                            /* Match! */
1122
 
                            if (is_codec) {
1123
 
                                pjmedia_sdp_media *o, *a;
1124
 
                                unsigned o_fmt_idx, a_fmt_idx;
1125
 
 
1126
 
                                o = (pjmedia_sdp_media*)offer;
1127
 
                                a = (pjmedia_sdp_media*)preanswer;
1128
 
                                o_fmt_idx = prefer_remote_codec_order? i:j;
1129
 
                                a_fmt_idx = prefer_remote_codec_order? j:i;
1130
 
 
1131
 
                                /* Call custom format matching callbacks */
1132
 
                                if (custom_fmt_match(pool, &or_.enc_name,
1133
 
                                                     o, o_fmt_idx,
1134
 
                                                     a, a_fmt_idx,
1135
 
                                                     ALLOW_MODIFY_ANSWER) !=
1136
 
                                    PJ_SUCCESS)
1137
 
                                {
1138
 
                                    continue;
1139
 
                                }
1140
 
                                found_matching_codec = 1;
1141
 
                            } else {
1142
 
                                found_matching_telephone_event = 1;
1143
 
                            }
1144
 
 
1145
 
                            pt_offer[pt_answer_count] = 
1146
 
                                                prefer_remote_codec_order?
1147
 
                                                offer->desc.fmt[i]:
1148
 
                                                offer->desc.fmt[j];
1149
 
                            pt_answer[pt_answer_count++] = 
1150
 
                                                prefer_remote_codec_order? 
1151
 
                                                preanswer->desc.fmt[j]:
1152
 
                                                preanswer->desc.fmt[i];
1153
 
                            break;
1154
 
                        }
1155
 
                    }
1156
 
                }
1157
 
            }
1158
 
 
1159
 
        } else {
1160
 
            /* This is a non-standard, brain damaged SDP where the payload
1161
 
             * type is non-numeric. It exists e.g. in Microsoft RTC based
1162
 
             * UA, to indicate instant messaging capability.
1163
 
             * Example:
1164
 
             *  - m=x-ms-message 5060 sip null
1165
 
             */
1166
 
            master_has_other = 1;
1167
 
            if (found_matching_other)
1168
 
                continue;
1169
 
 
1170
 
            for (j=0; j<slave->desc.fmt_count; ++j) {
1171
 
                if (!pj_strcmp(&master->desc.fmt[i], &slave->desc.fmt[j])) {
1172
 
                    /* Match */
1173
 
                    found_matching_other = 1;
1174
 
                    pt_offer[pt_answer_count] = prefer_remote_codec_order?
1175
 
                                                offer->desc.fmt[i]:
1176
 
                                                offer->desc.fmt[j];
1177
 
                    pt_answer[pt_answer_count++] = prefer_remote_codec_order? 
1178
 
                                                   preanswer->desc.fmt[j]:
1179
 
                                                   preanswer->desc.fmt[i];
1180
 
                    break;
1181
 
                }
1182
 
            }
1183
 
        }
1184
 
    }
1185
 
 
1186
 
    /* See if all types of master can be matched. */
1187
 
    if (master_has_codec && !found_matching_codec) {
1188
 
        return PJMEDIA_SDPNEG_NOANSCODEC;
1189
 
    }
1190
 
 
1191
 
    /* If this comment is removed, negotiation will fail if remote has offered
1192
 
       telephone-event and local is not configured with telephone-event
1193
 
 
1194
 
    if (offer_has_telephone_event && !found_matching_telephone_event) {
1195
 
        return PJMEDIA_SDPNEG_NOANSTELEVENT;
1196
 
    }
1197
 
    */
1198
 
 
1199
 
    if (master_has_other && !found_matching_other) {
1200
 
        return PJMEDIA_SDPNEG_NOANSUNKNOWN;
1201
 
    }
1202
 
 
1203
 
    /* Seems like everything is in order.
1204
 
     * Build the answer by cloning from preanswer, but rearrange the payload
1205
 
     * to suit the offer.
1206
 
     */
1207
 
    answer = pjmedia_sdp_media_clone(pool, preanswer);
1208
 
    for (i=0; i<pt_answer_count; ++i) {
1209
 
        unsigned j;
1210
 
        for (j=i; j<answer->desc.fmt_count; ++j) {
1211
 
            if (!pj_strcmp(&answer->desc.fmt[j], &pt_answer[i]))
1212
 
                break;
1213
 
        }
1214
 
        pj_assert(j != answer->desc.fmt_count);
1215
 
        str_swap(&answer->desc.fmt[i], &answer->desc.fmt[j]);
1216
 
    }
1217
 
    
1218
 
    /* Remove unwanted local formats. */
1219
 
    for (i=pt_answer_count; i<answer->desc.fmt_count; ++i) {
1220
 
        pjmedia_sdp_attr *a;
1221
 
 
1222
 
        /* Remove rtpmap for this format */
1223
 
        a = pjmedia_sdp_media_find_attr2(answer, "rtpmap", 
1224
 
                                         &answer->desc.fmt[i]);
1225
 
        if (a) {
1226
 
            pjmedia_sdp_media_remove_attr(answer, a);
1227
 
        }
1228
 
 
1229
 
        /* Remove fmtp for this format */
1230
 
        a = pjmedia_sdp_media_find_attr2(answer, "fmtp", 
1231
 
                                         &answer->desc.fmt[i]);
1232
 
        if (a) {
1233
 
            pjmedia_sdp_media_remove_attr(answer, a);
1234
 
        }
1235
 
    }
1236
 
    answer->desc.fmt_count = pt_answer_count;
1237
 
 
1238
 
#if PJMEDIA_SDP_NEG_ANSWER_SYMMETRIC_PT
1239
 
    apply_answer_symmetric_pt(pool, answer, pt_answer_count,
1240
 
                              pt_offer, pt_answer);
1241
 
#endif
1242
 
 
1243
 
    /* Update media direction. */
1244
 
    update_media_direction(pool, offer, answer);
1245
 
 
1246
 
    *p_answer = answer;
1247
 
    return PJ_SUCCESS;
1248
 
}
1249
 
 
1250
 
/* Create complete answer for remote's offer. */
1251
 
static pj_status_t create_answer( pj_pool_t *pool,
1252
 
                                  pj_bool_t prefer_remote_codec_order,
1253
 
                                  const pjmedia_sdp_session *initial,
1254
 
                                  const pjmedia_sdp_session *offer,
1255
 
                                  pjmedia_sdp_session **p_answer)
1256
 
{
1257
 
    pj_status_t status = PJMEDIA_SDPNEG_ENOMEDIA;
1258
 
    pj_bool_t has_active = PJ_FALSE;
1259
 
    pjmedia_sdp_session *answer;
1260
 
    char media_used[PJMEDIA_MAX_SDP_MEDIA];
1261
 
    unsigned i;
1262
 
 
1263
 
    /* Validate remote offer. 
1264
 
     * This should have been validated before.
1265
 
     */
1266
 
    PJ_ASSERT_RETURN((status=pjmedia_sdp_validate(offer))==PJ_SUCCESS, status);
1267
 
 
1268
 
    /* Create initial answer by duplicating initial SDP,
1269
 
     * but clear all media lines. The media lines will be filled up later.
1270
 
     */
1271
 
    answer = pjmedia_sdp_session_clone(pool, initial);
1272
 
    PJ_ASSERT_RETURN(answer != NULL, PJ_ENOMEM);
1273
 
 
1274
 
    answer->media_count = 0;
1275
 
 
1276
 
    pj_bzero(media_used, sizeof(media_used));
1277
 
 
1278
 
    /* For each media line, create our answer based on our initial
1279
 
     * capability.
1280
 
     */
1281
 
    for (i=0; i<offer->media_count; ++i) {
1282
 
        const pjmedia_sdp_media *om;    /* offer */
1283
 
        const pjmedia_sdp_media *im;    /* initial media */
1284
 
        pjmedia_sdp_media *am = NULL;   /* answer/result */
1285
 
        unsigned j;
1286
 
 
1287
 
        om = offer->media[i];
1288
 
 
1289
 
        /* Find media description in our initial capability that matches
1290
 
         * the media type and transport type of offer's media, has
1291
 
         * matching codec, and has not been used to answer other offer.
1292
 
         */
1293
 
        for (im=NULL, j=0; j<initial->media_count; ++j) {
1294
 
            im = initial->media[j];
1295
 
            if (pj_strcmp(&om->desc.media, &im->desc.media)==0 &&
1296
 
                pj_strcmp(&om->desc.transport, &im->desc.transport)==0 &&
1297
 
                media_used[j] == 0)
1298
 
            {
1299
 
                pj_status_t status2;
1300
 
 
1301
 
                /* See if it has matching codec. */
1302
 
                status2 = match_offer(pool, prefer_remote_codec_order, 
1303
 
                                      om, im, initial, &am);
1304
 
                if (status2 == PJ_SUCCESS) {
1305
 
                    /* Mark media as used. */
1306
 
                    media_used[j] = 1;
1307
 
                    break;
1308
 
                } else {
1309
 
                    status = status2;
1310
 
                }
1311
 
            }
1312
 
        }
1313
 
 
1314
 
        if (j==initial->media_count) {
1315
 
            /* No matching media.
1316
 
             * Reject the offer by setting the port to zero in the answer.
1317
 
             */
1318
 
            /* For simplicity in the construction of the answer, we'll
1319
 
             * just clone the media from the offer. Anyway receiver will
1320
 
             * ignore anything in the media once it sees that the port
1321
 
             * number is zero.
1322
 
             */
1323
 
            am = sdp_media_clone_deactivate(pool, om, om, answer);
1324
 
        } else {
1325
 
            /* The answer is in am */
1326
 
            pj_assert(am != NULL);
1327
 
        }
1328
 
 
1329
 
        /* Add the media answer */
1330
 
        answer->media[answer->media_count++] = am;
1331
 
 
1332
 
        /* Check if this media is active.*/
1333
 
        if (am->desc.port != 0)
1334
 
            has_active = PJ_TRUE;
1335
 
    }
1336
 
 
1337
 
    *p_answer = answer;
1338
 
 
1339
 
    return has_active ? PJ_SUCCESS : status;
1340
 
}
1341
 
 
1342
 
/* Cancel offer */
1343
 
PJ_DEF(pj_status_t) pjmedia_sdp_neg_cancel_offer(pjmedia_sdp_neg *neg)
1344
 
{
1345
 
    PJ_ASSERT_RETURN(neg, PJ_EINVAL);
1346
 
 
1347
 
    /* Must be in LOCAL_OFFER state. */
1348
 
    PJ_ASSERT_RETURN(neg->state == PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER ||
1349
 
                     neg->state == PJMEDIA_SDP_NEG_STATE_REMOTE_OFFER,
1350
 
                     PJMEDIA_SDPNEG_EINSTATE);
1351
 
 
1352
 
    /* Reset state to done */
1353
 
    neg->state = PJMEDIA_SDP_NEG_STATE_DONE;
1354
 
 
1355
 
    /* Clear temporary SDP */
1356
 
    neg->neg_local_sdp = neg->neg_remote_sdp = NULL;
1357
 
    neg->has_remote_answer = PJ_FALSE;
1358
 
 
1359
 
    return PJ_SUCCESS;
1360
 
}
1361
 
 
1362
 
 
1363
 
/* The best bit: SDP negotiation function! */
1364
 
PJ_DEF(pj_status_t) pjmedia_sdp_neg_negotiate( pj_pool_t *pool,
1365
 
                                               pjmedia_sdp_neg *neg,
1366
 
                                               pj_bool_t allow_asym)
1367
 
{
1368
 
    pj_status_t status;
1369
 
 
1370
 
    /* Check arguments are valid. */
1371
 
    PJ_ASSERT_RETURN(pool && neg, PJ_EINVAL);
1372
 
 
1373
 
    /* Must be in STATE_WAIT_NEGO state. */
1374
 
    PJ_ASSERT_RETURN(neg->state == PJMEDIA_SDP_NEG_STATE_WAIT_NEGO, 
1375
 
                     PJMEDIA_SDPNEG_EINSTATE);
1376
 
 
1377
 
    /* Must have remote offer. */
1378
 
    PJ_ASSERT_RETURN(neg->neg_remote_sdp, PJ_EBUG);
1379
 
 
1380
 
    if (neg->has_remote_answer) {
1381
 
        pjmedia_sdp_session *active;
1382
 
        status = process_answer(pool, neg->neg_local_sdp, neg->neg_remote_sdp,
1383
 
                                allow_asym, &active);
1384
 
        if (status == PJ_SUCCESS) {
1385
 
            /* Only update active SDPs when negotiation is successfull */
1386
 
            neg->active_local_sdp = active;
1387
 
            neg->active_remote_sdp = neg->neg_remote_sdp;
1388
 
        }
1389
 
    } else {
1390
 
        pjmedia_sdp_session *answer = NULL;
1391
 
 
1392
 
        status = create_answer(pool, neg->prefer_remote_codec_order, 
1393
 
                               neg->neg_local_sdp, neg->neg_remote_sdp,
1394
 
                               &answer);
1395
 
        if (status == PJ_SUCCESS) {
1396
 
            pj_uint32_t active_ver;
1397
 
 
1398
 
            if (neg->active_local_sdp)
1399
 
                active_ver = neg->active_local_sdp->origin.version;
1400
 
            else
1401
 
                active_ver = neg->initial_sdp->origin.version;
1402
 
 
1403
 
            /* Only update active SDPs when negotiation is successfull */
1404
 
            neg->active_local_sdp = answer;
1405
 
            neg->active_remote_sdp = neg->neg_remote_sdp;
1406
 
 
1407
 
            /* Increment SDP version */
1408
 
            neg->active_local_sdp->origin.version = ++active_ver;
1409
 
        }
1410
 
    }
1411
 
 
1412
 
    /* State is DONE regardless */
1413
 
    neg->state = PJMEDIA_SDP_NEG_STATE_DONE;
1414
 
 
1415
 
    /* Save state */
1416
 
    neg->answer_was_remote = neg->has_remote_answer;
1417
 
 
1418
 
    /* Clear temporary SDP */
1419
 
    neg->neg_local_sdp = neg->neg_remote_sdp = NULL;
1420
 
    neg->has_remote_answer = PJ_FALSE;
1421
 
 
1422
 
    return status;
1423
 
}
1424
 
 
1425
 
 
1426
 
static pj_status_t custom_fmt_match(pj_pool_t *pool,
1427
 
                                    const pj_str_t *fmt_name,
1428
 
                                    pjmedia_sdp_media *offer,
1429
 
                                    unsigned o_fmt_idx,
1430
 
                                    pjmedia_sdp_media *answer,
1431
 
                                    unsigned a_fmt_idx,
1432
 
                                    unsigned option)
1433
 
{
1434
 
    unsigned i;
1435
 
 
1436
 
    for (i = 0; i < fmt_match_cb_cnt; ++i) {
1437
 
        if (pj_stricmp(fmt_name, &fmt_match_cb[i].fmt_name) == 0) {
1438
 
            pj_assert(fmt_match_cb[i].cb);
1439
 
            return (*fmt_match_cb[i].cb)(pool, offer, o_fmt_idx,
1440
 
                                         answer, a_fmt_idx,
1441
 
                                         option);
1442
 
        }
1443
 
    }
1444
 
 
1445
 
    /* Not customized format matching found, should be matched */
1446
 
    return PJ_SUCCESS;
1447
 
}
1448
 
 
1449
 
/* Register customized SDP format negotiation callback function. */
1450
 
PJ_DECL(pj_status_t) pjmedia_sdp_neg_register_fmt_match_cb(
1451
 
                                        const pj_str_t *fmt_name,
1452
 
                                        pjmedia_sdp_neg_fmt_match_cb cb)
1453
 
{
1454
 
    struct fmt_match_cb_t *f = NULL;
1455
 
    unsigned i;
1456
 
 
1457
 
    PJ_ASSERT_RETURN(fmt_name, PJ_EINVAL);
1458
 
 
1459
 
    /* Check if the callback for the format name has been registered */
1460
 
    for (i = 0; i < fmt_match_cb_cnt; ++i) {
1461
 
        if (pj_stricmp(fmt_name, &fmt_match_cb[i].fmt_name) == 0)
1462
 
            break;
1463
 
    }
1464
 
 
1465
 
    /* Unregistration */
1466
 
    
1467
 
    if (cb == NULL) {
1468
 
        if (i == fmt_match_cb_cnt)
1469
 
            return PJ_ENOTFOUND;
1470
 
 
1471
 
        pj_array_erase(fmt_match_cb, sizeof(fmt_match_cb[0]),
1472
 
                       fmt_match_cb_cnt, i);
1473
 
        fmt_match_cb_cnt--;
1474
 
 
1475
 
        return PJ_SUCCESS;
1476
 
    }
1477
 
 
1478
 
    /* Registration */
1479
 
 
1480
 
    if (i < fmt_match_cb_cnt) {
1481
 
        /* The same format name has been registered before */
1482
 
        if (cb != fmt_match_cb[i].cb)
1483
 
            return PJ_EEXISTS;
1484
 
        else
1485
 
            return PJ_SUCCESS;
1486
 
    }
1487
 
 
1488
 
    if (fmt_match_cb_cnt >= PJ_ARRAY_SIZE(fmt_match_cb))
1489
 
        return PJ_ETOOMANY;
1490
 
 
1491
 
    f = &fmt_match_cb[fmt_match_cb_cnt++];
1492
 
    f->fmt_name = *fmt_name;
1493
 
    f->cb = cb;
1494
 
 
1495
 
    return PJ_SUCCESS;
1496
 
}
1497
 
 
1498
 
 
1499
 
/* Match format in the SDP media offer and answer. */
1500
 
PJ_DEF(pj_status_t) pjmedia_sdp_neg_fmt_match(pj_pool_t *pool,
1501
 
                                              pjmedia_sdp_media *offer,
1502
 
                                              unsigned o_fmt_idx,
1503
 
                                              pjmedia_sdp_media *answer,
1504
 
                                              unsigned a_fmt_idx,
1505
 
                                              unsigned option)
1506
 
{
1507
 
    const pjmedia_sdp_attr *attr;
1508
 
    pjmedia_sdp_rtpmap o_rtpmap, a_rtpmap;
1509
 
    unsigned o_pt;
1510
 
    unsigned a_pt;
1511
 
 
1512
 
    o_pt = pj_strtoul(&offer->desc.fmt[o_fmt_idx]);
1513
 
    a_pt = pj_strtoul(&answer->desc.fmt[a_fmt_idx]);
1514
 
 
1515
 
    if (o_pt < 96 || a_pt < 96) {
1516
 
        if (o_pt == a_pt)
1517
 
            return PJ_SUCCESS;
1518
 
        else
1519
 
            return PJMEDIA_SDP_EFORMATNOTEQUAL;
1520
 
    }
1521
 
 
1522
 
    /* Get the format rtpmap from the offer. */
1523
 
    attr = pjmedia_sdp_media_find_attr2(offer, "rtpmap", 
1524
 
                                        &offer->desc.fmt[o_fmt_idx]);
1525
 
    if (!attr) {
1526
 
        pj_assert(!"Bug! Offer haven't been validated");
1527
 
        return PJ_EBUG;
1528
 
    }
1529
 
    pjmedia_sdp_attr_get_rtpmap(attr, &o_rtpmap);
1530
 
 
1531
 
    /* Get the format rtpmap from the answer. */
1532
 
    attr = pjmedia_sdp_media_find_attr2(answer, "rtpmap", 
1533
 
                                        &answer->desc.fmt[a_fmt_idx]);
1534
 
    if (!attr) {
1535
 
        pj_assert(!"Bug! Answer haven't been validated");
1536
 
        return PJ_EBUG;
1537
 
    }
1538
 
    pjmedia_sdp_attr_get_rtpmap(attr, &a_rtpmap);
1539
 
 
1540
 
    if (pj_stricmp(&o_rtpmap.enc_name, &a_rtpmap.enc_name) != 0 ||
1541
 
        o_rtpmap.clock_rate != a_rtpmap.clock_rate)
1542
 
    {
1543
 
        return PJMEDIA_SDP_EFORMATNOTEQUAL;
1544
 
    }
1545
 
 
1546
 
    return custom_fmt_match(pool, &o_rtpmap.enc_name,
1547
 
                            offer, o_fmt_idx, answer, a_fmt_idx, option);
1548
 
}
1549