sdp.cpp 16.6 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
/*
 *  Copyright (C) 2009 Savoir-Faire Linux inc.
 *
 *  Author: Emmanuel Milou <emmanuel.milou@savoirfairelinux.com>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include "sdp.h"
#include "global.h"
23
#include "manager.h"
24 25


26 27 28 29 30 31 32 33 34 35 36 37
static const pj_str_t STR_AUDIO = { (char*) "audio", 5};
static const pj_str_t STR_VIDEO = { (char*) "video", 5};
static const pj_str_t STR_IN = { (char*) "IN", 2 };
static const pj_str_t STR_IP4 = { (char*) "IP4", 3};
static const pj_str_t STR_IP6 = { (char*) "IP6", 3};
static const pj_str_t STR_RTP_AVP = { (char*) "RTP/AVP", 7 };
static const pj_str_t STR_SDP_NAME = { (char*) "sflphone", 8 };
static const pj_str_t STR_SENDRECV = { (char*) "sendrecv", 8 };
static const pj_str_t STR_RTPMAP = { (char*) "rtpmap", 6 };


Sdp::Sdp (pj_pool_t *pool)
38 39 40 41 42 43 44 45
		: _local_media_cap()
		, _session_media (0)
		, _negociator (NULL)
		, _ip_addr ("")
		, _local_offer (NULL)
		, _negociated_offer (NULL)
		, _pool (NULL)
		, _local_extern_audio_port (0)
46 47 48 49
{
    _pool = pool;
}

50 51
Sdp::~Sdp()
{
52

53
    //unsigned int k;
54

55
    /*
56 57 58
    for( k=0; k<_session_media.size(); k++ ){
        delete _session_media[k];
        _session_media[k] = 0;
59
    }*/
60 61

    //for( k=0; k<_local_media_cap.size(); k++ ){
62 63
    //  delete _local_media_cap[k];
    //_local_media_cap[k] = 0;
64
    //}
65
}
66

67 68
void Sdp::set_media_descriptor_line (sdpMedia *media, pjmedia_sdp_media** p_med)
{
69

70 71 72 73 74
    pjmedia_sdp_media* med;
    pjmedia_sdp_rtpmap rtpmap;
    pjmedia_sdp_attr *attr;
    AudioCodec *codec;
    int count, i;
75
    std::string tmp;
76

77
    med = PJ_POOL_ZALLOC_T (_pool, pjmedia_sdp_media);
78

79
    // Get the right media format
80 81
    pj_strdup (_pool, &med->desc.media,
               (media->get_media_type() == MIME_TYPE_AUDIO) ? &STR_AUDIO : &STR_VIDEO);
82
    med->desc.port_count = 1;
83
    med->desc.port = media->get_port();
84
    pj_strdup (_pool, &med->desc.transport, &STR_RTP_AVP);
85

86 87 88
    // Media format ( RTP payload )
    count = media->get_media_codec_list().size();
    med->desc.fmt_count = count;
89

90
    // add the payload list
91 92 93 94

    for (i=0; i<count; i++) {
        codec = media->get_media_codec_list() [i];
        tmp = this->convert_int_to_string (codec->getPayload ());
95
        _debug ("%s\n", tmp.c_str());
96
        pj_strdup2 (_pool, &med->desc.fmt[i], tmp.c_str());
97 98 99 100 101 102

        // Add a rtpmap field for each codec
        // We could add one only for dynamic payloads because the codecs with static RTP payloads
        // are entirely defined in the RFC 3351, but if we want to add other attributes like an asymmetric
        // connection, the rtpmap attribute will be useful to specify for which codec it is applicable
        rtpmap.pt = med->desc.fmt[i];
103
        rtpmap.enc_name = pj_str ( (char*) codec->getCodecName().c_str());
104 105
        rtpmap.clock_rate = codec->getClockRate();
        // Add the channel number only if different from 1
106 107 108

        if (codec->getChannel() > 1)
            rtpmap.param = pj_str ( (char*) codec->getChannel());
109 110
        else
            rtpmap.param.slen = 0;
111 112 113

        pjmedia_sdp_rtpmap_to_attr (_pool, &rtpmap, &attr);

114
        med->attr[med->attr_count++] = attr;
115
    }
116

117
    // Add the direction stream
118 119 120 121
    attr = (pjmedia_sdp_attr*) pj_pool_zalloc (_pool, sizeof (pjmedia_sdp_attr));

    pj_strdup2 (_pool, &attr->name, media->get_stream_direction_str().c_str());

122
    med->attr[ med->attr_count++] = attr;
123

124 125
    *p_med = med;
}
126

