Home | History | Annotate | Download | only in webrtc
      1 /*
      2  * libjingle
      3  * Copyright 2011, Google Inc.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions are met:
      7  *
      8  *  1. Redistributions of source code must retain the above copyright notice,
      9  *     this list of conditions and the following disclaimer.
     10  *  2. Redistributions in binary form must reproduce the above copyright notice,
     11  *     this list of conditions and the following disclaimer in the documentation
     12  *     and/or other materials provided with the distribution.
     13  *  3. The name of the author may not be used to endorse or promote products
     14  *     derived from this software without specific prior written permission.
     15  *
     16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
     17  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
     18  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
     19  * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     20  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
     22  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
     23  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
     24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
     25  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     26  */
     27 
     28 #include <set>
     29 #include <string>
     30 #include <vector>
     31 
     32 #include "talk/app/webrtc/jsepsessiondescription.h"
     33 #include "talk/app/webrtc/webrtcsdp.h"
     34 #include "talk/base/gunit.h"
     35 #include "talk/base/logging.h"
     36 #include "talk/base/messagedigest.h"
     37 #include "talk/base/scoped_ptr.h"
     38 #include "talk/base/sslfingerprint.h"
     39 #include "talk/base/stringencode.h"
     40 #include "talk/base/stringutils.h"
     41 #include "talk/media/base/constants.h"
     42 #include "talk/p2p/base/constants.h"
     43 #include "talk/session/media/mediasession.h"
     44 
     45 using cricket::AudioCodec;
     46 using cricket::AudioContentDescription;
     47 using cricket::Candidate;
     48 using cricket::ContentInfo;
     49 using cricket::CryptoParams;
     50 using cricket::ContentGroup;
     51 using cricket::DataCodec;
     52 using cricket::DataContentDescription;
     53 using cricket::ICE_CANDIDATE_COMPONENT_RTCP;
     54 using cricket::ICE_CANDIDATE_COMPONENT_RTP;
     55 using cricket::kFecSsrcGroupSemantics;
     56 using cricket::LOCAL_PORT_TYPE;
     57 using cricket::NS_JINGLE_DRAFT_SCTP;
     58 using cricket::NS_JINGLE_ICE_UDP;
     59 using cricket::NS_JINGLE_RTP;
     60 using cricket::RtpHeaderExtension;
     61 using cricket::RELAY_PORT_TYPE;
     62 using cricket::SessionDescription;
     63 using cricket::StreamParams;
     64 using cricket::STUN_PORT_TYPE;
     65 using cricket::TransportDescription;
     66 using cricket::TransportInfo;
     67 using cricket::VideoCodec;
     68 using cricket::VideoContentDescription;
     69 using webrtc::IceCandidateCollection;
     70 using webrtc::IceCandidateInterface;
     71 using webrtc::JsepIceCandidate;
     72 using webrtc::JsepSessionDescription;
     73 using webrtc::SdpParseError;
     74 using webrtc::SessionDescriptionInterface;
     75 
     76 typedef std::vector<AudioCodec> AudioCodecs;
     77 typedef std::vector<Candidate> Candidates;
     78 
     79 static const char kSessionTime[] = "t=0 0\r\n";
     80 static const uint32 kCandidatePriority = 2130706432U;  // pref = 1.0
     81 static const char kCandidateUfragVoice[] = "ufrag_voice";
     82 static const char kCandidatePwdVoice[] = "pwd_voice";
     83 static const char kAttributeIcePwdVoice[] = "a=ice-pwd:pwd_voice\r\n";
     84 static const char kCandidateUfragVideo[] = "ufrag_video";
     85 static const char kCandidatePwdVideo[] = "pwd_video";
     86 static const char kCandidateUfragData[] = "ufrag_data";
     87 static const char kCandidatePwdData[] = "pwd_data";
     88 static const char kAttributeIcePwdVideo[] = "a=ice-pwd:pwd_video\r\n";
     89 static const uint32 kCandidateGeneration = 2;
     90 static const char kCandidateFoundation1[] = "a0+B/1";
     91 static const char kCandidateFoundation2[] = "a0+B/2";
     92 static const char kCandidateFoundation3[] = "a0+B/3";
     93 static const char kCandidateFoundation4[] = "a0+B/4";
     94 static const char kAttributeCryptoVoice[] =
     95     "a=crypto:1 AES_CM_128_HMAC_SHA1_32 "
     96     "inline:NzB4d1BINUAvLEw6UzF3WSJ+PSdFcGdUJShpX1Zj|2^20|1:32 "
     97     "dummy_session_params\r\n";
     98 static const char kAttributeCryptoVideo[] =
     99     "a=crypto:1 AES_CM_128_HMAC_SHA1_80 "
    100     "inline:d0RmdmcmVCspeEc3QGZiNWpVLFJhQX1cfHAwJSoj|2^20|1:32\r\n";
    101 static const char kFingerprint[] = "a=fingerprint:sha-1 "
    102     "4A:AD:B9:B1:3F:82:18:3B:54:02:12:DF:3E:5D:49:6B:19:E5:7C:AB\r\n";
    103 static const int kExtmapId = 1;
    104 static const char kExtmapUri[] = "http://example.com/082005/ext.htm#ttime";
    105 static const char kExtmap[] =
    106     "a=extmap:1 http://example.com/082005/ext.htm#ttime\r\n";
    107 static const char kExtmapWithDirectionAndAttribute[] =
    108     "a=extmap:1/sendrecv http://example.com/082005/ext.htm#ttime a1 a2\r\n";
    109 
    110 static const uint8 kIdentityDigest[] = {0x4A, 0xAD, 0xB9, 0xB1,
    111                                         0x3F, 0x82, 0x18, 0x3B,
    112                                         0x54, 0x02, 0x12, 0xDF,
    113                                         0x3E, 0x5D, 0x49, 0x6B,
    114                                         0x19, 0xE5, 0x7C, 0xAB};
    115 
    116 struct CodecParams {
    117   int max_ptime;
    118   int ptime;
    119   int min_ptime;
    120   int sprop_stereo;
    121   int stereo;
    122   int useinband;
    123   int maxaveragebitrate;
    124 };
    125 
    126 // Reference sdp string
    127 static const char kSdpFullString[] =
    128     "v=0\r\n"
    129     "o=- 18446744069414584320 18446462598732840960 IN IP4 127.0.0.1\r\n"
    130     "s=-\r\n"
    131     "t=0 0\r\n"
    132     "a=msid-semantic: WMS local_stream_1 local_stream_2\r\n"
    133     "m=audio 2345 RTP/SAVPF 111 103 104\r\n"
    134     "c=IN IP4 74.125.127.126\r\n"
    135     "a=rtcp:2347 IN IP4 74.125.127.126\r\n"
    136     "a=candidate:a0+B/1 1 udp 2130706432 192.168.1.5 1234 typ host "
    137     "generation 2\r\n"
    138     "a=candidate:a0+B/1 2 udp 2130706432 192.168.1.5 1235 typ host "
    139     "generation 2\r\n"
    140     "a=candidate:a0+B/2 1 udp 2130706432 ::1 1238 typ host "
    141     "generation 2\r\n"
    142     "a=candidate:a0+B/2 2 udp 2130706432 ::1 1239 typ host "
    143     "generation 2\r\n"
    144     "a=candidate:a0+B/3 1 udp 2130706432 74.125.127.126 2345 typ srflx "
    145     "raddr 192.168.1.5 rport 2346 "
    146     "generation 2\r\n"
    147     "a=candidate:a0+B/3 2 udp 2130706432 74.125.127.126 2347 typ srflx "
    148     "raddr 192.168.1.5 rport 2348 "
    149     "generation 2\r\n"
    150     "a=ice-ufrag:ufrag_voice\r\na=ice-pwd:pwd_voice\r\n"
    151     "a=mid:audio_content_name\r\n"
    152     "a=sendrecv\r\n"
    153     "a=rtcp-mux\r\n"
    154     "a=crypto:1 AES_CM_128_HMAC_SHA1_32 "
    155     "inline:NzB4d1BINUAvLEw6UzF3WSJ+PSdFcGdUJShpX1Zj|2^20|1:32 "
    156     "dummy_session_params\r\n"
    157     "a=rtpmap:111 opus/48000/2\r\n"
    158     "a=rtpmap:103 ISAC/16000\r\n"
    159     "a=rtpmap:104 CELT/32000/2\r\n"
    160     "a=ssrc:1 cname:stream_1_cname\r\n"
    161     "a=ssrc:1 msid:local_stream_1 audio_track_id_1\r\n"
    162     "a=ssrc:1 mslabel:local_stream_1\r\n"
    163     "a=ssrc:1 label:audio_track_id_1\r\n"
    164     "a=ssrc:4 cname:stream_2_cname\r\n"
    165     "a=ssrc:4 msid:local_stream_2 audio_track_id_2\r\n"
    166     "a=ssrc:4 mslabel:local_stream_2\r\n"
    167     "a=ssrc:4 label:audio_track_id_2\r\n"
    168     "m=video 3457 RTP/SAVPF 120\r\n"
    169     "c=IN IP4 74.125.224.39\r\n"
    170     "a=rtcp:3456 IN IP4 74.125.224.39\r\n"
    171     "a=candidate:a0+B/1 2 udp 2130706432 192.168.1.5 1236 typ host "
    172     "generation 2\r\n"
    173     "a=candidate:a0+B/1 1 udp 2130706432 192.168.1.5 1237 typ host "
    174     "generation 2\r\n"
    175     "a=candidate:a0+B/2 2 udp 2130706432 ::1 1240 typ host "
    176     "generation 2\r\n"
    177     "a=candidate:a0+B/2 1 udp 2130706432 ::1 1241 typ host "
    178     "generation 2\r\n"
    179     "a=candidate:a0+B/4 2 udp 2130706432 74.125.224.39 3456 typ relay "
    180     "generation 2\r\n"
    181     "a=candidate:a0+B/4 1 udp 2130706432 74.125.224.39 3457 typ relay "
    182     "generation 2\r\n"
    183     "a=ice-ufrag:ufrag_video\r\na=ice-pwd:pwd_video\r\n"
    184     "a=mid:video_content_name\r\n"
    185     "a=sendrecv\r\n"
    186     "a=crypto:1 AES_CM_128_HMAC_SHA1_80 "
    187     "inline:d0RmdmcmVCspeEc3QGZiNWpVLFJhQX1cfHAwJSoj|2^20|1:32\r\n"
    188     "a=rtpmap:120 VP8/90000\r\n"
    189     "a=ssrc:2 cname:stream_1_cname\r\n"
    190     "a=ssrc:2 msid:local_stream_1 video_track_id_1\r\n"
    191     "a=ssrc:2 mslabel:local_stream_1\r\n"
    192     "a=ssrc:2 label:video_track_id_1\r\n"
    193     "a=ssrc:3 cname:stream_1_cname\r\n"
    194     "a=ssrc:3 msid:local_stream_1 video_track_id_2\r\n"
    195     "a=ssrc:3 mslabel:local_stream_1\r\n"
    196     "a=ssrc:3 label:video_track_id_2\r\n"
    197     "a=ssrc-group:FEC 5 6\r\n"
    198     "a=ssrc:5 cname:stream_2_cname\r\n"
    199     "a=ssrc:5 msid:local_stream_2 video_track_id_3\r\n"
    200     "a=ssrc:5 mslabel:local_stream_2\r\n"
    201     "a=ssrc:5 label:video_track_id_3\r\n"
    202     "a=ssrc:6 cname:stream_2_cname\r\n"
    203     "a=ssrc:6 msid:local_stream_2 video_track_id_3\r\n"
    204     "a=ssrc:6 mslabel:local_stream_2\r\n"
    205     "a=ssrc:6 label:video_track_id_3\r\n";
    206 
    207 // SDP reference string without the candidates.
    208 static const char kSdpString[] =
    209     "v=0\r\n"
    210     "o=- 18446744069414584320 18446462598732840960 IN IP4 127.0.0.1\r\n"
    211     "s=-\r\n"
    212     "t=0 0\r\n"
    213     "a=msid-semantic: WMS local_stream_1 local_stream_2\r\n"
    214     "m=audio 1 RTP/SAVPF 111 103 104\r\n"
    215     "c=IN IP4 0.0.0.0\r\n"
    216     "a=rtcp:1 IN IP4 0.0.0.0\r\n"
    217     "a=ice-ufrag:ufrag_voice\r\na=ice-pwd:pwd_voice\r\n"
    218     "a=mid:audio_content_name\r\n"
    219     "a=sendrecv\r\n"
    220     "a=rtcp-mux\r\n"
    221     "a=crypto:1 AES_CM_128_HMAC_SHA1_32 "
    222     "inline:NzB4d1BINUAvLEw6UzF3WSJ+PSdFcGdUJShpX1Zj|2^20|1:32 "
    223     "dummy_session_params\r\n"
    224     "a=rtpmap:111 opus/48000/2\r\n"
    225     "a=rtpmap:103 ISAC/16000\r\n"
    226     "a=rtpmap:104 CELT/32000/2\r\n"
    227     "a=ssrc:1 cname:stream_1_cname\r\n"
    228     "a=ssrc:1 msid:local_stream_1 audio_track_id_1\r\n"
    229     "a=ssrc:1 mslabel:local_stream_1\r\n"
    230     "a=ssrc:1 label:audio_track_id_1\r\n"
    231     "a=ssrc:4 cname:stream_2_cname\r\n"
    232     "a=ssrc:4 msid:local_stream_2 audio_track_id_2\r\n"
    233     "a=ssrc:4 mslabel:local_stream_2\r\n"
    234     "a=ssrc:4 label:audio_track_id_2\r\n"
    235     "m=video 1 RTP/SAVPF 120\r\n"
    236     "c=IN IP4 0.0.0.0\r\n"
    237     "a=rtcp:1 IN IP4 0.0.0.0\r\n"
    238     "a=ice-ufrag:ufrag_video\r\na=ice-pwd:pwd_video\r\n"
    239     "a=mid:video_content_name\r\n"
    240     "a=sendrecv\r\n"
    241     "a=crypto:1 AES_CM_128_HMAC_SHA1_80 "
    242     "inline:d0RmdmcmVCspeEc3QGZiNWpVLFJhQX1cfHAwJSoj|2^20|1:32\r\n"
    243     "a=rtpmap:120 VP8/90000\r\n"
    244     "a=ssrc:2 cname:stream_1_cname\r\n"
    245     "a=ssrc:2 msid:local_stream_1 video_track_id_1\r\n"
    246     "a=ssrc:2 mslabel:local_stream_1\r\n"
    247     "a=ssrc:2 label:video_track_id_1\r\n"
    248     "a=ssrc:3 cname:stream_1_cname\r\n"
    249     "a=ssrc:3 msid:local_stream_1 video_track_id_2\r\n"
    250     "a=ssrc:3 mslabel:local_stream_1\r\n"
    251     "a=ssrc:3 label:video_track_id_2\r\n"
    252     "a=ssrc-group:FEC 5 6\r\n"
    253     "a=ssrc:5 cname:stream_2_cname\r\n"
    254     "a=ssrc:5 msid:local_stream_2 video_track_id_3\r\n"
    255     "a=ssrc:5 mslabel:local_stream_2\r\n"
    256     "a=ssrc:5 label:video_track_id_3\r\n"
    257     "a=ssrc:6 cname:stream_2_cname\r\n"
    258     "a=ssrc:6 msid:local_stream_2 video_track_id_3\r\n"
    259     "a=ssrc:6 mslabel:local_stream_2\r\n"
    260     "a=ssrc:6 label:video_track_id_3\r\n";
    261 
    262 static const char kSdpRtpDataChannelString[] =
    263     "m=application 1 RTP/SAVPF 101\r\n"
    264     "c=IN IP4 0.0.0.0\r\n"
    265     "a=rtcp:1 IN IP4 0.0.0.0\r\n"
    266     "a=ice-ufrag:ufrag_data\r\n"
    267     "a=ice-pwd:pwd_data\r\n"
    268     "a=mid:data_content_name\r\n"
    269     "a=sendrecv\r\n"
    270     "a=crypto:1 AES_CM_128_HMAC_SHA1_80 "
    271     "inline:FvLcvU2P3ZWmQxgPAgcDu7Zl9vftYElFOjEzhWs5\r\n"
    272     "a=rtpmap:101 google-data/90000\r\n"
    273     "a=ssrc:10 cname:data_channel_cname\r\n"
    274     "a=ssrc:10 msid:data_channel data_channeld0\r\n"
    275     "a=ssrc:10 mslabel:data_channel\r\n"
    276     "a=ssrc:10 label:data_channeld0\r\n";
    277 
    278 static const char kSdpSctpDataChannelString[] =
    279     "m=application 1 DTLS/SCTP 5000\r\n"
    280     "c=IN IP4 0.0.0.0\r\n"
    281     "a=ice-ufrag:ufrag_data\r\n"
    282     "a=ice-pwd:pwd_data\r\n"
    283     "a=mid:data_content_name\r\n"
    284     "a=fmtp:5000 protocol=webrtc-datachannel; streams=10\r\n";
    285 
    286 static const char kSdpSctpDataChannelWithCandidatesString[] =
    287     "m=application 2345 DTLS/SCTP 5000\r\n"
    288     "c=IN IP4 74.125.127.126\r\n"
    289     "a=candidate:a0+B/1 1 udp 2130706432 192.168.1.5 1234 typ host "
    290     "generation 2\r\n"
    291     "a=candidate:a0+B/2 1 udp 2130706432 ::1 1238 typ host "
    292     "generation 2\r\n"
    293     "a=candidate:a0+B/3 1 udp 2130706432 74.125.127.126 2345 typ srflx "
    294     "raddr 192.168.1.5 rport 2346 "
    295     "generation 2\r\n"
    296     "a=ice-ufrag:ufrag_data\r\n"
    297     "a=ice-pwd:pwd_data\r\n"
    298     "a=mid:data_content_name\r\n"
    299     "a=fmtp:5000 protocol=webrtc-datachannel; streams=10\r\n";
    300 
    301 
    302 // One candidate reference string as per W3c spec.
    303 // candidate:<blah> not a=candidate:<blah>CRLF
    304 static const char kRawCandidate[] =
    305     "candidate:a0+B/1 1 udp 2130706432 192.168.1.5 1234 typ host generation 2";
    306 // One candidate reference string.
    307 static const char kSdpOneCandidate[] =
    308     "a=candidate:a0+B/1 1 udp 2130706432 192.168.1.5 1234 typ host "
    309     "generation 2\r\n";
    310 
    311 // One candidate reference string.
    312 static const char kSdpOneCandidateOldFormat[] =
    313     "a=candidate:a0+B/1 1 udp 2130706432 192.168.1.5 1234 typ host network_name"
    314     " eth0 username user_rtp password password_rtp generation 2\r\n";
    315 
    316 // Session id and version
    317 static const char kSessionId[] = "18446744069414584320";
    318 static const char kSessionVersion[] = "18446462598732840960";
    319 
    320 // Ice options
    321 static const char kIceOption1[] = "iceoption1";
    322 static const char kIceOption2[] = "iceoption2";
    323 static const char kIceOption3[] = "iceoption3";
    324 
    325 // Content name
    326 static const char kAudioContentName[] = "audio_content_name";
    327 static const char kVideoContentName[] = "video_content_name";
    328 static const char kDataContentName[] = "data_content_name";
    329 
    330 // MediaStream 1
    331 static const char kStreamLabel1[] = "local_stream_1";
    332 static const char kStream1Cname[] = "stream_1_cname";
    333 static const char kAudioTrackId1[] = "audio_track_id_1";
    334 static const uint32 kAudioTrack1Ssrc = 1;
    335 static const char kVideoTrackId1[] = "video_track_id_1";
    336 static const uint32 kVideoTrack1Ssrc = 2;
    337 static const char kVideoTrackId2[] = "video_track_id_2";
    338 static const uint32 kVideoTrack2Ssrc = 3;
    339 
    340 // MediaStream 2
    341 static const char kStreamLabel2[] = "local_stream_2";
    342 static const char kStream2Cname[] = "stream_2_cname";
    343 static const char kAudioTrackId2[] = "audio_track_id_2";
    344 static const uint32 kAudioTrack2Ssrc = 4;
    345 static const char kVideoTrackId3[] = "video_track_id_3";
    346 static const uint32 kVideoTrack3Ssrc = 5;
    347 static const uint32 kVideoTrack4Ssrc = 6;
    348 
    349 // DataChannel
    350 static const char kDataChannelLabel[] = "data_channel";
    351 static const char kDataChannelMsid[] = "data_channeld0";
    352 static const char kDataChannelCname[] = "data_channel_cname";
    353 static const uint32 kDataChannelSsrc = 10;
    354 
    355 // Candidate
    356 static const char kDummyMid[] = "dummy_mid";
    357 static const int kDummyIndex = 123;
    358 
    359 // Misc
    360 static const char kDummyString[] = "dummy";
    361 
    362 // Helper functions
    363 
    364 static bool SdpDeserialize(const std::string& message,
    365                            JsepSessionDescription* jdesc) {
    366   return webrtc::SdpDeserialize(message, jdesc, NULL);
    367 }
    368 
    369 static bool SdpDeserializeCandidate(const std::string& message,
    370                                     JsepIceCandidate* candidate) {
    371   return webrtc::SdpDeserializeCandidate(message, candidate, NULL);
    372 }
    373 
    374 // Add some extra |newlines| to the |message| after |line|.
    375 static void InjectAfter(const std::string& line,
    376                         const std::string& newlines,
    377                         std::string* message) {
    378   const std::string tmp = line + newlines;
    379   talk_base::replace_substrs(line.c_str(), line.length(),
    380                              tmp.c_str(), tmp.length(), message);
    381 }
    382 
    383 static void Replace(const std::string& line,
    384                     const std::string& newlines,
    385                     std::string* message) {
    386   talk_base::replace_substrs(line.c_str(), line.length(),
    387                              newlines.c_str(), newlines.length(), message);
    388 }
    389 
    390 static void ReplaceAndTryToParse(const char* search, const char* replace) {
    391   JsepSessionDescription desc(kDummyString);
    392   std::string sdp = kSdpFullString;
    393   Replace(search, replace, &sdp);
    394   SdpParseError error;
    395   bool ret = webrtc::SdpDeserialize(sdp, &desc, &error);
    396   EXPECT_FALSE(ret);
    397   EXPECT_NE(std::string::npos, error.line.find(replace));
    398 }
    399 
    400 static void ReplaceDirection(cricket::MediaContentDirection direction,
    401                              std::string* message) {
    402   std::string new_direction;
    403   switch (direction) {
    404     case cricket::MD_INACTIVE:
    405       new_direction = "a=inactive";
    406       break;
    407     case cricket::MD_SENDONLY:
    408       new_direction = "a=sendonly";
    409       break;
    410     case cricket::MD_RECVONLY:
    411       new_direction = "a=recvonly";
    412       break;
    413     case cricket::MD_SENDRECV:
    414     default:
    415       new_direction = "a=sendrecv";
    416       break;
    417   }
    418   Replace("a=sendrecv", new_direction, message);
    419 }
    420 
    421 static void ReplaceRejected(bool audio_rejected, bool video_rejected,
    422                             std::string* message) {
    423   if (audio_rejected) {
    424     Replace("m=audio 2345", "m=audio 0", message);
    425   }
    426   if (video_rejected) {
    427     Replace("m=video 3457", "m=video 0", message);
    428   }
    429 }
    430 
    431 // WebRtcSdpTest
    432 
    433 class WebRtcSdpTest : public testing::Test {
    434  public:
    435   WebRtcSdpTest()
    436      : jdesc_(kDummyString) {
    437     // AudioContentDescription
    438     audio_desc_ = CreateAudioContentDescription();
    439     AudioCodec opus(111, "opus", 48000, 0, 2, 3);
    440     audio_desc_->AddCodec(opus);
    441     audio_desc_->AddCodec(AudioCodec(103, "ISAC", 16000, 32000, 1, 2));
    442     audio_desc_->AddCodec(AudioCodec(104, "CELT", 32000, 0, 2, 1));
    443     desc_.AddContent(kAudioContentName, NS_JINGLE_RTP, audio_desc_);
    444 
    445     // VideoContentDescription
    446     talk_base::scoped_ptr<VideoContentDescription> video(
    447         new VideoContentDescription());
    448     video_desc_ = video.get();
    449     StreamParams video_stream1;
    450     video_stream1.id = kVideoTrackId1;
    451     video_stream1.cname = kStream1Cname;
    452     video_stream1.sync_label = kStreamLabel1;
    453     video_stream1.ssrcs.push_back(kVideoTrack1Ssrc);
    454     video->AddStream(video_stream1);
    455     StreamParams video_stream2;
    456     video_stream2.id = kVideoTrackId2;
    457     video_stream2.cname = kStream1Cname;
    458     video_stream2.sync_label = kStreamLabel1;
    459     video_stream2.ssrcs.push_back(kVideoTrack2Ssrc);
    460     video->AddStream(video_stream2);
    461     StreamParams video_stream3;
    462     video_stream3.id = kVideoTrackId3;
    463     video_stream3.cname = kStream2Cname;
    464     video_stream3.sync_label = kStreamLabel2;
    465     video_stream3.ssrcs.push_back(kVideoTrack3Ssrc);
    466     video_stream3.ssrcs.push_back(kVideoTrack4Ssrc);
    467     cricket::SsrcGroup ssrc_group(kFecSsrcGroupSemantics, video_stream3.ssrcs);
    468     video_stream3.ssrc_groups.push_back(ssrc_group);
    469     video->AddStream(video_stream3);
    470     video->AddCrypto(CryptoParams(1, "AES_CM_128_HMAC_SHA1_80",
    471         "inline:d0RmdmcmVCspeEc3QGZiNWpVLFJhQX1cfHAwJSoj|2^20|1:32", ""));
    472     video->set_protocol(cricket::kMediaProtocolSavpf);
    473     video->AddCodec(VideoCodec(
    474         120,
    475         JsepSessionDescription::kDefaultVideoCodecName,
    476         JsepSessionDescription::kMaxVideoCodecWidth,
    477         JsepSessionDescription::kMaxVideoCodecHeight,
    478         JsepSessionDescription::kDefaultVideoCodecFramerate,
    479         JsepSessionDescription::kDefaultVideoCodecPreference));
    480 
    481     desc_.AddContent(kVideoContentName, NS_JINGLE_RTP,
    482                      video.release());
    483 
    484     // TransportInfo
    485     EXPECT_TRUE(desc_.AddTransportInfo(
    486         TransportInfo(kAudioContentName,
    487                       TransportDescription(NS_JINGLE_ICE_UDP,
    488                                            std::vector<std::string>(),
    489                                            kCandidateUfragVoice,
    490                                            kCandidatePwdVoice,
    491                                            cricket::ICEMODE_FULL,
    492                                            NULL, Candidates()))));
    493     EXPECT_TRUE(desc_.AddTransportInfo(
    494         TransportInfo(kVideoContentName,
    495                       TransportDescription(NS_JINGLE_ICE_UDP,
    496                                            std::vector<std::string>(),
    497                                            kCandidateUfragVideo,
    498                                            kCandidatePwdVideo,
    499                                            cricket::ICEMODE_FULL,
    500                                            NULL, Candidates()))));
    501 
    502     // v4 host
    503     int port = 1234;
    504     talk_base::SocketAddress address("192.168.1.5", port++);
    505     Candidate candidate1(
    506         "", ICE_CANDIDATE_COMPONENT_RTP, "udp", address, kCandidatePriority,
    507         "", "", LOCAL_PORT_TYPE,
    508         "", kCandidateGeneration, kCandidateFoundation1);
    509     address.SetPort(port++);
    510     Candidate candidate2(
    511         "", ICE_CANDIDATE_COMPONENT_RTCP, "udp", address, kCandidatePriority,
    512         "", "", LOCAL_PORT_TYPE,
    513         "", kCandidateGeneration, kCandidateFoundation1);
    514     address.SetPort(port++);
    515     Candidate candidate3(
    516         "", ICE_CANDIDATE_COMPONENT_RTCP, "udp", address, kCandidatePriority,
    517         "", "", LOCAL_PORT_TYPE,
    518         "", kCandidateGeneration, kCandidateFoundation1);
    519     address.SetPort(port++);
    520     Candidate candidate4(
    521         "", ICE_CANDIDATE_COMPONENT_RTP, "udp", address, kCandidatePriority,
    522         "", "", LOCAL_PORT_TYPE,
    523         "", kCandidateGeneration, kCandidateFoundation1);
    524 
    525     // v6 host
    526     talk_base::SocketAddress v6_address("::1", port++);
    527     cricket::Candidate candidate5(
    528         "", cricket::ICE_CANDIDATE_COMPONENT_RTP,
    529         "udp", v6_address, kCandidatePriority,
    530         "", "", cricket::LOCAL_PORT_TYPE,
    531         "", kCandidateGeneration, kCandidateFoundation2);
    532     v6_address.SetPort(port++);
    533     cricket::Candidate candidate6(
    534         "", cricket::ICE_CANDIDATE_COMPONENT_RTCP,
    535         "udp", v6_address, kCandidatePriority,
    536         "", "", cricket::LOCAL_PORT_TYPE,
    537         "", kCandidateGeneration, kCandidateFoundation2);
    538     v6_address.SetPort(port++);
    539     cricket::Candidate candidate7(
    540         "", cricket::ICE_CANDIDATE_COMPONENT_RTCP,
    541         "udp", v6_address, kCandidatePriority,
    542         "", "", cricket::LOCAL_PORT_TYPE,
    543         "", kCandidateGeneration, kCandidateFoundation2);
    544     v6_address.SetPort(port++);
    545     cricket::Candidate candidate8(
    546         "", cricket::ICE_CANDIDATE_COMPONENT_RTP,
    547         "udp", v6_address, kCandidatePriority,
    548         "", "", cricket::LOCAL_PORT_TYPE,
    549         "", kCandidateGeneration, kCandidateFoundation2);
    550 
    551     // stun
    552     int port_stun = 2345;
    553     talk_base::SocketAddress address_stun("74.125.127.126", port_stun++);
    554     talk_base::SocketAddress rel_address_stun("192.168.1.5", port_stun++);
    555     cricket::Candidate candidate9
    556         ("", cricket::ICE_CANDIDATE_COMPONENT_RTP,
    557          "udp", address_stun, kCandidatePriority,
    558          "", "", STUN_PORT_TYPE,
    559          "", kCandidateGeneration, kCandidateFoundation3);
    560     candidate9.set_related_address(rel_address_stun);
    561 
    562     address_stun.SetPort(port_stun++);
    563     rel_address_stun.SetPort(port_stun++);
    564     cricket::Candidate candidate10(
    565         "", cricket::ICE_CANDIDATE_COMPONENT_RTCP,
    566         "udp", address_stun, kCandidatePriority,
    567         "", "", STUN_PORT_TYPE,
    568         "", kCandidateGeneration, kCandidateFoundation3);
    569     candidate10.set_related_address(rel_address_stun);
    570 
    571     // relay
    572     int port_relay = 3456;
    573     talk_base::SocketAddress address_relay("74.125.224.39", port_relay++);
    574     cricket::Candidate candidate11(
    575         "", cricket::ICE_CANDIDATE_COMPONENT_RTCP,
    576         "udp", address_relay, kCandidatePriority,
    577         "", "",
    578         cricket::RELAY_PORT_TYPE, "",
    579         kCandidateGeneration, kCandidateFoundation4);
    580     address_relay.SetPort(port_relay++);
    581     cricket::Candidate candidate12(
    582         "", cricket::ICE_CANDIDATE_COMPONENT_RTP,
    583         "udp", address_relay, kCandidatePriority,
    584         "", "",
    585         RELAY_PORT_TYPE, "",
    586         kCandidateGeneration, kCandidateFoundation4);
    587 
    588     // voice
    589     candidates_.push_back(candidate1);
    590     candidates_.push_back(candidate2);
    591     candidates_.push_back(candidate5);
    592     candidates_.push_back(candidate6);
    593     candidates_.push_back(candidate9);
    594     candidates_.push_back(candidate10);
    595 
    596     // video
    597     candidates_.push_back(candidate3);
    598     candidates_.push_back(candidate4);
    599     candidates_.push_back(candidate7);
    600     candidates_.push_back(candidate8);
    601     candidates_.push_back(candidate11);
    602     candidates_.push_back(candidate12);
    603 
    604     jcandidate_.reset(new JsepIceCandidate(std::string("audio_content_name"),
    605                                            0, candidate1));
    606 
    607     // Set up JsepSessionDescription.
    608     jdesc_.Initialize(desc_.Copy(), kSessionId, kSessionVersion);
    609     std::string mline_id;
    610     int mline_index = 0;
    611     for (size_t i = 0; i< candidates_.size(); ++i) {
    612       // In this test, the audio m line index will be 0, and the video m line
    613       // will be 1.
    614       bool is_video = (i > 5);
    615       mline_id = is_video ? "video_content_name" : "audio_content_name";
    616       mline_index = is_video ? 1 : 0;
    617       JsepIceCandidate jice(mline_id,
    618                             mline_index,
    619                             candidates_.at(i));
    620       jdesc_.AddCandidate(&jice);
    621     }
    622   }
    623 
    624   AudioContentDescription* CreateAudioContentDescription() {
    625     AudioContentDescription* audio = new AudioContentDescription();
    626     audio->set_rtcp_mux(true);
    627     StreamParams audio_stream1;
    628     audio_stream1.id = kAudioTrackId1;
    629     audio_stream1.cname = kStream1Cname;
    630     audio_stream1.sync_label = kStreamLabel1;
    631     audio_stream1.ssrcs.push_back(kAudioTrack1Ssrc);
    632     audio->AddStream(audio_stream1);
    633     StreamParams audio_stream2;
    634     audio_stream2.id = kAudioTrackId2;
    635     audio_stream2.cname = kStream2Cname;
    636     audio_stream2.sync_label = kStreamLabel2;
    637     audio_stream2.ssrcs.push_back(kAudioTrack2Ssrc);
    638     audio->AddStream(audio_stream2);
    639     audio->AddCrypto(CryptoParams(1, "AES_CM_128_HMAC_SHA1_32",
    640         "inline:NzB4d1BINUAvLEw6UzF3WSJ+PSdFcGdUJShpX1Zj|2^20|1:32",
    641         "dummy_session_params"));
    642     audio->set_protocol(cricket::kMediaProtocolSavpf);
    643     return audio;
    644   }
    645 
    646   template <class MCD>
    647   void CompareMediaContentDescription(const MCD* cd1,
    648                                       const MCD* cd2) {
    649     // type
    650     EXPECT_EQ(cd1->type(), cd1->type());
    651 
    652     // content direction
    653     EXPECT_EQ(cd1->direction(), cd2->direction());
    654 
    655     // rtcp_mux
    656     EXPECT_EQ(cd1->rtcp_mux(), cd2->rtcp_mux());
    657 
    658     // cryptos
    659     EXPECT_EQ(cd1->cryptos().size(), cd2->cryptos().size());
    660     if (cd1->cryptos().size() != cd2->cryptos().size()) {
    661       ADD_FAILURE();
    662       return;
    663     }
    664     for (size_t i = 0; i< cd1->cryptos().size(); ++i) {
    665       const CryptoParams c1 = cd1->cryptos().at(i);
    666       const CryptoParams c2 = cd2->cryptos().at(i);
    667       EXPECT_TRUE(c1.Matches(c2));
    668       EXPECT_EQ(c1.key_params, c2.key_params);
    669       EXPECT_EQ(c1.session_params, c2.session_params);
    670     }
    671     // protocol
    672     EXPECT_EQ(cd1->protocol(), cd2->protocol());
    673 
    674     // codecs
    675     EXPECT_EQ(cd1->codecs(), cd2->codecs());
    676 
    677     // bandwidth
    678     EXPECT_EQ(cd1->bandwidth(), cd2->bandwidth());
    679 
    680     // streams
    681     EXPECT_EQ(cd1->streams(), cd2->streams());
    682 
    683     // extmap
    684     ASSERT_EQ(cd1->rtp_header_extensions().size(),
    685               cd2->rtp_header_extensions().size());
    686     for (size_t i = 0; i< cd1->rtp_header_extensions().size(); ++i) {
    687       const RtpHeaderExtension ext1 = cd1->rtp_header_extensions().at(i);
    688       const RtpHeaderExtension ext2 = cd2->rtp_header_extensions().at(i);
    689       EXPECT_EQ(ext1.uri, ext2.uri);
    690       EXPECT_EQ(ext1.id, ext2.id);
    691     }
    692 
    693     // buffered mode latency
    694     EXPECT_EQ(cd1->buffered_mode_latency(), cd2->buffered_mode_latency());
    695   }
    696 
    697 
    698   void CompareSessionDescription(const SessionDescription& desc1,
    699                                  const SessionDescription& desc2) {
    700     // Compare content descriptions.
    701     if (desc1.contents().size() != desc2.contents().size()) {
    702       ADD_FAILURE();
    703       return;
    704     }
    705     for (size_t i = 0 ; i < desc1.contents().size(); ++i) {
    706       const cricket::ContentInfo& c1 = desc1.contents().at(i);
    707       const cricket::ContentInfo& c2 = desc2.contents().at(i);
    708       // content name
    709       EXPECT_EQ(c1.name, c2.name);
    710       // content type
    711       // Note, ASSERT will return from the function, but will not stop the test.
    712       ASSERT_EQ(c1.type, c2.type);
    713 
    714       ASSERT_EQ(IsAudioContent(&c1), IsAudioContent(&c2));
    715       if (IsAudioContent(&c1)) {
    716         const AudioContentDescription* acd1 =
    717             static_cast<const AudioContentDescription*>(c1.description);
    718         const AudioContentDescription* acd2 =
    719             static_cast<const AudioContentDescription*>(c2.description);
    720         CompareMediaContentDescription<AudioContentDescription>(acd1, acd2);
    721       }
    722 
    723       ASSERT_EQ(IsVideoContent(&c1), IsVideoContent(&c2));
    724       if (IsVideoContent(&c1)) {
    725         const VideoContentDescription* vcd1 =
    726             static_cast<const VideoContentDescription*>(c1.description);
    727         const VideoContentDescription* vcd2 =
    728             static_cast<const VideoContentDescription*>(c2.description);
    729         CompareMediaContentDescription<VideoContentDescription>(vcd1, vcd2);
    730       }
    731 
    732       ASSERT_EQ(IsDataContent(&c1), IsDataContent(&c2));
    733       if (IsDataContent(&c1)) {
    734         const DataContentDescription* dcd1 =
    735             static_cast<const DataContentDescription*>(c1.description);
    736         const DataContentDescription* dcd2 =
    737             static_cast<const DataContentDescription*>(c2.description);
    738         CompareMediaContentDescription<DataContentDescription>(dcd1, dcd2);
    739       }
    740     }
    741 
    742     // group
    743     const cricket::ContentGroups groups1 = desc1.groups();
    744     const cricket::ContentGroups groups2 = desc2.groups();
    745     EXPECT_EQ(groups1.size(), groups1.size());
    746     if (groups1.size() != groups2.size()) {
    747       ADD_FAILURE();
    748       return;
    749     }
    750     for (size_t i = 0; i < groups1.size(); ++i) {
    751       const cricket::ContentGroup group1 = groups1.at(i);
    752       const cricket::ContentGroup group2 = groups2.at(i);
    753       EXPECT_EQ(group1.semantics(), group2.semantics());
    754       const cricket::ContentNames names1 = group1.content_names();
    755       const cricket::ContentNames names2 = group2.content_names();
    756       EXPECT_EQ(names1.size(), names2.size());
    757       if (names1.size() != names2.size()) {
    758         ADD_FAILURE();
    759         return;
    760       }
    761       cricket::ContentNames::const_iterator iter1 = names1.begin();
    762       cricket::ContentNames::const_iterator iter2 = names2.begin();
    763       while (iter1 != names1.end()) {
    764         EXPECT_EQ(*iter1++, *iter2++);
    765       }
    766     }
    767 
    768     // transport info
    769     const cricket::TransportInfos transports1 = desc1.transport_infos();
    770     const cricket::TransportInfos transports2 = desc2.transport_infos();
    771     EXPECT_EQ(transports1.size(), transports2.size());
    772     if (transports1.size() != transports2.size()) {
    773       ADD_FAILURE();
    774       return;
    775     }
    776     for (size_t i = 0; i < transports1.size(); ++i) {
    777       const cricket::TransportInfo transport1 = transports1.at(i);
    778       const cricket::TransportInfo transport2 = transports2.at(i);
    779       EXPECT_EQ(transport1.content_name, transport2.content_name);
    780       EXPECT_EQ(transport1.description.transport_type,
    781                 transport2.description.transport_type);
    782       EXPECT_EQ(transport1.description.ice_ufrag,
    783                 transport2.description.ice_ufrag);
    784       EXPECT_EQ(transport1.description.ice_pwd,
    785                 transport2.description.ice_pwd);
    786       if (transport1.description.identity_fingerprint) {
    787         EXPECT_EQ(*transport1.description.identity_fingerprint,
    788                   *transport2.description.identity_fingerprint);
    789       } else {
    790         EXPECT_EQ(transport1.description.identity_fingerprint.get(),
    791                   transport2.description.identity_fingerprint.get());
    792       }
    793       EXPECT_EQ(transport1.description.transport_options,
    794                 transport2.description.transport_options);
    795       EXPECT_TRUE(CompareCandidates(transport1.description.candidates,
    796                                     transport2.description.candidates));
    797     }
    798   }
    799 
    800   bool CompareCandidates(const Candidates& cs1, const Candidates& cs2) {
    801     EXPECT_EQ(cs1.size(), cs2.size());
    802     if (cs1.size() != cs2.size())
    803       return false;
    804     for (size_t i = 0; i< cs1.size(); ++i) {
    805       const Candidate c1 = cs1.at(i);
    806       const Candidate c2 = cs2.at(i);
    807       EXPECT_TRUE(c1.IsEquivalent(c2));
    808     }
    809     return true;
    810   }
    811 
    812   bool CompareSessionDescription(
    813       const JsepSessionDescription& desc1,
    814       const JsepSessionDescription& desc2) {
    815     EXPECT_EQ(desc1.session_id(), desc2.session_id());
    816     EXPECT_EQ(desc1.session_version(), desc2.session_version());
    817     CompareSessionDescription(*desc1.description(), *desc2.description());
    818     if (desc1.number_of_mediasections() != desc2.number_of_mediasections())
    819       return false;
    820     for (size_t i = 0; i < desc1.number_of_mediasections(); ++i) {
    821       const IceCandidateCollection* cc1 = desc1.candidates(i);
    822       const IceCandidateCollection* cc2 = desc2.candidates(i);
    823       if (cc1->count() != cc2->count())
    824         return false;
    825       for (size_t j = 0; j < cc1->count(); ++j) {
    826         const IceCandidateInterface* c1 = cc1->at(j);
    827         const IceCandidateInterface* c2 = cc2->at(j);
    828         EXPECT_EQ(c1->sdp_mid(), c2->sdp_mid());
    829         EXPECT_EQ(c1->sdp_mline_index(), c2->sdp_mline_index());
    830         EXPECT_TRUE(c1->candidate().IsEquivalent(c2->candidate()));
    831       }
    832     }
    833     return true;
    834   }
    835 
    836   // Disable the ice-ufrag and ice-pwd in given |sdp| message by replacing
    837   // them with invalid keywords so that the parser will just ignore them.
    838   bool RemoveCandidateUfragPwd(std::string* sdp) {
    839     const char ice_ufrag[] = "a=ice-ufrag";
    840     const char ice_ufragx[] = "a=xice-ufrag";
    841     const char ice_pwd[] = "a=ice-pwd";
    842     const char ice_pwdx[] = "a=xice-pwd";
    843     talk_base::replace_substrs(ice_ufrag, strlen(ice_ufrag),
    844         ice_ufragx, strlen(ice_ufragx), sdp);
    845     talk_base::replace_substrs(ice_pwd, strlen(ice_pwd),
    846         ice_pwdx, strlen(ice_pwdx), sdp);
    847     return true;
    848   }
    849 
    850   // Update the candidates in |jdesc| to use the given |ufrag| and |pwd|.
    851   bool UpdateCandidateUfragPwd(JsepSessionDescription* jdesc, int mline_index,
    852       const std::string& ufrag, const std::string& pwd) {
    853     std::string content_name;
    854     if (mline_index == 0) {
    855       content_name = kAudioContentName;
    856     } else if (mline_index == 1) {
    857       content_name = kVideoContentName;
    858     } else {
    859       ASSERT(false);
    860     }
    861     TransportInfo transport_info(
    862         content_name, TransportDescription(NS_JINGLE_ICE_UDP,
    863                                            std::vector<std::string>(),
    864                                            ufrag, pwd, cricket::ICEMODE_FULL,
    865                                            NULL, Candidates()));
    866     SessionDescription* desc =
    867         const_cast<SessionDescription*>(jdesc->description());
    868     desc->RemoveTransportInfoByName(content_name);
    869     EXPECT_TRUE(desc->AddTransportInfo(transport_info));
    870     for (size_t i = 0; i < jdesc_.number_of_mediasections(); ++i) {
    871       const IceCandidateCollection* cc = jdesc_.candidates(i);
    872       for (size_t j = 0; j < cc->count(); ++j) {
    873         if (cc->at(j)->sdp_mline_index() == mline_index) {
    874           const_cast<Candidate&>(cc->at(j)->candidate()).set_username(
    875               ufrag);
    876           const_cast<Candidate&>(cc->at(j)->candidate()).set_password(
    877               pwd);
    878         }
    879       }
    880     }
    881     return true;
    882   }
    883 
    884   void AddIceOptions(const std::string& content_name,
    885                      const std::vector<std::string>& transport_options) {
    886     ASSERT_TRUE(desc_.GetTransportInfoByName(content_name) != NULL);
    887     cricket::TransportInfo transport_info =
    888         *(desc_.GetTransportInfoByName(content_name));
    889     desc_.RemoveTransportInfoByName(content_name);
    890     transport_info.description.transport_options = transport_options;
    891     desc_.AddTransportInfo(transport_info);
    892   }
    893 
    894   void AddFingerprint() {
    895     desc_.RemoveTransportInfoByName(kAudioContentName);
    896     desc_.RemoveTransportInfoByName(kVideoContentName);
    897     talk_base::SSLFingerprint fingerprint(talk_base::DIGEST_SHA_1,
    898                                           kIdentityDigest,
    899                                           sizeof(kIdentityDigest));
    900     EXPECT_TRUE(desc_.AddTransportInfo(
    901         TransportInfo(kAudioContentName,
    902                       TransportDescription(NS_JINGLE_ICE_UDP,
    903                                            std::vector<std::string>(),
    904                                            kCandidateUfragVoice,
    905                                            kCandidatePwdVoice,
    906                                            cricket::ICEMODE_FULL, &fingerprint,
    907                                            Candidates()))));
    908     EXPECT_TRUE(desc_.AddTransportInfo(
    909         TransportInfo(kVideoContentName,
    910                       TransportDescription(NS_JINGLE_ICE_UDP,
    911                                            std::vector<std::string>(),
    912                                            kCandidateUfragVideo,
    913                                            kCandidatePwdVideo,
    914                                            cricket::ICEMODE_FULL, &fingerprint,
    915                                            Candidates()))));
    916   }
    917 
    918   void AddExtmap() {
    919     audio_desc_ = static_cast<AudioContentDescription*>(
    920         audio_desc_->Copy());
    921     video_desc_ = static_cast<VideoContentDescription*>(
    922         video_desc_->Copy());
    923     audio_desc_->AddRtpHeaderExtension(
    924         RtpHeaderExtension(kExtmapUri, kExtmapId));
    925     video_desc_->AddRtpHeaderExtension(
    926         RtpHeaderExtension(kExtmapUri, kExtmapId));
    927     desc_.RemoveContentByName(kAudioContentName);
    928     desc_.RemoveContentByName(kVideoContentName);
    929     desc_.AddContent(kAudioContentName, NS_JINGLE_RTP, audio_desc_);
    930     desc_.AddContent(kVideoContentName, NS_JINGLE_RTP, video_desc_);
    931   }
    932 
    933   void RemoveCryptos() {
    934     audio_desc_->set_cryptos(std::vector<CryptoParams>());
    935     video_desc_->set_cryptos(std::vector<CryptoParams>());
    936   }
    937 
    938   bool TestSerializeDirection(cricket::MediaContentDirection direction) {
    939     audio_desc_->set_direction(direction);
    940     video_desc_->set_direction(direction);
    941     std::string new_sdp = kSdpFullString;
    942     ReplaceDirection(direction, &new_sdp);
    943 
    944     if (!jdesc_.Initialize(desc_.Copy(),
    945                            jdesc_.session_id(),
    946                            jdesc_.session_version())) {
    947       return false;
    948     }
    949     std::string message = webrtc::SdpSerialize(jdesc_);
    950     EXPECT_EQ(new_sdp, message);
    951     return true;
    952   }
    953 
    954   bool TestSerializeRejected(bool audio_rejected, bool video_rejected) {
    955     audio_desc_ = static_cast<AudioContentDescription*>(
    956         audio_desc_->Copy());
    957     video_desc_ = static_cast<VideoContentDescription*>(
    958         video_desc_->Copy());
    959     desc_.RemoveContentByName(kAudioContentName);
    960     desc_.RemoveContentByName(kVideoContentName);
    961     desc_.AddContent(kAudioContentName, NS_JINGLE_RTP, audio_rejected,
    962                      audio_desc_);
    963     desc_.AddContent(kVideoContentName, NS_JINGLE_RTP, video_rejected,
    964                      video_desc_);
    965     std::string new_sdp = kSdpFullString;
    966     ReplaceRejected(audio_rejected, video_rejected, &new_sdp);
    967 
    968     if (!jdesc_.Initialize(desc_.Copy(),
    969                            jdesc_.session_id(),
    970                            jdesc_.session_version())) {
    971       return false;
    972     }
    973     std::string message = webrtc::SdpSerialize(jdesc_);
    974     EXPECT_EQ(new_sdp, message);
    975     return true;
    976   }
    977 
    978   void AddSctpDataChannel() {
    979     talk_base::scoped_ptr<DataContentDescription> data(
    980         new DataContentDescription());
    981     data_desc_ = data.get();
    982     data_desc_->set_protocol(cricket::kMediaProtocolDtlsSctp);
    983     desc_.AddContent(kDataContentName, NS_JINGLE_DRAFT_SCTP, data.release());
    984     EXPECT_TRUE(desc_.AddTransportInfo(
    985            TransportInfo(kDataContentName,
    986                          TransportDescription(NS_JINGLE_ICE_UDP,
    987                                               std::vector<std::string>(),
    988                                               kCandidateUfragData,
    989                                               kCandidatePwdData,
    990                                               cricket::ICEMODE_FULL,
    991                                               NULL, Candidates()))));
    992   }
    993 
    994   void AddRtpDataChannel() {
    995     talk_base::scoped_ptr<DataContentDescription> data(
    996         new DataContentDescription());
    997     data_desc_ = data.get();
    998 
    999     data_desc_->AddCodec(DataCodec(101, "google-data", 1));
   1000     StreamParams data_stream;
   1001     data_stream.id = kDataChannelMsid;
   1002     data_stream.cname = kDataChannelCname;
   1003     data_stream.sync_label = kDataChannelLabel;
   1004     data_stream.ssrcs.push_back(kDataChannelSsrc);
   1005     data_desc_->AddStream(data_stream);
   1006     data_desc_->AddCrypto(CryptoParams(
   1007         1, "AES_CM_128_HMAC_SHA1_80",
   1008         "inline:FvLcvU2P3ZWmQxgPAgcDu7Zl9vftYElFOjEzhWs5", ""));
   1009     data_desc_->set_protocol(cricket::kMediaProtocolSavpf);
   1010     desc_.AddContent(kDataContentName, NS_JINGLE_RTP, data.release());
   1011     EXPECT_TRUE(desc_.AddTransportInfo(
   1012            TransportInfo(kDataContentName,
   1013                          TransportDescription(NS_JINGLE_ICE_UDP,
   1014                                               std::vector<std::string>(),
   1015                                               kCandidateUfragData,
   1016                                               kCandidatePwdData,
   1017                                               cricket::ICEMODE_FULL,
   1018                                               NULL, Candidates()))));
   1019   }
   1020 
   1021   bool TestDeserializeDirection(cricket::MediaContentDirection direction) {
   1022     std::string new_sdp = kSdpFullString;
   1023     ReplaceDirection(direction, &new_sdp);
   1024     JsepSessionDescription new_jdesc(kDummyString);
   1025 
   1026     EXPECT_TRUE(SdpDeserialize(new_sdp, &new_jdesc));
   1027 
   1028     audio_desc_->set_direction(direction);
   1029     video_desc_->set_direction(direction);
   1030     if (!jdesc_.Initialize(desc_.Copy(),
   1031                            jdesc_.session_id(),
   1032                            jdesc_.session_version())) {
   1033       return false;
   1034     }
   1035     EXPECT_TRUE(CompareSessionDescription(jdesc_, new_jdesc));
   1036     return true;
   1037   }
   1038 
   1039   bool TestDeserializeRejected(bool audio_rejected, bool video_rejected) {
   1040     std::string new_sdp = kSdpFullString;
   1041     ReplaceRejected(audio_rejected, video_rejected, &new_sdp);
   1042     JsepSessionDescription new_jdesc(JsepSessionDescription::kOffer);
   1043 
   1044     EXPECT_TRUE(SdpDeserialize(new_sdp, &new_jdesc));
   1045     audio_desc_ = static_cast<AudioContentDescription*>(
   1046         audio_desc_->Copy());
   1047     video_desc_ = static_cast<VideoContentDescription*>(
   1048         video_desc_->Copy());
   1049     desc_.RemoveContentByName(kAudioContentName);
   1050     desc_.RemoveContentByName(kVideoContentName);
   1051     desc_.AddContent(kAudioContentName, NS_JINGLE_RTP, audio_rejected,
   1052                      audio_desc_);
   1053     desc_.AddContent(kVideoContentName, NS_JINGLE_RTP, video_rejected,
   1054                      video_desc_);
   1055     if (!jdesc_.Initialize(desc_.Copy(),
   1056                            jdesc_.session_id(),
   1057                            jdesc_.session_version())) {
   1058       return false;
   1059     }
   1060     EXPECT_TRUE(CompareSessionDescription(jdesc_, new_jdesc));
   1061     return true;
   1062   }
   1063 
   1064   void TestDeserializeExtmap(bool session_level, bool media_level) {
   1065     AddExtmap();
   1066     JsepSessionDescription new_jdesc("dummy");
   1067     ASSERT_TRUE(new_jdesc.Initialize(desc_.Copy(),
   1068                                      jdesc_.session_id(),
   1069                                      jdesc_.session_version()));
   1070     JsepSessionDescription jdesc_with_extmap("dummy");
   1071     std::string sdp_with_extmap = kSdpString;
   1072     if (session_level) {
   1073       InjectAfter(kSessionTime, kExtmapWithDirectionAndAttribute,
   1074                   &sdp_with_extmap);
   1075     }
   1076     if (media_level) {
   1077       InjectAfter(kAttributeIcePwdVoice, kExtmapWithDirectionAndAttribute,
   1078                   &sdp_with_extmap);
   1079       InjectAfter(kAttributeIcePwdVideo, kExtmapWithDirectionAndAttribute,
   1080                   &sdp_with_extmap);
   1081     }
   1082     // The extmap can't be present at the same time in both session level and
   1083     // media level.
   1084     if (session_level && media_level) {
   1085       SdpParseError error;
   1086       EXPECT_FALSE(webrtc::SdpDeserialize(sdp_with_extmap,
   1087                    &jdesc_with_extmap, &error));
   1088       EXPECT_NE(std::string::npos, error.description.find("a=extmap"));
   1089     } else {
   1090       EXPECT_TRUE(SdpDeserialize(sdp_with_extmap, &jdesc_with_extmap));
   1091       EXPECT_TRUE(CompareSessionDescription(jdesc_with_extmap, new_jdesc));
   1092     }
   1093   }
   1094 
   1095   void VerifyCodecParameter(const cricket::CodecParameterMap& params,
   1096       const std::string& name, int expected_value) {
   1097     cricket::CodecParameterMap::const_iterator found = params.find(name);
   1098     ASSERT_TRUE(found != params.end());
   1099     EXPECT_EQ(found->second, talk_base::ToString<int>(expected_value));
   1100   }
   1101 
   1102   void TestDeserializeCodecParams(const CodecParams& params,
   1103                                   JsepSessionDescription* jdesc_output) {
   1104     std::string sdp =
   1105         "v=0\r\n"
   1106         "o=- 18446744069414584320 18446462598732840960 IN IP4 127.0.0.1\r\n"
   1107         "s=-\r\n"
   1108         "t=0 0\r\n"
   1109         // Include semantics for WebRTC Media Streams since it is supported by
   1110         // this parser, and will be added to the SDP when serializing a session
   1111         // description.
   1112         "a=msid-semantic: WMS\r\n"
   1113         // Pl type 111 preferred.
   1114         "m=audio 1 RTP/SAVPF 111 104 103 102\r\n"
   1115         // Pltype 111 listed before 103 and 104 in the map.
   1116         "a=rtpmap:111 opus/48000/2\r\n"
   1117         // Pltype 103 listed before 104.
   1118         "a=rtpmap:103 ISAC/16000\r\n"
   1119         "a=rtpmap:104 CELT/32000/2\r\n"
   1120         "a=rtpmap:102 ISAC/32000/1\r\n"
   1121         "a=fmtp:111 0-15,66,70\r\n"
   1122         "a=fmtp:111 ";
   1123     std::ostringstream os;
   1124     os << "minptime=" << params.min_ptime
   1125        << "; stereo=" << params.stereo
   1126        << "; sprop-stereo=" << params.sprop_stereo
   1127        << "; useinbandfec=" << params.useinband
   1128        << " maxaveragebitrate=" << params.maxaveragebitrate << "\r\n"
   1129        << "a=ptime:" << params.ptime << "\r\n"
   1130        << "a=maxptime:" << params.max_ptime << "\r\n";
   1131     sdp += os.str();
   1132 
   1133     // Deserialize
   1134     SdpParseError error;
   1135     EXPECT_TRUE(webrtc::SdpDeserialize(sdp, jdesc_output, &error));
   1136 
   1137     const ContentInfo* ac = GetFirstAudioContent(jdesc_output->description());
   1138     ASSERT_TRUE(ac != NULL);
   1139     const AudioContentDescription* acd =
   1140         static_cast<const AudioContentDescription*>(ac->description);
   1141     ASSERT_FALSE(acd->codecs().empty());
   1142     cricket::AudioCodec opus = acd->codecs()[0];
   1143     EXPECT_EQ("opus", opus.name);
   1144     EXPECT_EQ(111, opus.id);
   1145     VerifyCodecParameter(opus.params, "minptime", params.min_ptime);
   1146     VerifyCodecParameter(opus.params, "stereo", params.stereo);
   1147     VerifyCodecParameter(opus.params, "sprop-stereo", params.sprop_stereo);
   1148     VerifyCodecParameter(opus.params, "useinbandfec", params.useinband);
   1149     VerifyCodecParameter(opus.params, "maxaveragebitrate",
   1150                          params.maxaveragebitrate);
   1151     for (size_t i = 0; i < acd->codecs().size(); ++i) {
   1152       cricket::AudioCodec codec = acd->codecs()[i];
   1153       VerifyCodecParameter(codec.params, "ptime", params.ptime);
   1154       VerifyCodecParameter(codec.params, "maxptime", params.max_ptime);
   1155       if (codec.name == "ISAC") {
   1156         if (codec.clockrate == 16000) {
   1157           EXPECT_EQ(32000, codec.bitrate);
   1158         } else {
   1159           EXPECT_EQ(56000, codec.bitrate);
   1160         }
   1161       }
   1162     }
   1163   }
   1164 
   1165   void TestDeserializeRtcpFb(JsepSessionDescription* jdesc_output,
   1166                              bool use_wildcard) {
   1167     std::string sdp =
   1168         "v=0\r\n"
   1169         "o=- 18446744069414584320 18446462598732840960 IN IP4 127.0.0.1\r\n"
   1170         "s=-\r\n"
   1171         "t=0 0\r\n"
   1172         // Include semantics for WebRTC Media Streams since it is supported by
   1173         // this parser, and will be added to the SDP when serializing a session
   1174         // description.
   1175         "a=msid-semantic: WMS\r\n"
   1176         "m=audio 1 RTP/SAVPF 111\r\n"
   1177         "a=rtpmap:111 opus/48000/2\r\n"
   1178         "a=rtcp-fb:111 nack\r\n"
   1179         "m=video 3457 RTP/SAVPF 101\r\n"
   1180         "a=rtpmap:101 VP8/90000\r\n"
   1181         "a=rtcp-fb:101 nack\r\n"
   1182         "a=rtcp-fb:101 goog-remb\r\n"
   1183         "a=rtcp-fb:101 ccm fir\r\n";
   1184     std::ostringstream os;
   1185     os << "a=rtcp-fb:" << (use_wildcard ? "*" : "101") <<  " ccm fir\r\n";
   1186     sdp += os.str();
   1187     // Deserialize
   1188     SdpParseError error;
   1189     EXPECT_TRUE(webrtc::SdpDeserialize(sdp, jdesc_output, &error));
   1190     const ContentInfo* ac = GetFirstAudioContent(jdesc_output->description());
   1191     ASSERT_TRUE(ac != NULL);
   1192     const AudioContentDescription* acd =
   1193         static_cast<const AudioContentDescription*>(ac->description);
   1194     ASSERT_FALSE(acd->codecs().empty());
   1195     cricket::AudioCodec opus = acd->codecs()[0];
   1196     EXPECT_EQ(111, opus.id);
   1197     EXPECT_TRUE(opus.HasFeedbackParam(
   1198         cricket::FeedbackParam(cricket::kRtcpFbParamNack,
   1199                                cricket::kParamValueEmpty)));
   1200 
   1201     const ContentInfo* vc = GetFirstVideoContent(jdesc_output->description());
   1202     ASSERT_TRUE(vc != NULL);
   1203     const VideoContentDescription* vcd =
   1204         static_cast<const VideoContentDescription*>(vc->description);
   1205     ASSERT_FALSE(vcd->codecs().empty());
   1206     cricket::VideoCodec vp8 = vcd->codecs()[0];
   1207     EXPECT_STREQ(webrtc::JsepSessionDescription::kDefaultVideoCodecName,
   1208                  vp8.name.c_str());
   1209     EXPECT_EQ(101, vp8.id);
   1210     EXPECT_TRUE(vp8.HasFeedbackParam(
   1211         cricket::FeedbackParam(cricket::kRtcpFbParamNack,
   1212                                cricket::kParamValueEmpty)));
   1213     EXPECT_TRUE(vp8.HasFeedbackParam(
   1214         cricket::FeedbackParam(cricket::kRtcpFbParamRemb,
   1215                                cricket::kParamValueEmpty)));
   1216     EXPECT_TRUE(vp8.HasFeedbackParam(
   1217         cricket::FeedbackParam(cricket::kRtcpFbParamCcm,
   1218                                cricket::kRtcpFbCcmParamFir)));
   1219   }
   1220 
   1221   // Two SDP messages can mean the same thing but be different strings, e.g.
   1222   // some of the lines can be serialized in different order.
   1223   // However, a deserialized description can be compared field by field and has
   1224   // no order. If deserializer has already been tested, serializing then
   1225   // deserializing and comparing JsepSessionDescription will test
   1226   // the serializer sufficiently.
   1227   void TestSerialize(const JsepSessionDescription& jdesc) {
   1228     std::string message = webrtc::SdpSerialize(jdesc);
   1229     JsepSessionDescription jdesc_output_des(kDummyString);
   1230     SdpParseError error;
   1231     EXPECT_TRUE(webrtc::SdpDeserialize(message, &jdesc_output_des, &error));
   1232     EXPECT_TRUE(CompareSessionDescription(jdesc, jdesc_output_des));
   1233   }
   1234 
   1235  protected:
   1236   SessionDescription desc_;
   1237   AudioContentDescription* audio_desc_;
   1238   VideoContentDescription* video_desc_;
   1239   DataContentDescription* data_desc_;
   1240   Candidates candidates_;
   1241   talk_base::scoped_ptr<IceCandidateInterface> jcandidate_;
   1242   JsepSessionDescription jdesc_;
   1243 };
   1244 
   1245 void TestMismatch(const std::string& string1, const std::string& string2) {
   1246   int position = 0;
   1247   for (size_t i = 0; i < string1.length() && i < string2.length(); ++i) {
   1248     if (string1.c_str()[i] != string2.c_str()[i]) {
   1249       position = static_cast<int>(i);
   1250       break;
   1251     }
   1252   }
   1253   EXPECT_EQ(0, position) << "Strings mismatch at the " << position
   1254                          << " character\n"
   1255                          << " 1: " << string1.substr(position, 20) << "\n"
   1256                          << " 2: " << string2.substr(position, 20) << "\n";
   1257 }
   1258 
   1259 std::string GetLine(const std::string& message,
   1260                     const std::string& session_description_name) {
   1261   size_t start = message.find(session_description_name);
   1262   if (std::string::npos == start) {
   1263     return "";
   1264   }
   1265   size_t stop = message.find("\r\n", start);
   1266   if (std::string::npos == stop) {
   1267     return "";
   1268   }
   1269   if (stop <= start) {
   1270     return "";
   1271   }
   1272   return message.substr(start, stop - start);
   1273 }
   1274 
   1275 TEST_F(WebRtcSdpTest, SerializeSessionDescription) {
   1276   // SessionDescription with desc and candidates.
   1277   std::string message = webrtc::SdpSerialize(jdesc_);
   1278   TestMismatch(std::string(kSdpFullString), message);
   1279 }
   1280 
   1281 TEST_F(WebRtcSdpTest, SerializeSessionDescriptionEmpty) {
   1282   JsepSessionDescription jdesc_empty(kDummyString);
   1283   EXPECT_EQ("", webrtc::SdpSerialize(jdesc_empty));
   1284 }
   1285 
   1286 // This tests serialization of SDP with a=crypto and a=fingerprint, as would be
   1287 // the case in a DTLS offer.
   1288 TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithFingerprint) {
   1289   AddFingerprint();
   1290   JsepSessionDescription jdesc_with_fingerprint(kDummyString);
   1291   ASSERT_TRUE(jdesc_with_fingerprint.Initialize(desc_.Copy(),
   1292                                                 kSessionId, kSessionVersion));
   1293   std::string message = webrtc::SdpSerialize(jdesc_with_fingerprint);
   1294 
   1295   std::string sdp_with_fingerprint = kSdpString;
   1296   InjectAfter(kAttributeIcePwdVoice,
   1297               kFingerprint, &sdp_with_fingerprint);
   1298   InjectAfter(kAttributeIcePwdVideo,
   1299               kFingerprint, &sdp_with_fingerprint);
   1300 
   1301   EXPECT_EQ(sdp_with_fingerprint, message);
   1302 }
   1303 
   1304 // This tests serialization of SDP with a=fingerprint with no a=crypto, as would
   1305 // be the case in a DTLS answer.
   1306 TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithFingerprintNoCryptos) {
   1307   AddFingerprint();
   1308   RemoveCryptos();
   1309   JsepSessionDescription jdesc_with_fingerprint(kDummyString);
   1310   ASSERT_TRUE(jdesc_with_fingerprint.Initialize(desc_.Copy(),
   1311                                                 kSessionId, kSessionVersion));
   1312   std::string message = webrtc::SdpSerialize(jdesc_with_fingerprint);
   1313 
   1314   std::string sdp_with_fingerprint = kSdpString;
   1315   Replace(kAttributeCryptoVoice, "", &sdp_with_fingerprint);
   1316   Replace(kAttributeCryptoVideo, "", &sdp_with_fingerprint);
   1317   InjectAfter(kAttributeIcePwdVoice,
   1318               kFingerprint, &sdp_with_fingerprint);
   1319   InjectAfter(kAttributeIcePwdVideo,
   1320               kFingerprint, &sdp_with_fingerprint);
   1321 
   1322   EXPECT_EQ(sdp_with_fingerprint, message);
   1323 }
   1324 
   1325 TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithoutCandidates) {
   1326   // JsepSessionDescription with desc but without candidates.
   1327   JsepSessionDescription jdesc_no_candidates(kDummyString);
   1328   ASSERT_TRUE(jdesc_no_candidates.Initialize(desc_.Copy(),
   1329                                              kSessionId, kSessionVersion));
   1330   std::string message = webrtc::SdpSerialize(jdesc_no_candidates);
   1331   EXPECT_EQ(std::string(kSdpString), message);
   1332 }
   1333 
   1334 TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithBundle) {
   1335   ContentGroup group(cricket::GROUP_TYPE_BUNDLE);
   1336   group.AddContentName(kAudioContentName);
   1337   group.AddContentName(kVideoContentName);
   1338   desc_.AddGroup(group);
   1339   ASSERT_TRUE(jdesc_.Initialize(desc_.Copy(),
   1340                                 jdesc_.session_id(),
   1341                                 jdesc_.session_version()));
   1342   std::string message = webrtc::SdpSerialize(jdesc_);
   1343   std::string sdp_with_bundle = kSdpFullString;
   1344   InjectAfter(kSessionTime,
   1345               "a=group:BUNDLE audio_content_name video_content_name\r\n",
   1346               &sdp_with_bundle);
   1347   EXPECT_EQ(sdp_with_bundle, message);
   1348 }
   1349 
   1350 TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithBandwidth) {
   1351   VideoContentDescription* vcd = static_cast<VideoContentDescription*>(
   1352       GetFirstVideoContent(&desc_)->description);
   1353   vcd->set_bandwidth(100 * 1000);
   1354   AudioContentDescription* acd = static_cast<AudioContentDescription*>(
   1355       GetFirstAudioContent(&desc_)->description);
   1356   acd->set_bandwidth(50 * 1000);
   1357   ASSERT_TRUE(jdesc_.Initialize(desc_.Copy(),
   1358                                 jdesc_.session_id(),
   1359                                 jdesc_.session_version()));
   1360   std::string message = webrtc::SdpSerialize(jdesc_);
   1361   std::string sdp_with_bandwidth = kSdpFullString;
   1362   InjectAfter("a=mid:video_content_name\r\na=sendrecv\r\n",
   1363               "b=AS:100\r\n",
   1364               &sdp_with_bandwidth);
   1365   InjectAfter("a=mid:audio_content_name\r\na=sendrecv\r\n",
   1366               "b=AS:50\r\n",
   1367               &sdp_with_bandwidth);
   1368   EXPECT_EQ(sdp_with_bandwidth, message);
   1369 }
   1370 
   1371 TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithIceOptions) {
   1372   std::vector<std::string> transport_options;
   1373   transport_options.push_back(kIceOption1);
   1374   transport_options.push_back(kIceOption3);
   1375   AddIceOptions(kAudioContentName, transport_options);
   1376   transport_options.clear();
   1377   transport_options.push_back(kIceOption2);
   1378   transport_options.push_back(kIceOption3);
   1379   AddIceOptions(kVideoContentName, transport_options);
   1380   ASSERT_TRUE(jdesc_.Initialize(desc_.Copy(),
   1381                                 jdesc_.session_id(),
   1382                                 jdesc_.session_version()));
   1383   std::string message = webrtc::SdpSerialize(jdesc_);
   1384   std::string sdp_with_ice_options = kSdpFullString;
   1385   InjectAfter(kAttributeIcePwdVoice,
   1386               "a=ice-options:iceoption1 iceoption3\r\n",
   1387               &sdp_with_ice_options);
   1388   InjectAfter(kAttributeIcePwdVideo,
   1389               "a=ice-options:iceoption2 iceoption3\r\n",
   1390               &sdp_with_ice_options);
   1391   EXPECT_EQ(sdp_with_ice_options, message);
   1392 }
   1393 
   1394 TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithRecvOnlyContent) {
   1395   EXPECT_TRUE(TestSerializeDirection(cricket::MD_RECVONLY));
   1396 }
   1397 
   1398 TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithSendOnlyContent) {
   1399   EXPECT_TRUE(TestSerializeDirection(cricket::MD_SENDONLY));
   1400 }
   1401 
   1402 TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithInactiveContent) {
   1403   EXPECT_TRUE(TestSerializeDirection(cricket::MD_INACTIVE));
   1404 }
   1405 
   1406 TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithAudioRejected) {
   1407   EXPECT_TRUE(TestSerializeRejected(true, false));
   1408 }
   1409 
   1410 TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithVideoRejected) {
   1411   EXPECT_TRUE(TestSerializeRejected(false, true));
   1412 }
   1413 
   1414 TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithAudioVideoRejected) {
   1415   EXPECT_TRUE(TestSerializeRejected(true, true));
   1416 }
   1417 
   1418 TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithRtpDataChannel) {
   1419   AddRtpDataChannel();
   1420   JsepSessionDescription jsep_desc(kDummyString);
   1421 
   1422   ASSERT_TRUE(jsep_desc.Initialize(desc_.Copy(), kSessionId, kSessionVersion));
   1423   std::string message = webrtc::SdpSerialize(jsep_desc);
   1424 
   1425   std::string expected_sdp = kSdpString;
   1426   expected_sdp.append(kSdpRtpDataChannelString);
   1427   EXPECT_EQ(expected_sdp, message);
   1428 }
   1429 
   1430 TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithSctpDataChannel) {
   1431   AddSctpDataChannel();
   1432   JsepSessionDescription jsep_desc(kDummyString);
   1433 
   1434   ASSERT_TRUE(jsep_desc.Initialize(desc_.Copy(), kSessionId, kSessionVersion));
   1435   std::string message = webrtc::SdpSerialize(jsep_desc);
   1436 
   1437   std::string expected_sdp = kSdpString;
   1438   expected_sdp.append(kSdpSctpDataChannelString);
   1439   EXPECT_EQ(message, expected_sdp);
   1440 }
   1441 
   1442 TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithExtmap) {
   1443   AddExtmap();
   1444   JsepSessionDescription desc_with_extmap("dummy");
   1445   ASSERT_TRUE(desc_with_extmap.Initialize(desc_.Copy(),
   1446                                           kSessionId, kSessionVersion));
   1447   std::string message = webrtc::SdpSerialize(desc_with_extmap);
   1448 
   1449   std::string sdp_with_extmap = kSdpString;
   1450   InjectAfter("a=mid:audio_content_name\r\n",
   1451               kExtmap, &sdp_with_extmap);
   1452   InjectAfter("a=mid:video_content_name\r\n",
   1453               kExtmap, &sdp_with_extmap);
   1454 
   1455   EXPECT_EQ(sdp_with_extmap, message);
   1456 }
   1457 
   1458 
   1459 TEST_F(WebRtcSdpTest, SerializeCandidates) {
   1460   std::string message = webrtc::SdpSerializeCandidate(*jcandidate_);
   1461   EXPECT_EQ(std::string(kSdpOneCandidate), message);
   1462 }
   1463 
   1464 TEST_F(WebRtcSdpTest, DeserializeSessionDescription) {
   1465   JsepSessionDescription jdesc(kDummyString);
   1466   // Deserialize
   1467   EXPECT_TRUE(SdpDeserialize(kSdpFullString, &jdesc));
   1468   // Verify
   1469   EXPECT_TRUE(CompareSessionDescription(jdesc_, jdesc));
   1470 }
   1471 
   1472 TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithoutCarriageReturn) {
   1473   JsepSessionDescription jdesc(kDummyString);
   1474   std::string sdp_without_carriage_return = kSdpFullString;
   1475   Replace("\r\n", "\n", &sdp_without_carriage_return);
   1476   // Deserialize
   1477   EXPECT_TRUE(SdpDeserialize(sdp_without_carriage_return, &jdesc));
   1478   // Verify
   1479   EXPECT_TRUE(CompareSessionDescription(jdesc_, jdesc));
   1480 }
   1481 
   1482 TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithoutCandidates) {
   1483   // SessionDescription with desc but without candidates.
   1484   JsepSessionDescription jdesc_no_candidates(kDummyString);
   1485   ASSERT_TRUE(jdesc_no_candidates.Initialize(desc_.Copy(),
   1486                                              kSessionId, kSessionVersion));
   1487   JsepSessionDescription new_jdesc(kDummyString);
   1488   EXPECT_TRUE(SdpDeserialize(kSdpString, &new_jdesc));
   1489   EXPECT_TRUE(CompareSessionDescription(jdesc_no_candidates, new_jdesc));
   1490 }
   1491 
   1492 TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithoutRtpmap) {
   1493   static const char kSdpNoRtpmapString[] =
   1494       "v=0\r\n"
   1495       "o=- 11 22 IN IP4 127.0.0.1\r\n"
   1496       "s=-\r\n"
   1497       "t=0 0\r\n"
   1498       "m=audio 49232 RTP/AVP 0 18 103\r\n"
   1499       // Codec that doesn't appear in the m= line will be ignored.
   1500       "a=rtpmap:104 CELT/32000/2\r\n"
   1501       // The rtpmap line for static payload codec is optional.
   1502       "a=rtpmap:18 G729/16000\r\n"
   1503       "a=rtpmap:103 ISAC/16000\r\n";
   1504 
   1505   JsepSessionDescription jdesc(kDummyString);
   1506   EXPECT_TRUE(SdpDeserialize(kSdpNoRtpmapString, &jdesc));
   1507   cricket::AudioContentDescription* audio =
   1508     static_cast<AudioContentDescription*>(
   1509         jdesc.description()->GetContentDescriptionByName(cricket::CN_AUDIO));
   1510   AudioCodecs ref_codecs;
   1511   // The codecs in the AudioContentDescription will be sorted by preference.
   1512   ref_codecs.push_back(AudioCodec(0, "PCMU", 8000, 0, 1, 3));
   1513   ref_codecs.push_back(AudioCodec(18, "G729", 16000, 0, 1, 2));
   1514   ref_codecs.push_back(AudioCodec(103, "ISAC", 16000, 32000, 1, 1));
   1515   EXPECT_EQ(ref_codecs, audio->codecs());
   1516 }
   1517 
   1518 // Ensure that we can deserialize SDP with a=fingerprint properly.
   1519 TEST_F(WebRtcSdpTest, DeserializeJsepSessionDescriptionWithFingerprint) {
   1520   // Add a DTLS a=fingerprint attribute to our session description.
   1521   AddFingerprint();
   1522   JsepSessionDescription new_jdesc(kDummyString);
   1523   ASSERT_TRUE(new_jdesc.Initialize(desc_.Copy(),
   1524                                    jdesc_.session_id(),
   1525                                    jdesc_.session_version()));
   1526 
   1527   JsepSessionDescription jdesc_with_fingerprint(kDummyString);
   1528   std::string sdp_with_fingerprint = kSdpString;
   1529   InjectAfter(kAttributeIcePwdVoice, kFingerprint, &sdp_with_fingerprint);
   1530   InjectAfter(kAttributeIcePwdVideo, kFingerprint, &sdp_with_fingerprint);
   1531   EXPECT_TRUE(SdpDeserialize(sdp_with_fingerprint, &jdesc_with_fingerprint));
   1532   EXPECT_TRUE(CompareSessionDescription(jdesc_with_fingerprint, new_jdesc));
   1533 }
   1534 
   1535 TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithBundle) {
   1536   JsepSessionDescription jdesc_with_bundle(kDummyString);
   1537   std::string sdp_with_bundle = kSdpFullString;
   1538   InjectAfter(kSessionTime,
   1539               "a=group:BUNDLE audio_content_name video_content_name\r\n",
   1540               &sdp_with_bundle);
   1541   EXPECT_TRUE(SdpDeserialize(sdp_with_bundle, &jdesc_with_bundle));
   1542   ContentGroup group(cricket::GROUP_TYPE_BUNDLE);
   1543   group.AddContentName(kAudioContentName);
   1544   group.AddContentName(kVideoContentName);
   1545   desc_.AddGroup(group);
   1546   ASSERT_TRUE(jdesc_.Initialize(desc_.Copy(),
   1547                                 jdesc_.session_id(),
   1548                                 jdesc_.session_version()));
   1549   EXPECT_TRUE(CompareSessionDescription(jdesc_, jdesc_with_bundle));
   1550 }
   1551 
   1552 TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithBandwidth) {
   1553   JsepSessionDescription jdesc_with_bandwidth(kDummyString);
   1554   std::string sdp_with_bandwidth = kSdpFullString;
   1555   InjectAfter("a=mid:video_content_name\r\na=sendrecv\r\n",
   1556               "b=AS:100\r\n",
   1557               &sdp_with_bandwidth);
   1558   InjectAfter("a=mid:audio_content_name\r\na=sendrecv\r\n",
   1559               "b=AS:50\r\n",
   1560               &sdp_with_bandwidth);
   1561   EXPECT_TRUE(
   1562       SdpDeserialize(sdp_with_bandwidth, &jdesc_with_bandwidth));
   1563   VideoContentDescription* vcd = static_cast<VideoContentDescription*>(
   1564       GetFirstVideoContent(&desc_)->description);
   1565   vcd->set_bandwidth(100 * 1000);
   1566   AudioContentDescription* acd = static_cast<AudioContentDescription*>(
   1567       GetFirstAudioContent(&desc_)->description);
   1568   acd->set_bandwidth(50 * 1000);
   1569   ASSERT_TRUE(jdesc_.Initialize(desc_.Copy(),
   1570                                 jdesc_.session_id(),
   1571                                 jdesc_.session_version()));
   1572   EXPECT_TRUE(CompareSessionDescription(jdesc_, jdesc_with_bandwidth));
   1573 }
   1574 
   1575 TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithIceOptions) {
   1576   JsepSessionDescription jdesc_with_ice_options(kDummyString);
   1577   std::string sdp_with_ice_options = kSdpFullString;
   1578   InjectAfter(kSessionTime,
   1579               "a=ice-options:iceoption3\r\n",
   1580               &sdp_with_ice_options);
   1581   InjectAfter(kAttributeIcePwdVoice,
   1582               "a=ice-options:iceoption1\r\n",
   1583               &sdp_with_ice_options);
   1584   InjectAfter(kAttributeIcePwdVideo,
   1585               "a=ice-options:iceoption2\r\n",
   1586               &sdp_with_ice_options);
   1587   EXPECT_TRUE(SdpDeserialize(sdp_with_ice_options, &jdesc_with_ice_options));
   1588   std::vector<std::string> transport_options;
   1589   transport_options.push_back(kIceOption3);
   1590   transport_options.push_back(kIceOption1);
   1591   AddIceOptions(kAudioContentName, transport_options);
   1592   transport_options.clear();
   1593   transport_options.push_back(kIceOption3);
   1594   transport_options.push_back(kIceOption2);
   1595   AddIceOptions(kVideoContentName, transport_options);
   1596   ASSERT_TRUE(jdesc_.Initialize(desc_.Copy(),
   1597                                 jdesc_.session_id(),
   1598                                 jdesc_.session_version()));
   1599   EXPECT_TRUE(CompareSessionDescription(jdesc_, jdesc_with_ice_options));
   1600 }
   1601 
   1602 TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithUfragPwd) {
   1603   // Remove the original ice-ufrag and ice-pwd
   1604   JsepSessionDescription jdesc_with_ufrag_pwd(kDummyString);
   1605   std::string sdp_with_ufrag_pwd = kSdpFullString;
   1606   EXPECT_TRUE(RemoveCandidateUfragPwd(&sdp_with_ufrag_pwd));
   1607   // Add session level ufrag and pwd
   1608   InjectAfter(kSessionTime,
   1609       "a=ice-pwd:session+level+icepwd\r\n"
   1610       "a=ice-ufrag:session+level+iceufrag\r\n",
   1611       &sdp_with_ufrag_pwd);
   1612   // Add media level ufrag and pwd for audio
   1613   InjectAfter("a=mid:audio_content_name\r\n",
   1614       "a=ice-pwd:media+level+icepwd\r\na=ice-ufrag:media+level+iceufrag\r\n",
   1615       &sdp_with_ufrag_pwd);
   1616   // Update the candidate ufrag and pwd to the expected ones.
   1617   EXPECT_TRUE(UpdateCandidateUfragPwd(&jdesc_, 0,
   1618       "media+level+iceufrag", "media+level+icepwd"));
   1619   EXPECT_TRUE(UpdateCandidateUfragPwd(&jdesc_, 1,
   1620       "session+level+iceufrag", "session+level+icepwd"));
   1621   EXPECT_TRUE(SdpDeserialize(sdp_with_ufrag_pwd, &jdesc_with_ufrag_pwd));
   1622   EXPECT_TRUE(CompareSessionDescription(jdesc_, jdesc_with_ufrag_pwd));
   1623 }
   1624 
   1625 
   1626 TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithRecvOnlyContent) {
   1627   EXPECT_TRUE(TestDeserializeDirection(cricket::MD_RECVONLY));
   1628 }
   1629 
   1630 TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithSendOnlyContent) {
   1631   EXPECT_TRUE(TestDeserializeDirection(cricket::MD_SENDONLY));
   1632 }
   1633 
   1634 TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithInactiveContent) {
   1635   EXPECT_TRUE(TestDeserializeDirection(cricket::MD_INACTIVE));
   1636 }
   1637 
   1638 TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithRejectedAudio) {
   1639   EXPECT_TRUE(TestDeserializeRejected(true, false));
   1640 }
   1641 
   1642 TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithRejectedVideo) {
   1643   EXPECT_TRUE(TestDeserializeRejected(false, true));
   1644 }
   1645 
   1646 TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithRejectedAudioVideo) {
   1647   EXPECT_TRUE(TestDeserializeRejected(true, true));
   1648 }
   1649 
   1650 // Tests that we can still handle the sdp uses mslabel and label instead of
   1651 // msid for backward compatibility.
   1652 TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithoutMsid) {
   1653   JsepSessionDescription jdesc(kDummyString);
   1654   std::string sdp_without_msid = kSdpFullString;
   1655   Replace("msid", "xmsid", &sdp_without_msid);
   1656   // Deserialize
   1657   EXPECT_TRUE(SdpDeserialize(sdp_without_msid, &jdesc));
   1658   // Verify
   1659   EXPECT_TRUE(CompareSessionDescription(jdesc_, jdesc));
   1660 }
   1661 
   1662 TEST_F(WebRtcSdpTest, DeserializeCandidate) {
   1663   JsepIceCandidate jcandidate(kDummyMid, kDummyIndex);
   1664 
   1665   std::string sdp = kSdpOneCandidate;
   1666   EXPECT_TRUE(SdpDeserializeCandidate(sdp, &jcandidate));
   1667   EXPECT_EQ(kDummyMid, jcandidate.sdp_mid());
   1668   EXPECT_EQ(kDummyIndex, jcandidate.sdp_mline_index());
   1669   EXPECT_TRUE(jcandidate.candidate().IsEquivalent(jcandidate_->candidate()));
   1670 
   1671   // Candidate line without generation extension.
   1672   sdp = kSdpOneCandidate;
   1673   Replace(" generation 2", "", &sdp);
   1674   EXPECT_TRUE(SdpDeserializeCandidate(sdp, &jcandidate));
   1675   EXPECT_EQ(kDummyMid, jcandidate.sdp_mid());
   1676   EXPECT_EQ(kDummyIndex, jcandidate.sdp_mline_index());
   1677   Candidate expected = jcandidate_->candidate();
   1678   expected.set_generation(0);
   1679   EXPECT_TRUE(jcandidate.candidate().IsEquivalent(expected));
   1680 
   1681   // Multiple candidate lines.
   1682   // Only the first line will be deserialized. The rest will be ignored.
   1683   sdp = kSdpOneCandidate;
   1684   sdp.append("a=candidate:1 2 tcp 1234 192.168.1.100 5678 typ host\r\n");
   1685   EXPECT_TRUE(SdpDeserializeCandidate(sdp, &jcandidate));
   1686   EXPECT_EQ(kDummyMid, jcandidate.sdp_mid());
   1687   EXPECT_EQ(kDummyIndex, jcandidate.sdp_mline_index());
   1688   EXPECT_TRUE(jcandidate.candidate().IsEquivalent(jcandidate_->candidate()));
   1689 }
   1690 
   1691 // This test verifies the deserialization of candidate-attribute
   1692 // as per RFC 5245. Candiate-attribute will be of the format
   1693 // candidate:<blah>. This format will be used when candidates
   1694 // are trickled.
   1695 TEST_F(WebRtcSdpTest, DeserializeRawCandidateAttribute) {
   1696   JsepIceCandidate jcandidate(kDummyMid, kDummyIndex);
   1697 
   1698   std::string candidate_attribute = kRawCandidate;
   1699   EXPECT_TRUE(SdpDeserializeCandidate(candidate_attribute, &jcandidate));
   1700   EXPECT_EQ(kDummyMid, jcandidate.sdp_mid());
   1701   EXPECT_EQ(kDummyIndex, jcandidate.sdp_mline_index());
   1702   EXPECT_TRUE(jcandidate.candidate().IsEquivalent(jcandidate_->candidate()));
   1703   EXPECT_EQ(2u, jcandidate.candidate().generation());
   1704 
   1705   // Candidate line without generation extension.
   1706   candidate_attribute = kRawCandidate;
   1707   Replace(" generation 2", "", &candidate_attribute);
   1708   EXPECT_TRUE(SdpDeserializeCandidate(candidate_attribute, &jcandidate));
   1709   EXPECT_EQ(kDummyMid, jcandidate.sdp_mid());
   1710   EXPECT_EQ(kDummyIndex, jcandidate.sdp_mline_index());
   1711   Candidate expected = jcandidate_->candidate();
   1712   expected.set_generation(0);
   1713   EXPECT_TRUE(jcandidate.candidate().IsEquivalent(expected));
   1714 
   1715   // Candidate line without candidate:
   1716   candidate_attribute = kRawCandidate;
   1717   Replace("candidate:", "", &candidate_attribute);
   1718   EXPECT_FALSE(SdpDeserializeCandidate(candidate_attribute, &jcandidate));
   1719 
   1720   // Concatenating additional candidate. Expecting deserialization to fail.
   1721   candidate_attribute = kRawCandidate;
   1722   candidate_attribute.append("candidate:1 2 udp 1234 192.168.1.1 typ host");
   1723   EXPECT_FALSE(SdpDeserializeCandidate(candidate_attribute, &jcandidate));
   1724 }
   1725 
   1726 TEST_F(WebRtcSdpTest, DeserializeSdpWithRtpDataChannels) {
   1727   AddRtpDataChannel();
   1728   JsepSessionDescription jdesc(kDummyString);
   1729   ASSERT_TRUE(jdesc.Initialize(desc_.Copy(), kSessionId, kSessionVersion));
   1730 
   1731   std::string sdp_with_data = kSdpString;
   1732   sdp_with_data.append(kSdpRtpDataChannelString);
   1733   JsepSessionDescription jdesc_output(kDummyString);
   1734 
   1735   // Deserialize
   1736   EXPECT_TRUE(SdpDeserialize(sdp_with_data, &jdesc_output));
   1737   // Verify
   1738   EXPECT_TRUE(CompareSessionDescription(jdesc, jdesc_output));
   1739 }
   1740 
   1741 TEST_F(WebRtcSdpTest, DeserializeSdpWithSctpDataChannels) {
   1742   AddSctpDataChannel();
   1743   JsepSessionDescription jdesc(kDummyString);
   1744   ASSERT_TRUE(jdesc.Initialize(desc_.Copy(), kSessionId, kSessionVersion));
   1745 
   1746   std::string sdp_with_data = kSdpString;
   1747   sdp_with_data.append(kSdpSctpDataChannelString);
   1748   JsepSessionDescription jdesc_output(kDummyString);
   1749 
   1750   EXPECT_TRUE(SdpDeserialize(sdp_with_data, &jdesc_output));
   1751   EXPECT_TRUE(CompareSessionDescription(jdesc, jdesc_output));
   1752 }
   1753 
   1754 TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithSessionLevelExtmap) {
   1755   TestDeserializeExtmap(true, false);
   1756 }
   1757 
   1758 TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithMediaLevelExtmap) {
   1759   TestDeserializeExtmap(false, true);
   1760 }
   1761 
   1762 TEST_F(WebRtcSdpTest, DeserializeSessionDescriptionWithInvalidExtmap) {
   1763   TestDeserializeExtmap(true, true);
   1764 }
   1765 
   1766 TEST_F(WebRtcSdpTest, DeserializeCandidateWithDifferentTransport) {
   1767   JsepIceCandidate jcandidate(kDummyMid, kDummyIndex);
   1768   std::string new_sdp = kSdpOneCandidate;
   1769   Replace("udp", "unsupported_transport", &new_sdp);
   1770   EXPECT_FALSE(SdpDeserializeCandidate(new_sdp, &jcandidate));
   1771   new_sdp = kSdpOneCandidate;
   1772   Replace("udp", "uDP", &new_sdp);
   1773   EXPECT_TRUE(SdpDeserializeCandidate(new_sdp, &jcandidate));
   1774   EXPECT_EQ(kDummyMid, jcandidate.sdp_mid());
   1775   EXPECT_EQ(kDummyIndex, jcandidate.sdp_mline_index());
   1776   EXPECT_TRUE(jcandidate.candidate().IsEquivalent(jcandidate_->candidate()));
   1777 }
   1778 
   1779 TEST_F(WebRtcSdpTest, DeserializeCandidateOldFormat) {
   1780   JsepIceCandidate jcandidate(kDummyMid, kDummyIndex);
   1781   EXPECT_TRUE(SdpDeserializeCandidate(kSdpOneCandidateOldFormat,&jcandidate));
   1782   EXPECT_EQ(kDummyMid, jcandidate.sdp_mid());
   1783   EXPECT_EQ(kDummyIndex, jcandidate.sdp_mline_index());
   1784   Candidate ref_candidate = jcandidate_->candidate();
   1785   ref_candidate.set_username("user_rtp");
   1786   ref_candidate.set_password("password_rtp");
   1787   EXPECT_TRUE(jcandidate.candidate().IsEquivalent(ref_candidate));
   1788 }
   1789 
   1790 TEST_F(WebRtcSdpTest, DeserializeBrokenSdp) {
   1791   const char kSdpDestroyer[] = "!@#$%^&";
   1792   const char kSdpInvalidLine1[] = " =candidate";
   1793   const char kSdpInvalidLine2[] = "a+candidate";
   1794   const char kSdpInvalidLine3[] = "a= candidate";
   1795   // Broken fingerprint.
   1796   const char kSdpInvalidLine4[] = "a=fingerprint:sha-1 "
   1797       "4AAD:B9:B1:3F:82:18:3B:54:02:12:DF:3E:5D:49:6B:19:E5:7C:AB";
   1798   // Extra field.
   1799   const char kSdpInvalidLine5[] = "a=fingerprint:sha-1 "
   1800       "4A:AD:B9:B1:3F:82:18:3B:54:02:12:DF:3E:5D:49:6B:19:E5:7C:AB XXX";
   1801   // Missing space.
   1802   const char kSdpInvalidLine6[] = "a=fingerprint:sha-1"
   1803       "4A:AD:B9:B1:3F:82:18:3B:54:02:12:DF:3E:5D:49:6B:19:E5:7C:AB";
   1804 
   1805   // Broken session description
   1806   ReplaceAndTryToParse("v=", kSdpDestroyer);
   1807   ReplaceAndTryToParse("o=", kSdpDestroyer);
   1808   ReplaceAndTryToParse("s=-", kSdpDestroyer);
   1809   // Broken time description
   1810   ReplaceAndTryToParse("t=", kSdpDestroyer);
   1811 
   1812   // Broken media description
   1813   ReplaceAndTryToParse("m=video", kSdpDestroyer);
   1814 
   1815   // Invalid lines
   1816   ReplaceAndTryToParse("a=candidate", kSdpInvalidLine1);
   1817   ReplaceAndTryToParse("a=candidate", kSdpInvalidLine2);
   1818   ReplaceAndTryToParse("a=candidate", kSdpInvalidLine3);
   1819 
   1820   // Bogus fingerprint replacing a=sendrev. We selected this attribute
   1821   // because it's orthogonal to what we are replacing and hence
   1822   // safe.
   1823   ReplaceAndTryToParse("a=sendrecv", kSdpInvalidLine4);
   1824   ReplaceAndTryToParse("a=sendrecv", kSdpInvalidLine5);
   1825   ReplaceAndTryToParse("a=sendrecv", kSdpInvalidLine6);
   1826 }
   1827 
   1828 TEST_F(WebRtcSdpTest, DeserializeSdpWithReorderedPltypes) {
   1829   JsepSessionDescription jdesc_output(kDummyString);
   1830 
   1831   const char kSdpWithReorderedPlTypesString[] =
   1832       "v=0\r\n"
   1833       "o=- 18446744069414584320 18446462598732840960 IN IP4 127.0.0.1\r\n"
   1834       "s=-\r\n"
   1835       "t=0 0\r\n"
   1836       "m=audio 1 RTP/SAVPF 104 103\r\n"  // Pl type 104 preferred.
   1837       "a=rtpmap:111 opus/48000/2\r\n"  // Pltype 111 listed before 103 and 104
   1838                                        // in the map.
   1839       "a=rtpmap:103 ISAC/16000\r\n"  // Pltype 103 listed before 104 in the map.
   1840       "a=rtpmap:104 CELT/32000/2\r\n";
   1841 
   1842   // Deserialize
   1843   EXPECT_TRUE(SdpDeserialize(kSdpWithReorderedPlTypesString, &jdesc_output));
   1844 
   1845   const ContentInfo* ac = GetFirstAudioContent(jdesc_output.description());
   1846   ASSERT_TRUE(ac != NULL);
   1847   const AudioContentDescription* acd =
   1848       static_cast<const AudioContentDescription*>(ac->description);
   1849   ASSERT_FALSE(acd->codecs().empty());
   1850   EXPECT_EQ("CELT", acd->codecs()[0].name);
   1851   EXPECT_EQ(104, acd->codecs()[0].id);
   1852 }
   1853 
   1854 TEST_F(WebRtcSdpTest, DeserializeSerializeCodecParams) {
   1855   JsepSessionDescription jdesc_output(kDummyString);
   1856   CodecParams params;
   1857   params.max_ptime = 40;
   1858   params.ptime = 30;
   1859   params.min_ptime = 10;
   1860   params.sprop_stereo = 1;
   1861   params.stereo = 1;
   1862   params.useinband = 1;
   1863   params.maxaveragebitrate = 128000;
   1864   TestDeserializeCodecParams(params, &jdesc_output);
   1865   TestSerialize(jdesc_output);
   1866 }
   1867 
   1868 TEST_F(WebRtcSdpTest, DeserializeSerializeRtcpFb) {
   1869   const bool kUseWildcard = false;
   1870   JsepSessionDescription jdesc_output(kDummyString);
   1871   TestDeserializeRtcpFb(&jdesc_output, kUseWildcard);
   1872   TestSerialize(jdesc_output);
   1873 }
   1874 
   1875 TEST_F(WebRtcSdpTest, DeserializeSerializeRtcpFbWildcard) {
   1876   const bool kUseWildcard = true;
   1877   JsepSessionDescription jdesc_output(kDummyString);
   1878   TestDeserializeRtcpFb(&jdesc_output, kUseWildcard);
   1879   TestSerialize(jdesc_output);
   1880 }
   1881 
   1882 TEST_F(WebRtcSdpTest, DeserializeVideoFmtp) {
   1883   JsepSessionDescription jdesc_output(kDummyString);
   1884 
   1885   const char kSdpWithFmtpString[] =
   1886       "v=0\r\n"
   1887       "o=- 18446744069414584320 18446462598732840960 IN IP4 127.0.0.1\r\n"
   1888       "s=-\r\n"
   1889       "t=0 0\r\n"
   1890       "m=video 3457 RTP/SAVPF 120\r\n"
   1891       "a=rtpmap:120 VP8/90000\r\n"
   1892       "a=fmtp:120 x-google-min-bitrate=10; x-google-max-quantization=40\r\n";
   1893 
   1894   // Deserialize
   1895   SdpParseError error;
   1896   EXPECT_TRUE(webrtc::SdpDeserialize(kSdpWithFmtpString, &jdesc_output,
   1897                                      &error));
   1898 
   1899   const ContentInfo* vc = GetFirstVideoContent(jdesc_output.description());
   1900   ASSERT_TRUE(vc != NULL);
   1901   const VideoContentDescription* vcd =
   1902       static_cast<const VideoContentDescription*>(vc->description);
   1903   ASSERT_FALSE(vcd->codecs().empty());
   1904   cricket::VideoCodec vp8 = vcd->codecs()[0];
   1905   EXPECT_EQ("VP8", vp8.name);
   1906   EXPECT_EQ(120, vp8.id);
   1907   cricket::CodecParameterMap::iterator found =
   1908       vp8.params.find("x-google-min-bitrate");
   1909   ASSERT_TRUE(found != vp8.params.end());
   1910   EXPECT_EQ(found->second, "10");
   1911   found = vp8.params.find("x-google-max-quantization");
   1912   ASSERT_TRUE(found != vp8.params.end());
   1913   EXPECT_EQ(found->second, "40");
   1914 }
   1915 
   1916 TEST_F(WebRtcSdpTest, SerializeVideoFmtp) {
   1917   VideoContentDescription* vcd = static_cast<VideoContentDescription*>(
   1918       GetFirstVideoContent(&desc_)->description);
   1919 
   1920   cricket::VideoCodecs codecs = vcd->codecs();
   1921   codecs[0].params["x-google-min-bitrate"] = "10";
   1922   vcd->set_codecs(codecs);
   1923 
   1924   ASSERT_TRUE(jdesc_.Initialize(desc_.Copy(),
   1925                                 jdesc_.session_id(),
   1926                                 jdesc_.session_version()));
   1927   std::string message = webrtc::SdpSerialize(jdesc_);
   1928   std::string sdp_with_fmtp = kSdpFullString;
   1929   InjectAfter("a=rtpmap:120 VP8/90000\r\n",
   1930               "a=fmtp:120 x-google-min-bitrate=10\r\n",
   1931               &sdp_with_fmtp);
   1932   EXPECT_EQ(sdp_with_fmtp, message);
   1933 }
   1934 
   1935 TEST_F(WebRtcSdpTest, DeserializeSdpWithIceLite) {
   1936   JsepSessionDescription jdesc_with_icelite(kDummyString);
   1937   std::string sdp_with_icelite = kSdpFullString;
   1938   EXPECT_TRUE(SdpDeserialize(sdp_with_icelite, &jdesc_with_icelite));
   1939   cricket::SessionDescription* desc = jdesc_with_icelite.description();
   1940   const cricket::TransportInfo* tinfo1 =
   1941       desc->GetTransportInfoByName("audio_content_name");
   1942   EXPECT_EQ(cricket::ICEMODE_FULL, tinfo1->description.ice_mode);
   1943   const cricket::TransportInfo* tinfo2 =
   1944       desc->GetTransportInfoByName("video_content_name");
   1945   EXPECT_EQ(cricket::ICEMODE_FULL, tinfo2->description.ice_mode);
   1946   InjectAfter(kSessionTime,
   1947               "a=ice-lite\r\n",
   1948               &sdp_with_icelite);
   1949   EXPECT_TRUE(SdpDeserialize(sdp_with_icelite, &jdesc_with_icelite));
   1950   desc = jdesc_with_icelite.description();
   1951   const cricket::TransportInfo* atinfo =
   1952       desc->GetTransportInfoByName("audio_content_name");
   1953   EXPECT_EQ(cricket::ICEMODE_LITE, atinfo->description.ice_mode);
   1954   const cricket::TransportInfo* vtinfo =
   1955         desc->GetTransportInfoByName("video_content_name");
   1956   EXPECT_EQ(cricket::ICEMODE_LITE, vtinfo->description.ice_mode);
   1957 }
   1958 
   1959 // Verifies that the candidates in the input SDP are parsed and serialized
   1960 // correctly in the output SDP.
   1961 TEST_F(WebRtcSdpTest, RoundTripSdpWithSctpDataChannelsWithCandidates) {
   1962   std::string sdp_with_data = kSdpString;
   1963   sdp_with_data.append(kSdpSctpDataChannelWithCandidatesString);
   1964   JsepSessionDescription jdesc_output(kDummyString);
   1965 
   1966   EXPECT_TRUE(SdpDeserialize(sdp_with_data, &jdesc_output));
   1967   EXPECT_EQ(sdp_with_data, webrtc::SdpSerialize(jdesc_output));
   1968 }
   1969