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 "talk/app/webrtc/webrtcsdp.h"
     29 
     30 #include <limits.h>
     31 #include <stdio.h>
     32 #include <algorithm>
     33 #include <string>
     34 #include <vector>
     35 
     36 #include "talk/app/webrtc/jsepicecandidate.h"
     37 #include "talk/app/webrtc/jsepsessiondescription.h"
     38 #include "talk/base/common.h"
     39 #include "talk/base/logging.h"
     40 #include "talk/base/messagedigest.h"
     41 #include "talk/base/stringutils.h"
     42 #include "talk/media/base/codec.h"
     43 #include "talk/media/base/constants.h"
     44 #include "talk/media/base/cryptoparams.h"
     45 #include "talk/media/sctp/sctpdataengine.h"
     46 #include "talk/p2p/base/candidate.h"
     47 #include "talk/p2p/base/constants.h"
     48 #include "talk/p2p/base/port.h"
     49 #include "talk/session/media/mediasession.h"
     50 #include "talk/session/media/mediasessionclient.h"
     51 
     52 using cricket::AudioContentDescription;
     53 using cricket::Candidate;
     54 using cricket::Candidates;
     55 using cricket::ContentDescription;
     56 using cricket::ContentInfo;
     57 using cricket::CryptoParams;
     58 using cricket::DataContentDescription;
     59 using cricket::ICE_CANDIDATE_COMPONENT_RTP;
     60 using cricket::ICE_CANDIDATE_COMPONENT_RTCP;
     61 using cricket::kCodecParamMaxBitrate;
     62 using cricket::kCodecParamMaxPTime;
     63 using cricket::kCodecParamMaxQuantization;
     64 using cricket::kCodecParamMinBitrate;
     65 using cricket::kCodecParamMinPTime;
     66 using cricket::kCodecParamPTime;
     67 using cricket::kCodecParamSPropStereo;
     68 using cricket::kCodecParamStereo;
     69 using cricket::kCodecParamUseInbandFec;
     70 using cricket::kCodecParamSctpProtocol;
     71 using cricket::kCodecParamSctpStreams;
     72 using cricket::kCodecParamMaxAverageBitrate;
     73 using cricket::kWildcardPayloadType;
     74 using cricket::MediaContentDescription;
     75 using cricket::MediaType;
     76 using cricket::NS_JINGLE_ICE_UDP;
     77 using cricket::RtpHeaderExtension;
     78 using cricket::SsrcGroup;
     79 using cricket::StreamParams;
     80 using cricket::StreamParamsVec;
     81 using cricket::TransportDescription;
     82 using cricket::TransportInfo;
     83 using cricket::VideoContentDescription;
     84 using talk_base::SocketAddress;
     85 
     86 typedef std::vector<RtpHeaderExtension> RtpHeaderExtensions;
     87 
     88 namespace cricket {
     89 class SessionDescription;
     90 }
     91 
     92 namespace webrtc {
     93 
     94 // Line type
     95 // RFC 4566
     96 // An SDP session description consists of a number of lines of text of
     97 // the form:
     98 // <type>=<value>
     99 // where <type> MUST be exactly one case-significant character.
    100 static const int kLinePrefixLength = 2;  // Lenght of <type>=
    101 static const char kLineTypeVersion = 'v';
    102 static const char kLineTypeOrigin = 'o';
    103 static const char kLineTypeSessionName = 's';
    104 static const char kLineTypeSessionInfo = 'i';
    105 static const char kLineTypeSessionUri = 'u';
    106 static const char kLineTypeSessionEmail = 'e';
    107 static const char kLineTypeSessionPhone = 'p';
    108 static const char kLineTypeSessionBandwidth = 'b';
    109 static const char kLineTypeTiming = 't';
    110 static const char kLineTypeRepeatTimes = 'r';
    111 static const char kLineTypeTimeZone = 'z';
    112 static const char kLineTypeEncryptionKey = 'k';
    113 static const char kLineTypeMedia = 'm';
    114 static const char kLineTypeConnection = 'c';
    115 static const char kLineTypeAttributes = 'a';
    116 
    117 // Attributes
    118 static const char kAttributeGroup[] = "group";
    119 static const char kAttributeMid[] = "mid";
    120 static const char kAttributeRtcpMux[] = "rtcp-mux";
    121 static const char kAttributeSsrc[] = "ssrc";
    122 static const char kSsrcAttributeCname[] = "cname";
    123 static const char kAttributeExtmap[] = "extmap";
    124 // draft-alvestrand-mmusic-msid-01
    125 // a=msid-semantic: WMS
    126 static const char kAttributeMsidSemantics[] = "msid-semantic";
    127 static const char kMediaStreamSemantic[] = "WMS";
    128 static const char kSsrcAttributeMsid[] = "msid";
    129 static const char kDefaultMsid[] = "default";
    130 static const char kMsidAppdataAudio[] = "a";
    131 static const char kMsidAppdataVideo[] = "v";
    132 static const char kMsidAppdataData[] = "d";
    133 static const char kSsrcAttributeMslabel[] = "mslabel";
    134 static const char kSSrcAttributeLabel[] = "label";
    135 static const char kAttributeSsrcGroup[] = "ssrc-group";
    136 static const char kAttributeCrypto[] = "crypto";
    137 static const char kAttributeCandidate[] = "candidate";
    138 static const char kAttributeCandidateTyp[] = "typ";
    139 static const char kAttributeCandidateRaddr[] = "raddr";
    140 static const char kAttributeCandidateRport[] = "rport";
    141 static const char kAttributeCandidateUsername[] = "username";
    142 static const char kAttributeCandidatePassword[] = "password";
    143 static const char kAttributeCandidateGeneration[] = "generation";
    144 static const char kAttributeFingerprint[] = "fingerprint";
    145 static const char kAttributeSetup[] = "setup";
    146 static const char kAttributeFmtp[] = "fmtp";
    147 static const char kAttributeRtpmap[] = "rtpmap";
    148 static const char kAttributeSctpmap[] = "sctpmap";
    149 static const char kAttributeRtcp[] = "rtcp";
    150 static const char kAttributeIceUfrag[] = "ice-ufrag";
    151 static const char kAttributeIcePwd[] = "ice-pwd";
    152 static const char kAttributeIceLite[] = "ice-lite";
    153 static const char kAttributeIceOption[] = "ice-options";
    154 static const char kAttributeSendOnly[] = "sendonly";
    155 static const char kAttributeRecvOnly[] = "recvonly";
    156 static const char kAttributeRtcpFb[] = "rtcp-fb";
    157 static const char kAttributeSendRecv[] = "sendrecv";
    158 static const char kAttributeInactive[] = "inactive";
    159 
    160 // Experimental flags
    161 static const char kAttributeXGoogleFlag[] = "x-google-flag";
    162 static const char kValueConference[] = "conference";
    163 static const char kAttributeXGoogleBufferLatency[] =
    164     "x-google-buffer-latency";
    165 
    166 // Candidate
    167 static const char kCandidateHost[] = "host";
    168 static const char kCandidateSrflx[] = "srflx";
    169 // TODO: How to map the prflx with circket candidate type
    170 // static const char kCandidatePrflx[] = "prflx";
    171 static const char kCandidateRelay[] = "relay";
    172 
    173 static const char kSdpDelimiterEqual = '=';
    174 static const char kSdpDelimiterSpace = ' ';
    175 static const char kSdpDelimiterColon = ':';
    176 static const char kSdpDelimiterSemicolon = ';';
    177 static const char kSdpDelimiterSlash = '/';
    178 static const char kNewLine = '\n';
    179 static const char kReturn = '\r';
    180 static const char kLineBreak[] = "\r\n";
    181 
    182 // TODO: Generate the Session and Time description
    183 // instead of hardcoding.
    184 static const char kSessionVersion[] = "v=0";
    185 // RFC 4566
    186 static const char kSessionOriginUsername[] = "-";
    187 static const char kSessionOriginSessionId[] = "0";
    188 static const char kSessionOriginSessionVersion[] = "0";
    189 static const char kSessionOriginNettype[] = "IN";
    190 static const char kSessionOriginAddrtype[] = "IP4";
    191 static const char kSessionOriginAddress[] = "127.0.0.1";
    192 static const char kSessionName[] = "s=-";
    193 static const char kTimeDescription[] = "t=0 0";
    194 static const char kAttrGroup[] = "a=group:BUNDLE";
    195 static const char kConnectionNettype[] = "IN";
    196 static const char kConnectionAddrtype[] = "IP4";
    197 static const char kMediaTypeVideo[] = "video";
    198 static const char kMediaTypeAudio[] = "audio";
    199 static const char kMediaTypeData[] = "application";
    200 static const char kMediaPortRejected[] = "0";
    201 static const char kDefaultAddress[] = "0.0.0.0";
    202 static const char kDefaultPort[] = "1";
    203 // RFC 3556
    204 static const char kApplicationSpecificMaximum[] = "AS";
    205 
    206 static const int kDefaultVideoClockrate = 90000;
    207 
    208 // ISAC special-case.
    209 static const char kIsacCodecName[] = "ISAC";  // From webrtcvoiceengine.cc
    210 static const int kIsacWbDefaultRate = 32000;  // From acm_common_defs.h
    211 static const int kIsacSwbDefaultRate = 56000;  // From acm_common_defs.h
    212 
    213 static const int kDefaultSctpFmt = 5000;
    214 static const char kDefaultSctpmapProtocol[] = "webrtc-datachannel";
    215 
    216 struct SsrcInfo {
    217   SsrcInfo()
    218       : msid_identifier(kDefaultMsid),
    219         // TODO(ronghuawu): What should we do if the appdata doesn't appear?
    220         // Create random string (which will be used as track label later)?
    221         msid_appdata(talk_base::CreateRandomString(8)) {
    222   }
    223   uint32 ssrc_id;
    224   std::string cname;
    225   std::string msid_identifier;
    226   std::string msid_appdata;
    227 
    228   // For backward compatibility.
    229   // TODO(ronghuawu): Remove below 2 fields once all the clients support msid.
    230   std::string label;
    231   std::string mslabel;
    232 };
    233 typedef std::vector<SsrcInfo> SsrcInfoVec;
    234 typedef std::vector<SsrcGroup> SsrcGroupVec;
    235 
    236 // Serializes the passed in SessionDescription to a SDP string.
    237 // desc - The SessionDescription object to be serialized.
    238 static std::string SdpSerializeSessionDescription(
    239     const JsepSessionDescription& jdesc);
    240 template <class T>
    241 static void AddFmtpLine(const T& codec, std::string* message);
    242 static void BuildMediaDescription(const ContentInfo* content_info,
    243                                   const TransportInfo* transport_info,
    244                                   const MediaType media_type,
    245                                   std::string* message);
    246 static void BuildSctpContentAttributes(std::string* message);
    247 static void BuildRtpContentAttributes(
    248     const MediaContentDescription* media_desc,
    249     const MediaType media_type,
    250     std::string* message);
    251 static void BuildRtpMap(const MediaContentDescription* media_desc,
    252                         const MediaType media_type,
    253                         std::string* message);
    254 static void BuildCandidate(const std::vector<Candidate>& candidates,
    255                            std::string* message);
    256 static void BuildIceOptions(const std::vector<std::string>& transport_options,
    257                             std::string* message);
    258 
    259 static bool ParseSessionDescription(const std::string& message, size_t* pos,
    260                                     std::string* session_id,
    261                                     std::string* session_version,
    262                                     bool* supports_msid,
    263                                     TransportDescription* session_td,
    264                                     RtpHeaderExtensions* session_extmaps,
    265                                     cricket::SessionDescription* desc,
    266                                     SdpParseError* error);
    267 static bool ParseGroupAttribute(const std::string& line,
    268                                 cricket::SessionDescription* desc,
    269                                 SdpParseError* error);
    270 static bool ParseMediaDescription(
    271     const std::string& message,
    272     const TransportDescription& session_td,
    273     const RtpHeaderExtensions& session_extmaps,
    274     bool supports_msid,
    275     size_t* pos, cricket::SessionDescription* desc,
    276     std::vector<JsepIceCandidate*>* candidates,
    277     SdpParseError* error);
    278 static bool ParseContent(const std::string& message,
    279                          const MediaType media_type,
    280                          int mline_index,
    281                          const std::string& protocol,
    282                          const std::vector<int>& codec_preference,
    283                          size_t* pos,
    284                          std::string* content_name,
    285                          MediaContentDescription* media_desc,
    286                          TransportDescription* transport,
    287                          std::vector<JsepIceCandidate*>* candidates,
    288                          SdpParseError* error);
    289 static bool ParseSsrcAttribute(const std::string& line,
    290                                SsrcInfoVec* ssrc_infos,
    291                                SdpParseError* error);
    292 static bool ParseSsrcGroupAttribute(const std::string& line,
    293                                     SsrcGroupVec* ssrc_groups,
    294                                     SdpParseError* error);
    295 static bool ParseCryptoAttribute(const std::string& line,
    296                                  MediaContentDescription* media_desc,
    297                                  SdpParseError* error);
    298 static bool ParseRtpmapAttribute(const std::string& line,
    299                                  const MediaType media_type,
    300                                  const std::vector<int>& codec_preference,
    301                                  MediaContentDescription* media_desc,
    302                                  SdpParseError* error);
    303 static bool ParseFmtpAttributes(const std::string& line,
    304                                 const MediaType media_type,
    305                                 MediaContentDescription* media_desc,
    306                                 SdpParseError* error);
    307 static bool ParseFmtpParam(const std::string& line, std::string* parameter,
    308                            std::string* value, SdpParseError* error);
    309 static bool ParseCandidate(const std::string& message, Candidate* candidate,
    310                            SdpParseError* error, bool is_raw);
    311 static bool ParseRtcpFbAttribute(const std::string& line,
    312                                  const MediaType media_type,
    313                                  MediaContentDescription* media_desc,
    314                                  SdpParseError* error);
    315 static bool ParseIceOptions(const std::string& line,
    316                             std::vector<std::string>* transport_options,
    317                             SdpParseError* error);
    318 static bool ParseExtmap(const std::string& line,
    319                         RtpHeaderExtension* extmap,
    320                         SdpParseError* error);
    321 static bool ParseFingerprintAttribute(const std::string& line,
    322                                       talk_base::SSLFingerprint** fingerprint,
    323                                       SdpParseError* error);
    324 static bool ParseDtlsSetup(const std::string& line,
    325                            cricket::ConnectionRole* role,
    326                            SdpParseError* error);
    327 
    328 // Helper functions
    329 
    330 // Below ParseFailed*** functions output the line that caused the parsing
    331 // failure and the detailed reason (|description|) of the failure to |error|.
    332 // The functions always return false so that they can be used directly in the
    333 // following way when error happens:
    334 // "return ParseFailed***(...);"
    335 
    336 // The line starting at |line_start| of |message| is the failing line.
    337 // The reason for the failure should be provided in the |description|.
    338 // An example of a description could be "unknown character".
    339 static bool ParseFailed(const std::string& message,
    340                         size_t line_start,
    341                         const std::string& description,
    342                         SdpParseError* error) {
    343   // Get the first line of |message| from |line_start|.
    344   std::string first_line;
    345   size_t line_end = message.find(kNewLine, line_start);
    346   if (line_end != std::string::npos) {
    347     if (line_end > 0 && (message.at(line_end - 1) == kReturn)) {
    348       --line_end;
    349     }
    350     first_line = message.substr(line_start, (line_end - line_start));
    351   } else {
    352     first_line = message.substr(line_start);
    353   }
    354 
    355   if (error) {
    356     error->line = first_line;
    357     error->description = description;
    358   }
    359   LOG(LS_ERROR) << "Failed to parse: \"" << first_line
    360                 << "\". Reason: " << description;
    361   return false;
    362 }
    363 
    364 // |line| is the failing line. The reason for the failure should be
    365 // provided in the |description|.
    366 static bool ParseFailed(const std::string& line,
    367                         const std::string& description,
    368                         SdpParseError* error) {
    369   return ParseFailed(line, 0, description, error);
    370 }
    371 
    372 // Parses failure where the failing SDP line isn't know or there are multiple
    373 // failing lines.
    374 static bool ParseFailed(const std::string& description,
    375                         SdpParseError* error) {
    376   return ParseFailed("", description, error);
    377 }
    378 
    379 // |line| is the failing line. The failure is due to the fact that |line|
    380 // doesn't have |expected_fields| fields.
    381 static bool ParseFailedExpectFieldNum(const std::string& line,
    382                                       int expected_fields,
    383                                       SdpParseError* error) {
    384   std::ostringstream description;
    385   description << "Expects " << expected_fields << " fields.";
    386   return ParseFailed(line, description.str(), error);
    387 }
    388 
    389 // |line| is the failing line. The failure is due to the fact that |line| has
    390 // less than |expected_min_fields| fields.
    391 static bool ParseFailedExpectMinFieldNum(const std::string& line,
    392                                          int expected_min_fields,
    393                                          SdpParseError* error) {
    394   std::ostringstream description;
    395   description << "Expects at least " << expected_min_fields << " fields.";
    396   return ParseFailed(line, description.str(), error);
    397 }
    398 
    399 // |line| is the failing line. The failure is due to the fact that it failed to
    400 // get the value of |attribute|.
    401 static bool ParseFailedGetValue(const std::string& line,
    402                                 const std::string& attribute,
    403                                 SdpParseError* error) {
    404   std::ostringstream description;
    405   description << "Failed to get the value of attribute: " << attribute;
    406   return ParseFailed(line, description.str(), error);
    407 }
    408 
    409 // The line starting at |line_start| of |message| is the failing line. The
    410 // failure is due to the line type (e.g. the "m" part of the "m-line")
    411 // not matching what is expected. The expected line type should be
    412 // provided as |line_type|.
    413 static bool ParseFailedExpectLine(const std::string& message,
    414                                   size_t line_start,
    415                                   const char line_type,
    416                                   const std::string& line_value,
    417                                   SdpParseError* error) {
    418   std::ostringstream description;
    419   description << "Expect line: " << line_type << "=" << line_value;
    420   return ParseFailed(message, line_start, description.str(), error);
    421 }
    422 
    423 static bool AddLine(const std::string& line, std::string* message) {
    424   if (!message)
    425     return false;
    426 
    427   message->append(line);
    428   message->append(kLineBreak);
    429   return true;
    430 }
    431 
    432 static bool GetLine(const std::string& message,
    433                     size_t* pos,
    434                     std::string* line) {
    435   size_t line_begin = *pos;
    436   size_t line_end = message.find(kNewLine, line_begin);
    437   if (line_end == std::string::npos) {
    438     return false;
    439   }
    440   // Update the new start position
    441   *pos = line_end + 1;
    442   if (line_end > 0 && (message.at(line_end - 1) == kReturn)) {
    443     --line_end;
    444   }
    445   *line = message.substr(line_begin, (line_end - line_begin));
    446   const char* cline = line->c_str();
    447   // RFC 4566
    448   // An SDP session description consists of a number of lines of text of
    449   // the form:
    450   // <type>=<value>
    451   // where <type> MUST be exactly one case-significant character and
    452   // <value> is structured text whose format depends on <type>.
    453   // Whitespace MUST NOT be used on either side of the "=" sign.
    454   if (cline[0] == kSdpDelimiterSpace ||
    455       cline[1] != kSdpDelimiterEqual ||
    456       cline[2] == kSdpDelimiterSpace) {
    457     *pos = line_begin;
    458     return false;
    459   }
    460   return true;
    461 }
    462 
    463 // Init |os| to "|type|=|value|".
    464 static void InitLine(const char type,
    465                      const std::string& value,
    466                      std::ostringstream* os) {
    467   os->str("");
    468   *os << type << kSdpDelimiterEqual << value;
    469 }
    470 
    471 // Init |os| to "a=|attribute|".
    472 static void InitAttrLine(const std::string& attribute, std::ostringstream* os) {
    473   InitLine(kLineTypeAttributes, attribute, os);
    474 }
    475 
    476 // Writes a SDP attribute line based on |attribute| and |value| to |message|.
    477 static void AddAttributeLine(const std::string& attribute, int value,
    478                              std::string* message) {
    479   std::ostringstream os;
    480   InitAttrLine(attribute, &os);
    481   os << kSdpDelimiterColon << value;
    482   AddLine(os.str(), message);
    483 }
    484 
    485 // Returns the first line of the message without the line breaker.
    486 static bool GetFirstLine(const std::string& message, std::string* line) {
    487   size_t pos = 0;
    488   if (!GetLine(message, &pos, line)) {
    489     // If GetLine failed, just return the full |message|.
    490     *line = message;
    491   }
    492   return true;
    493 }
    494 
    495 static bool IsLineType(const std::string& message,
    496                        const char type,
    497                        size_t line_start) {
    498   if (message.size() < line_start + kLinePrefixLength) {
    499     return false;
    500   }
    501   const char* cmessage = message.c_str();
    502   return (cmessage[line_start] == type &&
    503           cmessage[line_start + 1] == kSdpDelimiterEqual);
    504 }
    505 
    506 static bool IsLineType(const std::string& line,
    507                        const char type) {
    508   return IsLineType(line, type, 0);
    509 }
    510 
    511 static bool GetLineWithType(const std::string& message, size_t* pos,
    512                             std::string* line, const char type) {
    513   if (!IsLineType(message, type, *pos)) {
    514     return false;
    515   }
    516 
    517   if (!GetLine(message, pos, line))
    518     return false;
    519 
    520   return true;
    521 }
    522 
    523 static bool HasAttribute(const std::string& line,
    524                          const std::string& attribute) {
    525   return (line.compare(kLinePrefixLength, attribute.size(), attribute) == 0);
    526 }
    527 
    528 // Verifies the candiate to be of the format candidate:<blah>
    529 static bool IsRawCandidate(const std::string& line) {
    530   // Checking candiadte-attribute is starting with "candidate" str.
    531   if (line.compare(0, strlen(kAttributeCandidate), kAttributeCandidate) != 0) {
    532     return false;
    533   }
    534   const size_t first_candidate = line.find(kSdpDelimiterColon);
    535   if (first_candidate == std::string::npos)
    536     return false;
    537   // In this format we only expecting one candiate. If any additional
    538   // candidates present, whole string will be discared.
    539   const size_t any_other = line.find(kSdpDelimiterColon, first_candidate + 1);
    540   return (any_other == std::string::npos);
    541 }
    542 
    543 static bool AddSsrcLine(uint32 ssrc_id, const std::string& attribute,
    544                         const std::string& value, std::string* message) {
    545   // RFC 5576
    546   // a=ssrc:<ssrc-id> <attribute>:<value>
    547   std::ostringstream os;
    548   InitAttrLine(kAttributeSsrc, &os);
    549   os << kSdpDelimiterColon << ssrc_id << kSdpDelimiterSpace
    550      << attribute << kSdpDelimiterColon << value;
    551   return AddLine(os.str(), message);
    552 }
    553 
    554 // Split the message into two parts by the first delimiter.
    555 static bool SplitByDelimiter(const std::string& message,
    556                              const char delimiter,
    557                              std::string* field1,
    558                              std::string* field2) {
    559   // Find the first delimiter
    560   size_t pos = message.find(delimiter);
    561   if (pos == std::string::npos) {
    562     return false;
    563   }
    564   *field1 = message.substr(0, pos);
    565   // The rest is the value.
    566   *field2 = message.substr(pos + 1);
    567   return true;
    568 }
    569 
    570 // Get value only from <attribute>:<value>.
    571 static bool GetValue(const std::string& message, const std::string& attribute,
    572                      std::string* value, SdpParseError* error) {
    573   std::string leftpart;
    574   if (!SplitByDelimiter(message, kSdpDelimiterColon, &leftpart, value)) {
    575     return ParseFailedGetValue(message, attribute, error);
    576   }
    577   // The left part should end with the expected attribute.
    578   if (leftpart.length() < attribute.length() ||
    579       leftpart.compare(leftpart.length() - attribute.length(),
    580                        attribute.length(), attribute) != 0) {
    581     return ParseFailedGetValue(message, attribute, error);
    582   }
    583   return true;
    584 }
    585 
    586 static bool CaseInsensitiveFind(std::string str1, std::string str2) {
    587   std::transform(str1.begin(), str1.end(), str1.begin(),
    588                  ::tolower);
    589   std::transform(str2.begin(), str2.end(), str2.begin(),
    590                  ::tolower);
    591   return str1.find(str2) != std::string::npos;
    592 }
    593 
    594 void CreateTracksFromSsrcInfos(const SsrcInfoVec& ssrc_infos,
    595                                StreamParamsVec* tracks) {
    596   ASSERT(tracks != NULL);
    597   for (SsrcInfoVec::const_iterator ssrc_info = ssrc_infos.begin();
    598        ssrc_info != ssrc_infos.end(); ++ssrc_info) {
    599     if (ssrc_info->cname.empty()) {
    600       continue;
    601     }
    602 
    603     std::string sync_label;
    604     std::string track_id;
    605     if (ssrc_info->msid_identifier == kDefaultMsid &&
    606         !ssrc_info->mslabel.empty()) {
    607       // If there's no msid and there's mslabel, we consider this is a sdp from
    608       // a older version of client that doesn't support msid.
    609       // In that case, we use the mslabel and label to construct the track.
    610       sync_label = ssrc_info->mslabel;
    611       track_id = ssrc_info->label;
    612     } else {
    613       sync_label = ssrc_info->msid_identifier;
    614       // The appdata consists of the "id" attribute of a MediaStreamTrack, which
    615       // is corresponding to the "id" attribute of StreamParams.
    616       track_id = ssrc_info->msid_appdata;
    617     }
    618     if (sync_label.empty() || track_id.empty()) {
    619       ASSERT(false);
    620       continue;
    621     }
    622 
    623     StreamParamsVec::iterator track = tracks->begin();
    624     for (; track != tracks->end(); ++track) {
    625       if (track->id == track_id) {
    626         break;
    627       }
    628     }
    629     if (track == tracks->end()) {
    630       // If we don't find an existing track, create a new one.
    631       tracks->push_back(StreamParams());
    632       track = tracks->end() - 1;
    633     }
    634     track->add_ssrc(ssrc_info->ssrc_id);
    635     track->cname = ssrc_info->cname;
    636     track->sync_label = sync_label;
    637     track->id = track_id;
    638   }
    639 }
    640 
    641 void GetMediaStreamLabels(const ContentInfo* content,
    642                           std::set<std::string>* labels) {
    643   const MediaContentDescription* media_desc =
    644       static_cast<const MediaContentDescription*>(
    645           content->description);
    646   const cricket::StreamParamsVec& streams =  media_desc->streams();
    647   for (cricket::StreamParamsVec::const_iterator it = streams.begin();
    648        it != streams.end(); ++it) {
    649     labels->insert(it->sync_label);
    650   }
    651 }
    652 
    653 // RFC 5245
    654 // It is RECOMMENDED that default candidates be chosen based on the
    655 // likelihood of those candidates to work with the peer that is being
    656 // contacted.  It is RECOMMENDED that relayed > reflexive > host.
    657 static const int kPreferenceUnknown = 0;
    658 static const int kPreferenceHost = 1;
    659 static const int kPreferenceReflexive = 2;
    660 static const int kPreferenceRelayed = 3;
    661 
    662 static int GetCandidatePreferenceFromType(const std::string& type) {
    663   int preference = kPreferenceUnknown;
    664   if (type == cricket::LOCAL_PORT_TYPE) {
    665     preference = kPreferenceHost;
    666   } else if (type == cricket::STUN_PORT_TYPE) {
    667     preference = kPreferenceReflexive;
    668   } else if (type == cricket::RELAY_PORT_TYPE) {
    669     preference = kPreferenceRelayed;
    670   } else {
    671     ASSERT(false);
    672   }
    673   return preference;
    674 }
    675 
    676 // Get ip and port of the default destination from the |candidates| with
    677 // the given value of |component_id|.
    678 // RFC 5245
    679 // The value of |component_id| currently supported are 1 (RTP) and 2 (RTCP).
    680 // TODO: Decide the default destination in webrtcsession and
    681 // pass it down via SessionDescription.
    682 static bool GetDefaultDestination(const std::vector<Candidate>& candidates,
    683     int component_id, std::string* port, std::string* ip) {
    684   *port = kDefaultPort;
    685   *ip = kDefaultAddress;
    686   int current_preference = kPreferenceUnknown;
    687   for (std::vector<Candidate>::const_iterator it = candidates.begin();
    688        it != candidates.end(); ++it) {
    689     if (it->component() != component_id) {
    690       continue;
    691     }
    692     const int preference = GetCandidatePreferenceFromType(it->type());
    693     // See if this candidate is more preferable then the current one.
    694     if (preference <= current_preference) {
    695       continue;
    696     }
    697     current_preference = preference;
    698     *port = it->address().PortAsString();
    699     *ip = it->address().ipaddr().ToString();
    700   }
    701   return true;
    702 }
    703 
    704 // Update the media default destination.
    705 static void UpdateMediaDefaultDestination(
    706     const std::vector<Candidate>& candidates, std::string* mline) {
    707   // RFC 4566
    708   // m=<media> <port> <proto> <fmt> ...
    709   std::vector<std::string> fields;
    710   talk_base::split(*mline, kSdpDelimiterSpace, &fields);
    711   if (fields.size() < 3) {
    712     return;
    713   }
    714 
    715   bool is_rtp =
    716       fields[2].empty() ||
    717       talk_base::starts_with(fields[2].data(),
    718                              cricket::kMediaProtocolRtpPrefix);
    719 
    720   std::ostringstream os;
    721   std::string rtp_port, rtp_ip;
    722   if (GetDefaultDestination(candidates, ICE_CANDIDATE_COMPONENT_RTP,
    723                             &rtp_port, &rtp_ip)) {
    724     // Found default RTP candidate.
    725     // RFC 5245
    726     // The default candidates are added to the SDP as the default
    727     // destination for media.  For streams based on RTP, this is done by
    728     // placing the IP address and port of the RTP candidate into the c and m
    729     // lines, respectively.
    730 
    731     // Update the port in the m line.
    732     // If this is a m-line with port equal to 0, we don't change it.
    733     if (fields[1] != kMediaPortRejected) {
    734       mline->replace(fields[0].size() + 1,
    735                      fields[1].size(),
    736                      rtp_port);
    737     }
    738     // Add the c line.
    739     // RFC 4566
    740     // c=<nettype> <addrtype> <connection-address>
    741     InitLine(kLineTypeConnection, kConnectionNettype, &os);
    742     os << " " << kConnectionAddrtype << " " << rtp_ip;
    743     AddLine(os.str(), mline);
    744   }
    745 
    746   if (is_rtp) {
    747     std::string rtcp_port, rtcp_ip;
    748     if (GetDefaultDestination(candidates, ICE_CANDIDATE_COMPONENT_RTCP,
    749                               &rtcp_port, &rtcp_ip)) {
    750       // Found default RTCP candidate.
    751       // RFC 5245
    752       // If the agent is utilizing RTCP, it MUST encode the RTCP candidate
    753       // using the a=rtcp attribute as defined in RFC 3605.
    754 
    755       // RFC 3605
    756       // rtcp-attribute =  "a=rtcp:" port  [nettype space addrtype space
    757       // connection-address] CRLF
    758       InitAttrLine(kAttributeRtcp, &os);
    759       os << kSdpDelimiterColon
    760          << rtcp_port << " "
    761          << kConnectionNettype << " "
    762          << kConnectionAddrtype << " "
    763          << rtcp_ip;
    764       AddLine(os.str(), mline);
    765     }
    766   }
    767 }
    768 
    769 // Get candidates according to the mline index from SessionDescriptionInterface.
    770 static void GetCandidatesByMindex(const SessionDescriptionInterface& desci,
    771                                   int mline_index,
    772                                   std::vector<Candidate>* candidates) {
    773   if (!candidates) {
    774     return;
    775   }
    776   const IceCandidateCollection* cc = desci.candidates(mline_index);
    777   for (size_t i = 0; i < cc->count(); ++i) {
    778     const IceCandidateInterface* candidate = cc->at(i);
    779     candidates->push_back(candidate->candidate());
    780   }
    781 }
    782 
    783 std::string SdpSerialize(const JsepSessionDescription& jdesc) {
    784   std::string sdp = SdpSerializeSessionDescription(jdesc);
    785 
    786   std::string sdp_with_candidates;
    787   size_t pos = 0;
    788   std::string line;
    789   int mline_index = -1;
    790   while (GetLine(sdp, &pos, &line)) {
    791     if (IsLineType(line, kLineTypeMedia)) {
    792       ++mline_index;
    793       std::vector<Candidate> candidates;
    794       GetCandidatesByMindex(jdesc, mline_index, &candidates);
    795       // Media line may append other lines inside the
    796       // UpdateMediaDefaultDestination call, so add the kLineBreak here first.
    797       line.append(kLineBreak);
    798       UpdateMediaDefaultDestination(candidates, &line);
    799       sdp_with_candidates.append(line);
    800       // Build the a=candidate lines.
    801       BuildCandidate(candidates, &sdp_with_candidates);
    802     } else {
    803       // Copy old line to new sdp without change.
    804       AddLine(line, &sdp_with_candidates);
    805     }
    806   }
    807   sdp = sdp_with_candidates;
    808 
    809   return sdp;
    810 }
    811 
    812 std::string SdpSerializeSessionDescription(
    813     const JsepSessionDescription& jdesc) {
    814   const cricket::SessionDescription* desc = jdesc.description();
    815   if (!desc) {
    816     return "";
    817   }
    818 
    819   std::string message;
    820 
    821   // Session Description.
    822   AddLine(kSessionVersion, &message);
    823   // Session Origin
    824   // RFC 4566
    825   // o=<username> <sess-id> <sess-version> <nettype> <addrtype>
    826   // <unicast-address>
    827   std::ostringstream os;
    828   InitLine(kLineTypeOrigin, kSessionOriginUsername, &os);
    829   const std::string session_id = jdesc.session_id().empty() ?
    830       kSessionOriginSessionId : jdesc.session_id();
    831   const std::string session_version = jdesc.session_version().empty() ?
    832       kSessionOriginSessionVersion : jdesc.session_version();
    833   os << " " << session_id << " " << session_version << " "
    834      << kSessionOriginNettype << " " << kSessionOriginAddrtype << " "
    835      << kSessionOriginAddress;
    836   AddLine(os.str(), &message);
    837   AddLine(kSessionName, &message);
    838 
    839   // Time Description.
    840   AddLine(kTimeDescription, &message);
    841 
    842   // Group
    843   if (desc->HasGroup(cricket::GROUP_TYPE_BUNDLE)) {
    844     std::string group_line = kAttrGroup;
    845     const cricket::ContentGroup* group =
    846         desc->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
    847     ASSERT(group != NULL);
    848     const cricket::ContentNames& content_names = group->content_names();
    849     for (cricket::ContentNames::const_iterator it = content_names.begin();
    850          it != content_names.end(); ++it) {
    851       group_line.append(" ");
    852       group_line.append(*it);
    853     }
    854     AddLine(group_line, &message);
    855   }
    856 
    857   // MediaStream semantics
    858   InitAttrLine(kAttributeMsidSemantics, &os);
    859   os << kSdpDelimiterColon << " " << kMediaStreamSemantic;
    860   std::set<std::string> media_stream_labels;
    861   const ContentInfo* audio_content = GetFirstAudioContent(desc);
    862   if (audio_content)
    863     GetMediaStreamLabels(audio_content, &media_stream_labels);
    864   const ContentInfo* video_content = GetFirstVideoContent(desc);
    865   if (video_content)
    866     GetMediaStreamLabels(video_content, &media_stream_labels);
    867   for (std::set<std::string>::const_iterator it =
    868       media_stream_labels.begin(); it != media_stream_labels.end(); ++it) {
    869     os << " " << *it;
    870   }
    871   AddLine(os.str(), &message);
    872 
    873   if (audio_content) {
    874     BuildMediaDescription(audio_content,
    875                           desc->GetTransportInfoByName(audio_content->name),
    876                           cricket::MEDIA_TYPE_AUDIO, &message);
    877   }
    878 
    879 
    880   if (video_content) {
    881     BuildMediaDescription(video_content,
    882                           desc->GetTransportInfoByName(video_content->name),
    883                           cricket::MEDIA_TYPE_VIDEO, &message);
    884   }
    885 
    886   const ContentInfo* data_content = GetFirstDataContent(desc);
    887   if (data_content) {
    888     BuildMediaDescription(data_content,
    889                           desc->GetTransportInfoByName(data_content->name),
    890                           cricket::MEDIA_TYPE_DATA, &message);
    891   }
    892 
    893 
    894   return message;
    895 }
    896 
    897 // Serializes the passed in IceCandidateInterface to a SDP string.
    898 // candidate - The candidate to be serialized.
    899 std::string SdpSerializeCandidate(
    900     const IceCandidateInterface& candidate) {
    901   std::string message;
    902   std::vector<cricket::Candidate> candidates;
    903   candidates.push_back(candidate.candidate());
    904   BuildCandidate(candidates, &message);
    905   return message;
    906 }
    907 
    908 bool SdpDeserialize(const std::string& message,
    909                     JsepSessionDescription* jdesc,
    910                     SdpParseError* error) {
    911   std::string session_id;
    912   std::string session_version;
    913   TransportDescription session_td(NS_JINGLE_ICE_UDP,
    914                                   std::string(), std::string());
    915   RtpHeaderExtensions session_extmaps;
    916   cricket::SessionDescription* desc = new cricket::SessionDescription();
    917   std::vector<JsepIceCandidate*> candidates;
    918   size_t current_pos = 0;
    919   bool supports_msid = false;
    920 
    921   // Session Description
    922   if (!ParseSessionDescription(message, &current_pos, &session_id,
    923                                &session_version, &supports_msid, &session_td,
    924                                &session_extmaps, desc, error)) {
    925     delete desc;
    926     return false;
    927   }
    928 
    929   // Media Description
    930   if (!ParseMediaDescription(message, session_td, session_extmaps,
    931                              supports_msid, &current_pos, desc, &candidates,
    932                              error)) {
    933     delete desc;
    934     for (std::vector<JsepIceCandidate*>::const_iterator
    935          it = candidates.begin(); it != candidates.end(); ++it) {
    936       delete *it;
    937     }
    938     return false;
    939   }
    940 
    941   jdesc->Initialize(desc, session_id, session_version);
    942 
    943   for (std::vector<JsepIceCandidate*>::const_iterator
    944        it = candidates.begin(); it != candidates.end(); ++it) {
    945     jdesc->AddCandidate(*it);
    946     delete *it;
    947   }
    948   return true;
    949 }
    950 
    951 bool SdpDeserializeCandidate(const std::string& message,
    952                              JsepIceCandidate* jcandidate,
    953                              SdpParseError* error) {
    954   ASSERT(jcandidate != NULL);
    955   Candidate candidate;
    956   if (!ParseCandidate(message, &candidate, error, true)) {
    957     return false;
    958   }
    959   jcandidate->SetCandidate(candidate);
    960   return true;
    961 }
    962 
    963 bool ParseCandidate(const std::string& message, Candidate* candidate,
    964                     SdpParseError* error, bool is_raw) {
    965   ASSERT(candidate != NULL);
    966 
    967   // Get the first line from |message|.
    968   std::string first_line;
    969   GetFirstLine(message, &first_line);
    970 
    971   size_t start_pos = kLinePrefixLength;  // Starting position to parse.
    972   if (IsRawCandidate(first_line)) {
    973     // From WebRTC draft section 4.8.1.1 candidate-attribute will be
    974     // just candidate:<candidate> not a=candidate:<blah>CRLF
    975     start_pos = 0;
    976   } else if (!IsLineType(first_line, kLineTypeAttributes) ||
    977              !HasAttribute(first_line, kAttributeCandidate)) {
    978     // Must start with a=candidate line.
    979     // Expecting to be of the format a=candidate:<blah>CRLF.
    980     if (is_raw) {
    981       std::ostringstream description;
    982       description << "Expect line: "
    983                   << kAttributeCandidate
    984                   << ":" << "<candidate-str>";
    985       return ParseFailed(first_line, 0, description.str(), error);
    986     } else {
    987       return ParseFailedExpectLine(first_line, 0, kLineTypeAttributes,
    988                                    kAttributeCandidate, error);
    989     }
    990   }
    991 
    992   std::vector<std::string> fields;
    993   talk_base::split(first_line.substr(start_pos),
    994                    kSdpDelimiterSpace, &fields);
    995   // RFC 5245
    996   // a=candidate:<foundation> <component-id> <transport> <priority>
    997   // <connection-address> <port> typ <candidate-types>
    998   // [raddr <connection-address>] [rport <port>]
    999   // *(SP extension-att-name SP extension-att-value)
   1000   const size_t expected_min_fields = 8;
   1001   if (fields.size() < expected_min_fields ||
   1002       (fields[6] != kAttributeCandidateTyp)) {
   1003     return ParseFailedExpectMinFieldNum(first_line, expected_min_fields, error);
   1004   }
   1005   std::string foundation;
   1006   if (!GetValue(fields[0], kAttributeCandidate, &foundation, error)) {
   1007     return false;
   1008   }
   1009   const int component_id = talk_base::FromString<int>(fields[1]);
   1010   const std::string transport = fields[2];
   1011   const uint32 priority = talk_base::FromString<uint32>(fields[3]);
   1012   const std::string connection_address = fields[4];
   1013   const int port = talk_base::FromString<int>(fields[5]);
   1014   SocketAddress address(connection_address, port);
   1015 
   1016   cricket::ProtocolType protocol;
   1017   if (!StringToProto(transport.c_str(), &protocol)) {
   1018     return ParseFailed(first_line, "Unsupported transport type.", error);
   1019   }
   1020 
   1021   std::string candidate_type;
   1022   const std::string type = fields[7];
   1023   if (type == kCandidateHost) {
   1024     candidate_type = cricket::LOCAL_PORT_TYPE;
   1025   } else if (type == kCandidateSrflx) {
   1026     candidate_type = cricket::STUN_PORT_TYPE;
   1027   } else if (type == kCandidateRelay) {
   1028     candidate_type = cricket::RELAY_PORT_TYPE;
   1029   } else {
   1030     return ParseFailed(first_line, "Unsupported candidate type.", error);
   1031   }
   1032 
   1033   size_t current_position = expected_min_fields;
   1034   SocketAddress related_address;
   1035   // The 2 optional fields for related address
   1036   // [raddr <connection-address>] [rport <port>]
   1037   if (fields.size() >= (current_position + 2) &&
   1038       fields[current_position] == kAttributeCandidateRaddr) {
   1039     related_address.SetIP(fields[++current_position]);
   1040     ++current_position;
   1041   }
   1042   if (fields.size() >= (current_position + 2) &&
   1043       fields[current_position] == kAttributeCandidateRport) {
   1044     related_address.SetPort(
   1045         talk_base::FromString<int>(fields[++current_position]));
   1046     ++current_position;
   1047   }
   1048 
   1049   // Extension
   1050   // Empty string as the candidate username and password.
   1051   // Will be updated later with the ice-ufrag and ice-pwd.
   1052   // TODO: Remove the username/password extension, which is currently
   1053   // kept for backwards compatibility.
   1054   std::string username;
   1055   std::string password;
   1056   uint32 generation = 0;
   1057   for (size_t i = current_position; i + 1 < fields.size(); ++i) {
   1058     // RFC 5245
   1059     // *(SP extension-att-name SP extension-att-value)
   1060     if (fields[i] == kAttributeCandidateGeneration) {
   1061       generation = talk_base::FromString<uint32>(fields[++i]);
   1062     } else if (fields[i] == kAttributeCandidateUsername) {
   1063       username = fields[++i];
   1064     } else if (fields[i] == kAttributeCandidatePassword) {
   1065       password = fields[++i];
   1066     } else {
   1067       // Skip the unknown extension.
   1068       ++i;
   1069     }
   1070   }
   1071 
   1072   // Empty string as the candidate id and network name.
   1073   const std::string id;
   1074   const std::string network_name;
   1075   *candidate = Candidate(id, component_id, cricket::ProtoToString(protocol),
   1076       address, priority, username, password, candidate_type, network_name,
   1077       generation, foundation);
   1078   candidate->set_related_address(related_address);
   1079   return true;
   1080 }
   1081 
   1082 bool ParseIceOptions(const std::string& line,
   1083                      std::vector<std::string>* transport_options,
   1084                      SdpParseError* error) {
   1085   std::string ice_options;
   1086   if (!GetValue(line, kAttributeIceOption, &ice_options, error)) {
   1087     return false;
   1088   }
   1089   std::vector<std::string> fields;
   1090   talk_base::split(ice_options, kSdpDelimiterSpace, &fields);
   1091   for (size_t i = 0; i < fields.size(); ++i) {
   1092     transport_options->push_back(fields[i]);
   1093   }
   1094   return true;
   1095 }
   1096 
   1097 bool ParseExtmap(const std::string& line, RtpHeaderExtension* extmap,
   1098                  SdpParseError* error) {
   1099   // RFC 5285
   1100   // a=extmap:<value>["/"<direction>] <URI> <extensionattributes>
   1101   std::vector<std::string> fields;
   1102   talk_base::split(line.substr(kLinePrefixLength),
   1103                    kSdpDelimiterSpace, &fields);
   1104   const size_t expected_min_fields = 2;
   1105   if (fields.size() < expected_min_fields) {
   1106     return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
   1107   }
   1108   std::string uri = fields[1];
   1109 
   1110   std::string value_direction;
   1111   if (!GetValue(fields[0], kAttributeExtmap, &value_direction, error)) {
   1112     return false;
   1113   }
   1114   std::vector<std::string> sub_fields;
   1115   talk_base::split(value_direction, kSdpDelimiterSlash, &sub_fields);
   1116   int value = talk_base::FromString<int>(sub_fields[0]);
   1117 
   1118   *extmap = RtpHeaderExtension(uri, value);
   1119   return true;
   1120 }
   1121 
   1122 void BuildMediaDescription(const ContentInfo* content_info,
   1123                            const TransportInfo* transport_info,
   1124                            const MediaType media_type,
   1125                            std::string* message) {
   1126   ASSERT(message != NULL);
   1127   if (content_info == NULL || message == NULL) {
   1128     return;
   1129   }
   1130   // TODO: Rethink if we should use sprintfn instead of stringstream.
   1131   // According to the style guide, streams should only be used for logging.
   1132   // http://google-styleguide.googlecode.com/svn/
   1133   // trunk/cppguide.xml?showone=Streams#Streams
   1134   std::ostringstream os;
   1135   const MediaContentDescription* media_desc =
   1136       static_cast<const MediaContentDescription*>(
   1137           content_info->description);
   1138   ASSERT(media_desc != NULL);
   1139 
   1140   bool is_sctp = (media_desc->protocol() == cricket::kMediaProtocolDtlsSctp);
   1141 
   1142   // RFC 4566
   1143   // m=<media> <port> <proto> <fmt>
   1144   // fmt is a list of payload type numbers that MAY be used in the session.
   1145   const char* type = NULL;
   1146   if (media_type == cricket::MEDIA_TYPE_AUDIO)
   1147     type = kMediaTypeAudio;
   1148   else if (media_type == cricket::MEDIA_TYPE_VIDEO)
   1149     type = kMediaTypeVideo;
   1150   else if (media_type == cricket::MEDIA_TYPE_DATA)
   1151     type = kMediaTypeData;
   1152   else
   1153     ASSERT(false);
   1154 
   1155   std::string fmt;
   1156   if (media_type == cricket::MEDIA_TYPE_VIDEO) {
   1157     const VideoContentDescription* video_desc =
   1158         static_cast<const VideoContentDescription*>(media_desc);
   1159     for (std::vector<cricket::VideoCodec>::const_iterator it =
   1160              video_desc->codecs().begin();
   1161          it != video_desc->codecs().end(); ++it) {
   1162       fmt.append(" ");
   1163       fmt.append(talk_base::ToString<int>(it->id));
   1164     }
   1165   } else if (media_type == cricket::MEDIA_TYPE_AUDIO) {
   1166     const AudioContentDescription* audio_desc =
   1167         static_cast<const AudioContentDescription*>(media_desc);
   1168     for (std::vector<cricket::AudioCodec>::const_iterator it =
   1169              audio_desc->codecs().begin();
   1170          it != audio_desc->codecs().end(); ++it) {
   1171       fmt.append(" ");
   1172       fmt.append(talk_base::ToString<int>(it->id));
   1173     }
   1174   } else if (media_type == cricket::MEDIA_TYPE_DATA) {
   1175     if (is_sctp) {
   1176       fmt.append(" ");
   1177       // TODO(jiayl): Replace the hard-coded string with the fmt read out of the
   1178       // ContentDescription.
   1179       fmt.append(talk_base::ToString<int>(kDefaultSctpFmt));
   1180     } else {
   1181       const DataContentDescription* data_desc =
   1182           static_cast<const DataContentDescription*>(media_desc);
   1183       for (std::vector<cricket::DataCodec>::const_iterator it =
   1184            data_desc->codecs().begin();
   1185            it != data_desc->codecs().end(); ++it) {
   1186         fmt.append(" ");
   1187         fmt.append(talk_base::ToString<int>(it->id));
   1188       }
   1189     }
   1190   }
   1191   // The fmt must never be empty. If no codecs are found, set the fmt attribute
   1192   // to 0.
   1193   if (fmt.empty()) {
   1194     fmt = " 0";
   1195   }
   1196 
   1197   // The port number in the m line will be updated later when associate with
   1198   // the candidates.
   1199   // RFC 3264
   1200   // To reject an offered stream, the port number in the corresponding stream in
   1201   // the answer MUST be set to zero.
   1202   const std::string port = content_info->rejected ?
   1203       kMediaPortRejected : kDefaultPort;
   1204 
   1205   talk_base::SSLFingerprint* fp = (transport_info) ?
   1206       transport_info->description.identity_fingerprint.get() : NULL;
   1207 
   1208   InitLine(kLineTypeMedia, type, &os);
   1209   os << " " << port << " " << media_desc->protocol() << fmt;
   1210   AddLine(os.str(), message);
   1211 
   1212   // Use the transport_info to build the media level ice-ufrag and ice-pwd.
   1213   if (transport_info) {
   1214     // RFC 5245
   1215     // ice-pwd-att           = "ice-pwd" ":" password
   1216     // ice-ufrag-att         = "ice-ufrag" ":" ufrag
   1217     // ice-ufrag
   1218     InitAttrLine(kAttributeIceUfrag, &os);
   1219     os << kSdpDelimiterColon << transport_info->description.ice_ufrag;
   1220     AddLine(os.str(), message);
   1221     // ice-pwd
   1222     InitAttrLine(kAttributeIcePwd, &os);
   1223     os << kSdpDelimiterColon << transport_info->description.ice_pwd;
   1224     AddLine(os.str(), message);
   1225 
   1226     // draft-petithuguenin-mmusic-ice-attributes-level-03
   1227     BuildIceOptions(transport_info->description.transport_options, message);
   1228 
   1229     // RFC 4572
   1230     // fingerprint-attribute  =
   1231     //   "fingerprint" ":" hash-func SP fingerprint
   1232     if (fp) {
   1233       // Insert the fingerprint attribute.
   1234       InitAttrLine(kAttributeFingerprint, &os);
   1235       os << kSdpDelimiterColon
   1236          << fp->algorithm << kSdpDelimiterSpace
   1237          << fp->GetRfc4572Fingerprint();
   1238       AddLine(os.str(), message);
   1239 
   1240       // Inserting setup attribute.
   1241       if (transport_info->description.connection_role !=
   1242               cricket::CONNECTIONROLE_NONE) {
   1243         // Making sure we are not using "passive" mode.
   1244         cricket::ConnectionRole role =
   1245             transport_info->description.connection_role;
   1246         std::string dtls_role_str;
   1247         VERIFY(cricket::ConnectionRoleToString(role, &dtls_role_str));
   1248         InitAttrLine(kAttributeSetup, &os);
   1249         os << kSdpDelimiterColon << dtls_role_str;
   1250         AddLine(os.str(), message);
   1251       }
   1252     }
   1253   }
   1254 
   1255   // RFC 3388
   1256   // mid-attribute      = "a=mid:" identification-tag
   1257   // identification-tag = token
   1258   // Use the content name as the mid identification-tag.
   1259   InitAttrLine(kAttributeMid, &os);
   1260   os << kSdpDelimiterColon << content_info->name;
   1261   AddLine(os.str(), message);
   1262 
   1263   if (is_sctp) {
   1264     BuildSctpContentAttributes(message);
   1265   } else {
   1266     BuildRtpContentAttributes(media_desc, media_type, message);
   1267   }
   1268 }
   1269 
   1270 void BuildSctpContentAttributes(std::string* message) {
   1271   // draft-ietf-mmusic-sctp-sdp-04
   1272   // a=sctpmap:sctpmap-number  protocol  [streams]
   1273   std::ostringstream os;
   1274   InitAttrLine(kAttributeSctpmap, &os);
   1275   os << kSdpDelimiterColon << kDefaultSctpFmt << kSdpDelimiterSpace
   1276      << kDefaultSctpmapProtocol << kSdpDelimiterSpace
   1277      << (cricket::kMaxSctpSid + 1);
   1278   AddLine(os.str(), message);
   1279 }
   1280 
   1281 void BuildRtpContentAttributes(
   1282     const MediaContentDescription* media_desc,
   1283     const MediaType media_type,
   1284     std::string* message) {
   1285   std::ostringstream os;
   1286   // RFC 5285
   1287   // a=extmap:<value>["/"<direction>] <URI> <extensionattributes>
   1288   // The definitions MUST be either all session level or all media level. This
   1289   // implementation uses all media level.
   1290   for (size_t i = 0; i < media_desc->rtp_header_extensions().size(); ++i) {
   1291     InitAttrLine(kAttributeExtmap, &os);
   1292     os << kSdpDelimiterColon << media_desc->rtp_header_extensions()[i].id
   1293        << kSdpDelimiterSpace << media_desc->rtp_header_extensions()[i].uri;
   1294     AddLine(os.str(), message);
   1295   }
   1296 
   1297   // RFC 3264
   1298   // a=sendrecv || a=sendonly || a=sendrecv || a=inactive
   1299 
   1300   cricket::MediaContentDirection direction = media_desc->direction();
   1301   if (media_desc->streams().empty() && direction == cricket::MD_SENDRECV) {
   1302     direction = cricket::MD_RECVONLY;
   1303   }
   1304 
   1305   switch (direction) {
   1306     case cricket::MD_INACTIVE:
   1307       InitAttrLine(kAttributeInactive, &os);
   1308       break;
   1309     case cricket::MD_SENDONLY:
   1310       InitAttrLine(kAttributeSendOnly, &os);
   1311       break;
   1312     case cricket::MD_RECVONLY:
   1313       InitAttrLine(kAttributeRecvOnly, &os);
   1314       break;
   1315     case cricket::MD_SENDRECV:
   1316     default:
   1317       InitAttrLine(kAttributeSendRecv, &os);
   1318       break;
   1319   }
   1320   AddLine(os.str(), message);
   1321 
   1322   // RFC 4566
   1323   // b=AS:<bandwidth>
   1324   // We should always use the default bandwidth for RTP-based data
   1325   // channels.  Don't allow SDP to set the bandwidth, because that
   1326   // would give JS the opportunity to "break the Internet".
   1327   // TODO(pthatcher): But we need to temporarily allow the SDP to control
   1328   // this for backwards-compatibility.  Once we don't need that any
   1329   // more, remove this.
   1330   bool support_dc_sdp_bandwidth_temporarily = true;
   1331   if (media_desc->bandwidth() >= 1000 &&
   1332       (media_type != cricket::MEDIA_TYPE_DATA ||
   1333        support_dc_sdp_bandwidth_temporarily)) {
   1334     InitLine(kLineTypeSessionBandwidth, kApplicationSpecificMaximum, &os);
   1335     os << kSdpDelimiterColon << (media_desc->bandwidth() / 1000);
   1336     AddLine(os.str(), message);
   1337   }
   1338 
   1339   // RFC 5761
   1340   // a=rtcp-mux
   1341   if (media_desc->rtcp_mux()) {
   1342     InitAttrLine(kAttributeRtcpMux, &os);
   1343     AddLine(os.str(), message);
   1344   }
   1345 
   1346   // RFC 4568
   1347   // a=crypto:<tag> <crypto-suite> <key-params> [<session-params>]
   1348   for (std::vector<CryptoParams>::const_iterator it =
   1349            media_desc->cryptos().begin();
   1350        it != media_desc->cryptos().end(); ++it) {
   1351     InitAttrLine(kAttributeCrypto, &os);
   1352     os << kSdpDelimiterColon << it->tag << " " << it->cipher_suite << " "
   1353        << it->key_params;
   1354     if (!it->session_params.empty()) {
   1355       os << " " << it->session_params;
   1356     }
   1357     AddLine(os.str(), message);
   1358   }
   1359 
   1360   // RFC 4566
   1361   // a=rtpmap:<payload type> <encoding name>/<clock rate>
   1362   // [/<encodingparameters>]
   1363   BuildRtpMap(media_desc, media_type, message);
   1364 
   1365   // Specify latency for buffered mode.
   1366   // a=x-google-buffer-latency:<value>
   1367   if (media_desc->buffered_mode_latency() != cricket::kBufferedModeDisabled) {
   1368     std::ostringstream os;
   1369     InitAttrLine(kAttributeXGoogleBufferLatency, &os);
   1370     os << kSdpDelimiterColon << media_desc->buffered_mode_latency();
   1371     AddLine(os.str(), message);
   1372   }
   1373 
   1374   for (StreamParamsVec::const_iterator track = media_desc->streams().begin();
   1375        track != media_desc->streams().end(); ++track) {
   1376     // Require that the track belongs to a media stream,
   1377     // ie the sync_label is set. This extra check is necessary since the
   1378     // MediaContentDescription always contains a streamparam with an ssrc even
   1379     // if no track or media stream have been created.
   1380     if (track->sync_label.empty()) continue;
   1381 
   1382     // Build the ssrc-group lines.
   1383     for (size_t i = 0; i < track->ssrc_groups.size(); ++i) {
   1384       // RFC 5576
   1385       // a=ssrc-group:<semantics> <ssrc-id> ...
   1386       if (track->ssrc_groups[i].ssrcs.empty()) {
   1387         continue;
   1388       }
   1389       std::ostringstream os;
   1390       InitAttrLine(kAttributeSsrcGroup, &os);
   1391       os << kSdpDelimiterColon << track->ssrc_groups[i].semantics;
   1392       std::vector<uint32>::const_iterator ssrc =
   1393           track->ssrc_groups[i].ssrcs.begin();
   1394       for (; ssrc != track->ssrc_groups[i].ssrcs.end(); ++ssrc) {
   1395         os << kSdpDelimiterSpace << talk_base::ToString<uint32>(*ssrc);
   1396       }
   1397       AddLine(os.str(), message);
   1398     }
   1399     // Build the ssrc lines for each ssrc.
   1400     for (size_t i = 0; i < track->ssrcs.size(); ++i) {
   1401       uint32 ssrc = track->ssrcs[i];
   1402       // RFC 5576
   1403       // a=ssrc:<ssrc-id> cname:<value>
   1404       AddSsrcLine(ssrc, kSsrcAttributeCname,
   1405                   track->cname, message);
   1406 
   1407       // draft-alvestrand-mmusic-msid-00
   1408       // a=ssrc:<ssrc-id> msid:identifier [appdata]
   1409       // The appdata consists of the "id" attribute of a MediaStreamTrack, which
   1410       // is corresponding to the "name" attribute of StreamParams.
   1411       std::string appdata = track->id;
   1412       std::ostringstream os;
   1413       InitAttrLine(kAttributeSsrc, &os);
   1414       os << kSdpDelimiterColon << ssrc << kSdpDelimiterSpace
   1415          << kSsrcAttributeMsid << kSdpDelimiterColon << track->sync_label
   1416          << kSdpDelimiterSpace << appdata;
   1417       AddLine(os.str(), message);
   1418 
   1419       // TODO(ronghuawu): Remove below code which is for backward compatibility.
   1420       // draft-alvestrand-rtcweb-mid-01
   1421       // a=ssrc:<ssrc-id> mslabel:<value>
   1422       // The label isn't yet defined.
   1423       // a=ssrc:<ssrc-id> label:<value>
   1424       AddSsrcLine(ssrc, kSsrcAttributeMslabel, track->sync_label, message);
   1425       AddSsrcLine(ssrc, kSSrcAttributeLabel, track->id, message);
   1426     }
   1427   }
   1428 }
   1429 
   1430 void WriteFmtpHeader(int payload_type, std::ostringstream* os) {
   1431   // fmtp header: a=fmtp:|payload_type| <parameters>
   1432   // Add a=fmtp
   1433   InitAttrLine(kAttributeFmtp, os);
   1434   // Add :|payload_type|
   1435   *os << kSdpDelimiterColon << payload_type;
   1436 }
   1437 
   1438 void WriteRtcpFbHeader(int payload_type, std::ostringstream* os) {
   1439   // rtcp-fb header: a=rtcp-fb:|payload_type|
   1440   // <parameters>/<ccm <ccm_parameters>>
   1441   // Add a=rtcp-fb
   1442   InitAttrLine(kAttributeRtcpFb, os);
   1443   // Add :
   1444   *os << kSdpDelimiterColon;
   1445   if (payload_type == kWildcardPayloadType) {
   1446     *os << "*";
   1447   } else {
   1448     *os << payload_type;
   1449   }
   1450 }
   1451 
   1452 void WriteFmtpParameter(const std::string& parameter_name,
   1453                         const std::string& parameter_value,
   1454                         std::ostringstream* os) {
   1455   // fmtp parameters: |parameter_name|=|parameter_value|
   1456   *os << parameter_name << kSdpDelimiterEqual << parameter_value;
   1457 }
   1458 
   1459 void WriteFmtpParameters(const cricket::CodecParameterMap& parameters,
   1460                          std::ostringstream* os) {
   1461   for (cricket::CodecParameterMap::const_iterator fmtp = parameters.begin();
   1462        fmtp != parameters.end(); ++fmtp) {
   1463     // Each new parameter, except the first one starts with ";" and " ".
   1464     if (fmtp != parameters.begin()) {
   1465       *os << kSdpDelimiterSemicolon;
   1466     }
   1467     *os << kSdpDelimiterSpace;
   1468     WriteFmtpParameter(fmtp->first, fmtp->second, os);
   1469   }
   1470 }
   1471 
   1472 bool IsFmtpParam(const std::string& name) {
   1473   const char* kFmtpParams[] = {
   1474     kCodecParamMinPTime, kCodecParamSPropStereo,
   1475     kCodecParamStereo, kCodecParamUseInbandFec,
   1476     kCodecParamMaxBitrate, kCodecParamMinBitrate, kCodecParamMaxQuantization,
   1477     kCodecParamSctpProtocol, kCodecParamSctpStreams,
   1478     kCodecParamMaxAverageBitrate
   1479   };
   1480   for (size_t i = 0; i < ARRAY_SIZE(kFmtpParams); ++i) {
   1481     if (_stricmp(name.c_str(), kFmtpParams[i]) == 0) {
   1482       return true;
   1483     }
   1484   }
   1485   return false;
   1486 }
   1487 
   1488 // Retreives fmtp parameters from |params|, which may contain other parameters
   1489 // as well, and puts them in |fmtp_parameters|.
   1490 void GetFmtpParams(const cricket::CodecParameterMap& params,
   1491                    cricket::CodecParameterMap* fmtp_parameters) {
   1492   for (cricket::CodecParameterMap::const_iterator iter = params.begin();
   1493        iter != params.end(); ++iter) {
   1494     if (IsFmtpParam(iter->first)) {
   1495       (*fmtp_parameters)[iter->first] = iter->second;
   1496     }
   1497   }
   1498 }
   1499 
   1500 template <class T>
   1501 void AddFmtpLine(const T& codec, std::string* message) {
   1502   cricket::CodecParameterMap fmtp_parameters;
   1503   GetFmtpParams(codec.params, &fmtp_parameters);
   1504   if (fmtp_parameters.empty()) {
   1505     // No need to add an fmtp if it will have no (optional) parameters.
   1506     return;
   1507   }
   1508   std::ostringstream os;
   1509   WriteFmtpHeader(codec.id, &os);
   1510   WriteFmtpParameters(fmtp_parameters, &os);
   1511   AddLine(os.str(), message);
   1512   return;
   1513 }
   1514 
   1515 template <class T>
   1516 void AddRtcpFbLines(const T& codec, std::string* message) {
   1517   for (std::vector<cricket::FeedbackParam>::const_iterator iter =
   1518            codec.feedback_params.params().begin();
   1519        iter != codec.feedback_params.params().end(); ++iter) {
   1520     std::ostringstream os;
   1521     WriteRtcpFbHeader(codec.id, &os);
   1522     os << " " << iter->id();
   1523     if (!iter->param().empty()) {
   1524       os << " " << iter->param();
   1525     }
   1526     AddLine(os.str(), message);
   1527   }
   1528 }
   1529 
   1530 bool GetMinValue(const std::vector<int>& values, int* value) {
   1531   if (values.empty()) {
   1532     return false;
   1533   }
   1534   std::vector<int>::const_iterator found =
   1535       std::min_element(values.begin(), values.end());
   1536   *value = *found;
   1537   return true;
   1538 }
   1539 
   1540 bool GetParameter(const std::string& name,
   1541                   const cricket::CodecParameterMap& params, int* value) {
   1542   std::map<std::string, std::string>::const_iterator found =
   1543       params.find(name);
   1544   if (found == params.end()) {
   1545     return false;
   1546   }
   1547   *value = talk_base::FromString<int>(found->second);
   1548   return true;
   1549 }
   1550 
   1551 void BuildRtpMap(const MediaContentDescription* media_desc,
   1552                  const MediaType media_type,
   1553                  std::string* message) {
   1554   ASSERT(message != NULL);
   1555   ASSERT(media_desc != NULL);
   1556   std::ostringstream os;
   1557   if (media_type == cricket::MEDIA_TYPE_VIDEO) {
   1558     const VideoContentDescription* video_desc =
   1559         static_cast<const VideoContentDescription*>(media_desc);
   1560     for (std::vector<cricket::VideoCodec>::const_iterator it =
   1561              video_desc->codecs().begin();
   1562          it != video_desc->codecs().end(); ++it) {
   1563       // RFC 4566
   1564       // a=rtpmap:<payload type> <encoding name>/<clock rate>
   1565       // [/<encodingparameters>]
   1566       if (it->id != kWildcardPayloadType) {
   1567         InitAttrLine(kAttributeRtpmap, &os);
   1568         os << kSdpDelimiterColon << it->id << " " << it->name
   1569          << "/" << kDefaultVideoClockrate;
   1570         AddLine(os.str(), message);
   1571       }
   1572       AddRtcpFbLines(*it, message);
   1573       AddFmtpLine(*it, message);
   1574     }
   1575   } else if (media_type == cricket::MEDIA_TYPE_AUDIO) {
   1576     const AudioContentDescription* audio_desc =
   1577         static_cast<const AudioContentDescription*>(media_desc);
   1578     std::vector<int> ptimes;
   1579     std::vector<int> maxptimes;
   1580     int max_minptime = 0;
   1581     for (std::vector<cricket::AudioCodec>::const_iterator it =
   1582              audio_desc->codecs().begin();
   1583          it != audio_desc->codecs().end(); ++it) {
   1584       ASSERT(!it->name.empty());
   1585       // RFC 4566
   1586       // a=rtpmap:<payload type> <encoding name>/<clock rate>
   1587       // [/<encodingparameters>]
   1588       InitAttrLine(kAttributeRtpmap, &os);
   1589       os << kSdpDelimiterColon << it->id << " ";
   1590       os << it->name << "/" << it->clockrate;
   1591       if (it->channels != 1) {
   1592         os << "/" << it->channels;
   1593       }
   1594       AddLine(os.str(), message);
   1595       AddRtcpFbLines(*it, message);
   1596       AddFmtpLine(*it, message);
   1597       int minptime = 0;
   1598       if (GetParameter(kCodecParamMinPTime, it->params, &minptime)) {
   1599         max_minptime = std::max(minptime, max_minptime);
   1600       }
   1601       int ptime;
   1602       if (GetParameter(kCodecParamPTime, it->params, &ptime)) {
   1603         ptimes.push_back(ptime);
   1604       }
   1605       int maxptime;
   1606       if (GetParameter(kCodecParamMaxPTime, it->params, &maxptime)) {
   1607         maxptimes.push_back(maxptime);
   1608       }
   1609     }
   1610     // Populate the maxptime attribute with the smallest maxptime of all codecs
   1611     // under the same m-line.
   1612     int min_maxptime = INT_MAX;
   1613     if (GetMinValue(maxptimes, &min_maxptime)) {
   1614       AddAttributeLine(kCodecParamMaxPTime, min_maxptime, message);
   1615     }
   1616     ASSERT(min_maxptime > max_minptime);
   1617     // Populate the ptime attribute with the smallest ptime or the largest
   1618     // minptime, whichever is the largest, for all codecs under the same m-line.
   1619     int ptime = INT_MAX;
   1620     if (GetMinValue(ptimes, &ptime)) {
   1621       ptime = std::min(ptime, min_maxptime);
   1622       ptime = std::max(ptime, max_minptime);
   1623       AddAttributeLine(kCodecParamPTime, ptime, message);
   1624     }
   1625   } else if (media_type == cricket::MEDIA_TYPE_DATA) {
   1626     const DataContentDescription* data_desc =
   1627         static_cast<const DataContentDescription*>(media_desc);
   1628     for (std::vector<cricket::DataCodec>::const_iterator it =
   1629          data_desc->codecs().begin();
   1630          it != data_desc->codecs().end(); ++it) {
   1631       // RFC 4566
   1632       // a=rtpmap:<payload type> <encoding name>/<clock rate>
   1633       // [/<encodingparameters>]
   1634       InitAttrLine(kAttributeRtpmap, &os);
   1635       os << kSdpDelimiterColon << it->id << " "
   1636          << it->name << "/" << it->clockrate;
   1637       AddLine(os.str(), message);
   1638     }
   1639   }
   1640 }
   1641 
   1642 void BuildCandidate(const std::vector<Candidate>& candidates,
   1643                     std::string* message) {
   1644   std::ostringstream os;
   1645 
   1646   for (std::vector<Candidate>::const_iterator it = candidates.begin();
   1647        it != candidates.end(); ++it) {
   1648     // RFC 5245
   1649     // a=candidate:<foundation> <component-id> <transport> <priority>
   1650     // <connection-address> <port> typ <candidate-types>
   1651     // [raddr <connection-address>] [rport <port>]
   1652     // *(SP extension-att-name SP extension-att-value)
   1653     std::string type;
   1654     // Map the cricket candidate type to "host" / "srflx" / "prflx" / "relay"
   1655     if (it->type() == cricket::LOCAL_PORT_TYPE) {
   1656       type = kCandidateHost;
   1657     } else if (it->type() == cricket::STUN_PORT_TYPE) {
   1658       type = kCandidateSrflx;
   1659     } else if (it->type() == cricket::RELAY_PORT_TYPE) {
   1660       type = kCandidateRelay;
   1661     } else {
   1662       ASSERT(false);
   1663     }
   1664 
   1665     InitAttrLine(kAttributeCandidate, &os);
   1666     os << kSdpDelimiterColon
   1667        << it->foundation() << " " << it->component() << " "
   1668        << it->protocol() << " " << it->priority() << " "
   1669        << it->address().ipaddr().ToString() << " "
   1670        << it->address().PortAsString() << " "
   1671        << kAttributeCandidateTyp << " " << type << " ";
   1672 
   1673     // Related address
   1674     if (!it->related_address().IsNil()) {
   1675       os << kAttributeCandidateRaddr << " "
   1676          << it->related_address().ipaddr().ToString() << " "
   1677          << kAttributeCandidateRport << " "
   1678          << it->related_address().PortAsString() << " ";
   1679     }
   1680 
   1681     // Extensions
   1682     os << kAttributeCandidateGeneration << " " << it->generation();
   1683 
   1684     AddLine(os.str(), message);
   1685   }
   1686 }
   1687 
   1688 void BuildIceOptions(const std::vector<std::string>& transport_options,
   1689                      std::string* message) {
   1690   if (!transport_options.empty()) {
   1691     std::ostringstream os;
   1692     InitAttrLine(kAttributeIceOption, &os);
   1693     os << kSdpDelimiterColon << transport_options[0];
   1694     for (size_t i = 1; i < transport_options.size(); ++i) {
   1695       os << kSdpDelimiterSpace << transport_options[i];
   1696     }
   1697     AddLine(os.str(), message);
   1698   }
   1699 }
   1700 
   1701 bool ParseSessionDescription(const std::string& message, size_t* pos,
   1702                              std::string* session_id,
   1703                              std::string* session_version,
   1704                              bool* supports_msid,
   1705                              TransportDescription* session_td,
   1706                              RtpHeaderExtensions* session_extmaps,
   1707                              cricket::SessionDescription* desc,
   1708                              SdpParseError* error) {
   1709   std::string line;
   1710 
   1711   // RFC 4566
   1712   // v=  (protocol version)
   1713   if (!GetLineWithType(message, pos, &line, kLineTypeVersion)) {
   1714     return ParseFailedExpectLine(message, *pos, kLineTypeVersion,
   1715                                  std::string(), error);
   1716   }
   1717   // RFC 4566
   1718   // o=<username> <sess-id> <sess-version> <nettype> <addrtype>
   1719   // <unicast-address>
   1720   if (!GetLineWithType(message, pos, &line, kLineTypeOrigin)) {
   1721     return ParseFailedExpectLine(message, *pos, kLineTypeOrigin,
   1722                                  std::string(), error);
   1723   }
   1724   std::vector<std::string> fields;
   1725   talk_base::split(line.substr(kLinePrefixLength),
   1726                    kSdpDelimiterSpace, &fields);
   1727   const size_t expected_fields = 6;
   1728   if (fields.size() != expected_fields) {
   1729     return ParseFailedExpectFieldNum(line, expected_fields, error);
   1730   }
   1731   *session_id = fields[1];
   1732   *session_version = fields[2];
   1733 
   1734   // RFC 4566
   1735   // s=  (session name)
   1736   if (!GetLineWithType(message, pos, &line, kLineTypeSessionName)) {
   1737     return ParseFailedExpectLine(message, *pos, kLineTypeSessionName,
   1738                                  std::string(), error);
   1739   }
   1740 
   1741   // Optional lines
   1742   // Those are the optional lines, so shouldn't return false if not present.
   1743   // RFC 4566
   1744   // i=* (session information)
   1745   GetLineWithType(message, pos, &line, kLineTypeSessionInfo);
   1746 
   1747   // RFC 4566
   1748   // u=* (URI of description)
   1749   GetLineWithType(message, pos, &line, kLineTypeSessionUri);
   1750 
   1751   // RFC 4566
   1752   // e=* (email address)
   1753   GetLineWithType(message, pos, &line, kLineTypeSessionEmail);
   1754 
   1755   // RFC 4566
   1756   // p=* (phone number)
   1757   GetLineWithType(message, pos, &line, kLineTypeSessionPhone);
   1758 
   1759   // RFC 4566
   1760   // c=* (connection information -- not required if included in
   1761   //      all media)
   1762   GetLineWithType(message, pos, &line, kLineTypeConnection);
   1763 
   1764   // RFC 4566
   1765   // b=* (zero or more bandwidth information lines)
   1766   while (GetLineWithType(message, pos, &line, kLineTypeSessionBandwidth)) {
   1767     // By pass zero or more b lines.
   1768   }
   1769 
   1770   // RFC 4566
   1771   // One or more time descriptions ("t=" and "r=" lines; see below)
   1772   // t=  (time the session is active)
   1773   // r=* (zero or more repeat times)
   1774   // Ensure there's at least one time description
   1775   if (!GetLineWithType(message, pos, &line, kLineTypeTiming)) {
   1776     return ParseFailedExpectLine(message, *pos, kLineTypeTiming, std::string(),
   1777                                  error);
   1778   }
   1779 
   1780   while (GetLineWithType(message, pos, &line, kLineTypeRepeatTimes)) {
   1781     // By pass zero or more r lines.
   1782   }
   1783 
   1784   // Go through the rest of the time descriptions
   1785   while (GetLineWithType(message, pos, &line, kLineTypeTiming)) {
   1786     while (GetLineWithType(message, pos, &line, kLineTypeRepeatTimes)) {
   1787       // By pass zero or more r lines.
   1788     }
   1789   }
   1790 
   1791   // RFC 4566
   1792   // z=* (time zone adjustments)
   1793   GetLineWithType(message, pos, &line, kLineTypeTimeZone);
   1794 
   1795   // RFC 4566
   1796   // k=* (encryption key)
   1797   GetLineWithType(message, pos, &line, kLineTypeEncryptionKey);
   1798 
   1799   // RFC 4566
   1800   // a=* (zero or more session attribute lines)
   1801   while (GetLineWithType(message, pos, &line, kLineTypeAttributes)) {
   1802     if (HasAttribute(line, kAttributeGroup)) {
   1803       if (!ParseGroupAttribute(line, desc, error)) {
   1804         return false;
   1805       }
   1806     } else if (HasAttribute(line, kAttributeIceUfrag)) {
   1807       if (!GetValue(line, kAttributeIceUfrag,
   1808                     &(session_td->ice_ufrag), error)) {
   1809         return false;
   1810       }
   1811     } else if (HasAttribute(line, kAttributeIcePwd)) {
   1812       if (!GetValue(line, kAttributeIcePwd, &(session_td->ice_pwd), error)) {
   1813         return false;
   1814       }
   1815     } else if (HasAttribute(line, kAttributeIceLite)) {
   1816       session_td->ice_mode = cricket::ICEMODE_LITE;
   1817     } else if (HasAttribute(line, kAttributeIceOption)) {
   1818       if (!ParseIceOptions(line, &(session_td->transport_options), error)) {
   1819         return false;
   1820       }
   1821     } else if (HasAttribute(line, kAttributeFingerprint)) {
   1822       if (session_td->identity_fingerprint.get()) {
   1823         return ParseFailed(
   1824             line,
   1825             "Can't have multiple fingerprint attributes at the same level.",
   1826             error);
   1827       }
   1828       talk_base::SSLFingerprint* fingerprint = NULL;
   1829       if (!ParseFingerprintAttribute(line, &fingerprint, error)) {
   1830         return false;
   1831       }
   1832       session_td->identity_fingerprint.reset(fingerprint);
   1833     } else if (HasAttribute(line, kAttributeSetup)) {
   1834       if (!ParseDtlsSetup(line, &(session_td->connection_role), error)) {
   1835         return false;
   1836       }
   1837     } else if (HasAttribute(line, kAttributeMsidSemantics)) {
   1838       std::string semantics;
   1839       if (!GetValue(line, kAttributeMsidSemantics, &semantics, error)) {
   1840         return false;
   1841       }
   1842       *supports_msid = CaseInsensitiveFind(semantics, kMediaStreamSemantic);
   1843     } else if (HasAttribute(line, kAttributeExtmap)) {
   1844       RtpHeaderExtension extmap;
   1845       if (!ParseExtmap(line, &extmap, error)) {
   1846         return false;
   1847       }
   1848       session_extmaps->push_back(extmap);
   1849     }
   1850   }
   1851 
   1852   return true;
   1853 }
   1854 
   1855 bool ParseGroupAttribute(const std::string& line,
   1856                          cricket::SessionDescription* desc,
   1857                          SdpParseError* error) {
   1858   ASSERT(desc != NULL);
   1859 
   1860   // RFC 5888 and draft-holmberg-mmusic-sdp-bundle-negotiation-00
   1861   // a=group:BUNDLE video voice
   1862   std::vector<std::string> fields;
   1863   talk_base::split(line.substr(kLinePrefixLength),
   1864                    kSdpDelimiterSpace, &fields);
   1865   std::string semantics;
   1866   if (!GetValue(fields[0], kAttributeGroup, &semantics, error)) {
   1867     return false;
   1868   }
   1869   cricket::ContentGroup group(semantics);
   1870   for (size_t i = 1; i < fields.size(); ++i) {
   1871     group.AddContentName(fields[i]);
   1872   }
   1873   desc->AddGroup(group);
   1874   return true;
   1875 }
   1876 
   1877 static bool ParseFingerprintAttribute(const std::string& line,
   1878                                       talk_base::SSLFingerprint** fingerprint,
   1879                                       SdpParseError* error) {
   1880   if (!IsLineType(line, kLineTypeAttributes) ||
   1881       !HasAttribute(line, kAttributeFingerprint)) {
   1882     return ParseFailedExpectLine(line, 0, kLineTypeAttributes,
   1883                                  kAttributeFingerprint, error);
   1884   }
   1885 
   1886   std::vector<std::string> fields;
   1887   talk_base::split(line.substr(kLinePrefixLength),
   1888                    kSdpDelimiterSpace, &fields);
   1889   const size_t expected_fields = 2;
   1890   if (fields.size() != expected_fields) {
   1891     return ParseFailedExpectFieldNum(line, expected_fields, error);
   1892   }
   1893 
   1894   // The first field here is "fingerprint:<hash>.
   1895   std::string algorithm;
   1896   if (!GetValue(fields[0], kAttributeFingerprint, &algorithm, error)) {
   1897     return false;
   1898   }
   1899 
   1900   // Downcase the algorithm. Note that we don't need to downcase the
   1901   // fingerprint because hex_decode can handle upper-case.
   1902   std::transform(algorithm.begin(), algorithm.end(), algorithm.begin(),
   1903                  ::tolower);
   1904 
   1905   // The second field is the digest value. De-hexify it.
   1906   *fingerprint = talk_base::SSLFingerprint::CreateFromRfc4572(
   1907       algorithm, fields[1]);
   1908   if (!*fingerprint) {
   1909     return ParseFailed(line,
   1910                        "Failed to create fingerprint from the digest.",
   1911                        error);
   1912   }
   1913 
   1914   return true;
   1915 }
   1916 
   1917 static bool ParseDtlsSetup(const std::string& line,
   1918                            cricket::ConnectionRole* role,
   1919                            SdpParseError* error) {
   1920   // setup-attr           =  "a=setup:" role
   1921   // role                 =  "active" / "passive" / "actpass" / "holdconn"
   1922   std::vector<std::string> fields;
   1923   talk_base::split(line.substr(kLinePrefixLength), kSdpDelimiterColon, &fields);
   1924   const size_t expected_fields = 2;
   1925   if (fields.size() != expected_fields) {
   1926     return ParseFailedExpectFieldNum(line, expected_fields, error);
   1927   }
   1928   std::string role_str = fields[1];
   1929   if (!cricket::StringToConnectionRole(role_str, role)) {
   1930     return ParseFailed(line, "Invalid attribute value.", error);
   1931   }
   1932   return true;
   1933 }
   1934 
   1935 // RFC 3551
   1936 //  PT   encoding    media type  clock rate   channels
   1937 //                      name                    (Hz)
   1938 //  0    PCMU        A            8,000       1
   1939 //  1    reserved    A
   1940 //  2    reserved    A
   1941 //  3    GSM         A            8,000       1
   1942 //  4    G723        A            8,000       1
   1943 //  5    DVI4        A            8,000       1
   1944 //  6    DVI4        A           16,000       1
   1945 //  7    LPC         A            8,000       1
   1946 //  8    PCMA        A            8,000       1
   1947 //  9    G722        A            8,000       1
   1948 //  10   L16         A           44,100       2
   1949 //  11   L16         A           44,100       1
   1950 //  12   QCELP       A            8,000       1
   1951 //  13   CN          A            8,000       1
   1952 //  14   MPA         A           90,000       (see text)
   1953 //  15   G728        A            8,000       1
   1954 //  16   DVI4        A           11,025       1
   1955 //  17   DVI4        A           22,050       1
   1956 //  18   G729        A            8,000       1
   1957 struct StaticPayloadAudioCodec {
   1958   const char* name;
   1959   int clockrate;
   1960   int channels;
   1961 };
   1962 static const StaticPayloadAudioCodec kStaticPayloadAudioCodecs[] = {
   1963   { "PCMU", 8000, 1 },
   1964   { "reserved", 0, 0 },
   1965   { "reserved", 0, 0 },
   1966   { "GSM", 8000, 1 },
   1967   { "G723", 8000, 1 },
   1968   { "DVI4", 8000, 1 },
   1969   { "DVI4", 16000, 1 },
   1970   { "LPC", 8000, 1 },
   1971   { "PCMA", 8000, 1 },
   1972   { "G722", 8000, 1 },
   1973   { "L16", 44100, 2 },
   1974   { "L16", 44100, 1 },
   1975   { "QCELP", 8000, 1 },
   1976   { "CN", 8000, 1 },
   1977   { "MPA", 90000, 1 },
   1978   { "G728", 8000, 1 },
   1979   { "DVI4", 11025, 1 },
   1980   { "DVI4", 22050, 1 },
   1981   { "G729", 8000, 1 },
   1982 };
   1983 
   1984 void MaybeCreateStaticPayloadAudioCodecs(
   1985     const std::vector<int>& fmts, AudioContentDescription* media_desc) {
   1986   if (!media_desc) {
   1987     return;
   1988   }
   1989   int preference = static_cast<int>(fmts.size());
   1990   std::vector<int>::const_iterator it = fmts.begin();
   1991   bool add_new_codec = false;
   1992   for (; it != fmts.end(); ++it) {
   1993     int payload_type = *it;
   1994     if (!media_desc->HasCodec(payload_type) &&
   1995         payload_type >= 0 &&
   1996         payload_type < ARRAY_SIZE(kStaticPayloadAudioCodecs)) {
   1997       std::string encoding_name = kStaticPayloadAudioCodecs[payload_type].name;
   1998       int clock_rate = kStaticPayloadAudioCodecs[payload_type].clockrate;
   1999       int channels = kStaticPayloadAudioCodecs[payload_type].channels;
   2000       media_desc->AddCodec(cricket::AudioCodec(payload_type, encoding_name,
   2001                                                clock_rate, 0, channels,
   2002                                                preference));
   2003       add_new_codec = true;
   2004     }
   2005     --preference;
   2006   }
   2007   if (add_new_codec) {
   2008     media_desc->SortCodecs();
   2009   }
   2010 }
   2011 
   2012 template <class C>
   2013 static C* ParseContentDescription(const std::string& message,
   2014                                   const MediaType media_type,
   2015                                   int mline_index,
   2016                                   const std::string& protocol,
   2017                                   const std::vector<int>& codec_preference,
   2018                                   size_t* pos,
   2019                                   std::string* content_name,
   2020                                   TransportDescription* transport,
   2021                                   std::vector<JsepIceCandidate*>* candidates,
   2022                                   webrtc::SdpParseError* error) {
   2023   C* media_desc = new C();
   2024   switch (media_type) {
   2025     case cricket::MEDIA_TYPE_AUDIO:
   2026       *content_name = cricket::CN_AUDIO;
   2027       break;
   2028     case cricket::MEDIA_TYPE_VIDEO:
   2029       *content_name = cricket::CN_VIDEO;
   2030       break;
   2031     case cricket::MEDIA_TYPE_DATA:
   2032       *content_name = cricket::CN_DATA;
   2033       break;
   2034     default:
   2035       ASSERT(false);
   2036       break;
   2037   }
   2038   if (!ParseContent(message, media_type, mline_index, protocol,
   2039                     codec_preference, pos, content_name,
   2040                     media_desc, transport, candidates, error)) {
   2041     delete media_desc;
   2042     return NULL;
   2043   }
   2044   // Sort the codecs according to the m-line fmt list.
   2045   media_desc->SortCodecs();
   2046   return media_desc;
   2047 }
   2048 
   2049 bool ParseMediaDescription(const std::string& message,
   2050                            const TransportDescription& session_td,
   2051                            const RtpHeaderExtensions& session_extmaps,
   2052                            bool supports_msid,
   2053                            size_t* pos,
   2054                            cricket::SessionDescription* desc,
   2055                            std::vector<JsepIceCandidate*>* candidates,
   2056                            SdpParseError* error) {
   2057   ASSERT(desc != NULL);
   2058   std::string line;
   2059   int mline_index = -1;
   2060 
   2061   // Zero or more media descriptions
   2062   // RFC 4566
   2063   // m=<media> <port> <proto> <fmt>
   2064   while (GetLineWithType(message, pos, &line, kLineTypeMedia)) {
   2065     ++mline_index;
   2066 
   2067     std::vector<std::string> fields;
   2068     talk_base::split(line.substr(kLinePrefixLength),
   2069                      kSdpDelimiterSpace, &fields);
   2070     const size_t expected_min_fields = 4;
   2071     if (fields.size() < expected_min_fields) {
   2072       return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
   2073     }
   2074     bool rejected = false;
   2075     // RFC 3264
   2076     // To reject an offered stream, the port number in the corresponding stream
   2077     // in the answer MUST be set to zero.
   2078     if (fields[1] == kMediaPortRejected) {
   2079       rejected = true;
   2080     }
   2081 
   2082     std::string protocol = fields[2];
   2083     bool is_sctp = (protocol == cricket::kMediaProtocolDtlsSctp);
   2084 
   2085     // <fmt>
   2086     std::vector<int> codec_preference;
   2087     for (size_t j = 3 ; j < fields.size(); ++j) {
   2088       codec_preference.push_back(talk_base::FromString<int>(fields[j]));
   2089     }
   2090 
   2091     // Make a temporary TransportDescription based on |session_td|.
   2092     // Some of this gets overwritten by ParseContent.
   2093     TransportDescription transport(NS_JINGLE_ICE_UDP,
   2094                                    session_td.transport_options,
   2095                                    session_td.ice_ufrag,
   2096                                    session_td.ice_pwd,
   2097                                    session_td.ice_mode,
   2098                                    session_td.connection_role,
   2099                                    session_td.identity_fingerprint.get(),
   2100                                    Candidates());
   2101 
   2102     talk_base::scoped_ptr<MediaContentDescription> content;
   2103     std::string content_name;
   2104     if (HasAttribute(line, kMediaTypeVideo)) {
   2105       content.reset(ParseContentDescription<VideoContentDescription>(
   2106                     message, cricket::MEDIA_TYPE_VIDEO, mline_index, protocol,
   2107                     codec_preference, pos, &content_name,
   2108                     &transport, candidates, error));
   2109     } else if (HasAttribute(line, kMediaTypeAudio)) {
   2110       content.reset(ParseContentDescription<AudioContentDescription>(
   2111                     message, cricket::MEDIA_TYPE_AUDIO, mline_index, protocol,
   2112                     codec_preference, pos, &content_name,
   2113                     &transport, candidates, error));
   2114       MaybeCreateStaticPayloadAudioCodecs(
   2115           codec_preference,
   2116           static_cast<AudioContentDescription*>(content.get()));
   2117     } else if (HasAttribute(line, kMediaTypeData)) {
   2118       DataContentDescription* desc =
   2119           ParseContentDescription<DataContentDescription>(
   2120                     message, cricket::MEDIA_TYPE_DATA, mline_index, protocol,
   2121                     codec_preference, pos, &content_name,
   2122                     &transport, candidates, error);
   2123 
   2124       if (protocol == cricket::kMediaProtocolDtlsSctp) {
   2125         // Add the SCTP Port number as a pseudo-codec "port" parameter
   2126         cricket::DataCodec codec_port(
   2127             cricket::kGoogleSctpDataCodecId, cricket::kGoogleSctpDataCodecName,
   2128             0);
   2129         codec_port.SetParam(cricket::kCodecParamPort, fields[3]);
   2130         LOG(INFO) << "ParseMediaDescription: Got SCTP Port Number "
   2131                   << fields[3];
   2132         desc->AddCodec(codec_port);
   2133       }
   2134 
   2135       content.reset(desc);
   2136 
   2137       // We should always use the default bandwidth for RTP-based data
   2138       // channels.  Don't allow SDP to set the bandwidth, because that
   2139       // would give JS the opportunity to "break the Internet".
   2140       // TODO(pthatcher): But we need to temporarily allow the SDP to control
   2141       // this for backwards-compatibility.  Once we don't need that any
   2142       // more, remove this.
   2143       bool support_dc_sdp_bandwidth_temporarily = true;
   2144       if (content.get() && !support_dc_sdp_bandwidth_temporarily) {
   2145         content->set_bandwidth(cricket::kAutoBandwidth);
   2146       }
   2147     } else {
   2148       LOG(LS_WARNING) << "Unsupported media type: " << line;
   2149       continue;
   2150     }
   2151     if (!content.get()) {
   2152       // ParseContentDescription returns NULL if failed.
   2153       return false;
   2154     }
   2155 
   2156     if (!is_sctp) {
   2157       // Make sure to set the media direction correctly. If the direction is not
   2158       // MD_RECVONLY or Inactive and no streams are parsed,
   2159       // a default MediaStream will be created to prepare for receiving media.
   2160       if (supports_msid && content->streams().empty() &&
   2161           content->direction() == cricket::MD_SENDRECV) {
   2162         content->set_direction(cricket::MD_RECVONLY);
   2163       }
   2164 
   2165       // Set the extmap.
   2166       if (!session_extmaps.empty() &&
   2167           !content->rtp_header_extensions().empty()) {
   2168         return ParseFailed("",
   2169                            "The a=extmap MUST be either all session level or "
   2170                            "all media level.",
   2171                            error);
   2172       }
   2173       for (size_t i = 0; i < session_extmaps.size(); ++i) {
   2174         content->AddRtpHeaderExtension(session_extmaps[i]);
   2175       }
   2176     }
   2177     content->set_protocol(protocol);
   2178     desc->AddContent(content_name,
   2179                      is_sctp ? cricket::NS_JINGLE_DRAFT_SCTP :
   2180                                cricket::NS_JINGLE_RTP,
   2181                      rejected,
   2182                      content.release());
   2183     // Create TransportInfo with the media level "ice-pwd" and "ice-ufrag".
   2184     TransportInfo transport_info(content_name, transport);
   2185 
   2186     if (!desc->AddTransportInfo(transport_info)) {
   2187       std::ostringstream description;
   2188       description << "Failed to AddTransportInfo with content name: "
   2189                   << content_name;
   2190       return ParseFailed("", description.str(), error);
   2191     }
   2192   }
   2193 
   2194   size_t end_of_message = message.size();
   2195   if (mline_index == -1 && *pos != end_of_message) {
   2196     ParseFailed(message, *pos, "Expects m line.", error);
   2197     return false;
   2198   }
   2199   return true;
   2200 }
   2201 
   2202 bool VerifyCodec(const cricket::Codec& codec) {
   2203   // Codec has not been populated correctly unless the name has been set. This
   2204   // can happen if an SDP has an fmtp or rtcp-fb with a payload type but doesn't
   2205   // have a corresponding "rtpmap" line.
   2206   cricket::Codec default_codec;
   2207   return default_codec.name != codec.name;
   2208 }
   2209 
   2210 bool VerifyAudioCodecs(const AudioContentDescription* audio_desc) {
   2211   const std::vector<cricket::AudioCodec>& codecs = audio_desc->codecs();
   2212   for (std::vector<cricket::AudioCodec>::const_iterator iter = codecs.begin();
   2213        iter != codecs.end(); ++iter) {
   2214     if (!VerifyCodec(*iter)) {
   2215       return false;
   2216     }
   2217   }
   2218   return true;
   2219 }
   2220 
   2221 bool VerifyVideoCodecs(const VideoContentDescription* video_desc) {
   2222   const std::vector<cricket::VideoCodec>& codecs = video_desc->codecs();
   2223   for (std::vector<cricket::VideoCodec>::const_iterator iter = codecs.begin();
   2224        iter != codecs.end(); ++iter) {
   2225     if (!VerifyCodec(*iter)) {
   2226       return false;
   2227     }
   2228   }
   2229   return true;
   2230 }
   2231 
   2232 void AddParameters(const cricket::CodecParameterMap& parameters,
   2233                    cricket::Codec* codec) {
   2234   for (cricket::CodecParameterMap::const_iterator iter =
   2235            parameters.begin(); iter != parameters.end(); ++iter) {
   2236     codec->SetParam(iter->first, iter->second);
   2237   }
   2238 }
   2239 
   2240 void AddFeedbackParameter(const cricket::FeedbackParam& feedback_param,
   2241                           cricket::Codec* codec) {
   2242   codec->AddFeedbackParam(feedback_param);
   2243 }
   2244 
   2245 void AddFeedbackParameters(const cricket::FeedbackParams& feedback_params,
   2246                            cricket::Codec* codec) {
   2247   for (std::vector<cricket::FeedbackParam>::const_iterator iter =
   2248            feedback_params.params().begin();
   2249        iter != feedback_params.params().end(); ++iter) {
   2250     codec->AddFeedbackParam(*iter);
   2251   }
   2252 }
   2253 
   2254 // Gets the current codec setting associated with |payload_type|. If there
   2255 // is no AudioCodec associated with that payload type it returns an empty codec
   2256 // with that payload type.
   2257 template <class T>
   2258 T GetCodec(const std::vector<T>& codecs, int payload_type) {
   2259   for (typename std::vector<T>::const_iterator codec = codecs.begin();
   2260        codec != codecs.end(); ++codec) {
   2261     if (codec->id == payload_type) {
   2262       return *codec;
   2263     }
   2264   }
   2265   T ret_val = T();
   2266   ret_val.id = payload_type;
   2267   return ret_val;
   2268 }
   2269 
   2270 // Updates or creates a new codec entry in the audio description.
   2271 template <class T, class U>
   2272 void AddOrReplaceCodec(MediaContentDescription* content_desc, const U& codec) {
   2273   T* desc = static_cast<T*>(content_desc);
   2274   std::vector<U> codecs = desc->codecs();
   2275   bool found = false;
   2276 
   2277   typename std::vector<U>::iterator iter;
   2278   for (iter = codecs.begin(); iter != codecs.end(); ++iter) {
   2279     if (iter->id == codec.id) {
   2280       *iter = codec;
   2281       found = true;
   2282       break;
   2283     }
   2284   }
   2285   if (!found) {
   2286     desc->AddCodec(codec);
   2287     return;
   2288   }
   2289   desc->set_codecs(codecs);
   2290 }
   2291 
   2292 // Adds or updates existing codec corresponding to |payload_type| according
   2293 // to |parameters|.
   2294 template <class T, class U>
   2295 void UpdateCodec(MediaContentDescription* content_desc, int payload_type,
   2296                  const cricket::CodecParameterMap& parameters) {
   2297   // Codec might already have been populated (from rtpmap).
   2298   U new_codec = GetCodec(static_cast<T*>(content_desc)->codecs(), payload_type);
   2299   AddParameters(parameters, &new_codec);
   2300   AddOrReplaceCodec<T, U>(content_desc, new_codec);
   2301 }
   2302 
   2303 // Adds or updates existing codec corresponding to |payload_type| according
   2304 // to |feedback_param|.
   2305 template <class T, class U>
   2306 void UpdateCodec(MediaContentDescription* content_desc, int payload_type,
   2307                  const cricket::FeedbackParam& feedback_param) {
   2308   // Codec might already have been populated (from rtpmap).
   2309   U new_codec = GetCodec(static_cast<T*>(content_desc)->codecs(), payload_type);
   2310   AddFeedbackParameter(feedback_param, &new_codec);
   2311   AddOrReplaceCodec<T, U>(content_desc, new_codec);
   2312 }
   2313 
   2314 bool PopWildcardCodec(std::vector<cricket::VideoCodec>* codecs,
   2315                       cricket::VideoCodec* wildcard_codec) {
   2316   for (std::vector<cricket::VideoCodec>::iterator iter = codecs->begin();
   2317        iter != codecs->end(); ++iter) {
   2318     if (iter->id == kWildcardPayloadType) {
   2319       *wildcard_codec = *iter;
   2320       codecs->erase(iter);
   2321       return true;
   2322     }
   2323   }
   2324   return false;
   2325 }
   2326 
   2327 void UpdateFromWildcardVideoCodecs(VideoContentDescription* video_desc) {
   2328   std::vector<cricket::VideoCodec> codecs = video_desc->codecs();
   2329   cricket::VideoCodec wildcard_codec;
   2330   if (!PopWildcardCodec(&codecs, &wildcard_codec)) {
   2331     return;
   2332   }
   2333   for (std::vector<cricket::VideoCodec>::iterator iter = codecs.begin();
   2334        iter != codecs.end(); ++iter) {
   2335     cricket::VideoCodec& codec = *iter;
   2336     AddFeedbackParameters(wildcard_codec.feedback_params, &codec);
   2337   }
   2338   video_desc->set_codecs(codecs);
   2339 }
   2340 
   2341 void AddAudioAttribute(const std::string& name, const std::string& value,
   2342                        AudioContentDescription* audio_desc) {
   2343   if (value.empty()) {
   2344     return;
   2345   }
   2346   std::vector<cricket::AudioCodec> codecs = audio_desc->codecs();
   2347   for (std::vector<cricket::AudioCodec>::iterator iter = codecs.begin();
   2348        iter != codecs.end(); ++iter) {
   2349     iter->params[name] = value;
   2350   }
   2351   audio_desc->set_codecs(codecs);
   2352 }
   2353 
   2354 bool ParseContent(const std::string& message,
   2355                   const MediaType media_type,
   2356                   int mline_index,
   2357                   const std::string& protocol,
   2358                   const std::vector<int>& codec_preference,
   2359                   size_t* pos,
   2360                   std::string* content_name,
   2361                   MediaContentDescription* media_desc,
   2362                   TransportDescription* transport,
   2363                   std::vector<JsepIceCandidate*>* candidates,
   2364                   SdpParseError* error) {
   2365   ASSERT(media_desc != NULL);
   2366   ASSERT(content_name != NULL);
   2367   ASSERT(transport != NULL);
   2368 
   2369   // The media level "ice-ufrag" and "ice-pwd".
   2370   // The candidates before update the media level "ice-pwd" and "ice-ufrag".
   2371   Candidates candidates_orig;
   2372   std::string line;
   2373   std::string mline_id;
   2374   // Tracks created out of the ssrc attributes.
   2375   StreamParamsVec tracks;
   2376   SsrcInfoVec ssrc_infos;
   2377   SsrcGroupVec ssrc_groups;
   2378   std::string maxptime_as_string;
   2379   std::string ptime_as_string;
   2380 
   2381   bool is_rtp =
   2382       protocol.empty() ||
   2383       talk_base::starts_with(protocol.data(),
   2384                              cricket::kMediaProtocolRtpPrefix);
   2385 
   2386   // Loop until the next m line
   2387   while (!IsLineType(message, kLineTypeMedia, *pos)) {
   2388     if (!GetLine(message, pos, &line)) {
   2389       if (*pos >= message.size()) {
   2390         break;  // Done parsing
   2391       } else {
   2392         return ParseFailed(message, *pos, "Invalid SDP line.", error);
   2393       }
   2394     }
   2395 
   2396     if (IsLineType(line, kLineTypeSessionBandwidth)) {
   2397       std::string bandwidth;
   2398       if (HasAttribute(line, kApplicationSpecificMaximum)) {
   2399         if (!GetValue(line, kApplicationSpecificMaximum, &bandwidth, error)) {
   2400           return false;
   2401         } else {
   2402           media_desc->set_bandwidth(
   2403               talk_base::FromString<int>(bandwidth) * 1000);
   2404         }
   2405       }
   2406       continue;
   2407     }
   2408 
   2409     // RFC 4566
   2410     // b=* (zero or more bandwidth information lines)
   2411     if (IsLineType(line, kLineTypeSessionBandwidth)) {
   2412       std::string bandwidth;
   2413       if (HasAttribute(line, kApplicationSpecificMaximum)) {
   2414         if (!GetValue(line, kApplicationSpecificMaximum, &bandwidth, error)) {
   2415           return false;
   2416         } else {
   2417           media_desc->set_bandwidth(
   2418               talk_base::FromString<int>(bandwidth) * 1000);
   2419         }
   2420       }
   2421       continue;
   2422     }
   2423 
   2424     if (!IsLineType(line, kLineTypeAttributes)) {
   2425       // TODO: Handle other lines if needed.
   2426       LOG(LS_INFO) << "Ignored line: " << line;
   2427       continue;
   2428     }
   2429 
   2430     // Handle attributes common to SCTP and RTP.
   2431     if (HasAttribute(line, kAttributeMid)) {
   2432       // RFC 3388
   2433       // mid-attribute      = "a=mid:" identification-tag
   2434       // identification-tag = token
   2435       // Use the mid identification-tag as the content name.
   2436       if (!GetValue(line, kAttributeMid, &mline_id, error)) {
   2437         return false;
   2438       }
   2439       *content_name = mline_id;
   2440     } else if (HasAttribute(line, kAttributeCandidate)) {
   2441       Candidate candidate;
   2442       if (!ParseCandidate(line, &candidate, error, false)) {
   2443         return false;
   2444       }
   2445       candidates_orig.push_back(candidate);
   2446     } else if (HasAttribute(line, kAttributeIceUfrag)) {
   2447       if (!GetValue(line, kAttributeIceUfrag, &transport->ice_ufrag, error)) {
   2448         return false;
   2449       }
   2450     } else if (HasAttribute(line, kAttributeIcePwd)) {
   2451       if (!GetValue(line, kAttributeIcePwd, &transport->ice_pwd, error)) {
   2452         return false;
   2453       }
   2454     } else if (HasAttribute(line, kAttributeIceOption)) {
   2455       if (!ParseIceOptions(line, &transport->transport_options, error)) {
   2456         return false;
   2457       }
   2458     } else if (HasAttribute(line, kAttributeFmtp)) {
   2459       if (!ParseFmtpAttributes(line, media_type, media_desc, error)) {
   2460         return false;
   2461       }
   2462     } else if (HasAttribute(line, kAttributeFingerprint)) {
   2463       talk_base::SSLFingerprint* fingerprint = NULL;
   2464 
   2465       if (!ParseFingerprintAttribute(line, &fingerprint, error)) {
   2466         return false;
   2467       }
   2468       transport->identity_fingerprint.reset(fingerprint);
   2469     } else if (HasAttribute(line, kAttributeSetup)) {
   2470       if (!ParseDtlsSetup(line, &(transport->connection_role), error)) {
   2471         return false;
   2472       }
   2473     } else if (is_rtp) {
   2474       //
   2475       // RTP specific attrubtes
   2476       //
   2477       if (HasAttribute(line, kAttributeRtcpMux)) {
   2478         media_desc->set_rtcp_mux(true);
   2479       } else if (HasAttribute(line, kAttributeSsrcGroup)) {
   2480         if (!ParseSsrcGroupAttribute(line, &ssrc_groups, error)) {
   2481           return false;
   2482         }
   2483       } else if (HasAttribute(line, kAttributeSsrc)) {
   2484         if (!ParseSsrcAttribute(line, &ssrc_infos, error)) {
   2485           return false;
   2486         }
   2487       } else if (HasAttribute(line, kAttributeCrypto)) {
   2488         if (!ParseCryptoAttribute(line, media_desc, error)) {
   2489           return false;
   2490         }
   2491       } else if (HasAttribute(line, kAttributeRtpmap)) {
   2492         if (!ParseRtpmapAttribute(line, media_type, codec_preference,
   2493                                   media_desc, error)) {
   2494           return false;
   2495         }
   2496       } else if (HasAttribute(line, kCodecParamMaxPTime)) {
   2497         if (!GetValue(line, kCodecParamMaxPTime, &maxptime_as_string, error)) {
   2498           return false;
   2499         }
   2500       } else if (HasAttribute(line, kAttributeRtcpFb)) {
   2501         if (!ParseRtcpFbAttribute(line, media_type, media_desc, error)) {
   2502           return false;
   2503         }
   2504       } else if (HasAttribute(line, kCodecParamPTime)) {
   2505         if (!GetValue(line, kCodecParamPTime, &ptime_as_string, error)) {
   2506           return false;
   2507         }
   2508       } else if (HasAttribute(line, kAttributeSendOnly)) {
   2509         media_desc->set_direction(cricket::MD_SENDONLY);
   2510       } else if (HasAttribute(line, kAttributeRecvOnly)) {
   2511         media_desc->set_direction(cricket::MD_RECVONLY);
   2512       } else if (HasAttribute(line, kAttributeInactive)) {
   2513         media_desc->set_direction(cricket::MD_INACTIVE);
   2514       } else if (HasAttribute(line, kAttributeSendRecv)) {
   2515         media_desc->set_direction(cricket::MD_SENDRECV);
   2516       } else if (HasAttribute(line, kAttributeExtmap)) {
   2517         RtpHeaderExtension extmap;
   2518         if (!ParseExtmap(line, &extmap, error)) {
   2519           return false;
   2520         }
   2521         media_desc->AddRtpHeaderExtension(extmap);
   2522       } else if (HasAttribute(line, kAttributeXGoogleFlag)) {
   2523         // Experimental attribute.  Conference mode activates more aggressive
   2524         // AEC and NS settings.
   2525         // TODO: expose API to set these directly.
   2526         std::string flag_value;
   2527         if (!GetValue(line, kAttributeXGoogleFlag, &flag_value, error)) {
   2528           return false;
   2529         }
   2530         if (flag_value.compare(kValueConference) == 0)
   2531           media_desc->set_conference_mode(true);
   2532       } else if (HasAttribute(line, kAttributeXGoogleBufferLatency)) {
   2533         // Experimental attribute.
   2534         // TODO: expose API to set this directly.
   2535         std::string flag_value;
   2536         if (!GetValue(line, kAttributeXGoogleBufferLatency, &flag_value,
   2537                       error)) {
   2538           return false;
   2539         }
   2540         int buffer_latency = 0;
   2541         if (!talk_base::FromString(flag_value, &buffer_latency) ||
   2542             buffer_latency < 0) {
   2543           return ParseFailed(message, "Invalid buffer latency.", error);
   2544         }
   2545         media_desc->set_buffered_mode_latency(buffer_latency);
   2546       }
   2547     } else {
   2548       // Only parse lines that we are interested of.
   2549       LOG(LS_INFO) << "Ignored line: " << line;
   2550       continue;
   2551     }
   2552   }
   2553 
   2554   // Create tracks from the |ssrc_infos|.
   2555   CreateTracksFromSsrcInfos(ssrc_infos, &tracks);
   2556 
   2557   // Add the ssrc group to the track.
   2558   for (SsrcGroupVec::iterator ssrc_group = ssrc_groups.begin();
   2559        ssrc_group != ssrc_groups.end(); ++ssrc_group) {
   2560     if (ssrc_group->ssrcs.empty()) {
   2561       continue;
   2562     }
   2563     uint32 ssrc = ssrc_group->ssrcs.front();
   2564     for (StreamParamsVec::iterator track = tracks.begin();
   2565          track != tracks.end(); ++track) {
   2566       if (track->has_ssrc(ssrc)) {
   2567         track->ssrc_groups.push_back(*ssrc_group);
   2568       }
   2569     }
   2570   }
   2571 
   2572   // Add the new tracks to the |media_desc|.
   2573   for (StreamParamsVec::iterator track = tracks.begin();
   2574        track != tracks.end(); ++track) {
   2575     media_desc->AddStream(*track);
   2576   }
   2577 
   2578   if (media_type == cricket::MEDIA_TYPE_AUDIO) {
   2579     AudioContentDescription* audio_desc =
   2580         static_cast<AudioContentDescription*>(media_desc);
   2581     // Verify audio codec ensures that no audio codec has been populated with
   2582     // only fmtp.
   2583     if (!VerifyAudioCodecs(audio_desc)) {
   2584       return ParseFailed("Failed to parse audio codecs correctly.", error);
   2585     }
   2586     AddAudioAttribute(kCodecParamMaxPTime, maxptime_as_string, audio_desc);
   2587     AddAudioAttribute(kCodecParamPTime, ptime_as_string, audio_desc);
   2588   }
   2589 
   2590   if (media_type == cricket::MEDIA_TYPE_VIDEO) {
   2591       VideoContentDescription* video_desc =
   2592           static_cast<VideoContentDescription*>(media_desc);
   2593       UpdateFromWildcardVideoCodecs(video_desc);
   2594       // Verify video codec ensures that no video codec has been populated with
   2595       // only rtcp-fb.
   2596       if (!VerifyVideoCodecs(video_desc)) {
   2597         return ParseFailed("Failed to parse video codecs correctly.", error);
   2598       }
   2599   }
   2600 
   2601   // RFC 5245
   2602   // Update the candidates with the media level "ice-pwd" and "ice-ufrag".
   2603   for (Candidates::iterator it = candidates_orig.begin();
   2604        it != candidates_orig.end(); ++it) {
   2605     ASSERT((*it).username().empty());
   2606     (*it).set_username(transport->ice_ufrag);
   2607     ASSERT((*it).password().empty());
   2608     (*it).set_password(transport->ice_pwd);
   2609     candidates->push_back(
   2610         new JsepIceCandidate(mline_id, mline_index, *it));
   2611   }
   2612   return true;
   2613 }
   2614 
   2615 bool ParseSsrcAttribute(const std::string& line, SsrcInfoVec* ssrc_infos,
   2616                         SdpParseError* error) {
   2617   ASSERT(ssrc_infos != NULL);
   2618   // RFC 5576
   2619   // a=ssrc:<ssrc-id> <attribute>
   2620   // a=ssrc:<ssrc-id> <attribute>:<value>
   2621   std::string field1, field2;
   2622   if (!SplitByDelimiter(line.substr(kLinePrefixLength),
   2623                         kSdpDelimiterSpace,
   2624                         &field1,
   2625                         &field2)) {
   2626     const size_t expected_fields = 2;
   2627     return ParseFailedExpectFieldNum(line, expected_fields, error);
   2628   }
   2629 
   2630   // ssrc:<ssrc-id>
   2631   std::string ssrc_id_s;
   2632   if (!GetValue(field1, kAttributeSsrc, &ssrc_id_s, error)) {
   2633     return false;
   2634   }
   2635   uint32 ssrc_id = talk_base::FromString<uint32>(ssrc_id_s);
   2636 
   2637   std::string attribute;
   2638   std::string value;
   2639   if (!SplitByDelimiter(field2, kSdpDelimiterColon,
   2640                         &attribute, &value)) {
   2641     std::ostringstream description;
   2642     description << "Failed to get the ssrc attribute value from " << field2
   2643                 << ". Expected format <attribute>:<value>.";
   2644     return ParseFailed(line, description.str(), error);
   2645   }
   2646 
   2647   // Check if there's already an item for this |ssrc_id|. Create a new one if
   2648   // there isn't.
   2649   SsrcInfoVec::iterator ssrc_info = ssrc_infos->begin();
   2650   for (; ssrc_info != ssrc_infos->end(); ++ssrc_info) {
   2651     if (ssrc_info->ssrc_id == ssrc_id) {
   2652       break;
   2653     }
   2654   }
   2655   if (ssrc_info == ssrc_infos->end()) {
   2656     SsrcInfo info;
   2657     info.ssrc_id = ssrc_id;
   2658     ssrc_infos->push_back(info);
   2659     ssrc_info = ssrc_infos->end() - 1;
   2660   }
   2661 
   2662   // Store the info to the |ssrc_info|.
   2663   if (attribute == kSsrcAttributeCname) {
   2664     // RFC 5576
   2665     // cname:<value>
   2666     ssrc_info->cname = value;
   2667   } else if (attribute == kSsrcAttributeMsid) {
   2668     // draft-alvestrand-mmusic-msid-00
   2669     // "msid:" identifier [ " " appdata ]
   2670     std::vector<std::string> fields;
   2671     talk_base::split(value, kSdpDelimiterSpace, &fields);
   2672     if (fields.size() < 1 || fields.size() > 2) {
   2673       return ParseFailed(line,
   2674                          "Expected format \"msid:<identifier>[ <appdata>]\".",
   2675                          error);
   2676     }
   2677     ssrc_info->msid_identifier = fields[0];
   2678     if (fields.size() == 2) {
   2679       ssrc_info->msid_appdata = fields[1];
   2680     }
   2681   } else if (attribute == kSsrcAttributeMslabel) {
   2682     // draft-alvestrand-rtcweb-mid-01
   2683     // mslabel:<value>
   2684     ssrc_info->mslabel = value;
   2685   } else if (attribute == kSSrcAttributeLabel) {
   2686     // The label isn't defined.
   2687     // label:<value>
   2688     ssrc_info->label = value;
   2689   }
   2690   return true;
   2691 }
   2692 
   2693 bool ParseSsrcGroupAttribute(const std::string& line,
   2694                              SsrcGroupVec* ssrc_groups,
   2695                              SdpParseError* error) {
   2696   ASSERT(ssrc_groups != NULL);
   2697   // RFC 5576
   2698   // a=ssrc-group:<semantics> <ssrc-id> ...
   2699   std::vector<std::string> fields;
   2700   talk_base::split(line.substr(kLinePrefixLength),
   2701                    kSdpDelimiterSpace, &fields);
   2702   const size_t expected_min_fields = 2;
   2703   if (fields.size() < expected_min_fields) {
   2704     return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
   2705   }
   2706   std::string semantics;
   2707   if (!GetValue(fields[0], kAttributeSsrcGroup, &semantics, error)) {
   2708     return false;
   2709   }
   2710   std::vector<uint32> ssrcs;
   2711   for (size_t i = 1; i < fields.size(); ++i) {
   2712     uint32 ssrc = talk_base::FromString<uint32>(fields[i]);
   2713     ssrcs.push_back(ssrc);
   2714   }
   2715   ssrc_groups->push_back(SsrcGroup(semantics, ssrcs));
   2716   return true;
   2717 }
   2718 
   2719 bool ParseCryptoAttribute(const std::string& line,
   2720                           MediaContentDescription* media_desc,
   2721                           SdpParseError* error) {
   2722   std::vector<std::string> fields;
   2723   talk_base::split(line.substr(kLinePrefixLength),
   2724                    kSdpDelimiterSpace, &fields);
   2725   // RFC 4568
   2726   // a=crypto:<tag> <crypto-suite> <key-params> [<session-params>]
   2727   const size_t expected_min_fields = 3;
   2728   if (fields.size() < expected_min_fields) {
   2729     return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
   2730   }
   2731   std::string tag_value;
   2732   if (!GetValue(fields[0], kAttributeCrypto, &tag_value, error)) {
   2733     return false;
   2734   }
   2735   int tag = talk_base::FromString<int>(tag_value);
   2736   const std::string crypto_suite = fields[1];
   2737   const std::string key_params = fields[2];
   2738   std::string session_params;
   2739   if (fields.size() > 3) {
   2740     session_params = fields[3];
   2741   }
   2742   media_desc->AddCrypto(CryptoParams(tag, crypto_suite, key_params,
   2743                                      session_params));
   2744   return true;
   2745 }
   2746 
   2747 // Updates or creates a new codec entry in the audio description with according
   2748 // to |name|, |clockrate|, |bitrate|, |channels| and |preference|.
   2749 void UpdateCodec(int payload_type, const std::string& name, int clockrate,
   2750                  int bitrate, int channels, int preference,
   2751                  AudioContentDescription* audio_desc) {
   2752   // Codec may already be populated with (only) optional parameters
   2753   // (from an fmtp).
   2754   cricket::AudioCodec codec = GetCodec(audio_desc->codecs(), payload_type);
   2755   codec.name = name;
   2756   codec.clockrate = clockrate;
   2757   codec.bitrate = bitrate;
   2758   codec.channels = channels;
   2759   codec.preference = preference;
   2760   AddOrReplaceCodec<AudioContentDescription, cricket::AudioCodec>(audio_desc,
   2761                                                                   codec);
   2762 }
   2763 
   2764 // Updates or creates a new codec entry in the video description according to
   2765 // |name|, |width|, |height|, |framerate| and |preference|.
   2766 void UpdateCodec(int payload_type, const std::string& name, int width,
   2767                  int height, int framerate, int preference,
   2768                  VideoContentDescription* video_desc) {
   2769   // Codec may already be populated with (only) optional parameters
   2770   // (from an fmtp).
   2771   cricket::VideoCodec codec = GetCodec(video_desc->codecs(), payload_type);
   2772   codec.name = name;
   2773   codec.width = width;
   2774   codec.height = height;
   2775   codec.framerate = framerate;
   2776   codec.preference = preference;
   2777   AddOrReplaceCodec<VideoContentDescription, cricket::VideoCodec>(video_desc,
   2778                                                                   codec);
   2779 }
   2780 
   2781 bool ParseRtpmapAttribute(const std::string& line,
   2782                           const MediaType media_type,
   2783                           const std::vector<int>& codec_preference,
   2784                           MediaContentDescription* media_desc,
   2785                           SdpParseError* error) {
   2786   std::vector<std::string> fields;
   2787   talk_base::split(line.substr(kLinePrefixLength),
   2788                    kSdpDelimiterSpace, &fields);
   2789   // RFC 4566
   2790   // a=rtpmap:<payload type> <encoding name>/<clock rate>[/<encodingparameters>]
   2791   const size_t expected_min_fields = 2;
   2792   if (fields.size() < expected_min_fields) {
   2793     return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
   2794   }
   2795   std::string payload_type_value;
   2796   if (!GetValue(fields[0], kAttributeRtpmap, &payload_type_value, error)) {
   2797     return false;
   2798   }
   2799   const int payload_type = talk_base::FromString<int>(payload_type_value);
   2800 
   2801   // Set the preference order depending on the order of the pl type in the
   2802   // <fmt> of the m-line.
   2803   const int preference = codec_preference.end() -
   2804       std::find(codec_preference.begin(), codec_preference.end(),
   2805                 payload_type);
   2806   if (preference == 0) {
   2807     LOG(LS_WARNING) << "Ignore rtpmap line that did not appear in the "
   2808                     << "<fmt> of the m-line: " << line;
   2809     return true;
   2810   }
   2811   const std::string encoder = fields[1];
   2812   std::vector<std::string> codec_params;
   2813   talk_base::split(encoder, '/', &codec_params);
   2814   // <encoding name>/<clock rate>[/<encodingparameters>]
   2815   // 2 mandatory fields
   2816   if (codec_params.size() < 2 || codec_params.size() > 3) {
   2817     return ParseFailed(line,
   2818                        "Expected format \"<encoding name>/<clock rate>"
   2819                        "[/<encodingparameters>]\".",
   2820                        error);
   2821   }
   2822   const std::string encoding_name = codec_params[0];
   2823   const int clock_rate = talk_base::FromString<int>(codec_params[1]);
   2824   if (media_type == cricket::MEDIA_TYPE_VIDEO) {
   2825     VideoContentDescription* video_desc =
   2826         static_cast<VideoContentDescription*>(media_desc);
   2827     // TODO: We will send resolution in SDP. For now use
   2828     // JsepSessionDescription::kMaxVideoCodecWidth and kMaxVideoCodecHeight.
   2829     UpdateCodec(payload_type, encoding_name,
   2830                 JsepSessionDescription::kMaxVideoCodecWidth,
   2831                 JsepSessionDescription::kMaxVideoCodecHeight,
   2832                 JsepSessionDescription::kDefaultVideoCodecFramerate,
   2833                 preference, video_desc);
   2834   } else if (media_type == cricket::MEDIA_TYPE_AUDIO) {
   2835     // RFC 4566
   2836     // For audio streams, <encoding parameters> indicates the number
   2837     // of audio channels.  This parameter is OPTIONAL and may be
   2838     // omitted if the number of channels is one, provided that no
   2839     // additional parameters are needed.
   2840     int channels = 1;
   2841     if (codec_params.size() == 3) {
   2842       channels = talk_base::FromString<int>(codec_params[2]);
   2843     }
   2844     int bitrate = 0;
   2845     // The default behavior for ISAC (bitrate == 0) in webrtcvoiceengine.cc
   2846     // (specifically FindWebRtcCodec) is bandwidth-adaptive variable bitrate.
   2847     // The bandwidth adaptation doesn't always work well, so this code
   2848     // sets a fixed target bitrate instead.
   2849     if (_stricmp(encoding_name.c_str(), kIsacCodecName) == 0) {
   2850       if (clock_rate <= 16000) {
   2851         bitrate = kIsacWbDefaultRate;
   2852       } else {
   2853         bitrate = kIsacSwbDefaultRate;
   2854       }
   2855     }
   2856     AudioContentDescription* audio_desc =
   2857         static_cast<AudioContentDescription*>(media_desc);
   2858     UpdateCodec(payload_type, encoding_name, clock_rate, bitrate, channels,
   2859                 preference, audio_desc);
   2860   } else if (media_type == cricket::MEDIA_TYPE_DATA) {
   2861     DataContentDescription* data_desc =
   2862         static_cast<DataContentDescription*>(media_desc);
   2863     data_desc->AddCodec(cricket::DataCodec(payload_type, encoding_name,
   2864                                            preference));
   2865   }
   2866   return true;
   2867 }
   2868 
   2869 void PruneRight(const char delimiter, std::string* message) {
   2870   size_t trailing = message->find(delimiter);
   2871   if (trailing != std::string::npos) {
   2872     *message = message->substr(0, trailing);
   2873   }
   2874 }
   2875 
   2876 bool ParseFmtpParam(const std::string& line, std::string* parameter,
   2877                     std::string* value, SdpParseError* error) {
   2878   if (!SplitByDelimiter(line, kSdpDelimiterEqual, parameter, value)) {
   2879     ParseFailed(line, "Unable to parse fmtp parameter. \'=\' missing.", error);
   2880     return false;
   2881   }
   2882   // a=fmtp:<payload_type> <param1>=<value1>; <param2>=<value2>; ...
   2883   // When parsing the values the trailing ";" gets picked up. Remove them.
   2884   PruneRight(kSdpDelimiterSemicolon, value);
   2885   return true;
   2886 }
   2887 
   2888 bool ParseFmtpAttributes(const std::string& line, const MediaType media_type,
   2889                          MediaContentDescription* media_desc,
   2890                          SdpParseError* error) {
   2891   if (media_type != cricket::MEDIA_TYPE_AUDIO &&
   2892       media_type != cricket::MEDIA_TYPE_VIDEO) {
   2893     return true;
   2894   }
   2895   std::vector<std::string> fields;
   2896   talk_base::split(line.substr(kLinePrefixLength),
   2897                    kSdpDelimiterSpace, &fields);
   2898 
   2899   // RFC 5576
   2900   // a=fmtp:<format> <format specific parameters>
   2901   // At least two fields, whereas the second one is any of the optional
   2902   // parameters.
   2903   if (fields.size() < 2) {
   2904     ParseFailedExpectMinFieldNum(line, 2, error);
   2905     return false;
   2906   }
   2907 
   2908   std::string payload_type;
   2909   if (!GetValue(fields[0], kAttributeFmtp, &payload_type, error)) {
   2910     return false;
   2911   }
   2912 
   2913   cricket::CodecParameterMap codec_params;
   2914   for (std::vector<std::string>::const_iterator iter = fields.begin() + 1;
   2915        iter != fields.end(); ++iter) {
   2916     std::string name;
   2917     std::string value;
   2918     if (iter->find(kSdpDelimiterEqual) == std::string::npos) {
   2919       // Only fmtps with equals are currently supported. Other fmtp types
   2920       // should be ignored. Unknown fmtps do not constitute an error.
   2921       continue;
   2922     }
   2923     if (!ParseFmtpParam(*iter, &name, &value, error)) {
   2924       return false;
   2925     }
   2926     codec_params[name] = value;
   2927   }
   2928 
   2929   int int_payload_type = talk_base::FromString<int>(payload_type);
   2930   if (media_type == cricket::MEDIA_TYPE_AUDIO) {
   2931     UpdateCodec<AudioContentDescription, cricket::AudioCodec>(
   2932         media_desc, int_payload_type, codec_params);
   2933   } else if (media_type == cricket::MEDIA_TYPE_VIDEO) {
   2934     UpdateCodec<VideoContentDescription, cricket::VideoCodec>(
   2935         media_desc, int_payload_type, codec_params);
   2936   }
   2937   return true;
   2938 }
   2939 
   2940 bool ParseRtcpFbAttribute(const std::string& line, const MediaType media_type,
   2941                           MediaContentDescription* media_desc,
   2942                           SdpParseError* error) {
   2943   if (media_type != cricket::MEDIA_TYPE_AUDIO &&
   2944       media_type != cricket::MEDIA_TYPE_VIDEO) {
   2945     return true;
   2946   }
   2947   std::vector<std::string> rtcp_fb_fields;
   2948   talk_base::split(line.c_str(), kSdpDelimiterSpace, &rtcp_fb_fields);
   2949   if (rtcp_fb_fields.size() < 2) {
   2950     return ParseFailedGetValue(line, kAttributeRtcpFb, error);
   2951   }
   2952   std::string payload_type_string;
   2953   if (!GetValue(rtcp_fb_fields[0], kAttributeRtcpFb, &payload_type_string,
   2954                 error)) {
   2955     return false;
   2956   }
   2957   int payload_type = (payload_type_string == "*") ?
   2958       kWildcardPayloadType : talk_base::FromString<int>(payload_type_string);
   2959   std::string id = rtcp_fb_fields[1];
   2960   std::string param = "";
   2961   for (std::vector<std::string>::iterator iter = rtcp_fb_fields.begin() + 2;
   2962        iter != rtcp_fb_fields.end(); ++iter) {
   2963     param.append(*iter);
   2964   }
   2965   const cricket::FeedbackParam feedback_param(id, param);
   2966 
   2967   if (media_type == cricket::MEDIA_TYPE_AUDIO) {
   2968     UpdateCodec<AudioContentDescription, cricket::AudioCodec>(media_desc,
   2969                                                               payload_type,
   2970                                                               feedback_param);
   2971   } else if (media_type == cricket::MEDIA_TYPE_VIDEO) {
   2972     UpdateCodec<VideoContentDescription, cricket::VideoCodec>(media_desc,
   2973                                                               payload_type,
   2974                                                               feedback_param);
   2975   }
   2976   return true;
   2977 }
   2978 
   2979 }  // namespace webrtc
   2980