127 128
int Sdp::create_local_offer ()
{
129
    pj_status_t status;
130

131
    _debug ("Create local offer\n");
132 133
    // Build local media capabilities
    set_local_media_capabilities ();
134

135
    // Reference: RFC 4566 [5]
136

137
    /* Create and initialize basic SDP session */
138 139
    this->_local_offer = PJ_POOL_ZALLOC_T (_pool, pjmedia_sdp_session);
    this->_local_offer->conn = PJ_POOL_ZALLOC_T (_pool, pjmedia_sdp_conn);
140

141 142 143 144 145 146 147
    /* Initialize the fields of the struct */
    sdp_add_protocol();
    sdp_add_origin();
    sdp_add_session_name();
    sdp_add_connection_info();
    sdp_add_timing();
    //sdp_addAttributes( _pool );
148
    sdp_add_media_description();
149

150
    //toString ();
151

152
    // Validate the sdp session
153 154
    status = pjmedia_sdp_validate (this->_local_offer);

155 156
    if (status != PJ_SUCCESS)
        return status;
157

158 159
    return PJ_SUCCESS;
}
160

161 162
int Sdp::create_initial_offer()
{
163 164
    pj_status_t status;
    pjmedia_sdp_neg_state state;
165

166
    _debug ("Create initial offer\n");
167
    // Build the SDP session descriptor
168 169
    status = create_local_offer();

170
    if (status != PJ_SUCCESS) {
171
        return status;
172
    }
173

174
    // Create the SDP negociator instance with local offer
175
    status = pjmedia_sdp_neg_create_w_local_offer (_pool, get_local_sdp_session(), &_negociator);
176

177 178 179
    state = pjmedia_sdp_neg_get_state (_negociator);

    PJ_ASSERT_RETURN (status == PJ_SUCCESS, 1);
180

181
    return PJ_SUCCESS;
182 183
}

184 185
int Sdp::receiving_initial_offer (pjmedia_sdp_session* remote)
{
186 187 188 189
    // Create the SDP negociator instance by calling
    // pjmedia_sdp_neg_create_w_remote_offer with the remote offer, and by providing the local offer ( optional )

    pj_status_t status;
190

191 192
    _debug ("Receiving initial offer\n");

193 194 195 196
    // Create the SDP negociator instance by calling
    // pjmedia_sdp_neg_create_w_remote_offer with the remote offer, and by providing the local offer ( optional )

    // Build the local offer to respond
197 198
    status = create_local_offer();

199
    if (status != PJ_SUCCESS) {
200
        return status;
201
    }
202

Emmanuel Milou's avatar
Emmanuel Milou committed
203
    // Retrieve some useful remote information
204
    this->set_media_transport_info_from_remote_sdp (remote);
Emmanuel Milou's avatar
Emmanuel Milou committed
205

206 207 208 209
    status = pjmedia_sdp_neg_create_w_remote_offer (_pool,
             get_local_sdp_session(), remote, &_negociator);

    PJ_ASSERT_RETURN (status == PJ_SUCCESS, 1);
210

211
    return PJ_SUCCESS;
212 213
}

