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