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