/* 
 * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms oa the GNU General Public License as published by
 * the Free Software Foundation; either version 2 oa 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 oa
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy oa the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
 */
#include <pjmedia/sdp.h>
#include <pjmedia/sdp_neg.h>
#include "test.h"


#define THIS_FILE   "sdp_neg_test.c"
#define START_TEST  0

enum session_type
{
    REMOTE_OFFER,
    LOCAL_OFFER,
};

struct offer_answer
{                       
    enum session_type type;     /*  LOCAL_OFFER:        REMOTE_OFFER:   */
    char *sdp1;                 /* local offer          remote offer    */
    char *sdp2;                 /* remote answer        initial local   */
    char *sdp3;                 /* local active media   local answer    */
};

static struct test
{
    const char          *title;
    unsigned             offer_answer_count;
    struct offer_answer  offer_answer[4];
} test[] = 
{
    /* test 0: */
    {
        /*********************************************************************
         * RFC 3264 examples, section 10.1 (Alice's view)
         *
         * Difference from the example:
         *  - Bob's port number of the third media stream in the first answer
         *    is changed (make it different than Alice's)
         *  - in the second offer/answer exchange, Alice can't accept the
         *    additional line since she didn't specify the capability
         *    in the initial negotiator creation.
         */

        "RFC 3264 example 10.1 (Alice's view)",
        2,
        {
          {
            LOCAL_OFFER,
            /* Alice sends offer: */
            "v=0\r\n"
            "o=alice 2890844526 2890844526 IN IP4 host.anywhere.com\r\n"
            "s= \r\n"
            "c=IN IP4 host.anywhere.com\r\n"
            "t=0 0\r\n"
            "m=audio 49170 RTP/AVP 0\r\n"
            "a=rtpmap:0 PCMU/8000\r\n"
            "m=video 51372 RTP/AVP 31\r\n"
            "a=rtpmap:31 H261/90000\r\n"
            "m=video 53000 RTP/AVP 32\r\n"
            "a=rtpmap:32 MPV/90000\r\n",
            /* Received Bob's answer: */
            "v=0\r\n"
            "o=bob 2890844730 2890844730 IN IP4 host.example.com\r\n"
            "s= \r\n"
            "c=IN IP4 host.example.com\r\n"
            "t=0 0\r\n"
            "m=audio 49920 RTP/AVP 0\r\n"
            "a=rtpmap:0 PCMU/8000\r\n"
            "m=video 0 RTP/AVP 31\r\n"
            "m=video 53002 RTP/AVP 32\r\n"
            "a=rtpmap:32 MPV/90000\r\n",
            /* Alice's SDP now: */
            "v=0\r\n"
            "o=alice 2890844526 2890844526 IN IP4 host.anywhere.com\r\n"
            "s= \r\n"
            "c=IN IP4 host.anywhere.com\r\n"
            "t=0 0\r\n"
            "m=audio 49170 RTP/AVP 0\r\n"
            "a=rtpmap:0 PCMU/8000\r\n"
            "m=video 0 RTP/AVP 31\r\n"
            //"a=rtpmap:31 H261/90000\r\n"      /* <-- this is not necessary (port 0) */
            "m=video 53000 RTP/AVP 32\r\n"
            "a=rtpmap:32 MPV/90000\r\n"
          },
          {
            REMOTE_OFFER,
            /* Bob wants to change his local SDP 
             * (change local port for the first stream and add new stream)
             * Received SDP from Bob:
             */
            "v=0\r\n"
            "o=bob 2890844730 2890844731 IN IP4 host.example.com\r\n"
            "s=-\r\n"
            "c=IN IP4 host.example.com\r\n"
            "t=0 0\r\n"
            "m=audio 65422 RTP/AVP 0\r\n"
            "a=rtpmap:0 PCMU/8000\r\n"
            "m=video 0 RTP/AVP 31\r\n"
            "m=video 53002 RTP/AVP 32\r\n"
            "a=rtpmap:32 MPV/90000\r\n"
            "m=audio 51434 RTP/AVP 110\r\n"
            "a=rtpmap:110 telephone-events/8000\r\n"
            "a=recvonly\r\n",
            NULL,
            /* Alice's SDP now */
            "v=0\r\n"
            "o=alice 2890844526 2890844527 IN IP4 host.anywhere.com\r\n"
            "s= \r\n"
            "c=IN IP4 host.anywhere.com\r\n"
            "t=0 0\r\n"
            "m=audio 49170 RTP/AVP 0\r\n"
            "a=rtpmap:0 PCMU/8000\r\n"
            "m=video 0 RTP/AVP 31\r\n"
            //"a=rtpmap:31 H261/90000\r\n"      /* <-- this is not necessary (port 0) */
            "m=video 53000 RTP/AVP 32\r\n"
            "a=rtpmap:32 MPV/90000\r\n"
            "m=audio 0 RTP/AVP 110\r\n"
            /* <-- the following attributes are not necessary (port 0) */
            //"a=rtpmap:110 telephone-events/8000\r\n"
            //"a=sendonly\r\n"
          }
        }
    },

    /* test 1: */
    {
        /*********************************************************************
         * RFC 3264 examples, section 10.1. (Bob's view)
         *
         * Difference:
         *  - the SDP version in Bob's capability is changed to ver-1.
         */

        "RFC 3264 example 10.1 (Bob's view)",
        2,
        {
          {
            REMOTE_OFFER,
            /* Remote offer from Alice: */
            "v=0\r\n"
            "o=alice 2890844526 2890844526 IN IP4 host.anywhere.com\r\n"
            "s= \r\n"
            "c=IN IP4 host.anywhere.com\r\n"
            "t=0 0\r\n"
            "m=audio 49170 RTP/AVP 0\r\n"
            "a=rtpmap:0 PCMU/8000\r\n"
            "m=video 51372 RTP/AVP 31\r\n"
            "a=rtpmap:31 H261/90000\r\n"
            "m=video 53000 RTP/AVP 32\r\n"
            "a=rtpmap:32 MPV/90000\r\n",
            /* Bob's capability: */
            "v=0\r\n"
            "o=bob 2890844730 2890844729 IN IP4 host.example.com\r\n"
            "s= \r\n"
            "c=IN IP4 host.example.com\r\n"
            "t=0 0\r\n"
            "m=audio 49920 RTP/AVP 0\r\n"
            "a=rtpmap:0 PCMU/8000\r\n"
            "m=video 0 RTP/AVP 31\r\n"
            "m=video 53000 RTP/AVP 32\r\n"
            "a=rtpmap:32 MPV/90000\r\n",
            /* This's how Bob's answer should look like: */
            "v=0\r\n"
            "o=bob 2890844730 2890844730 IN IP4 host.example.com\r\n"
            "s= \r\n"
            "c=IN IP4 host.example.com\r\n"
            "t=0 0\r\n"
            "m=audio 49920 RTP/AVP 0\r\n"
            "a=rtpmap:0 PCMU/8000\r\n"
            "m=video 0 RTP/AVP 31\r\n"
            "m=video 53000 RTP/AVP 32\r\n"
            "a=rtpmap:32 MPV/90000\r\n"
          },
          {
            LOCAL_OFFER,
            /* Bob wants to change his local SDP 
             * (change local port for the first stream and add new stream)
             */
            "v=0\r\n"
            "o=bob 2890844730 2890844731 IN IP4 host.example.com\r\n"
            "s=-\r\n"
            "c=IN IP4 host.example.com\r\n"
            "t=0 0\r\n"
            "m=audio 65422 RTP/AVP 0\r\n"
            "a=rtpmap:0 PCMU/8000\r\n"
            "m=video 0 RTP/AVP 31\r\n"
            "m=video 53000 RTP/AVP 32\r\n"
            "a=rtpmap:32 MPV/90000\r\n"
            "m=audio 51434 RTP/AVP 110\r\n"
            "a=rtpmap:110 telephone-events/8000\r\n"
            "a=recvonly\r\n",
            /* Got answer from Alice */
            "v=0\r\n"
            "o=alice 2890844526 2890844527 IN IP4 host.anywhere.com\r\n"
            "s=-\r\n"
            "c=IN IP4 host.anywhere.com\r\n"
            "t=0 0\r\n"
            "m=audio 49170 RTP/AVP 0\r\n"
            "a=rtpmap:0 PCMU/8000\r\n"
            "m=video 0 RTP/AVP 31\r\n"
            "a=rtpmap:31 H261/90000\r\n"
            "m=video 53000 RTP/AVP 32\r\n"
            "a=rtpmap:32 MPV/90000\r\n"
            "m=audio 53122 RTP/AVP 110\r\n"
            "a=rtpmap:110 telephone-events/8000\r\n"
            "a=sendonly\r\n",
            /* This is how Bob's SDP should look like after negotiation */
            "v=0\r\n"
            "o=bob 2890844730 2890844731 IN IP4 host.example.com\r\n"
            "s=-\r\n"
            "c=IN IP4 host.example.com\r\n"
            "t=0 0\r\n"
            "m=audio 65422 RTP/AVP 0\r\n"
            "a=rtpmap:0 PCMU/8000\r\n"
            "m=video 0 RTP/AVP 31\r\n"
            "m=video 53000 RTP/AVP 32\r\n"
            "a=rtpmap:32 MPV/90000\r\n"
            "m=audio 51434 RTP/AVP 110\r\n"
            "a=rtpmap:110 telephone-events/8000\r\n"
            "a=recvonly\r\n"
          }
        }
    },

    /* test 2: */
    {
        /*********************************************************************
         * RFC 3264 examples, section 10.2.
         * This is from Alice's point of view.
         */

        "RFC 3264 example 10.2 (Alice's view)",
        2,
        {
          {
            LOCAL_OFFER,
            /* The initial offer from Alice to Bob indicates a single audio 
             * stream with the three audio codecs that are available in the 
             * DSP. The stream is marked as inactive, 
             */
            "v=0\r\n"
            "o=alice 2890844526 2890844526 IN IP4 host.anywhere.com\r\n"
            "s=-\r\n"
            "c=IN IP4 host.anywhere.com\r\n"
            "t=0 0\r\n"
            "m=audio 62986 RTP/AVP 0 4 18\r\n"
            "a=rtpmap:0 PCMU/8000\r\n"
            "a=rtpmap:4 G723/8000\r\n"
            "a=rtpmap:18 G729/8000\r\n"
            "a=inactive\r\n",
            /* Bob can support dynamic switching between PCMU and G.723. So, 
             * he sends the following answer:
             */
            "v=0\r\n"
            "o=bob 2890844730 2890844731 IN IP4 host.example.com\r\n"
            "s=-\r\n"
            "c=IN IP4 host.example.com\r\n"
            "t=0 0\r\n"
            "m=audio 54344 RTP/AVP 0 4\r\n"
            "a=rtpmap:0 PCMU/8000\r\n"
            "a=rtpmap:4 G723/8000\r\n"
            "a=inactive\r\n",
            /* This is how Alice's media should look like after negotiation */
            "v=0\r\n"
            "o=alice 2890844526 2890844526 IN IP4 host.anywhere.com\r\n"
            "s=-\r\n"
            "c=IN IP4 host.anywhere.com\r\n"
            "t=0 0\r\n"
            "m=audio 62986 RTP/AVP 0 4\r\n"
            "a=rtpmap:0 PCMU/8000\r\n"
            "a=rtpmap:4 G723/8000\r\n"
            "a=inactive\r\n",
          },
          {
            LOCAL_OFFER,
            /* Alice sends an updated offer with a sendrecv stream: */
            "v=0\r\n"
            "o=alice 2890844526 2890844527 IN IP4 host.anywhere.com\r\n"
            "s=-\r\n"
            "c=IN IP4 host.anywhere.com\r\n"
            "t=0 0\r\n"
            "m=audio 62986 RTP/AVP 4\r\n"
            "a=rtpmap:4 G723/8000\r\n"
            "a=sendrecv\r\n",
            /* Bob accepts the single codec: */
            "v=0\r\n"
            "o=bob 2890844730 2890844732 IN IP4 host.example.com\r\n"
            "s= \r\n"
            "c=IN IP4 host.example.com\r\n"
            "t=0 0\r\n"
            "m=audio 54344 RTP/AVP 4\r\n"
            "a=rtpmap:4 G723/8000\r\n"
            "a=sendrecv\r\n",
            /* This is how Alice's media should look like after negotiation */
            "v=0\r\n"
            "o=alice 2890844526 2890844527 IN IP4 host.anywhere.com\r\n"
            "s=-\r\n"
            "c=IN IP4 host.anywhere.com\r\n"
            "t=0 0\r\n"
            "m=audio 62986 RTP/AVP 4\r\n"
            "a=rtpmap:4 G723/8000\r\n"
            "a=sendrecv\r\n"
          }
        }
    },

#if 0
    // this test is commented, this causes error: 
    // No suitable codec for remote offer (PJMEDIA_SDPNEG_NOANSCODEC),
    // since currently the negotiator always answer with one codec, 
    // PCMU in this case, while PCMU is not included in the second offer.

    /* test 3: */
    {
        /*********************************************************************
         * RFC 3264 examples, section 10.2.
         * This is from Bob's point of view.
         *
         * Difference:
         *  - The SDP version number in Bob's initial capability is ver-1
         */

        "RFC 3264 example 10.2 (Bob's view)",
        2,
        {
          {
            REMOTE_OFFER,
            /* Bob received offer from Alice:
             */
            "v=0\r\n"
            "o=alice 2890844526 2890844526 IN IP4 host.anywhere.com\r\n"
            "s=-\r\n"
            "c=IN IP4 host.anywhere.com\r\n"
            "t=0 0\r\n"
            "m=audio 62986 RTP/AVP 0 4 18\r\n"
            "a=rtpmap:0 PCMU/8000\r\n"
            "a=rtpmap:4 G723/8000\r\n"
            "a=rtpmap:18 G729/8000\r\n"
            "a=inactive\r\n",
            /* Bob's capability:
             */
            "v=0\r\n"
            "o=bob 2890844730 2890844730 IN IP4 host.example.com\r\n"
            "s=-\r\n"
            "c=IN IP4 host.example.com\r\n"
            "t=0 0\r\n"
            "m=audio 54344 RTP/AVP 0 4\r\n"
            "a=rtpmap:0 PCMU/8000\r\n"
            "a=rtpmap:4 G723/8000\r\n"
            "a=inactive\r\n",
            /* This is how Bob's media should look like after negotiation */
            "v=0\r\n"
            "o=bob 2890844730 2890844731 IN IP4 host.example.com\r\n"
            "s=-\r\n"
            "c=IN IP4 host.example.com\r\n"
            "t=0 0\r\n"
            "m=audio 54344 RTP/AVP 0\r\n"
            "a=rtpmap:0 PCMU/8000\r\n"
            "a=inactive\r\n"
          },
          {
            REMOTE_OFFER,
            /* Received updated Alice's SDP: offer with a sendrecv stream: */
            "v=0\r\n"
            "o=alice 2890844526 2890844527 IN IP4 host.anywhere.com\r\n"
            "s=-\r\n"
            "c=IN IP4 host.anywhere.com\r\n"
            "t=0 0\r\n"
            "m=audio 62986 RTP/AVP 4\r\n"
            "a=rtpmap:4 G723/8000\r\n"
            "a=sendrecv\r\n",
            /* Bob accepts the single codec: */
            NULL,
            /* This is how Bob's media should look like after negotiation */
            "v=0\r\n"
            "o=bob 2890844730 2890844732 IN IP4 host.example.com\r\n"
            "s=-\r\n"
            "c=IN IP4 host.example.com\r\n"
            "t=0 0\r\n"
            "m=audio 54344 RTP/AVP 4\r\n"
            "a=rtpmap:4 G723/8000\r\n"
            "a=sendrecv\r\n",
          }
        }
    },
#endif

    /* test 4: */
    {
        /*********************************************************************
         * RFC 4317 Sample 2.1: Audio and Video 1 (Alice's view)
         *
         * This common scenario shows a video and audio session in which
         * multiple codecs are offered but only one is accepted.  As a result of
         * the exchange shown below, Alice and Bob may send only PCMU audio and
         * MPV video.  Note: Dynamic payload type 97 is used for iLBC codec
         */
        "RFC 4317 section 2.1: Audio and Video 1 (Alice's view)",
        1,
        {
          {
            LOCAL_OFFER,
            /* Alice's local offer: */
            "v=0\r\n"
            "o=alice 2890844526 2890844526 IN IP4 host.atlanta.example.com\r\n"
            "s=-\r\n"
            "c=IN IP4 host.atlanta.example.com\r\n"
            "t=0 0\r\n"
            "m=audio 49170 RTP/AVP 0 8 97\r\n"
            "a=rtpmap:0 PCMU/8000\r\n"
            "a=rtpmap:8 PCMA/8000\r\n"
            "a=rtpmap:97 iLBC/8000\r\n"
            "m=video 51372 RTP/AVP 31 32\r\n"
            "a=rtpmap:31 H261/90000\r\n"
            "a=rtpmap:32 MPV/90000\r\n",
            /* Received answer from Bob: */
            "v=0\r\n"
            "o=bob 2808844564 2808844564 IN IP4 host.biloxi.example.com\r\n"
            "s=-\r\n"
            "c=IN IP4 host.biloxi.example.com\r\n"
            "t=0 0\r\n"
            "m=audio 49174 RTP/AVP 0\r\n"
            "a=rtpmap:0 PCMU/8000\r\n"
            "m=video 49170 RTP/AVP 32\r\n"
            "a=rtpmap:32 MPV/90000\r\n",
            /* This is how Alice's media should look like now: */
            "v=0\r\n"
            "o=alice 2890844526 2890844526 IN IP4 host.atlanta.example.com\r\n"
            "s=-\r\n"
            "c=IN IP4 host.atlanta.example.com\r\n"
            "t=0 0\r\n"
            "m=audio 49170 RTP/AVP 0\r\n"
            "a=rtpmap:0 PCMU/8000\r\n"
            "m=video 51372 RTP/AVP 32\r\n"
            "a=rtpmap:32 MPV/90000\r\n"
          }
        }
    },

    /* test 5: */
    {
        /*********************************************************************
         * RFC 4317 Sample 2.1: Audio and Video 1 (Bob's view)
         *
         * This common scenario shows a video and audio session in which
         * multiple codecs are offered but only one is accepted.  As a result of
         * the exchange shown below, Alice and Bob may send only PCMU audio and
         * MPV video.  Note: Dynamic payload type 97 is used for iLBC codec
         *
         * Difference:
         *  - Bob's initial capability version number
         */
        "RFC 4317 section 2.1: Audio and Video 1 (Bob's view)",
        1,
        {
          {
            REMOTE_OFFER,
            /* Received Alice's local offer: */
            "v=0\r\n"
            "o=alice 2890844526 2890844526 IN IP4 host.atlanta.example.com\r\n"
            "s=-\r\n"
            "c=IN IP4 host.atlanta.example.com\r\n"
            "t=0 0\r\n"
            "m=audio 49170 RTP/AVP 0 8 97\r\n"
            "a=rtpmap:0 PCMU/8000\r\n"
            "a=rtpmap:8 PCMA/8000\r\n"
            "a=rtpmap:97 iLBC/8000\r\n"
            "m=video 51372 RTP/AVP 31 32\r\n"
            "a=rtpmap:31 H261/90000\r\n"
            "a=rtpmap:32 MPV/90000\r\n",
            /* Bob's capability: */
            "v=0\r\n"
            "o=bob 2808844564 2808844563 IN IP4 host.biloxi.example.com\r\n"
            "s=-\r\n"
            "c=IN IP4 host.biloxi.example.com\r\n"
            "t=0 0\r\n"
            "m=audio 49174 RTP/AVP 0\r\n"
            "a=rtpmap:0 PCMU/8000\r\n"
            "m=video 49170 RTP/AVP 32\r\n"
            "a=rtpmap:32 MPV/90000\r\n",
            /* This is how Bob's media should look like now: */
            "v=0\r\n"
            "o=bob 2808844564 2808844564 IN IP4 host.biloxi.example.com\r\n"
            "s=-\r\n"
            "c=IN IP4 host.biloxi.example.com\r\n"
            "t=0 0\r\n"
            "m=audio 49174 RTP/AVP 0\r\n"
            "a=rtpmap:0 PCMU/8000\r\n"
            "m=video 49170 RTP/AVP 32\r\n"
            "a=rtpmap:32 MPV/90000\r\n"
          }
        }
    },

    /* test 6: */
    {
        /*********************************************************************
         * RFC 4317 Sample 2.2: Audio and Video 2 (Alice's view)
         *
         * Difference:
         *  - Bob's initial capability version number
         */
        "RFC 4317 section 2.2: Audio and Video 2 (Alice's view)",
        2,
        {
          {
            LOCAL_OFFER,
            /* Alice sends offer: */
            "v=0\r\n"
            "o=alice 2890844526 2890844526 IN IP4 host.atlanta.example.com\r\n"
            "s=alice\r\n"
            "c=IN IP4 host.atlanta.example.com\r\n"
            "t=0 0\r\n"
            "m=audio 49170 RTP/AVP 0 8 97\r\n"
            "a=rtpmap:0 PCMU/8000\r\n"
            "a=rtpmap:8 PCMA/8000\r\n"
            "a=rtpmap:97 iLBC/8000\r\n"
            "m=video 51372 RTP/AVP 31 32\r\n"
            "a=rtpmap:31 H261/90000\r\n"
            "a=rtpmap:32 MPV/90000\r\n",
            /* Bob's answer: */
            "v=0\r\n"
            "o=bob 2808844564 2808844564 IN IP4 host.biloxi.example.com\r\n"
            "s=bob\r\n"
            "c=IN IP4 host.biloxi.example.com\r\n"
            "t=0 0\r\n"
            "m=audio 49172 RTP/AVP 0 8\r\n"
            "a=rtpmap:0 PCMU/8000\r\n"
            "a=rtpmap:8 PCMA/8000\r\n"
            "m=video 0 RTP/AVP 31\r\n"
            "a=rtpmap:31 H261/90000\r\n",
            /* This is how Alice's media should look like now: */
            "v=0\r\n"
            "o=alice 2890844526 2890844526 IN IP4 host.atlanta.example.com\r\n"
            "s=alice\r\n"
            "c=IN IP4 host.atlanta.example.com\r\n"
            "t=0 0\r\n"
            "m=audio 49170 RTP/AVP 0 8\r\n"
            "a=rtpmap:0 PCMU/8000\r\n"
            "a=rtpmap:8 PCMA/8000\r\n"
            // By #1088, the formats won't be negotiated when the media has port 0.
            //"m=video 0 RTP/AVP 31\r\n"
            "m=video 0 RTP/AVP 31 32\r\n"
            //"a=rtpmap:31 H261/90000\r\n"  /* <-- this is not necessary (port 0) */
          },
          {
            LOCAL_OFFER,
            /* Alice sends updated offer: */
            "v=0\r\n"
            "o=alice 2890844526 2890844527 IN IP4 host.atlanta.example.com\r\n"
            "s=alice\r\n"
            "c=IN IP4 host.atlanta.example.com\r\n"
            "t=0 0\r\n"
            "m=audio 51372 RTP/AVP 0\r\n"
            "a=rtpmap:0 PCMU/8000\r\n"
            "m=video 0 RTP/AVP 31\r\n"
            "a=rtpmap:31 H261/90000\r\n",
            /* Bob's answer: */
            "v=0\r\n"
            "o=bob 2808844564 2808844565 IN IP4 host.biloxi.example.com\r\n"
            "s=bob\r\n"
            "c=IN IP4 host.biloxi.example.com\r\n"
            "t=0 0\r\n"
            "m=audio 49172 RTP/AVP 0\r\n"
            "a=rtpmap:0 PCMU/8000\r\n"
            "m=video 0 RTP/AVP 31\r\n"
            "a=rtpmap:31 H261/90000\r\n",
            /* This is how Alice's SDP should look like: */
            "v=0\r\n"
            "o=alice 2890844526 2890844527 IN IP4 host.atlanta.example.com\r\n"
            "s=alice\r\n"
            "c=IN IP4 host.atlanta.example.com\r\n"
            "t=0 0\r\n"
            "m=audio 51372 RTP/AVP 0\r\n"
            "a=rtpmap:0 PCMU/8000\r\n"
            "m=video 0 RTP/AVP 31\r\n"
            //"a=rtpmap:31 H261/90000\r\n"  /* <-- this is not necessary (port 0) */
          }
        }
    },

    /* test 7: */
    {
        /*********************************************************************
         * RFC 4317 Sample 2.2: Audio and Video 2 (Bob's view)
         *
         * Difference:
         *  - Bob's initial capability version number
         */
        "RFC 4317 section 2.2: Audio and Video 2 (Bob's view)",
        2,
        {
          {
            REMOTE_OFFER,
            /* Received offer from alice: */
            "v=0\r\n"
            "o=alice 2890844526 2890844526 IN IP4 host.atlanta.example.com\r\n"
            "s=alice\r\n"
            "c=IN IP4 host.atlanta.example.com\r\n"
            "t=0 0\r\n"
            "m=audio 49170 RTP/AVP 0 8 97\r\n"
            "a=rtpmap:0 PCMU/8000\r\n"
            "a=rtpmap:8 PCMA/8000\r\n"
            "a=rtpmap:97 iLBC/8000\r\n"
            "m=video 51372 RTP/AVP 31 32\r\n"
            "a=rtpmap:31 H261/90000\r\n"
            "a=rtpmap:32 MPV/90000\r\n",
            /* Bob's initial capability: */
            "v=0\r\n"
            "o=bob 2808844564 2808844563 IN IP4 host.biloxi.example.com\r\n"
            "s=bob\r\n"
            "c=IN IP4 host.biloxi.example.com\r\n"
            "t=0 0\r\n"
            "m=audio 49172 RTP/AVP 0 8\r\n"
            "a=rtpmap:0 PCMU/8000\r\n"
            "a=rtpmap:8 PCMA/8000\r\n"
            "m=video 0 RTP/AVP 31\r\n"
            "a=rtpmap:31 H261/90000\r\n",
            /* This is how Bob's answer should look like now: */
            "v=0\r\n"
            "o=bob 2808844564 2808844564 IN IP4 host.biloxi.example.com\r\n"
            "s=bob\r\n"
            "c=IN IP4 host.biloxi.example.com\r\n"
            "t=0 0\r\n"
            "m=audio 49172 RTP/AVP 0\r\n"
            "a=rtpmap:0 PCMU/8000\r\n"
            "m=video 0 RTP/AVP 31\r\n"
            "a=rtpmap:31 H261/90000\r\n"
          },
          {
            REMOTE_OFFER,
            /* Received updated offer from Alice: */
            "v=0\r\n"
            "o=alice 2890844526 2890844527 IN IP4 host.atlanta.example.com\r\n"
            "s=alice\r\n"
            "c=IN IP4 host.atlanta.example.com\r\n"
            "t=0 0\r\n"
            "m=audio 51372 RTP/AVP 0\r\n"
            "a=rtpmap:0 PCMU/8000\r\n"
            "m=video 0 RTP/AVP 31\r\n"
            "a=rtpmap:31 H261/90000\r\n",
            /* Bob's answer: */
            NULL,
            /* This is how Bob's answer should look like: */
            "v=0\r\n"
            "o=bob 2808844564 2808844564 IN IP4 host.biloxi.example.com\r\n"
            "s=bob\r\n"
            "c=IN IP4 host.biloxi.example.com\r\n"
            "t=0 0\r\n"
            "m=audio 49172 RTP/AVP 0\r\n"
            "a=rtpmap:0 PCMU/8000\r\n"
            "m=video 0 RTP/AVP 31\r\n"
            //"a=rtpmap:31 H261/90000\r\n"  /* <-- this is not necessary (port 0) */
          }
        }
    },

    /* test 8: */
    {
        /*********************************************************************
         * RFC 4317 Sample 2.4: Audio and Telephone-Events (Alice's view)
         *
         */

        "RFC 4317 section 2.4: Audio and Telephone-Events (Alice's view)",
        1,
        {
          {
            LOCAL_OFFER,
            /* Alice sends offer: */
            "v=0\r\n"
            "o=alice 2890844526 2890844526 IN IP4 host.atlanta.example.com\r\n"
            "s=alice\r\n"
            "c=IN IP4 host.atlanta.example.com\r\n"
            "t=0 0\r\n"
            "m=audio 49170 RTP/AVP 0 97\r\n"
            "a=rtpmap:0 PCMU/8000\r\n"
            "a=rtpmap:97 iLBC/8000\r\n"
            "m=audio 49172 RTP/AVP 98\r\n"
            "a=rtpmap:98 telephone-event/8000\r\n"
            "a=sendonly\r\n",
            /* Received Bob's answer: */
            "v=0\r\n"
            "o=bob 2808844564 2808844564 IN IP4 host.biloxi.example.com\r\n"
            "s=bob\r\n"
            "c=IN IP4 host.biloxi.example.com\r\n"
            "t=0 0\r\n"
            "m=audio 49172 RTP/AVP 97\r\n"
            "a=rtpmap:97 iLBC/8000\r\n"
            "m=audio 49174 RTP/AVP 98\r\n"
            "a=rtpmap:98 telephone-event/8000\r\n"
            "a=recvonly\r\n",
            /* Alice's SDP now: */
            "v=0\r\n"
            "o=alice 2890844526 2890844526 IN IP4 host.atlanta.example.com\r\n"
            "s=alice\r\n"
            "c=IN IP4 host.atlanta.example.com\r\n"
            "t=0 0\r\n"
            "m=audio 49170 RTP/AVP 97\r\n"
            "a=rtpmap:97 iLBC/8000\r\n"
            "m=audio 49172 RTP/AVP 98\r\n"
            "a=rtpmap:98 telephone-event/8000\r\n"
            "a=sendonly\r\n"
          }
        }
    },


    /* test 9: */
    {
        /*********************************************************************
         * RFC 4317 Sample 2.4: Audio and Telephone-Events (Bob's view)
         *
         * Difference:
         *  - Bob's initial SDP version number
         *  - Bob's capability are added with more formats, and the
         *    stream order is interchanged to test the negotiator.
         */

        "RFC 4317 section 2.4: Audio and Telephone-Events (Bob's view)",
        1,
        {
          {
            REMOTE_OFFER,
            /* Received Alice's offer: */
            "v=0\r\n"
            "o=alice 2890844526 2890844526 IN IP4 host.atlanta.example.com\r\n"
            "s=alice\r\n"
            "c=IN IP4 host.atlanta.example.com\r\n"
            "t=0 0\r\n"
            "m=audio 49170 RTP/AVP 0 97\r\n"
            "a=rtpmap:0 PCMU/8000\r\n"
            "a=rtpmap:97 iLBC/8000\r\n"
            "m=audio 49172 RTP/AVP 98\r\n"
            "a=rtpmap:98 telephone-event/8000\r\n"
            "a=sendonly\r\n",
            /* Bob's initial capability: */
            "v=0\r\n"
            "o=bob 2808844564 2808844563 IN IP4 host.biloxi.example.com\r\n"
            "s=bob\r\n"
            "c=IN IP4 host.biloxi.example.com\r\n"
            "t=0 0\r\n"
            "m=audio 49174 RTP/AVP 4 98\r\n"
            "a=rtpmap:98 telephone-event/8000\r\n"
            "m=audio 49172 RTP/AVP 97 8 99\r\n"
            "a=rtpmap:97 iLBC/8000\r\n"
            "a=rtpmap:99 telephone-event/8000\r\n",
            /* Bob's answer should be: */
            "v=0\r\n"
            "o=bob 2808844564 2808844564 IN IP4 host.biloxi.example.com\r\n"
            "s=bob\r\n"
            "c=IN IP4 host.biloxi.example.com\r\n"
            "t=0 0\r\n"
            "m=audio 49172 RTP/AVP 97\r\n"
            "a=rtpmap:97 iLBC/8000\r\n"
            "m=audio 49174 RTP/AVP 98\r\n"
            "a=rtpmap:98 telephone-event/8000\r\n"
            "a=recvonly\r\n"
          }
        }
    },

    /* test 10: */
    {
        /*********************************************************************
         * RFC 4317 Sample 2.6: Audio with Telephone-Events (Alice's view)
         *
         */

        "RFC 4317 section 2.6: Audio with Telephone-Events (Alice's view)",
        1,
        {
          {
            LOCAL_OFFER,
            /* Alice sends offer: */
            "v=0\r\n"
            "o=alice 2890844526 2890844526 IN IP4 host.atlanta.example.com\r\n"
            "s=alice\r\n"
            "c=IN IP4 host.atlanta.example.com\r\n"
            "t=0 0\r\n"
            "m=audio 49170 RTP/AVP 0\r\n"
            "a=rtpmap:0 PCMU/8000\r\n"
            "m=audio 51372 RTP/AVP 97 101\r\n"
            "a=rtpmap:97 iLBC/8000\r\n"
            "a=rtpmap:101 telephone-event/8000\r\n",
            /* Received bob's answer: */
            "v=0\r\n"
            "o=bob 2808844564 2808844564 IN IP4 host.biloxi.example.com\r\n"
            "s=bob\r\n"
            "c=IN IP4 host.biloxi.example.com\r\n"
            "t=0 0\r\n"
            "m=audio 0 RTP/AVP 0\r\n"
            "a=rtpmap:0 PCMU/8000\r\n"
            "m=audio 49170 RTP/AVP 97 101\r\n"
            "a=rtpmap:97 iLBC/8000\r\n"
            "a=rtpmap:101 telephone-event/8000\r\n",
            /* Alice's local SDP should be: */
            "v=0\r\n"
            "o=alice 2890844526 2890844526 IN IP4 host.atlanta.example.com\r\n"
            "s=alice\r\n"
            "c=IN IP4 host.atlanta.example.com\r\n"
            "t=0 0\r\n"
            "m=audio 0 RTP/AVP 0\r\n"
            //"a=rtpmap:0 PCMU/8000\r\n"          /* <-- this is not necessary (port 0) */
            "m=audio 51372 RTP/AVP 97 101\r\n"
            "a=rtpmap:97 iLBC/8000\r\n"
            "a=rtpmap:101 telephone-event/8000\r\n"
          }
        }
    },

    /* test 11: */
    {
        /*********************************************************************
         * RFC 4317 Sample 2.6: Audio with Telephone-Events (Bob's view)
         *
         * Difference:
         *  - Bob's SDP version number
         *  - Bob's initial capability are expanded with multiple m lines
         *    and more formats
         */

        "RFC 4317 section 2.6: Audio with Telephone-Events (Bob's view)",
        1,
        {
          {
            REMOTE_OFFER,
            /* Received Alice's offer: */
            "v=0\r\n"
            "o=alice 2890844526 2890844526 IN IP4 host.atlanta.example.com\r\n"
            "s=alice\r\n"
            "c=IN IP4 host.atlanta.example.com\r\n"
            "t=0 0\r\n"
            "m=audio 49170 RTP/AVP 0\r\n"
            "a=rtpmap:0 PCMU/8000\r\n"
            "m=audio 51372 RTP/AVP 97 101\r\n"
            "a=rtpmap:97 iLBC/8000\r\n"
            "a=rtpmap:101 telephone-event/8000\r\n",
            /* Bob's initial capability also has video: */
            "v=0\r\n"
            "o=bob 2808844564 2808844563 IN IP4 host.biloxi.example.com\r\n"
            "s=bob\r\n"
            "c=IN IP4 host.biloxi.example.com\r\n"
            "t=0 0\r\n"
            "m=audio 49170 RTP/AVP 4 97 101\r\n"
            "a=rtpmap:4 G723/8000\r\n"
            "a=rtpmap:97 iLBC/8000\r\n"
            "a=rtpmap:101 telephone-event/8000\r\n"
            "m=video 1000 RTP/AVP 31\r\n"
            "a=rtpmap:31 H261/90000\r\n",
            /* Bob's answer should be: */
            "v=0\r\n"
            "o=bob 2808844564 2808844564 IN IP4 host.biloxi.example.com\r\n"
            "s=bob\r\n"
            "c=IN IP4 host.biloxi.example.com\r\n"
            "t=0 0\r\n"
            "m=audio 0 RTP/AVP 0\r\n"
            //"a=rtpmap:0 PCMU/8000\r\n"      /* <-- this is not necessary (port 0) */
            "m=audio 49170 RTP/AVP 97 101\r\n"
            "a=rtpmap:97 iLBC/8000\r\n"
            "a=rtpmap:101 telephone-event/8000\r\n",
          }
        }
    },

    /* test 12: */
    {
        /*********************************************************************
         * Ticket #527: More lenient SDP negotiator.
         */

        "Ticket #527 scenario #1: Partial answer",
        1,
        {
          {
            LOCAL_OFFER,
            /* Alice sends offer audio and video: */
            "v=0\r\n"
            "o=alice 2890844526 2890844526 IN IP4 host.atlanta.example.com\r\n"
            "s=alice\r\n"
            "c=IN IP4 host.atlanta.example.com\r\n"
            "t=0 0\r\n"
            "m=audio 49170 RTP/AVP 0\r\n"
            "a=rtpmap:0 PCMU/8000\r\n"
            "m=video 4000 RTP/AVP 31\r\n"
            "a=rtpmap:31 H261/90000\r\n",
            /* Receive Bob's answer only audio: */
            "v=0\r\n"
            "o=bob 2808844564 2808844563 IN IP4 host.biloxi.example.com\r\n"
            "s=bob\r\n"
            "c=IN IP4 host.biloxi.example.com\r\n"
            "t=0 0\r\n"
            "m=audio 49170 RTP/AVP 0\r\n"
            "a=rtpmap:0 PCMU/8000\r\n",
            /* Alice's local SDP should be: */
            "v=0\r\n"
            "o=alice 2890844526 2890844526 IN IP4 host.atlanta.example.com\r\n"
            "s=alice\r\n"
            "c=IN IP4 host.atlanta.example.com\r\n"
            "t=0 0\r\n"
            "m=audio 49170 RTP/AVP 0\r\n"
            "a=rtpmap:0 PCMU/8000\r\n"
            "m=video 0 RTP/AVP 31\r\n"
            //"a=rtpmap:31 H261/90000\r\n"    /* <-- this is not necessary (port 0) */
            "",
          }
        }
    },

    /* test 13: */
    {
        /*********************************************************************
         * Ticket #527: More lenient SDP negotiator.
         */

        "Ticket #527 scenario #1: Media mismatch in answer",
        1,
        {
          {
            LOCAL_OFFER,
            /* Alice sends offer audio and video: */
            "v=0\r\n"
            "o=alice 2890844526 2890844526 IN IP4 host.atlanta.example.com\r\n"
            "s=alice\r\n"
            "c=IN IP4 host.atlanta.example.com\r\n"
            "t=0 0\r\n"
            "m=audio 3000 RTP/AVP 0\r\n"
            "a=rtpmap:0 PCMU/8000\r\n"
            "m=video 4000 RTP/AVP 31\r\n"
            "a=rtpmap:31 H261/90000\r\n",
            /* Receive Bob's answer two audio: */
            "v=0\r\n"
            "o=bob 2808844564 2808844563 IN IP4 host.biloxi.example.com\r\n"
            "s=bob\r\n"
            "c=IN IP4 host.biloxi.example.com\r\n"
            "t=0 0\r\n"
            "m=audio 49170 RTP/AVP 0\r\n"
            "a=rtpmap:0 PCMU/8000\r\n"
            "m=audio 49172 RTP/AVP 0\r\n"
            "a=rtpmap:0 PCMU/8000\r\n",
            /* Alice's local SDP should be: */
            "v=0\r\n"
            "o=alice 2890844526 2890844526 IN IP4 host.atlanta.example.com\r\n"
            "s=alice\r\n"
            "c=IN IP4 host.atlanta.example.com\r\n"
            "t=0 0\r\n"
            "m=audio 3000 RTP/AVP 0\r\n"
            "a=rtpmap:0 PCMU/8000\r\n"
            "m=video 0 RTP/AVP 31\r\n"
            //"a=rtpmap:31 H261/90000\r\n"    /* <-- this is not necessary (port 0) */
            "",
          }
        }
    },

    /* test 14: */
    {
        /*********************************************************************
         * Ticket #527: More lenient SDP negotiator.
         */

        "Ticket #527 scenario #2: Modify offer - partial streams",
        2,
        {
          {
            LOCAL_OFFER,
            /* Alice sends offer: */
            "v=0\r\n"
            "o=alice 2890844526 2890844526 IN IP4 host.atlanta.example.com\r\n"
            "s=alice\r\n"
            "c=IN IP4 host.atlanta.example.com\r\n"
            "t=0 0\r\n"
            "m=audio 3000 RTP/AVP 0\r\n"
            "a=rtpmap:0 PCMU/8000\r\n"
            "m=audio 3100 RTP/AVP 0\r\n"
            "a=rtpmap:0 PCMU/8000\r\n"
            "m=video 3200 RTP/AVP 31\r\n"
            "a=rtpmap:31 H261/90000\r\n"
            "",
            /* Receive Bob's answer: */
            "v=0\r\n"
            "o=bob 2808844564 2808844563 IN IP4 host.biloxi.example.com\r\n"
            "s=bob\r\n"
            "c=IN IP4 host.biloxi.example.com\r\n"
            "t=0 0\r\n"
            "m=audio 4000 RTP/AVP 0\r\n"
            "a=rtpmap:0 PCMU/8000\r\n"
            "m=audio 4100 RTP/AVP 0\r\n"
            "a=rtpmap:0 PCMU/8000\r\n"
            "m=video 4200 RTP/AVP 31\r\n"
            "a=rtpmap:31 H261/90000\r\n"
            "",
            /* Alice's local SDP should be: */
            "v=0\r\n"
            "o=alice 2890844526 2890844526 IN IP4 host.atlanta.example.com\r\n"
            "s=alice\r\n"
            "c=IN IP4 host.atlanta.example.com\r\n"
            "t=0 0\r\n"
            "m=audio 3000 RTP/AVP 0\r\n"
            "a=rtpmap:0 PCMU/8000\r\n"
            "m=audio 3100 RTP/AVP 0\r\n"
            "a=rtpmap:0 PCMU/8000\r\n"
            "m=video 3200 RTP/AVP 31\r\n"
            "a=rtpmap:31 H261/90000\r\n"
            "",
          },
          {
            LOCAL_OFFER,
            /* Alice modifies offer with only specify one audio: */
            "v=0\r\n"
            "o=alice 2890844526 2890844526 IN IP4 host.atlanta.example.com\r\n"
            "s=alice\r\n"
            "c=IN IP4 host.atlanta.example.com\r\n"
            "t=0 0\r\n"
            "m=audio 5200 RTP/AVP 0\r\n"
            "a=rtpmap:0 PCMU/8000\r\n"
            "",
            /* Receive Bob's answer: */
            "v=0\r\n"
            "o=bob 2808844564 2808844563 IN IP4 host.biloxi.example.com\r\n"
            "s=bob\r\n"
            "c=IN IP4 host.biloxi.example.com\r\n"
            "t=0 0\r\n"
            "m=audio 7000 RTP/AVP 0\r\n"
            "a=rtpmap:0 PCMU/8000\r\n"
            "m=audio 0 RTP/AVP 0\r\n"
            "a=rtpmap:0 PCMU/8000\r\n"
            "m=video 0 RTP/AVP 31\r\n"
            "a=rtpmap:31 H261/90000\r\n"
            "",
            /* Alice's local SDP should be: */
            "v=0\r\n"
            "o=alice 2890844526 2890844527 IN IP4 host.atlanta.example.com\r\n"
            "s=alice\r\n"
            "c=IN IP4 host.atlanta.example.com\r\n"
            "t=0 0\r\n"
            "m=audio 5200 RTP/AVP 0\r\n"
            "a=rtpmap:0 PCMU/8000\r\n"
            "m=audio 0 RTP/AVP 0\r\n"
            //"a=rtpmap:0 PCMU/8000\r\n"      /* <-- this is not necessary (port 0) */
            "m=video 0 RTP/AVP 31\r\n"
            //"a=rtpmap:31 H261/90000\r\n"    /* <-- this is not necessary (port 0) */
            "",
          }
        }
    },

    /* test 15: */
    {
        /*********************************************************************
         * Ticket #527: More lenient SDP negotiator.
         */

        "Ticket #527 scenario #2: Modify offer - unordered m= lines",
        2,
        {
          {
            LOCAL_OFFER,
            /* Alice sends offer: */
            "v=0\r\n"
            "o=alice 2890844526 2890844526 IN IP4 host.atlanta.example.com\r\n"
            "s=alice\r\n"
            "c=IN IP4 host.atlanta.example.com\r\n"
            "t=0 0\r\n"
            "m=audio 3000 RTP/AVP 0\r\n"
            "a=rtpmap:0 PCMU/8000\r\n"
            "m=video 3200 RTP/AVP 31\r\n"
            "a=rtpmap:31 H261/90000\r\n"
            "",
            /* Receive Bob's answer: */
            "v=0\r\n"
            "o=bob 2808844564 2808844563 IN IP4 host.biloxi.example.com\r\n"
            "s=bob\r\n"
            "c=IN IP4 host.biloxi.example.com\r\n"
            "t=0 0\r\n"
            "m=audio 4000 RTP/AVP 0\r\n"
            "a=rtpmap:0 PCMU/8000\r\n"
            "m=video 4200 RTP/AVP 31\r\n"
            "a=rtpmap:31 H261/90000\r\n"
            "",
            /* Alice's local SDP should be: */
            "v=0\r\n"
            "o=alice 2890844526 2890844526 IN IP4 host.atlanta.example.com\r\n"
            "s=alice\r\n"
            "c=IN IP4 host.atlanta.example.com\r\n"
            "t=0 0\r\n"
            "m=audio 3000 RTP/AVP 0\r\n"
            "a=rtpmap:0 PCMU/8000\r\n"
            "m=video 3200 RTP/AVP 31\r\n"
            "a=rtpmap:31 H261/90000\r\n"
            "",
          },
          {
            LOCAL_OFFER,
            /* Alice modifies offer with unordered m= lines: */
            "v=0\r\n"
            "o=alice 2890844526 2890844526 IN IP4 host.atlanta.example.com\r\n"
            "s=alice\r\n"
            "c=IN IP4 host.atlanta.example.com\r\n"
            "t=0 0\r\n"
            "m=video 5000 RTP/AVP 31\r\n"
            "a=rtpmap:31 H261/90000\r\n"
            "m=audio 5200 RTP/AVP 0\r\n"
            "a=rtpmap:0 PCMU/8000\r\n"
            "",
            /* Receive Bob's answer: */
            "v=0\r\n"
            "o=bob 2808844564 2808844563 IN IP4 host.biloxi.example.com\r\n"
            "s=bob\r\n"
            "c=IN IP4 host.biloxi.example.com\r\n"
            "t=0 0\r\n"
            "m=audio 7000 RTP/AVP 0\r\n"
            "a=rtpmap:0 PCMU/8000\r\n"
            "m=video 2000 RTP/AVP 31\r\n"
            "a=rtpmap:31 H261/90000\r\n"
            "",
            /* Alice's local SDP should be: */
            "v=0\r\n"
            "o=alice 2890844526 2890844527 IN IP4 host.atlanta.example.com\r\n"
            "s=alice\r\n"
            "c=IN IP4 host.atlanta.example.com\r\n"
            "t=0 0\r\n"
            "m=audio 5200 RTP/AVP 0\r\n"
            "a=rtpmap:0 PCMU/8000\r\n"
            "m=video 5000 RTP/AVP 31\r\n"
            "a=rtpmap:31 H261/90000\r\n"
            "",
          }
        }
    },

    /* test 16: */
    {
        /*********************************************************************
         * Ticket #527: More lenient SDP negotiator.
         */

        "Ticket #527 scenario #2: Modify offer - partial & unordered streams",
        2,
        {
          {
            LOCAL_OFFER,
            /* Alice sends offer: */
            "v=0\r\n"
            "o=alice 2890844526 2890844526 IN IP4 host.atlanta.example.com\r\n"
            "s=alice\r\n"
            "c=IN IP4 host.atlanta.example.com\r\n"
            "t=0 0\r\n"
            "m=audio 3000 RTP/AVP 0\r\n"
            "a=rtpmap:0 PCMU/8000\r\n"
            "m=audio 3200 RTP/AVP 0\r\n"
            "a=rtpmap:0 PCMU/8000\r\n"
            "m=video 3400 RTP/AVP 31\r\n"
            "a=rtpmap:31 H261/90000\r\n"
            "",
            /* Receive Bob's answer: */
            "v=0\r\n"
            "o=bob 2808844564 2808844563 IN IP4 host.biloxi.example.com\r\n"
            "s=bob\r\n"
            "c=IN IP4 host.biloxi.example.com\r\n"
            "t=0 0\r\n"
            "m=audio 4000 RTP/AVP 0\r\n"
            "a=rtpmap:0 PCMU/8000\r\n"
            "m=audio 4200 RTP/AVP 0\r\n"
            "a=rtpmap:0 PCMU/8000\r\n"
            "m=video 4400 RTP/AVP 31\r\n"
            "a=rtpmap:31 H261/90000\r\n"
            "",
            /* Alice's local SDP should be: */
            "v=0\r\n"
            "o=alice 2890844526 2890844526 IN IP4 host.atlanta.example.com\r\n"
            "s=alice\r\n"
            "c=IN IP4 host.atlanta.example.com\r\n"
            "t=0 0\r\n"
            "m=audio 3000 RTP/AVP 0\r\n"
            "a=rtpmap:0 PCMU/8000\r\n"
            "m=audio 3200 RTP/AVP 0\r\n"
            "a=rtpmap:0 PCMU/8000\r\n"
            "m=video 3400 RTP/AVP 31\r\n"
            "a=rtpmap:31 H261/90000\r\n"
            "",
          },
          {
            LOCAL_OFFER,
            /* Alice modifies offer by specifying partial and unordered media: */
            "v=0\r\n"
            "o=alice 2890844526 2890844526 IN IP4 host.atlanta.example.com\r\n"
            "s=alice\r\n"
            "c=IN IP4 host.atlanta.example.com\r\n"
            "t=0 0\r\n"
            "m=video 5000 RTP/AVP 31\r\n"
            "a=rtpmap:31 H261/90000\r\n"
            "m=audio 7000 RTP/AVP 0\r\n"
            "a=rtpmap:0 PCMU/8000\r\n"
            "",
            /* Receive Bob's answer: */
            "v=0\r\n"
            "o=bob 2808844564 2808844563 IN IP4 host.biloxi.example.com\r\n"
            "s=bob\r\n"
            "c=IN IP4 host.biloxi.example.com\r\n"
            "t=0 0\r\n"
            "m=audio 4000 RTP/AVP 0\r\n"
            "a=rtpmap:0 PCMU/8000\r\n"
            "m=audio 0 RTP/AVP 0\r\n"
            "a=rtpmap:0 PCMU/8000\r\n"
            "m=video 4400 RTP/AVP 31\r\n"
            "a=rtpmap:31 H261/90000\r\n"
            "",
            /* Alice's local SDP should be: */
            "v=0\r\n"
            "o=alice 2890844526 2890844527 IN IP4 host.atlanta.example.com\r\n"
            "s=alice\r\n"
            "c=IN IP4 host.atlanta.example.com\r\n"
            "t=0 0\r\n"
            "m=audio 7000 RTP/AVP 0\r\n"
            "a=rtpmap:0 PCMU/8000\r\n"
            "m=audio 0 RTP/AVP 0\r\n"
            //"a=rtpmap:0 PCMU/8000\r\n"      /* <-- this is not necessary (port 0) */
            "m=video 5000 RTP/AVP 31\r\n"
            "a=rtpmap:31 H261/90000\r\n"
            "",
          }
        }
    },

    /* test 17: */
    {
        /*********************************************************************
         * Ticket #2088: : Handle multiple telephone-event formats.
         */

        "Ticket #2088: Handle multiple telephone-event formats",
        2,
        {
          {
            REMOTE_OFFER,
            /* Bob sends offer: */
            "v=0\r\n"
            "o=bob 2808844564 2808844563 IN IP4 host.biloxi.example.com\r\n"
            "s=bob\r\n"
            "c=IN IP4 host.biloxi.example.com\r\n"
            "t=0 0\r\n"
            "m=audio 3000 RTP/AVP 97 0 98 99\r\n"
            "a=rtpmap:97 Speex/16000\r\n"
            "a=rtpmap:0 PCMU/8000\r\n"
            "a=rtpmap:98 telephone-event/8000\r\n"
            "a=rtpmap:99 telephone-event/16000\r\n"
            "",
            /* Alice initial capability: */
            "v=0\r\n"
            "o=alice 2890844526 2890844526 IN IP4 host.atlanta.example.com\r\n"
            "s=alice\r\n"
            "c=IN IP4 host.atlanta.example.com\r\n"
            "t=0 0\r\n"
            "m=audio 4000 RTP/AVP 0 100 96 98\r\n"
            "a=rtpmap:0 PCMU/8000\r\n"
            "a=rtpmap:100 Speex/16000\r\n"
            "a=rtpmap:96 telephone-event/8000\r\n"
            "a=rtpmap:98 telephone-event/16000\r\n"
            "",
            /* Alice's local SDP should be: */
            "v=0\r\n"
            "o=alice 2890844526 2890844527 IN IP4 host.atlanta.example.com\r\n"
            "s=alice\r\n"
            "c=IN IP4 host.atlanta.example.com\r\n"
            "t=0 0\r\n"
            "m=audio 4000 RTP/AVP 97 99\r\n"
            "a=rtpmap:97 Speex/16000\r\n"
            "a=rtpmap:99 telephone-event/16000\r\n"
            "",
          },
          {
            LOCAL_OFFER,
            /* Alice updates offer */
            "v=0\r\n"
            "o=alice 2890844526 2890844528 IN IP4 host.atlanta.example.com\r\n"
            "s=alice\r\n"
            "c=IN IP4 host.atlanta.example.com\r\n"
            "t=0 0\r\n"
            "m=audio 4000 RTP/AVP 0 97 98 99\r\n"
            "a=rtpmap:0 PCMU/8000\r\n"
            "a=rtpmap:97 Speex/16000\r\n"
            "a=rtpmap:98 telephone-event/8000\r\n"
            "a=rtpmap:99 telephone-event/16000\r\n"
            "",
            /* Receive Bob's answer: */
            "v=0\r\n"
            "o=bob 2808844564 2808844563 IN IP4 host.biloxi.example.com\r\n"
            "s=bob\r\n"
            "c=IN IP4 host.biloxi.example.com\r\n"
            "t=0 0\r\n"
            "m=audio 3000 RTP/AVP 99 100\r\n"
            "a=rtpmap:99 Speex/16000\r\n"
            "a=rtpmap:100 telephone-event/16000\r\n"
            "",
            /* Alice's local SDP should be: */
            "v=0\r\n"
            "o=alice 2890844526 2890844528 IN IP4 host.atlanta.example.com\r\n"
            "s=alice\r\n"
            "c=IN IP4 host.atlanta.example.com\r\n"
            "t=0 0\r\n"
            "m=audio 4000 RTP/AVP 97 99\r\n"
            "a=rtpmap:97 Speex/16000\r\n"
            "a=rtpmap:99 telephone-event/16000\r\n"
            "",
          }
        }
    },

};

