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