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