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