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

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

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

Powered by Google App Engine
This is Rietveld 408576698