/* $Id: sdp_neg_test.c 3198 2010-06-04 13:41:34Z nanang $ */
/* 
 * Copyright (C) 2008-2009 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 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 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"
	    "",
	  }
	}
    },

};

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;
}