214
pj_status_t Sdp::check_sdp_answer(pjsip_inv_session *inv, pjsip_rx_data *rdata) 
215
{
216 217
    static const pj_str_t str_application = { (char*) "application", 11 };
    static const pj_str_t str_sdp = { (char*) "sdp", 3 };
218
    pj_status_t status;
219 220
    pjsip_msg * message = NULL;
    pjmedia_sdp_session * remote_sdp = NULL;
221
    
222
    if (pjmedia_sdp_neg_get_state(inv->neg) == PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER) {
223
    
224
        message = rdata->msg_info.msg;
225
    
226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250
        if(message == NULL) {
            _debug("No message");
            return PJMEDIA_SDP_EINSDP;
        }

        if (message->body == NULL) {
            _debug("Empty message body\n");
            return PJMEDIA_SDP_EINSDP;
        }

        if (pj_stricmp(&message->body->content_type.type, &str_application) || pj_stricmp(&message->body->content_type.subtype, &str_sdp)) {
            _debug("Incoming Message does not contain SDP\n");
            return PJMEDIA_SDP_EINSDP;
        }

        // Parse the SDP body.
        status = pjmedia_sdp_parse(rdata->tp_info.pool, (char*)message->body->data, message->body->len, &remote_sdp);
        if (status == PJ_SUCCESS) {
            status = pjmedia_sdp_validate(remote_sdp);
        }

        if (status != PJ_SUCCESS) {
            _debug("SDP cannot be validated\n");
            return PJMEDIA_SDP_EINSDP;
        }
251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270
    
        // This is an answer
        _debug("Got SDP answer %s\n", pjsip_rx_data_get_info(rdata));
        status = pjmedia_sdp_neg_set_remote_answer(inv->pool, inv->neg, remote_sdp);
        
        if (status != PJ_SUCCESS) {
            _debug("An error occured while processing remote answer %s\n", pjsip_rx_data_get_info(rdata));
            return PJMEDIA_SDP_EINSDP;
        }
        
        // Prefer our codecs to remote when possible
        pjmedia_sdp_neg_set_prefer_remote_codec_order(inv->neg, 0);
        
        status = pjmedia_sdp_neg_negotiate(inv->pool, inv->neg, 0);
        _debug("Negotiation returned with status %d PJ_SUCCESS being %d\n", status, PJ_SUCCESS); 
    }
    
    return status;
}

271 272
void Sdp::sdp_add_protocol (void)
{
273 274 275
    this->_local_offer->origin.version = 0;
}

276 277
void Sdp::sdp_add_origin (void)
{
278
    pj_time_val tv;
279
    pj_gettimeofday (&tv);
280

281
    this->_local_offer->origin.user = pj_str (pj_gethostname()->ptr);
282
    // Use Network Time Protocol format timestamp to ensure uniqueness.
283
    this->_local_offer->origin.id = tv.sec + 2208988800UL;
284
    // The type of network ( IN for INternet )
285
    this->_local_offer->origin.net_type = STR_IN;
286
    // The type of address
287
    this->_local_offer->origin.addr_type = STR_IP4;
288
    // The address of the machine from which the session was created
289
    this->_local_offer->origin.addr = pj_str ( (char*) _ip_addr.c_str());
290 291
}

292 293
void Sdp::sdp_add_session_name (void)
{
294 295
    this->_local_offer->name = STR_SDP_NAME;
}
296

297

298 299
void Sdp::sdp_add_connection_info (void)
{
300 301 302 303
    this->_local_offer->conn->net_type = _local_offer->origin.net_type;
    this->_local_offer->conn->addr_type = _local_offer->origin.addr_type;
    this->_local_offer->conn->addr = _local_offer->origin.addr;
}
304

305

306 307
void Sdp::sdp_add_timing (void)
{
308 309 310
    // RFC 3264: An offer/answer model session description protocol
    // As the session is created and destroyed through an external signaling mean (SIP), the line
    // should have a value of "0 0".
311

312 313
    this->_local_offer->time.start = this->_local_offer->time.stop = 0;
}
314

315 316
void Sdp::sdp_add_attributes()
{
317 318
    pjmedia_sdp_attr *a;
    this->_local_offer->attr_count = 1;
319
    a =  PJ_POOL_ZALLOC_T (_pool, pjmedia_sdp_attr);
320 321 322
    a->name=STR_SENDRECV;
    _local_offer->attr[0] = a;
}
323

324

325 326
void Sdp::sdp_add_media_description()
{
327 328
    pjmedia_sdp_media* med;
    int nb_media, i;
329

330
    med = PJ_POOL_ZALLOC_T (_pool, pjmedia_sdp_media);
331 332
    nb_media = get_local_media_cap().size();
    this->_local_offer->media_count = nb_media;
333

334 335
    for (i=0; i<nb_media; i++) {
        set_media_descriptor_line (get_local_media_cap() [i], &med);
336
        this->_local_offer->media[i] = med;
337 338 339
    }
}

340

341 342
std::string Sdp::media_to_string (void)
{
343 344
    int size, i;
    std::ostringstream res;
345

346
    size = _local_media_cap.size();
347 348

    for (i = 0; i < size ; i++) {
349
        res << _local_media_cap[i]->to_string();
350
    }
351

352
    res << std::endl;
353

354
    return res.str();
355 356
}

