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

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

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

Powered by Google App Engine
This is Rietveld 408576698