static const char *find_diff(const char *s1, const char *s2,
                             int *line)
{
    *line = 1;
    while (*s2 && *s1) {
        if (*s2 != *s1)
            return s2;
        if (*s2 == '\n')
            ++*line;
        ++s2, ++s1;
    }

    return s2;
}

static int compare_sdp_string(const char *cmp_title,
                              const char *title1,
                              const pjmedia_sdp_session *sdp1,
                              const char *title2,
                              const pjmedia_sdp_session *sdp2,
                              pj_status_t logical_cmp)
{
    char sdpbuf1[1024], sdpbuf2[1024];
    pj_ssize_t len1, len2;

    len1 = pjmedia_sdp_print(sdp1, sdpbuf1, sizeof(sdpbuf1));
    if (len1 < 1) {
        PJ_LOG(3,(THIS_FILE,"   error: printing sdp1"));
        return -1;
    }
    sdpbuf1[len1] = '\0';

    len2 = pjmedia_sdp_print(sdp2, sdpbuf2, sizeof(sdpbuf2));
    if (len2 < 1) {
        PJ_LOG(3,(THIS_FILE,"   error: printing sdp2"));
        return -1;
    }
    sdpbuf2[len2] = '\0';

    if (logical_cmp != PJ_SUCCESS) {
        char errbuf[80];

        pjmedia_strerror(logical_cmp, errbuf, sizeof(errbuf));

        PJ_LOG(3,(THIS_FILE,"%s mismatch: %s\n"
                            "%s:\n"
                            "%s\n"
                            "%s:\n"
                            "%s\n",
                            cmp_title,
                            errbuf,
                            title1, sdpbuf1, 
                            title2, sdpbuf2));

        return -1;

    } else if (strcmp(sdpbuf1, sdpbuf2) != 0) {
        int line;
        const char *diff;

        PJ_LOG(3,(THIS_FILE,"%s mismatch:\n"
                            "%s:\n"
                            "%s\n"
                            "%s:\n"
                            "%s\n",
                            cmp_title,
                            title1, sdpbuf1, 
                            title2, sdpbuf2));

        diff = find_diff(sdpbuf1, sdpbuf2, &line);
        PJ_LOG(3,(THIS_FILE,"Difference: line %d:\n"
                            "%s",
                            line, diff));
        return -1;

    } else {
        return 0;
    }

}