357 358
void Sdp::clean_session_media()
{
359
    _session_media.clear();
360 361
}

362
void Sdp::set_negotiated_sdp (const pjmedia_sdp_session *sdp)
363
{
364

365 366 367 368 369 370 371
    int nb_media, nb_codecs;
    int i,j, port;
    pjmedia_sdp_media *current;
    sdpMedia *media;
    std::string type, dir;
    CodecsMap codecs_list;
    CodecsMap::iterator iter;
372
    pjmedia_sdp_attr *attribute;
373
    pjmedia_sdp_rtpmap *rtpmap;
374

375
    _negociated_offer = (pjmedia_sdp_session*) sdp;
376

377 378 379 380
    codecs_list = Manager::instance().getCodecDescriptorMap().getCodecsMap();

    // retrieve the media information
    nb_media = _negociated_offer->media_count;
381 382 383

    for (i=0; i<nb_media ; i++) {
        // Retrieve the media
384 385 386
        current = _negociated_offer->media[i];
        type = current->desc.media.ptr;
        port = current->desc.port;
387
        media = new sdpMedia (type, port);
388 389
        // Retrieve the payload
        nb_codecs = current->desc.fmt_count;  // Must be one
390 391 392 393 394 395 396

        for (j=0 ; j<nb_codecs ; j++) {
            attribute = pjmedia_sdp_media_find_attr (current, &STR_RTPMAP, NULL);
            // pj_strtoul(attribute->pt)
            pjmedia_sdp_attr_to_rtpmap (_pool, attribute, &rtpmap);

            // _debug("================== set_negociated_offer ===================== %i\n", pj_strtoul(&rtpmap->pt));
397
            // _debug("================== set_negociated_offer ===================== %s\n", current->desc.fmt[j].ptr);
398 399 400
            // _debug("================== set_negociated_offer ===================== %i\n", atoi(current->desc.fmt[j].ptr));
            iter = codecs_list.find ( (AudioCodecType) pj_strtoul (&rtpmap->pt));

401 402
            if (iter==codecs_list.end())
                return;
403 404

            media->add_codec (iter->second);
405
        }
406 407

        _session_media.push_back (media);
408 409
    }
}
410

411 412
AudioCodec* Sdp::get_session_media (void)
{
413

414 415
    int nb_media;
    int nb_codec;
416
    AudioCodec *codec = NULL;
417
    std::vector<sdpMedia*> media_list;
418

419
    _debug ("sdp line %d - get_session_media ()\n", __LINE__);
420 421 422

    media_list = get_session_media_list ();
    nb_media = media_list.size();
423

424
    if (nb_media > 0) {
425
        nb_codec = media_list[0]->get_media_codec_list().size();
426

427
        if (nb_codec > 0) {
428
            codec = media_list[0]->get_media_codec_list() [0];
429 430
        }
    }
431

432
    return codec;
433 434
}

435

436

437 438
void Sdp::toString (void)
{
439 440

    std::ostringstream sdp;
441
    int count, i;
442

443 444 445 446 447
    sdp <<  "origin= " <<  _local_offer->origin.user.ptr << "\n";
    sdp << "origin.id= " << _local_offer->origin.id << "\n";
    sdp << "origin.version= " << _local_offer->origin.version<< "\n";
    sdp << "origin.net_type= " << _local_offer->origin.net_type.ptr<< "\n";
    sdp << "origin.addr_type= " << _local_offer->origin.addr_type.ptr<< "\n";
448

449
    sdp << "name=" << _local_offer->name.ptr<< "\n";
450

451 452 453
    sdp << "conn.net_type=" << _local_offer->conn->net_type.ptr<< "\n";
    sdp << "conn.addr_type=" << _local_offer->conn->addr_type.ptr<< "\n";
    sdp << "conn.addr=" << _local_offer->conn->addr.ptr<< "\n";
454

455 456
    sdp << "start=" <<_local_offer->time.start<< "\n";
    sdp << "stop=" <<_local_offer->time.stop<< "\n";
457

458 459
    sdp << "attr_count=" << _local_offer->attr_count << "\n";
    sdp << "media_count=" << _local_offer->media_count << "\n";
460 461 462 463
    sdp << "m=" << _local_offer->media[0]->desc.media.ptr << " ";
    sdp << _local_offer->media[0]->desc.port << " ";
    sdp << _local_offer->media[0]->desc.transport.ptr << " ";
    count = _local_offer->media[0]->desc.fmt_count;
464

465 466 467
    for (i=0; i<count; i++) {
        sdp << _local_offer->media[0]->desc.fmt[i].ptr << " ";
    }
468

469
    sdp << "\n";
470

471 472 473 474
    _debug ("LOCAL SDP: \n%s\n", sdp.str().c_str());

}

