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