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