475 476
void Sdp::set_local_media_capabilities ()
{
477

478
    CodecOrder selected_codecs;
479
    unsigned int i;
480 481 482
    sdpMedia *audio;
    CodecsMap codecs_list;
    CodecsMap::iterator iter;
483

484 485 486
    // Clean it first
    _local_media_cap.clear();

pierre-luc's avatar
pierre-luc committed
487
    _debug ("Fetch local media capabilities. Local extern audio port: %i\n" , get_local_extern_audio_port());
488 489

    /* Only one audio media used right now */
490
    audio = new sdpMedia (MIME_TYPE_AUDIO);
491 492
    audio->set_port (get_local_extern_audio_port());

493
    /* We retrieve the codecs selected by the user */
494
    selected_codecs = Manager::instance().getCodecDescriptorMap().getActiveCodecs();
495
    codecs_list = Manager::instance().getCodecDescriptorMap().getCodecsMap();
496 497 498 499 500

    for (i=0; i<selected_codecs.size(); i++) {
        iter=codecs_list.find (selected_codecs[i]);

        if (iter!=codecs_list.end()) {
501 502
            audio->add_codec (iter->second);
        }
503 504
    }

505
    _local_media_cap.push_back (audio);
506
}
507

508 509
void Sdp::attribute_port_to_all_media (int port)
{
510 511

    std::vector<sdpMedia*> medias;
512
    int i, size;
513

Emmanuel Milou's avatar
Emmanuel Milou committed
514 515
    set_local_extern_audio_port (port);

516
    medias = get_local_media_cap ();
517 518
    size = medias.size();

519
    for (i=0; i<size; i++) {
520
        medias[i]->set_port (port);
521
    }
522 523
}

524 525
std::string Sdp::convert_int_to_string (int value)
{
526 527 528 529
    std::ostringstream result;
    result << value;
    return result.str();
}
Emmanuel Milou's avatar
Emmanuel Milou committed
530

531
void Sdp::set_remote_ip_from_sdp (const pjmedia_sdp_session *r_sdp)
532
{
Emmanuel Milou's avatar
Emmanuel Milou committed
533

534
    std::string remote_ip(r_sdp->conn->addr.ptr, r_sdp->conn->addr.slen);
535 536
    _debug ("            Remote IP from fetching SDP: %s\n", remote_ip.c_str());
    this->set_remote_ip (remote_ip);
Emmanuel Milou's avatar
Emmanuel Milou committed
537
}
538

539
void Sdp::set_remote_audio_port_from_sdp (pjmedia_sdp_media *r_media)
540
{
541 542

    int remote_port;
543

544
    remote_port = r_media->desc.port;
545 546
    _debug ("            Remote Audio Port from fetching SDP: %d\n", remote_port);
    this->set_remote_audio_port (remote_port);
547 548
}

549
void Sdp::set_media_transport_info_from_remote_sdp (const pjmedia_sdp_session *remote_sdp)
550
{
551

552
    _debug ("Fetching media from sdp\n");
553

554 555 556 557
    pjmedia_sdp_media *r_media;

    this->get_remote_sdp_media_from_offer (remote_sdp, &r_media);

558 559
    if (r_media==NULL) {
        _debug ("SDP Failure: no remote sdp media found in the remote offer\n");
560 561
        return;
    }
562

563
    this->set_remote_audio_port_from_sdp (r_media);
564

565
    this->set_remote_ip_from_sdp (remote_sdp);
566 567
}

568
void Sdp::get_remote_sdp_media_from_offer (const pjmedia_sdp_session* remote_sdp, pjmedia_sdp_media** r_media)
569
{
570 571 572 573
    int count, i;

    count = remote_sdp->media_count;
    *r_media =  NULL;
574 575 576

    for (i = 0; i < count; ++i) {
        if (pj_stricmp2 (&remote_sdp->media[i]->desc.media, "audio") == 0) {
577 578 579 580 581 582
            *r_media = remote_sdp->media[i];
            return;
        }
    }
}