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