Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(996)

Side by Side Diff: webrtc/api/webrtcsdp.cc

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

Powered by Google App Engine
This is Rietveld 408576698