static int offer_answer_test(pj_pool_t *pool, pjmedia_sdp_neg **p_neg,
                             struct offer_answer *oa)
{
    pjmedia_sdp_session *sdp1;
    pjmedia_sdp_neg *neg;
    pj_status_t status;

    status = pjmedia_sdp_parse(pool, oa->sdp1, pj_ansi_strlen(oa->sdp1),
                                &sdp1);
    if (status != PJ_SUCCESS) {
        app_perror(status, "   error: unexpected parse status for sdp1");
        return -10;
    }

    status = pjmedia_sdp_validate(sdp1);
    if (status != PJ_SUCCESS) {
        app_perror(status, "   error: sdp1 validation failed");
        return -15;
    }

    neg = *p_neg;

    if (oa->type == LOCAL_OFFER) {
        
        /* 
         * Local creates offer first. 
         */
        pjmedia_sdp_session *sdp2, *sdp3;
        const pjmedia_sdp_session *active;

        if (neg == NULL) {
            /* Create negotiator with local offer. */
            status = pjmedia_sdp_neg_create_w_local_offer(pool, sdp1, &neg);
            if (status != PJ_SUCCESS) {
                app_perror(status, "   error: pjmedia_sdp_neg_create_w_local_offer");
                return -20;
            }
            *p_neg = neg;

        } else {
            /* Modify local offer */
            status = pjmedia_sdp_neg_modify_local_offer(pool, neg, sdp1);
            if (status != PJ_SUCCESS) {
                app_perror(status, "   error: pjmedia_sdp_neg_modify_local_offer");
                return -30;
            }
        }

        /* Parse and validate remote answer */
        status = pjmedia_sdp_parse(pool, oa->sdp2, pj_ansi_strlen(oa->sdp2),
                                   &sdp2);
        if (status != PJ_SUCCESS) {
            app_perror(status, "   error: parsing sdp2");
            return -40;
        }

        status = pjmedia_sdp_validate(sdp2);
        if (status != PJ_SUCCESS) {
            app_perror(status, "   error: sdp2 validation failed");
            return -50;
        }

        /* Give the answer to negotiator. */
        status = pjmedia_sdp_neg_set_remote_answer(pool, neg, sdp2);
        if (status != PJ_SUCCESS) {
            app_perror(status, "   error: pjmedia_sdp_neg_rx_remote_answer");
            return -60;
        }

        /* Negotiate remote answer with local answer */
        status = pjmedia_sdp_neg_negotiate(pool, neg, 0);
        if (status != PJ_SUCCESS) {
            app_perror(status, "   error: pjmedia_sdp_neg_negotiate");
            return -70;
        }

        /* Get the local active media. */
        status = pjmedia_sdp_neg_get_active_local(neg, &active);
        if (status != PJ_SUCCESS) {
            app_perror(status, "   error: pjmedia_sdp_neg_get_local");
            return -80;
        }

        /* Parse and validate the correct active media. */
        status = pjmedia_sdp_parse(pool, oa->sdp3, pj_ansi_strlen(oa->sdp3),
                                   &sdp3);
        if (status != PJ_SUCCESS) {
            app_perror(status, "   error: parsing sdp3");
            return -90;
        }

        status = pjmedia_sdp_validate(sdp3);
        if (status != PJ_SUCCESS) {
            app_perror(status, "   error: sdp3 validation failed");
            return -100;
        }

        /* Compare active with sdp3 */
        status = pjmedia_sdp_session_cmp(active, sdp3, 0);
        if (status != PJ_SUCCESS) {
            app_perror(status, "   error: active local comparison mismatch");
            compare_sdp_string("Logical cmp after negotiatin remote answer",
                               "Active local sdp from negotiator", active,
                               "The correct active local sdp", sdp3,
                               status);
            return -110;
        }

        /* Compare the string representation oa both sdps */
        status = compare_sdp_string("String cmp after negotiatin remote answer",
                                    "Active local sdp from negotiator", active,
                                    "The correct active local sdp", sdp3,
                                    PJ_SUCCESS);
        if (status != 0)
            return -120;

    } else {
        /* 
         * Remote creates offer first. 
         */

        pjmedia_sdp_session *sdp2 = NULL, *sdp3;
        const pjmedia_sdp_session *answer;

        if (oa->sdp2) {
            /* Parse and validate initial local capability */
            status = pjmedia_sdp_parse(pool, oa->sdp2, pj_ansi_strlen(oa->sdp2),
                                       &sdp2);
            if (status != PJ_SUCCESS) {
                app_perror(status, "   error: parsing sdp2");
                return -200;
            }

            status = pjmedia_sdp_validate(sdp2);
            if (status != PJ_SUCCESS) {
                app_perror(status, "   error: sdp2 validation failed");
                return -210;
            }
        } else if (neg) {
            const pjmedia_sdp_session *lsdp;
            status = pjmedia_sdp_neg_get_active_local(neg, &lsdp);
            if (status != PJ_SUCCESS) {
                app_perror(status, 
                           "   error: pjmedia_sdp_neg_get_active_local");
                return -215;
            }
            sdp2 = (pjmedia_sdp_session*)lsdp;
        }

        if (neg == NULL) {
            /* Create negotiator with remote offer. */
            status = pjmedia_sdp_neg_create_w_remote_offer(pool, sdp2, sdp1, &neg);
            if (status != PJ_SUCCESS) {
                app_perror(status, "   error: pjmedia_sdp_neg_create_w_remote_offer");
                return -220;
            }
            *p_neg = neg;

        } else {
            /* Received subsequent offer from remote. */
            status = pjmedia_sdp_neg_set_remote_offer(pool, neg, sdp1);
            if (status != PJ_SUCCESS) {
                app_perror(status, "   error: pjmedia_sdp_neg_rx_remote_offer");
                return -230;
            }

            status = pjmedia_sdp_neg_set_local_answer(pool, neg, sdp2);
            if (status != PJ_SUCCESS) {
                app_perror(status, "   error: pjmedia_sdp_neg_set_local_answer");
                return -235;
            }
        }

        /* Negotiate. */
        status = pjmedia_sdp_neg_negotiate(pool, neg, 0);
        if (status != PJ_SUCCESS) {
            app_perror(status, "   error: pjmedia_sdp_neg_negotiate");
            return -240;
        }
        
        /* Get our answer. */
        status = pjmedia_sdp_neg_get_active_local(neg, &answer);
        if (status != PJ_SUCCESS) {
            app_perror(status, "   error: pjmedia_sdp_neg_get_local");
            return -250;
        }

        /* Parse the correct answer. */
        status = pjmedia_sdp_parse(pool, oa->sdp3, pj_ansi_strlen(oa->sdp3),
                                   &sdp3);
        if (status != PJ_SUCCESS) {
            app_perror(status, "   error: parsing sdp3");
            return -260;
        }

        /* Validate the correct answer. */
        status = pjmedia_sdp_validate(sdp3);
        if (status != PJ_SUCCESS) {
            app_perror(status, "   error: sdp3 validation failed");
            return -270;
        }

        /* Compare answer from negotiator and the correct answer */
        status = pjmedia_sdp_session_cmp(sdp3, answer, 0);
        if (status != PJ_SUCCESS) {
            compare_sdp_string("Logical cmp after negotiating remote offer",
                               "Local answer from negotiator", answer,
                               "The correct local answer", sdp3,
                               status);

            return -280;
        }

        /* Compare the string representation oa both answers */
        status = compare_sdp_string("String cmp after negotiating remote offer",
                                    "Local answer from negotiator", answer,
                                    "The correct local answer", sdp3,
                                    PJ_SUCCESS);
        if (status != 0)
            return -290;

    }

    return 0;
}

static int perform_test(pj_pool_t *pool, int test_index)
{
    pjmedia_sdp_neg *neg = NULL;
    unsigned i;
    int rc;

    for (i=0; i<test[test_index].offer_answer_count; ++i) {

        rc = offer_answer_test(pool, &neg, &test[test_index].offer_answer[i]);
        if (rc != 0) {
            PJ_LOG(3,(THIS_FILE, "  test %d offer answer %d failed with status %d",
                      test_index, i, rc));
            return rc;
        }
    }

    return 0;
}

int sdp_neg_test()
{
    unsigned i;
    int status;

    for (i=START_TEST; i<PJ_ARRAY_SIZE(test); ++i) {
        pj_pool_t *pool;

        pool = pj_pool_create(mem, "sdp_neg_test", 4000, 4000, NULL);
        if (!pool)
            return PJ_ENOMEM;

        PJ_LOG(3,(THIS_FILE,"  test %d: %s", i, test[i].title));
        status = perform_test(pool, i);

        pj_pool_release(pool);

        if (status != 0) {
            return status;
        }
    }

    return 0;
}

