blob: 90e0007628ff5d9b94d2bc4063f37cea71d38a52 [file] [log] [blame]
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001/*
kjellanderb24317b2016-02-10 07:54:43 -08002 * Copyright 2011 The WebRTC project authors. All Rights Reserved.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003 *
kjellanderb24317b2016-02-10 07:54:43 -08004 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00009 */
10
Henrik Kjellander15583c12016-02-10 10:53:12 +010011#include "webrtc/api/webrtcsdp.h"
henrike@webrtc.org28e20752013-07-10 00:45:36 +000012
kjellandera96e2d72016-02-04 23:52:28 -080013#include <ctype.h>
henrike@webrtc.org28e20752013-07-10 00:45:36 +000014#include <limits.h>
15#include <stdio.h>
16#include <algorithm>
17#include <string>
18#include <vector>
19
Henrik Kjellander15583c12016-02-10 10:53:12 +010020#include "webrtc/api/jsepicecandidate.h"
21#include "webrtc/api/jsepsessiondescription.h"
tfarina5237aaf2015-11-10 23:44:30 -080022#include "webrtc/base/arraysize.h"
buildbot@webrtc.orga09a9992014-08-13 17:26:08 +000023#include "webrtc/base/common.h"
24#include "webrtc/base/logging.h"
25#include "webrtc/base/messagedigest.h"
26#include "webrtc/base/stringutils.h"
kjellandera96e2d72016-02-04 23:52:28 -080027#include "webrtc/media/base/codec.h"
kjellandera96e2d72016-02-04 23:52:28 -080028#include "webrtc/media/base/cryptoparams.h"
kjellanderf4752772016-03-02 05:42:30 -080029#include "webrtc/media/base/mediaconstants.h"
kjellandera96e2d72016-02-04 23:52:28 -080030#include "webrtc/media/base/rtputils.h"
31#include "webrtc/media/sctp/sctpdataengine.h"
32#include "webrtc/p2p/base/candidate.h"
kjellanderf4752772016-03-02 05:42:30 -080033#include "webrtc/p2p/base/p2pconstants.h"
kjellandera96e2d72016-02-04 23:52:28 -080034#include "webrtc/p2p/base/port.h"
kjellander@webrtc.org9b8df252016-02-12 06:47:59 +010035#include "webrtc/pc/mediasession.h"
henrike@webrtc.org28e20752013-07-10 00:45:36 +000036
37using cricket::AudioContentDescription;
38using cricket::Candidate;
39using cricket::Candidates;
40using cricket::ContentDescription;
41using cricket::ContentInfo;
42using cricket::CryptoParams;
43using cricket::DataContentDescription;
44using cricket::ICE_CANDIDATE_COMPONENT_RTP;
45using cricket::ICE_CANDIDATE_COMPONENT_RTCP;
46using cricket::kCodecParamMaxBitrate;
47using cricket::kCodecParamMaxPTime;
48using cricket::kCodecParamMaxQuantization;
49using cricket::kCodecParamMinBitrate;
50using cricket::kCodecParamMinPTime;
51using cricket::kCodecParamPTime;
52using cricket::kCodecParamSPropStereo;
buildbot@webrtc.orged97bb02014-05-07 11:15:20 +000053using cricket::kCodecParamStartBitrate;
henrike@webrtc.org28e20752013-07-10 00:45:36 +000054using cricket::kCodecParamStereo;
55using cricket::kCodecParamUseInbandFec;
Minyue Li7100dcd2015-03-27 05:05:59 +010056using cricket::kCodecParamUseDtx;
henrike@webrtc.org28e20752013-07-10 00:45:36 +000057using cricket::kCodecParamSctpProtocol;
58using cricket::kCodecParamSctpStreams;
henrike@webrtc.org1e09a712013-07-26 19:17:59 +000059using cricket::kCodecParamMaxAverageBitrate;
buildbot@webrtc.org5d639b32014-09-10 07:57:12 +000060using cricket::kCodecParamMaxPlaybackRate;
stefan@webrtc.org85d27942014-06-09 12:51:39 +000061using cricket::kCodecParamAssociatedPayloadType;
henrike@webrtc.org28e20752013-07-10 00:45:36 +000062using cricket::MediaContentDescription;
63using cricket::MediaType;
henrike@webrtc.org28e20752013-07-10 00:45:36 +000064using cricket::RtpHeaderExtension;
65using cricket::SsrcGroup;
66using cricket::StreamParams;
67using cricket::StreamParamsVec;
68using cricket::TransportDescription;
69using cricket::TransportInfo;
70using cricket::VideoContentDescription;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +000071using rtc::SocketAddress;
henrike@webrtc.org28e20752013-07-10 00:45:36 +000072
73typedef std::vector<RtpHeaderExtension> RtpHeaderExtensions;
74
75namespace cricket {
76class SessionDescription;
77}
78
79namespace webrtc {
80
81// Line type
82// RFC 4566
83// An SDP session description consists of a number of lines of text of
84// the form:
85// <type>=<value>
86// where <type> MUST be exactly one case-significant character.
deadbeef9d3584c2016-02-16 17:54:10 -080087static const int kLinePrefixLength = 2; // Length of <type>=
henrike@webrtc.org28e20752013-07-10 00:45:36 +000088static const char kLineTypeVersion = 'v';
89static const char kLineTypeOrigin = 'o';
90static const char kLineTypeSessionName = 's';
91static const char kLineTypeSessionInfo = 'i';
92static const char kLineTypeSessionUri = 'u';
93static const char kLineTypeSessionEmail = 'e';
94static const char kLineTypeSessionPhone = 'p';
95static const char kLineTypeSessionBandwidth = 'b';
96static const char kLineTypeTiming = 't';
97static const char kLineTypeRepeatTimes = 'r';
98static const char kLineTypeTimeZone = 'z';
99static const char kLineTypeEncryptionKey = 'k';
100static const char kLineTypeMedia = 'm';
101static const char kLineTypeConnection = 'c';
102static const char kLineTypeAttributes = 'a';
103
104// Attributes
105static const char kAttributeGroup[] = "group";
106static const char kAttributeMid[] = "mid";
deadbeef9d3584c2016-02-16 17:54:10 -0800107static const char kAttributeMsid[] = "msid";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000108static const char kAttributeRtcpMux[] = "rtcp-mux";
deadbeef13871492015-12-09 12:37:51 -0800109static const char kAttributeRtcpReducedSize[] = "rtcp-rsize";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000110static const char kAttributeSsrc[] = "ssrc";
111static const char kSsrcAttributeCname[] = "cname";
112static const char kAttributeExtmap[] = "extmap";
113// draft-alvestrand-mmusic-msid-01
114// a=msid-semantic: WMS
115static const char kAttributeMsidSemantics[] = "msid-semantic";
116static const char kMediaStreamSemantic[] = "WMS";
117static const char kSsrcAttributeMsid[] = "msid";
118static const char kDefaultMsid[] = "default";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000119static const char kSsrcAttributeMslabel[] = "mslabel";
120static const char kSSrcAttributeLabel[] = "label";
121static const char kAttributeSsrcGroup[] = "ssrc-group";
122static const char kAttributeCrypto[] = "crypto";
123static const char kAttributeCandidate[] = "candidate";
124static const char kAttributeCandidateTyp[] = "typ";
125static const char kAttributeCandidateRaddr[] = "raddr";
126static const char kAttributeCandidateRport[] = "rport";
honghaiza54a0802015-12-16 18:37:23 -0800127static const char kAttributeCandidateUfrag[] = "ufrag";
128static const char kAttributeCandidatePwd[] = "pwd";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000129static const char kAttributeCandidateGeneration[] = "generation";
honghaiza0c44ea2016-03-23 16:07:48 -0700130static const char kAttributeCandidateNetworkId[] = "network-id";
honghaize1a0c942016-02-16 14:54:56 -0800131static const char kAttributeCandidateNetworkCost[] = "network-cost";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000132static const char kAttributeFingerprint[] = "fingerprint";
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +0000133static const char kAttributeSetup[] = "setup";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000134static const char kAttributeFmtp[] = "fmtp";
135static const char kAttributeRtpmap[] = "rtpmap";
wu@webrtc.org78187522013-10-07 23:32:02 +0000136static const char kAttributeSctpmap[] = "sctpmap";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000137static const char kAttributeRtcp[] = "rtcp";
138static const char kAttributeIceUfrag[] = "ice-ufrag";
139static const char kAttributeIcePwd[] = "ice-pwd";
140static const char kAttributeIceLite[] = "ice-lite";
141static const char kAttributeIceOption[] = "ice-options";
142static const char kAttributeSendOnly[] = "sendonly";
143static const char kAttributeRecvOnly[] = "recvonly";
144static const char kAttributeRtcpFb[] = "rtcp-fb";
145static const char kAttributeSendRecv[] = "sendrecv";
146static const char kAttributeInactive[] = "inactive";
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +0000147// draft-ietf-mmusic-sctp-sdp-07
148// a=sctp-port
149static const char kAttributeSctpPort[] = "sctp-port";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000150
151// Experimental flags
152static const char kAttributeXGoogleFlag[] = "x-google-flag";
153static const char kValueConference[] = "conference";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000154
155// Candidate
156static const char kCandidateHost[] = "host";
157static const char kCandidateSrflx[] = "srflx";
Honghai Zhang7fb69db2016-03-14 11:59:18 -0700158static const char kCandidatePrflx[] = "prflx";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000159static const char kCandidateRelay[] = "relay";
mallinath@webrtc.org2d60c5e2014-08-08 22:29:20 +0000160static const char kTcpCandidateType[] = "tcptype";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000161
162static const char kSdpDelimiterEqual = '=';
163static const char kSdpDelimiterSpace = ' ';
164static const char kSdpDelimiterColon = ':';
165static const char kSdpDelimiterSemicolon = ';';
166static const char kSdpDelimiterSlash = '/';
167static const char kNewLine = '\n';
168static const char kReturn = '\r';
169static const char kLineBreak[] = "\r\n";
170
171// TODO: Generate the Session and Time description
172// instead of hardcoding.
173static const char kSessionVersion[] = "v=0";
174// RFC 4566
175static const char kSessionOriginUsername[] = "-";
176static const char kSessionOriginSessionId[] = "0";
177static const char kSessionOriginSessionVersion[] = "0";
178static const char kSessionOriginNettype[] = "IN";
179static const char kSessionOriginAddrtype[] = "IP4";
180static const char kSessionOriginAddress[] = "127.0.0.1";
181static const char kSessionName[] = "s=-";
182static const char kTimeDescription[] = "t=0 0";
183static const char kAttrGroup[] = "a=group:BUNDLE";
184static const char kConnectionNettype[] = "IN";
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000185static const char kConnectionIpv4Addrtype[] = "IP4";
186static const char kConnectionIpv6Addrtype[] = "IP6";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000187static const char kMediaTypeVideo[] = "video";
188static const char kMediaTypeAudio[] = "audio";
189static const char kMediaTypeData[] = "application";
190static const char kMediaPortRejected[] = "0";
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000191// draft-ietf-mmusic-trickle-ice-01
192// When no candidates have been gathered, set the connection
193// address to IP6 ::.
perkj@webrtc.orgd105cc82014-11-07 11:22:06 +0000194// TODO(perkj): FF can not parse IP6 ::. See http://crbug/430333
195// Use IPV4 per default.
196static const char kDummyAddress[] = "0.0.0.0";
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000197static const char kDummyPort[] = "9";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000198// RFC 3556
199static const char kApplicationSpecificMaximum[] = "AS";
200
201static const int kDefaultVideoClockrate = 90000;
202
203// ISAC special-case.
204static const char kIsacCodecName[] = "ISAC"; // From webrtcvoiceengine.cc
205static const int kIsacWbDefaultRate = 32000; // From acm_common_defs.h
206static const int kIsacSwbDefaultRate = 56000; // From acm_common_defs.h
207
wu@webrtc.org78187522013-10-07 23:32:02 +0000208static const char kDefaultSctpmapProtocol[] = "webrtc-datachannel";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000209
pkasting@chromium.orgd3245462015-02-23 21:28:22 +0000210// RTP payload type is in the 0-127 range. Use -1 to indicate "all" payload
211// types.
212const int kWildcardPayloadType = -1;
213
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000214struct SsrcInfo {
Peter Boström0c4e06b2015-10-07 12:23:21 +0200215 uint32_t ssrc_id;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000216 std::string cname;
deadbeef9d3584c2016-02-16 17:54:10 -0800217 std::string stream_id;
218 std::string track_id;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000219
220 // For backward compatibility.
221 // TODO(ronghuawu): Remove below 2 fields once all the clients support msid.
222 std::string label;
223 std::string mslabel;
224};
225typedef std::vector<SsrcInfo> SsrcInfoVec;
226typedef std::vector<SsrcGroup> SsrcGroupVec;
227
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000228template <class T>
229static void AddFmtpLine(const T& codec, std::string* message);
230static void BuildMediaDescription(const ContentInfo* content_info,
231 const TransportInfo* transport_info,
232 const MediaType media_type,
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000233 const std::vector<Candidate>& candidates,
deadbeef9d3584c2016-02-16 17:54:10 -0800234 bool unified_plan_sdp,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000235 std::string* message);
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +0000236static void BuildSctpContentAttributes(std::string* message, int sctp_port);
deadbeef9d3584c2016-02-16 17:54:10 -0800237static void BuildRtpContentAttributes(const MediaContentDescription* media_desc,
238 const MediaType media_type,
239 bool unified_plan_sdp,
240 std::string* message);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000241static void BuildRtpMap(const MediaContentDescription* media_desc,
242 const MediaType media_type,
243 std::string* message);
244static void BuildCandidate(const std::vector<Candidate>& candidates,
honghaiza54a0802015-12-16 18:37:23 -0800245 bool include_ufrag,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000246 std::string* message);
247static void BuildIceOptions(const std::vector<std::string>& transport_options,
248 std::string* message);
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +0000249static bool IsRtp(const std::string& protocol);
250static bool IsDtlsSctp(const std::string& protocol);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000251static bool ParseSessionDescription(const std::string& message, size_t* pos,
252 std::string* session_id,
253 std::string* session_version,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000254 TransportDescription* session_td,
255 RtpHeaderExtensions* session_extmaps,
256 cricket::SessionDescription* desc,
257 SdpParseError* error);
258static bool ParseGroupAttribute(const std::string& line,
259 cricket::SessionDescription* desc,
260 SdpParseError* error);
261static bool ParseMediaDescription(
262 const std::string& message,
263 const TransportDescription& session_td,
264 const RtpHeaderExtensions& session_extmaps,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000265 size_t* pos, cricket::SessionDescription* desc,
266 std::vector<JsepIceCandidate*>* candidates,
267 SdpParseError* error);
268static bool ParseContent(const std::string& message,
269 const MediaType media_type,
270 int mline_index,
271 const std::string& protocol,
272 const std::vector<int>& codec_preference,
273 size_t* pos,
274 std::string* content_name,
275 MediaContentDescription* media_desc,
276 TransportDescription* transport,
277 std::vector<JsepIceCandidate*>* candidates,
278 SdpParseError* error);
279static bool ParseSsrcAttribute(const std::string& line,
280 SsrcInfoVec* ssrc_infos,
281 SdpParseError* error);
282static bool ParseSsrcGroupAttribute(const std::string& line,
283 SsrcGroupVec* ssrc_groups,
284 SdpParseError* error);
285static bool ParseCryptoAttribute(const std::string& line,
286 MediaContentDescription* media_desc,
287 SdpParseError* error);
288static bool ParseRtpmapAttribute(const std::string& line,
289 const MediaType media_type,
290 const std::vector<int>& codec_preference,
291 MediaContentDescription* media_desc,
292 SdpParseError* error);
293static bool ParseFmtpAttributes(const std::string& line,
294 const MediaType media_type,
295 MediaContentDescription* media_desc,
296 SdpParseError* error);
297static bool ParseFmtpParam(const std::string& line, std::string* parameter,
298 std::string* value, SdpParseError* error);
299static bool ParseCandidate(const std::string& message, Candidate* candidate,
300 SdpParseError* error, bool is_raw);
301static bool ParseRtcpFbAttribute(const std::string& line,
302 const MediaType media_type,
303 MediaContentDescription* media_desc,
304 SdpParseError* error);
305static bool ParseIceOptions(const std::string& line,
306 std::vector<std::string>* transport_options,
307 SdpParseError* error);
308static bool ParseExtmap(const std::string& line,
309 RtpHeaderExtension* extmap,
310 SdpParseError* error);
311static bool ParseFingerprintAttribute(const std::string& line,
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000312 rtc::SSLFingerprint** fingerprint,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000313 SdpParseError* error);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +0000314static bool ParseDtlsSetup(const std::string& line,
315 cricket::ConnectionRole* role,
316 SdpParseError* error);
deadbeef9d3584c2016-02-16 17:54:10 -0800317static bool ParseMsidAttribute(const std::string& line,
318 std::string* stream_id,
319 std::string* track_id,
320 SdpParseError* error);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000321
322// Helper functions
323
324// Below ParseFailed*** functions output the line that caused the parsing
325// failure and the detailed reason (|description|) of the failure to |error|.
326// The functions always return false so that they can be used directly in the
327// following way when error happens:
328// "return ParseFailed***(...);"
329
330// The line starting at |line_start| of |message| is the failing line.
331// The reason for the failure should be provided in the |description|.
332// An example of a description could be "unknown character".
333static bool ParseFailed(const std::string& message,
334 size_t line_start,
335 const std::string& description,
336 SdpParseError* error) {
337 // Get the first line of |message| from |line_start|.
sergeyu@chromium.org5bc25c42013-12-05 00:24:06 +0000338 std::string first_line;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000339 size_t line_end = message.find(kNewLine, line_start);
340 if (line_end != std::string::npos) {
341 if (line_end > 0 && (message.at(line_end - 1) == kReturn)) {
342 --line_end;
343 }
344 first_line = message.substr(line_start, (line_end - line_start));
sergeyu@chromium.org5bc25c42013-12-05 00:24:06 +0000345 } else {
346 first_line = message.substr(line_start);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000347 }
348
349 if (error) {
350 error->line = first_line;
351 error->description = description;
352 }
353 LOG(LS_ERROR) << "Failed to parse: \"" << first_line
354 << "\". Reason: " << description;
355 return false;
356}
357
358// |line| is the failing line. The reason for the failure should be
359// provided in the |description|.
360static bool ParseFailed(const std::string& line,
361 const std::string& description,
362 SdpParseError* error) {
363 return ParseFailed(line, 0, description, error);
364}
365
366// Parses failure where the failing SDP line isn't know or there are multiple
367// failing lines.
368static bool ParseFailed(const std::string& description,
369 SdpParseError* error) {
370 return ParseFailed("", description, error);
371}
372
373// |line| is the failing line. The failure is due to the fact that |line|
374// doesn't have |expected_fields| fields.
375static bool ParseFailedExpectFieldNum(const std::string& line,
376 int expected_fields,
377 SdpParseError* error) {
378 std::ostringstream description;
379 description << "Expects " << expected_fields << " fields.";
380 return ParseFailed(line, description.str(), error);
381}
382
383// |line| is the failing line. The failure is due to the fact that |line| has
384// less than |expected_min_fields| fields.
385static bool ParseFailedExpectMinFieldNum(const std::string& line,
386 int expected_min_fields,
387 SdpParseError* error) {
388 std::ostringstream description;
389 description << "Expects at least " << expected_min_fields << " fields.";
390 return ParseFailed(line, description.str(), error);
391}
392
393// |line| is the failing line. The failure is due to the fact that it failed to
394// get the value of |attribute|.
395static bool ParseFailedGetValue(const std::string& line,
396 const std::string& attribute,
397 SdpParseError* error) {
398 std::ostringstream description;
399 description << "Failed to get the value of attribute: " << attribute;
400 return ParseFailed(line, description.str(), error);
401}
402
403// The line starting at |line_start| of |message| is the failing line. The
404// failure is due to the line type (e.g. the "m" part of the "m-line")
405// not matching what is expected. The expected line type should be
406// provided as |line_type|.
407static bool ParseFailedExpectLine(const std::string& message,
408 size_t line_start,
409 const char line_type,
410 const std::string& line_value,
411 SdpParseError* error) {
412 std::ostringstream description;
413 description << "Expect line: " << line_type << "=" << line_value;
414 return ParseFailed(message, line_start, description.str(), error);
415}
416
417static bool AddLine(const std::string& line, std::string* message) {
418 if (!message)
419 return false;
420
421 message->append(line);
422 message->append(kLineBreak);
423 return true;
424}
425
426static bool GetLine(const std::string& message,
427 size_t* pos,
428 std::string* line) {
429 size_t line_begin = *pos;
430 size_t line_end = message.find(kNewLine, line_begin);
431 if (line_end == std::string::npos) {
432 return false;
433 }
434 // Update the new start position
435 *pos = line_end + 1;
436 if (line_end > 0 && (message.at(line_end - 1) == kReturn)) {
437 --line_end;
438 }
439 *line = message.substr(line_begin, (line_end - line_begin));
440 const char* cline = line->c_str();
441 // RFC 4566
442 // An SDP session description consists of a number of lines of text of
443 // the form:
444 // <type>=<value>
445 // where <type> MUST be exactly one case-significant character and
446 // <value> is structured text whose format depends on <type>.
447 // Whitespace MUST NOT be used on either side of the "=" sign.
decurtis@webrtc.org8af11042015-01-07 19:15:51 +0000448 if (line->length() < 3 ||
449 !islower(cline[0]) ||
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000450 cline[1] != kSdpDelimiterEqual ||
451 cline[2] == kSdpDelimiterSpace) {
452 *pos = line_begin;
453 return false;
454 }
455 return true;
456}
457
458// Init |os| to "|type|=|value|".
459static void InitLine(const char type,
460 const std::string& value,
461 std::ostringstream* os) {
462 os->str("");
463 *os << type << kSdpDelimiterEqual << value;
464}
465
466// Init |os| to "a=|attribute|".
467static void InitAttrLine(const std::string& attribute, std::ostringstream* os) {
468 InitLine(kLineTypeAttributes, attribute, os);
469}
470
471// Writes a SDP attribute line based on |attribute| and |value| to |message|.
472static void AddAttributeLine(const std::string& attribute, int value,
473 std::string* message) {
474 std::ostringstream os;
475 InitAttrLine(attribute, &os);
476 os << kSdpDelimiterColon << value;
477 AddLine(os.str(), message);
478}
479
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000480static bool IsLineType(const std::string& message,
481 const char type,
482 size_t line_start) {
483 if (message.size() < line_start + kLinePrefixLength) {
484 return false;
485 }
486 const char* cmessage = message.c_str();
487 return (cmessage[line_start] == type &&
488 cmessage[line_start + 1] == kSdpDelimiterEqual);
489}
490
491static bool IsLineType(const std::string& line,
492 const char type) {
493 return IsLineType(line, type, 0);
494}
495
496static bool GetLineWithType(const std::string& message, size_t* pos,
497 std::string* line, const char type) {
498 if (!IsLineType(message, type, *pos)) {
499 return false;
500 }
501
502 if (!GetLine(message, pos, line))
503 return false;
504
505 return true;
506}
507
508static bool HasAttribute(const std::string& line,
509 const std::string& attribute) {
510 return (line.compare(kLinePrefixLength, attribute.size(), attribute) == 0);
511}
512
Peter Boström0c4e06b2015-10-07 12:23:21 +0200513static bool AddSsrcLine(uint32_t ssrc_id,
514 const std::string& attribute,
515 const std::string& value,
516 std::string* message) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000517 // RFC 5576
518 // a=ssrc:<ssrc-id> <attribute>:<value>
519 std::ostringstream os;
520 InitAttrLine(kAttributeSsrc, &os);
521 os << kSdpDelimiterColon << ssrc_id << kSdpDelimiterSpace
522 << attribute << kSdpDelimiterColon << value;
523 return AddLine(os.str(), message);
524}
525
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000526// Get value only from <attribute>:<value>.
527static bool GetValue(const std::string& message, const std::string& attribute,
528 std::string* value, SdpParseError* error) {
529 std::string leftpart;
Donald Curtis0e07f922015-05-15 09:21:23 -0700530 if (!rtc::tokenize_first(message, kSdpDelimiterColon, &leftpart, value)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000531 return ParseFailedGetValue(message, attribute, error);
532 }
533 // The left part should end with the expected attribute.
534 if (leftpart.length() < attribute.length() ||
535 leftpart.compare(leftpart.length() - attribute.length(),
536 attribute.length(), attribute) != 0) {
537 return ParseFailedGetValue(message, attribute, error);
538 }
539 return true;
540}
541
542static bool CaseInsensitiveFind(std::string str1, std::string str2) {
543 std::transform(str1.begin(), str1.end(), str1.begin(),
544 ::tolower);
545 std::transform(str2.begin(), str2.end(), str2.begin(),
546 ::tolower);
547 return str1.find(str2) != std::string::npos;
548}
549
wu@webrtc.org5e760e72014-04-02 23:19:09 +0000550template <class T>
551static bool GetValueFromString(const std::string& line,
552 const std::string& s,
553 T* t,
554 SdpParseError* error) {
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000555 if (!rtc::FromString(s, t)) {
wu@webrtc.org5e760e72014-04-02 23:19:09 +0000556 std::ostringstream description;
557 description << "Invalid value: " << s << ".";
558 return ParseFailed(line, description.str(), error);
559 }
560 return true;
561}
562
pkasting@chromium.orge9facf82015-02-17 20:36:28 +0000563static bool GetPayloadTypeFromString(const std::string& line,
564 const std::string& s,
565 int* payload_type,
566 SdpParseError* error) {
567 return GetValueFromString(line, s, payload_type, error) &&
568 cricket::IsValidRtpPayloadType(*payload_type);
569}
570
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800571// |msid_stream_id| and |msid_track_id| represent the stream/track ID from the
572// "a=msid" attribute, if it exists. They are empty if the attribute does not
573// exist.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000574void CreateTracksFromSsrcInfos(const SsrcInfoVec& ssrc_infos,
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800575 const std::string& msid_stream_id,
576 const std::string& msid_track_id,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000577 StreamParamsVec* tracks) {
578 ASSERT(tracks != NULL);
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800579 ASSERT(msid_stream_id.empty() == msid_track_id.empty());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000580 for (SsrcInfoVec::const_iterator ssrc_info = ssrc_infos.begin();
581 ssrc_info != ssrc_infos.end(); ++ssrc_info) {
582 if (ssrc_info->cname.empty()) {
583 continue;
584 }
585
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800586 std::string stream_id;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000587 std::string track_id;
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800588 if (ssrc_info->stream_id.empty() && !ssrc_info->mslabel.empty()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000589 // If there's no msid and there's mslabel, we consider this is a sdp from
590 // a older version of client that doesn't support msid.
591 // In that case, we use the mslabel and label to construct the track.
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800592 stream_id = ssrc_info->mslabel;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000593 track_id = ssrc_info->label;
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800594 } else if (ssrc_info->stream_id.empty() && !msid_stream_id.empty()) {
595 // If there's no msid in the SSRC attributes, but there's a global one
596 // (from a=msid), use that. This is the case with unified plan SDP.
597 stream_id = msid_stream_id;
598 track_id = msid_track_id;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000599 } else {
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800600 stream_id = ssrc_info->stream_id;
deadbeef9d3584c2016-02-16 17:54:10 -0800601 track_id = ssrc_info->track_id;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000602 }
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800603 // If a stream/track ID wasn't populated from the SSRC attributes OR the
604 // msid attribute, use default/random values.
605 if (stream_id.empty()) {
606 stream_id = kDefaultMsid;
607 }
608 if (track_id.empty()) {
609 // TODO(ronghuawu): What should we do if the track id doesn't appear?
610 // Create random string (which will be used as track label later)?
611 track_id = rtc::CreateRandomString(8);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000612 }
613
614 StreamParamsVec::iterator track = tracks->begin();
615 for (; track != tracks->end(); ++track) {
616 if (track->id == track_id) {
617 break;
618 }
619 }
620 if (track == tracks->end()) {
621 // If we don't find an existing track, create a new one.
622 tracks->push_back(StreamParams());
623 track = tracks->end() - 1;
624 }
625 track->add_ssrc(ssrc_info->ssrc_id);
626 track->cname = ssrc_info->cname;
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800627 track->sync_label = stream_id;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000628 track->id = track_id;
629 }
630}
631
632void GetMediaStreamLabels(const ContentInfo* content,
633 std::set<std::string>* labels) {
634 const MediaContentDescription* media_desc =
henrike@webrtc.org28654cb2013-07-22 21:07:49 +0000635 static_cast<const MediaContentDescription*>(
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000636 content->description);
637 const cricket::StreamParamsVec& streams = media_desc->streams();
638 for (cricket::StreamParamsVec::const_iterator it = streams.begin();
639 it != streams.end(); ++it) {
640 labels->insert(it->sync_label);
641 }
642}
643
644// RFC 5245
645// It is RECOMMENDED that default candidates be chosen based on the
646// likelihood of those candidates to work with the peer that is being
647// contacted. It is RECOMMENDED that relayed > reflexive > host.
648static const int kPreferenceUnknown = 0;
649static const int kPreferenceHost = 1;
650static const int kPreferenceReflexive = 2;
651static const int kPreferenceRelayed = 3;
652
653static int GetCandidatePreferenceFromType(const std::string& type) {
654 int preference = kPreferenceUnknown;
655 if (type == cricket::LOCAL_PORT_TYPE) {
656 preference = kPreferenceHost;
657 } else if (type == cricket::STUN_PORT_TYPE) {
658 preference = kPreferenceReflexive;
659 } else if (type == cricket::RELAY_PORT_TYPE) {
660 preference = kPreferenceRelayed;
661 } else {
662 ASSERT(false);
663 }
664 return preference;
665}
666
guoweis@webrtc.org57ac2c82015-02-06 00:45:13 +0000667// Get ip and port of the default destination from the |candidates| with the
668// given value of |component_id|. The default candidate should be the one most
669// likely to work, typically IPv4 relay.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000670// RFC 5245
671// The value of |component_id| currently supported are 1 (RTP) and 2 (RTCP).
672// TODO: Decide the default destination in webrtcsession and
673// pass it down via SessionDescription.
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000674static void GetDefaultDestination(
675 const std::vector<Candidate>& candidates,
676 int component_id, std::string* port,
677 std::string* ip, std::string* addr_type) {
perkj@webrtc.orgd105cc82014-11-07 11:22:06 +0000678 *addr_type = kConnectionIpv4Addrtype;
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000679 *port = kDummyPort;
680 *ip = kDummyAddress;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000681 int current_preference = kPreferenceUnknown;
guoweis@webrtc.org57ac2c82015-02-06 00:45:13 +0000682 int current_family = AF_UNSPEC;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000683 for (std::vector<Candidate>::const_iterator it = candidates.begin();
684 it != candidates.end(); ++it) {
685 if (it->component() != component_id) {
686 continue;
687 }
guoweis@webrtc.org57ac2c82015-02-06 00:45:13 +0000688 // Default destination should be UDP only.
689 if (it->protocol() != cricket::UDP_PROTOCOL_NAME) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000690 continue;
691 }
guoweis@webrtc.org57ac2c82015-02-06 00:45:13 +0000692 const int preference = GetCandidatePreferenceFromType(it->type());
693 const int family = it->address().ipaddr().family();
694 // See if this candidate is more preferable then the current one if it's the
695 // same family. Or if the current family is IPv4 already so we could safely
696 // ignore all IPv6 ones. WebRTC bug 4269.
697 // http://code.google.com/p/webrtc/issues/detail?id=4269
698 if ((preference <= current_preference && current_family == family) ||
699 (current_family == AF_INET && family == AF_INET6)) {
700 continue;
701 }
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000702 if (family == AF_INET) {
703 addr_type->assign(kConnectionIpv4Addrtype);
704 } else if (family == AF_INET6) {
705 addr_type->assign(kConnectionIpv6Addrtype);
706 }
guoweis@webrtc.org57ac2c82015-02-06 00:45:13 +0000707 current_preference = preference;
708 current_family = family;
709 *port = it->address().PortAsString();
710 *ip = it->address().ipaddr().ToString();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000711 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000712}
713
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000714// Update |mline|'s default destination and append a c line after it.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000715static void UpdateMediaDefaultDestination(
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000716 const std::vector<Candidate>& candidates,
jbauch083b73f2015-07-16 02:46:32 -0700717 const std::string& mline,
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000718 std::string* message) {
719 std::string new_lines;
720 AddLine(mline, &new_lines);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000721 // RFC 4566
722 // m=<media> <port> <proto> <fmt> ...
723 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000724 rtc::split(mline, kSdpDelimiterSpace, &fields);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000725 if (fields.size() < 3) {
726 return;
727 }
728
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000729 std::ostringstream os;
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000730 std::string rtp_port, rtp_ip, addr_type;
731 GetDefaultDestination(candidates, ICE_CANDIDATE_COMPONENT_RTP,
732 &rtp_port, &rtp_ip, &addr_type);
733 // Found default RTP candidate.
734 // RFC 5245
735 // The default candidates are added to the SDP as the default
736 // destination for media. For streams based on RTP, this is done by
737 // placing the IP address and port of the RTP candidate into the c and m
738 // lines, respectively.
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) {
742 new_lines.replace(fields[0].size() + 1,
743 fields[1].size(),
744 rtp_port);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000745 }
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000746 // Add the c line.
747 // RFC 4566
748 // c=<nettype> <addrtype> <connection-address>
749 InitLine(kLineTypeConnection, kConnectionNettype, &os);
750 os << " " << addr_type << " " << rtp_ip;
751 AddLine(os.str(), &new_lines);
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000752 message->append(new_lines);
753}
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000754
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000755// Gets "a=rtcp" line if found default RTCP candidate from |candidates|.
756static std::string GetRtcpLine(const std::vector<Candidate>& candidates) {
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000757 std::string rtcp_line, rtcp_port, rtcp_ip, addr_type;
758 GetDefaultDestination(candidates, ICE_CANDIDATE_COMPONENT_RTCP,
759 &rtcp_port, &rtcp_ip, &addr_type);
760 // Found default RTCP candidate.
761 // RFC 5245
762 // If the agent is utilizing RTCP, it MUST encode the RTCP candidate
763 // using the a=rtcp attribute as defined in RFC 3605.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000764
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000765 // RFC 3605
766 // rtcp-attribute = "a=rtcp:" port [nettype space addrtype space
767 // connection-address] CRLF
768 std::ostringstream os;
769 InitAttrLine(kAttributeRtcp, &os);
770 os << kSdpDelimiterColon
771 << rtcp_port << " "
772 << kConnectionNettype << " "
773 << addr_type << " "
774 << rtcp_ip;
775 rtcp_line = os.str();
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000776 return rtcp_line;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000777}
778
779// Get candidates according to the mline index from SessionDescriptionInterface.
780static void GetCandidatesByMindex(const SessionDescriptionInterface& desci,
781 int mline_index,
782 std::vector<Candidate>* candidates) {
783 if (!candidates) {
784 return;
785 }
786 const IceCandidateCollection* cc = desci.candidates(mline_index);
787 for (size_t i = 0; i < cc->count(); ++i) {
788 const IceCandidateInterface* candidate = cc->at(i);
789 candidates->push_back(candidate->candidate());
790 }
791}
792
deadbeef9d3584c2016-02-16 17:54:10 -0800793std::string SdpSerialize(const JsepSessionDescription& jdesc,
794 bool unified_plan_sdp) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000795 const cricket::SessionDescription* desc = jdesc.description();
796 if (!desc) {
797 return "";
798 }
799
800 std::string message;
801
802 // Session Description.
803 AddLine(kSessionVersion, &message);
804 // Session Origin
805 // RFC 4566
806 // o=<username> <sess-id> <sess-version> <nettype> <addrtype>
807 // <unicast-address>
808 std::ostringstream os;
809 InitLine(kLineTypeOrigin, kSessionOriginUsername, &os);
jbauch083b73f2015-07-16 02:46:32 -0700810 const std::string& session_id = jdesc.session_id().empty() ?
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000811 kSessionOriginSessionId : jdesc.session_id();
jbauch083b73f2015-07-16 02:46:32 -0700812 const std::string& session_version = jdesc.session_version().empty() ?
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000813 kSessionOriginSessionVersion : jdesc.session_version();
814 os << " " << session_id << " " << session_version << " "
815 << kSessionOriginNettype << " " << kSessionOriginAddrtype << " "
816 << kSessionOriginAddress;
817 AddLine(os.str(), &message);
818 AddLine(kSessionName, &message);
819
820 // Time Description.
821 AddLine(kTimeDescription, &message);
822
823 // Group
824 if (desc->HasGroup(cricket::GROUP_TYPE_BUNDLE)) {
825 std::string group_line = kAttrGroup;
826 const cricket::ContentGroup* group =
827 desc->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
828 ASSERT(group != NULL);
829 const cricket::ContentNames& content_names = group->content_names();
830 for (cricket::ContentNames::const_iterator it = content_names.begin();
831 it != content_names.end(); ++it) {
832 group_line.append(" ");
833 group_line.append(*it);
834 }
835 AddLine(group_line, &message);
836 }
837
838 // MediaStream semantics
839 InitAttrLine(kAttributeMsidSemantics, &os);
840 os << kSdpDelimiterColon << " " << kMediaStreamSemantic;
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000841
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000842 std::set<std::string> media_stream_labels;
843 const ContentInfo* audio_content = GetFirstAudioContent(desc);
844 if (audio_content)
845 GetMediaStreamLabels(audio_content, &media_stream_labels);
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000846
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000847 const ContentInfo* video_content = GetFirstVideoContent(desc);
848 if (video_content)
849 GetMediaStreamLabels(video_content, &media_stream_labels);
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000850
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000851 for (std::set<std::string>::const_iterator it =
852 media_stream_labels.begin(); it != media_stream_labels.end(); ++it) {
853 os << " " << *it;
854 }
855 AddLine(os.str(), &message);
856
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000857 // Preserve the order of the media contents.
858 int mline_index = -1;
859 for (cricket::ContentInfos::const_iterator it = desc->contents().begin();
860 it != desc->contents().end(); ++it) {
861 const MediaContentDescription* mdesc =
862 static_cast<const MediaContentDescription*>(it->description);
863 std::vector<Candidate> candidates;
864 GetCandidatesByMindex(jdesc, ++mline_index, &candidates);
deadbeef9d3584c2016-02-16 17:54:10 -0800865 BuildMediaDescription(&*it, desc->GetTransportInfoByName(it->name),
866 mdesc->type(), candidates, unified_plan_sdp,
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000867 &message);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000868 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000869 return message;
870}
871
872// Serializes the passed in IceCandidateInterface to a SDP string.
873// candidate - The candidate to be serialized.
Honghai Zhang7fb69db2016-03-14 11:59:18 -0700874std::string SdpSerializeCandidate(const IceCandidateInterface& candidate) {
875 return SdpSerializeCandidate(candidate.candidate());
876}
877
878// Serializes a cricket Candidate.
879std::string SdpSerializeCandidate(const cricket::Candidate& candidate) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000880 std::string message;
Honghai Zhang7fb69db2016-03-14 11:59:18 -0700881 std::vector<cricket::Candidate> candidates(1, candidate);
honghaiza54a0802015-12-16 18:37:23 -0800882 BuildCandidate(candidates, true, &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;
Peter Thatcher7cbd1882015-09-17 18:54:52 -0700897 TransportDescription session_td("", "");
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000898 RtpHeaderExtensions session_extmaps;
899 cricket::SessionDescription* desc = new cricket::SessionDescription();
900 std::vector<JsepIceCandidate*> candidates;
901 size_t current_pos = 0;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000902
903 // Session Description
904 if (!ParseSessionDescription(message, &current_pos, &session_id,
deadbeefc80741f2015-10-22 13:14:45 -0700905 &session_version, &session_td, &session_extmaps,
906 desc, error)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000907 delete desc;
908 return false;
909 }
910
911 // Media Description
deadbeefc80741f2015-10-22 13:14:45 -0700912 if (!ParseMediaDescription(message, session_td, session_extmaps, &current_pos,
913 desc, &candidates, error)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000914 delete desc;
915 for (std::vector<JsepIceCandidate*>::const_iterator
916 it = candidates.begin(); it != candidates.end(); ++it) {
917 delete *it;
918 }
919 return false;
920 }
921
922 jdesc->Initialize(desc, session_id, session_version);
923
924 for (std::vector<JsepIceCandidate*>::const_iterator
925 it = candidates.begin(); it != candidates.end(); ++it) {
926 jdesc->AddCandidate(*it);
927 delete *it;
928 }
929 return true;
930}
931
932bool SdpDeserializeCandidate(const std::string& message,
933 JsepIceCandidate* jcandidate,
934 SdpParseError* error) {
935 ASSERT(jcandidate != NULL);
936 Candidate candidate;
937 if (!ParseCandidate(message, &candidate, error, true)) {
938 return false;
939 }
940 jcandidate->SetCandidate(candidate);
941 return true;
942}
943
Honghai Zhang7fb69db2016-03-14 11:59:18 -0700944bool SdpDeserializeCandidate(const std::string& transport_name,
945 const std::string& message,
946 cricket::Candidate* candidate,
947 SdpParseError* error) {
948 ASSERT(candidate != nullptr);
949 if (!ParseCandidate(message, candidate, error, true)) {
950 return false;
951 }
952 candidate->set_transport_name(transport_name);
953 return true;
954}
955
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000956bool ParseCandidate(const std::string& message, Candidate* candidate,
957 SdpParseError* error, bool is_raw) {
958 ASSERT(candidate != NULL);
959
960 // Get the first line from |message|.
jiayl@webrtc.org7ec3f9f2014-08-08 23:09:15 +0000961 std::string first_line = message;
962 size_t pos = 0;
963 GetLine(message, &pos, &first_line);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000964
jiayl@webrtc.org7ec3f9f2014-08-08 23:09:15 +0000965 // Makes sure |message| contains only one line.
966 if (message.size() > first_line.size()) {
967 std::string left, right;
Donald Curtis0e07f922015-05-15 09:21:23 -0700968 if (rtc::tokenize_first(message, kNewLine, &left, &right) &&
969 !right.empty()) {
jiayl@webrtc.org7ec3f9f2014-08-08 23:09:15 +0000970 return ParseFailed(message, 0, "Expect one line only", error);
971 }
972 }
973
974 // From WebRTC draft section 4.8.1.1 candidate-attribute should be
975 // candidate:<candidate> when trickled, but we still support
976 // a=candidate:<blah>CRLF for backward compatibility and for parsing a line
977 // from the SDP.
978 if (IsLineType(first_line, kLineTypeAttributes)) {
979 first_line = first_line.substr(kLinePrefixLength);
980 }
981
982 std::string attribute_candidate;
983 std::string candidate_value;
984
985 // |first_line| must be in the form of "candidate:<value>".
Donald Curtis144d0182015-05-15 13:14:24 -0700986 if (!rtc::tokenize_first(first_line, kSdpDelimiterColon, &attribute_candidate,
987 &candidate_value) ||
jiayl@webrtc.org7ec3f9f2014-08-08 23:09:15 +0000988 attribute_candidate != kAttributeCandidate) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000989 if (is_raw) {
990 std::ostringstream description;
jiayl@webrtc.org7ec3f9f2014-08-08 23:09:15 +0000991 description << "Expect line: " << kAttributeCandidate
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000992 << ":" << "<candidate-str>";
993 return ParseFailed(first_line, 0, description.str(), error);
994 } else {
995 return ParseFailedExpectLine(first_line, 0, kLineTypeAttributes,
996 kAttributeCandidate, error);
997 }
998 }
999
1000 std::vector<std::string> fields;
jiayl@webrtc.org7ec3f9f2014-08-08 23:09:15 +00001001 rtc::split(candidate_value, kSdpDelimiterSpace, &fields);
1002
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001003 // RFC 5245
1004 // a=candidate:<foundation> <component-id> <transport> <priority>
1005 // <connection-address> <port> typ <candidate-types>
1006 // [raddr <connection-address>] [rport <port>]
1007 // *(SP extension-att-name SP extension-att-value)
1008 const size_t expected_min_fields = 8;
1009 if (fields.size() < expected_min_fields ||
1010 (fields[6] != kAttributeCandidateTyp)) {
1011 return ParseFailedExpectMinFieldNum(first_line, expected_min_fields, error);
1012 }
jbauch083b73f2015-07-16 02:46:32 -07001013 const std::string& foundation = fields[0];
jiayl@webrtc.org7ec3f9f2014-08-08 23:09:15 +00001014
wu@webrtc.org5e760e72014-04-02 23:19:09 +00001015 int component_id = 0;
1016 if (!GetValueFromString(first_line, fields[1], &component_id, error)) {
1017 return false;
1018 }
jbauch083b73f2015-07-16 02:46:32 -07001019 const std::string& transport = fields[2];
Peter Boström0c4e06b2015-10-07 12:23:21 +02001020 uint32_t priority = 0;
wu@webrtc.org5e760e72014-04-02 23:19:09 +00001021 if (!GetValueFromString(first_line, fields[3], &priority, error)) {
1022 return false;
1023 }
jbauch083b73f2015-07-16 02:46:32 -07001024 const std::string& connection_address = fields[4];
wu@webrtc.org5e760e72014-04-02 23:19:09 +00001025 int port = 0;
1026 if (!GetValueFromString(first_line, fields[5], &port, error)) {
1027 return false;
1028 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001029 SocketAddress address(connection_address, port);
1030
1031 cricket::ProtocolType protocol;
1032 if (!StringToProto(transport.c_str(), &protocol)) {
1033 return ParseFailed(first_line, "Unsupported transport type.", error);
1034 }
1035
1036 std::string candidate_type;
jbauch083b73f2015-07-16 02:46:32 -07001037 const std::string& type = fields[7];
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001038 if (type == kCandidateHost) {
1039 candidate_type = cricket::LOCAL_PORT_TYPE;
1040 } else if (type == kCandidateSrflx) {
1041 candidate_type = cricket::STUN_PORT_TYPE;
1042 } else if (type == kCandidateRelay) {
1043 candidate_type = cricket::RELAY_PORT_TYPE;
Honghai Zhang7fb69db2016-03-14 11:59:18 -07001044 } else if (type == kCandidatePrflx) {
1045 candidate_type = cricket::PRFLX_PORT_TYPE;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001046 } else {
1047 return ParseFailed(first_line, "Unsupported candidate type.", error);
1048 }
1049
1050 size_t current_position = expected_min_fields;
1051 SocketAddress related_address;
1052 // The 2 optional fields for related address
1053 // [raddr <connection-address>] [rport <port>]
1054 if (fields.size() >= (current_position + 2) &&
1055 fields[current_position] == kAttributeCandidateRaddr) {
1056 related_address.SetIP(fields[++current_position]);
1057 ++current_position;
1058 }
1059 if (fields.size() >= (current_position + 2) &&
1060 fields[current_position] == kAttributeCandidateRport) {
wu@webrtc.org5e760e72014-04-02 23:19:09 +00001061 int port = 0;
1062 if (!GetValueFromString(
1063 first_line, fields[++current_position], &port, error)) {
1064 return false;
1065 }
1066 related_address.SetPort(port);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001067 ++current_position;
1068 }
1069
mallinath@webrtc.org2d60c5e2014-08-08 22:29:20 +00001070 // If this is a TCP candidate, it has additional extension as defined in
1071 // RFC 6544.
1072 std::string tcptype;
1073 if (fields.size() >= (current_position + 2) &&
1074 fields[current_position] == kTcpCandidateType) {
1075 tcptype = fields[++current_position];
1076 ++current_position;
1077
1078 if (tcptype != cricket::TCPTYPE_ACTIVE_STR &&
1079 tcptype != cricket::TCPTYPE_PASSIVE_STR &&
1080 tcptype != cricket::TCPTYPE_SIMOPEN_STR) {
1081 return ParseFailed(first_line, "Invalid TCP candidate type.", error);
1082 }
1083
1084 if (protocol != cricket::PROTO_TCP) {
1085 return ParseFailed(first_line, "Invalid non-TCP candidate", error);
1086 }
1087 }
1088
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001089 // Extension
honghaiza54a0802015-12-16 18:37:23 -08001090 // Though non-standard, we support the ICE ufrag and pwd being signaled on
1091 // the candidate to avoid issues with confusing which generation a candidate
1092 // belongs to when trickling multiple generations at the same time.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001093 std::string username;
1094 std::string password;
Peter Boström0c4e06b2015-10-07 12:23:21 +02001095 uint32_t generation = 0;
honghaiza0c44ea2016-03-23 16:07:48 -07001096 uint16_t network_id = 0;
1097 uint16_t network_cost = 0;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001098 for (size_t i = current_position; i + 1 < fields.size(); ++i) {
1099 // RFC 5245
1100 // *(SP extension-att-name SP extension-att-value)
1101 if (fields[i] == kAttributeCandidateGeneration) {
wu@webrtc.org5e760e72014-04-02 23:19:09 +00001102 if (!GetValueFromString(first_line, fields[++i], &generation, error)) {
1103 return false;
1104 }
honghaiza54a0802015-12-16 18:37:23 -08001105 } else if (fields[i] == kAttributeCandidateUfrag) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001106 username = fields[++i];
honghaiza54a0802015-12-16 18:37:23 -08001107 } else if (fields[i] == kAttributeCandidatePwd) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001108 password = fields[++i];
honghaiza0c44ea2016-03-23 16:07:48 -07001109 } else if (fields[i] == kAttributeCandidateNetworkId) {
1110 if (!GetValueFromString(first_line, fields[++i], &network_id, error)) {
1111 return false;
1112 }
honghaize1a0c942016-02-16 14:54:56 -08001113 } else if (fields[i] == kAttributeCandidateNetworkCost) {
1114 if (!GetValueFromString(first_line, fields[++i], &network_cost, error)) {
1115 return false;
1116 }
honghaiza0c44ea2016-03-23 16:07:48 -07001117 network_cost = std::min(network_cost, cricket::kMaxNetworkCost);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001118 } else {
1119 // Skip the unknown extension.
1120 ++i;
1121 }
1122 }
1123
guoweis@webrtc.org61c12472015-01-15 06:53:07 +00001124 *candidate = Candidate(component_id, cricket::ProtoToString(protocol),
guoweis@webrtc.org950c5182014-12-16 23:01:31 +00001125 address, priority, username, password, candidate_type,
honghaiza0c44ea2016-03-23 16:07:48 -07001126 generation, foundation, network_id, network_cost);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001127 candidate->set_related_address(related_address);
mallinath@webrtc.org2d60c5e2014-08-08 22:29:20 +00001128 candidate->set_tcptype(tcptype);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001129 return true;
1130}
1131
1132bool ParseIceOptions(const std::string& line,
1133 std::vector<std::string>* transport_options,
1134 SdpParseError* error) {
1135 std::string ice_options;
1136 if (!GetValue(line, kAttributeIceOption, &ice_options, error)) {
1137 return false;
1138 }
1139 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001140 rtc::split(ice_options, kSdpDelimiterSpace, &fields);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001141 for (size_t i = 0; i < fields.size(); ++i) {
1142 transport_options->push_back(fields[i]);
1143 }
1144 return true;
1145}
1146
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00001147bool ParseSctpPort(const std::string& line,
1148 int* sctp_port,
1149 SdpParseError* error) {
1150 // draft-ietf-mmusic-sctp-sdp-07
1151 // a=sctp-port
1152 std::vector<std::string> fields;
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00001153 const size_t expected_min_fields = 2;
lally69f57602015-10-08 10:15:04 -07001154 rtc::split(line.substr(kLinePrefixLength), kSdpDelimiterColon, &fields);
1155 if (fields.size() < expected_min_fields) {
1156 fields.resize(0);
1157 rtc::split(line.substr(kLinePrefixLength), kSdpDelimiterSpace, &fields);
1158 }
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00001159 if (fields.size() < expected_min_fields) {
1160 return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
1161 }
1162 if (!rtc::FromString(fields[1], sctp_port)) {
lally69f57602015-10-08 10:15:04 -07001163 return ParseFailed(line, "Invalid sctp port value.", error);
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00001164 }
1165 return true;
1166}
1167
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001168bool ParseExtmap(const std::string& line, RtpHeaderExtension* extmap,
1169 SdpParseError* error) {
1170 // RFC 5285
1171 // a=extmap:<value>["/"<direction>] <URI> <extensionattributes>
1172 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001173 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001174 kSdpDelimiterSpace, &fields);
1175 const size_t expected_min_fields = 2;
1176 if (fields.size() < expected_min_fields) {
1177 return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
1178 }
1179 std::string uri = fields[1];
1180
1181 std::string value_direction;
1182 if (!GetValue(fields[0], kAttributeExtmap, &value_direction, error)) {
1183 return false;
1184 }
1185 std::vector<std::string> sub_fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001186 rtc::split(value_direction, kSdpDelimiterSlash, &sub_fields);
wu@webrtc.org5e760e72014-04-02 23:19:09 +00001187 int value = 0;
1188 if (!GetValueFromString(line, sub_fields[0], &value, error)) {
1189 return false;
1190 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001191
1192 *extmap = RtpHeaderExtension(uri, value);
1193 return true;
1194}
1195
1196void BuildMediaDescription(const ContentInfo* content_info,
1197 const TransportInfo* transport_info,
1198 const MediaType media_type,
wu@webrtc.org4c3e9912014-07-16 21:03:13 +00001199 const std::vector<Candidate>& candidates,
deadbeef9d3584c2016-02-16 17:54:10 -08001200 bool unified_plan_sdp,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001201 std::string* message) {
1202 ASSERT(message != NULL);
1203 if (content_info == NULL || message == NULL) {
1204 return;
1205 }
1206 // TODO: Rethink if we should use sprintfn instead of stringstream.
1207 // According to the style guide, streams should only be used for logging.
1208 // http://google-styleguide.googlecode.com/svn/
1209 // trunk/cppguide.xml?showone=Streams#Streams
1210 std::ostringstream os;
1211 const MediaContentDescription* media_desc =
henrike@webrtc.org28654cb2013-07-22 21:07:49 +00001212 static_cast<const MediaContentDescription*>(
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001213 content_info->description);
1214 ASSERT(media_desc != NULL);
1215
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +00001216 int sctp_port = cricket::kSctpDefaultPort;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001217
1218 // RFC 4566
1219 // m=<media> <port> <proto> <fmt>
1220 // fmt is a list of payload type numbers that MAY be used in the session.
1221 const char* type = NULL;
1222 if (media_type == cricket::MEDIA_TYPE_AUDIO)
1223 type = kMediaTypeAudio;
1224 else if (media_type == cricket::MEDIA_TYPE_VIDEO)
1225 type = kMediaTypeVideo;
1226 else if (media_type == cricket::MEDIA_TYPE_DATA)
1227 type = kMediaTypeData;
1228 else
1229 ASSERT(false);
1230
1231 std::string fmt;
1232 if (media_type == cricket::MEDIA_TYPE_VIDEO) {
1233 const VideoContentDescription* video_desc =
1234 static_cast<const VideoContentDescription*>(media_desc);
1235 for (std::vector<cricket::VideoCodec>::const_iterator it =
1236 video_desc->codecs().begin();
1237 it != video_desc->codecs().end(); ++it) {
1238 fmt.append(" ");
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001239 fmt.append(rtc::ToString<int>(it->id));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001240 }
1241 } else if (media_type == cricket::MEDIA_TYPE_AUDIO) {
1242 const AudioContentDescription* audio_desc =
1243 static_cast<const AudioContentDescription*>(media_desc);
1244 for (std::vector<cricket::AudioCodec>::const_iterator it =
1245 audio_desc->codecs().begin();
1246 it != audio_desc->codecs().end(); ++it) {
1247 fmt.append(" ");
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001248 fmt.append(rtc::ToString<int>(it->id));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001249 }
1250 } else if (media_type == cricket::MEDIA_TYPE_DATA) {
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +00001251 const DataContentDescription* data_desc =
1252 static_cast<const DataContentDescription*>(media_desc);
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00001253 if (IsDtlsSctp(media_desc->protocol())) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001254 fmt.append(" ");
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +00001255
1256 for (std::vector<cricket::DataCodec>::const_iterator it =
1257 data_desc->codecs().begin();
1258 it != data_desc->codecs().end(); ++it) {
1259 if (it->id == cricket::kGoogleSctpDataCodecId &&
1260 it->GetParam(cricket::kCodecParamPort, &sctp_port)) {
1261 break;
1262 }
1263 }
1264
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001265 fmt.append(rtc::ToString<int>(sctp_port));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001266 } else {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001267 for (std::vector<cricket::DataCodec>::const_iterator it =
1268 data_desc->codecs().begin();
1269 it != data_desc->codecs().end(); ++it) {
1270 fmt.append(" ");
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001271 fmt.append(rtc::ToString<int>(it->id));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001272 }
1273 }
1274 }
1275 // The fmt must never be empty. If no codecs are found, set the fmt attribute
1276 // to 0.
1277 if (fmt.empty()) {
1278 fmt = " 0";
1279 }
1280
1281 // The port number in the m line will be updated later when associate with
1282 // the candidates.
1283 // RFC 3264
1284 // To reject an offered stream, the port number in the corresponding stream in
1285 // the answer MUST be set to zero.
jbauch083b73f2015-07-16 02:46:32 -07001286 const std::string& port = content_info->rejected ?
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +00001287 kMediaPortRejected : kDummyPort;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001288
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001289 rtc::SSLFingerprint* fp = (transport_info) ?
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001290 transport_info->description.identity_fingerprint.get() : NULL;
1291
wu@webrtc.org4c3e9912014-07-16 21:03:13 +00001292 // Add the m and c lines.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001293 InitLine(kLineTypeMedia, type, &os);
1294 os << " " << port << " " << media_desc->protocol() << fmt;
wu@webrtc.org4c3e9912014-07-16 21:03:13 +00001295 std::string mline = os.str();
1296 UpdateMediaDefaultDestination(candidates, mline, message);
1297
1298 // RFC 4566
1299 // b=AS:<bandwidth>
Peter Thatcherc0c3a862015-06-24 15:31:25 -07001300 if (media_desc->bandwidth() >= 1000) {
wu@webrtc.org4c3e9912014-07-16 21:03:13 +00001301 InitLine(kLineTypeSessionBandwidth, kApplicationSpecificMaximum, &os);
1302 os << kSdpDelimiterColon << (media_desc->bandwidth() / 1000);
1303 AddLine(os.str(), message);
1304 }
1305
1306 // Add the a=rtcp line.
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00001307 if (IsRtp(media_desc->protocol())) {
wu@webrtc.org4c3e9912014-07-16 21:03:13 +00001308 std::string rtcp_line = GetRtcpLine(candidates);
1309 if (!rtcp_line.empty()) {
1310 AddLine(rtcp_line, message);
1311 }
1312 }
1313
honghaiza54a0802015-12-16 18:37:23 -08001314 // Build the a=candidate lines. We don't include ufrag and pwd in the
1315 // candidates in the SDP to avoid redundancy.
1316 BuildCandidate(candidates, false, message);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001317
1318 // Use the transport_info to build the media level ice-ufrag and ice-pwd.
1319 if (transport_info) {
1320 // RFC 5245
1321 // ice-pwd-att = "ice-pwd" ":" password
1322 // ice-ufrag-att = "ice-ufrag" ":" ufrag
1323 // ice-ufrag
deadbeef3f7219b2015-12-28 15:17:14 -08001324 if (!transport_info->description.ice_ufrag.empty()) {
1325 InitAttrLine(kAttributeIceUfrag, &os);
1326 os << kSdpDelimiterColon << transport_info->description.ice_ufrag;
1327 AddLine(os.str(), message);
1328 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001329 // ice-pwd
deadbeef3f7219b2015-12-28 15:17:14 -08001330 if (!transport_info->description.ice_pwd.empty()) {
1331 InitAttrLine(kAttributeIcePwd, &os);
1332 os << kSdpDelimiterColon << transport_info->description.ice_pwd;
1333 AddLine(os.str(), message);
1334 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001335
1336 // draft-petithuguenin-mmusic-ice-attributes-level-03
1337 BuildIceOptions(transport_info->description.transport_options, message);
1338
1339 // RFC 4572
1340 // fingerprint-attribute =
1341 // "fingerprint" ":" hash-func SP fingerprint
1342 if (fp) {
1343 // Insert the fingerprint attribute.
1344 InitAttrLine(kAttributeFingerprint, &os);
1345 os << kSdpDelimiterColon
1346 << fp->algorithm << kSdpDelimiterSpace
1347 << fp->GetRfc4572Fingerprint();
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001348 AddLine(os.str(), message);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001349
1350 // Inserting setup attribute.
1351 if (transport_info->description.connection_role !=
1352 cricket::CONNECTIONROLE_NONE) {
1353 // Making sure we are not using "passive" mode.
1354 cricket::ConnectionRole role =
1355 transport_info->description.connection_role;
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +00001356 std::string dtls_role_str;
1357 VERIFY(cricket::ConnectionRoleToString(role, &dtls_role_str));
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001358 InitAttrLine(kAttributeSetup, &os);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001359 os << kSdpDelimiterColon << dtls_role_str;
1360 AddLine(os.str(), message);
1361 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001362 }
1363 }
1364
1365 // RFC 3388
1366 // mid-attribute = "a=mid:" identification-tag
1367 // identification-tag = token
1368 // Use the content name as the mid identification-tag.
1369 InitAttrLine(kAttributeMid, &os);
1370 os << kSdpDelimiterColon << content_info->name;
1371 AddLine(os.str(), message);
1372
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00001373 if (IsDtlsSctp(media_desc->protocol())) {
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +00001374 BuildSctpContentAttributes(message, sctp_port);
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00001375 } else if (IsRtp(media_desc->protocol())) {
deadbeef9d3584c2016-02-16 17:54:10 -08001376 BuildRtpContentAttributes(media_desc, media_type, unified_plan_sdp,
1377 message);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001378 }
1379}
1380
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +00001381void BuildSctpContentAttributes(std::string* message, int sctp_port) {
wu@webrtc.org78187522013-10-07 23:32:02 +00001382 // draft-ietf-mmusic-sctp-sdp-04
1383 // a=sctpmap:sctpmap-number protocol [streams]
lally69f57602015-10-08 10:15:04 -07001384 // TODO(lally): switch this over to mmusic-sctp-sdp-12 (or later), with
1385 // 'a=sctp-port:'
wu@webrtc.org78187522013-10-07 23:32:02 +00001386 std::ostringstream os;
1387 InitAttrLine(kAttributeSctpmap, &os);
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +00001388 os << kSdpDelimiterColon << sctp_port << kSdpDelimiterSpace
wu@webrtc.org78187522013-10-07 23:32:02 +00001389 << kDefaultSctpmapProtocol << kSdpDelimiterSpace
1390 << (cricket::kMaxSctpSid + 1);
1391 AddLine(os.str(), message);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001392}
1393
deadbeef9d3584c2016-02-16 17:54:10 -08001394// If unified_plan_sdp is true, will use "a=msid".
1395void BuildRtpContentAttributes(const MediaContentDescription* media_desc,
1396 const MediaType media_type,
1397 bool unified_plan_sdp,
1398 std::string* message) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001399 std::ostringstream os;
1400 // RFC 5285
1401 // a=extmap:<value>["/"<direction>] <URI> <extensionattributes>
1402 // The definitions MUST be either all session level or all media level. This
1403 // implementation uses all media level.
1404 for (size_t i = 0; i < media_desc->rtp_header_extensions().size(); ++i) {
1405 InitAttrLine(kAttributeExtmap, &os);
1406 os << kSdpDelimiterColon << media_desc->rtp_header_extensions()[i].id
1407 << kSdpDelimiterSpace << media_desc->rtp_header_extensions()[i].uri;
1408 AddLine(os.str(), message);
1409 }
1410
1411 // RFC 3264
1412 // a=sendrecv || a=sendonly || a=sendrecv || a=inactive
deadbeefc80741f2015-10-22 13:14:45 -07001413 switch (media_desc->direction()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001414 case cricket::MD_INACTIVE:
1415 InitAttrLine(kAttributeInactive, &os);
1416 break;
1417 case cricket::MD_SENDONLY:
1418 InitAttrLine(kAttributeSendOnly, &os);
1419 break;
1420 case cricket::MD_RECVONLY:
1421 InitAttrLine(kAttributeRecvOnly, &os);
1422 break;
1423 case cricket::MD_SENDRECV:
1424 default:
1425 InitAttrLine(kAttributeSendRecv, &os);
1426 break;
1427 }
1428 AddLine(os.str(), message);
1429
deadbeef9d3584c2016-02-16 17:54:10 -08001430 // draft-ietf-mmusic-msid-11
1431 // a=msid:<stream id> <track id>
1432 if (unified_plan_sdp && !media_desc->streams().empty()) {
1433 if (media_desc->streams().size() > 1u) {
1434 LOG(LS_WARNING) << "Trying to serialize unified plan SDP with more than "
1435 << "one track in a media section. Omitting 'a=msid'.";
1436 } else {
1437 auto track = media_desc->streams().begin();
1438 const std::string& stream_id = track->sync_label;
deadbeef9d3584c2016-02-16 17:54:10 -08001439 InitAttrLine(kAttributeMsid, &os);
1440 os << kSdpDelimiterColon << stream_id << kSdpDelimiterSpace << track->id;
1441 AddLine(os.str(), message);
1442 }
1443 }
1444
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001445 // RFC 5761
1446 // a=rtcp-mux
1447 if (media_desc->rtcp_mux()) {
1448 InitAttrLine(kAttributeRtcpMux, &os);
1449 AddLine(os.str(), message);
1450 }
1451
deadbeef13871492015-12-09 12:37:51 -08001452 // RFC 5506
1453 // a=rtcp-rsize
1454 if (media_desc->rtcp_reduced_size()) {
1455 InitAttrLine(kAttributeRtcpReducedSize, &os);
1456 AddLine(os.str(), message);
1457 }
1458
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001459 // RFC 4568
1460 // a=crypto:<tag> <crypto-suite> <key-params> [<session-params>]
1461 for (std::vector<CryptoParams>::const_iterator it =
1462 media_desc->cryptos().begin();
1463 it != media_desc->cryptos().end(); ++it) {
1464 InitAttrLine(kAttributeCrypto, &os);
1465 os << kSdpDelimiterColon << it->tag << " " << it->cipher_suite << " "
1466 << it->key_params;
1467 if (!it->session_params.empty()) {
1468 os << " " << it->session_params;
1469 }
1470 AddLine(os.str(), message);
1471 }
1472
1473 // RFC 4566
1474 // a=rtpmap:<payload type> <encoding name>/<clock rate>
1475 // [/<encodingparameters>]
1476 BuildRtpMap(media_desc, media_type, message);
1477
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001478 for (StreamParamsVec::const_iterator track = media_desc->streams().begin();
1479 track != media_desc->streams().end(); ++track) {
1480 // Require that the track belongs to a media stream,
1481 // ie the sync_label is set. This extra check is necessary since the
1482 // MediaContentDescription always contains a streamparam with an ssrc even
1483 // if no track or media stream have been created.
1484 if (track->sync_label.empty()) continue;
1485
1486 // Build the ssrc-group lines.
1487 for (size_t i = 0; i < track->ssrc_groups.size(); ++i) {
1488 // RFC 5576
1489 // a=ssrc-group:<semantics> <ssrc-id> ...
1490 if (track->ssrc_groups[i].ssrcs.empty()) {
1491 continue;
1492 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001493 InitAttrLine(kAttributeSsrcGroup, &os);
1494 os << kSdpDelimiterColon << track->ssrc_groups[i].semantics;
Peter Boström0c4e06b2015-10-07 12:23:21 +02001495 std::vector<uint32_t>::const_iterator ssrc =
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001496 track->ssrc_groups[i].ssrcs.begin();
1497 for (; ssrc != track->ssrc_groups[i].ssrcs.end(); ++ssrc) {
Peter Boström0c4e06b2015-10-07 12:23:21 +02001498 os << kSdpDelimiterSpace << rtc::ToString<uint32_t>(*ssrc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001499 }
1500 AddLine(os.str(), message);
1501 }
1502 // Build the ssrc lines for each ssrc.
1503 for (size_t i = 0; i < track->ssrcs.size(); ++i) {
Peter Boström0c4e06b2015-10-07 12:23:21 +02001504 uint32_t ssrc = track->ssrcs[i];
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001505 // RFC 5576
1506 // a=ssrc:<ssrc-id> cname:<value>
1507 AddSsrcLine(ssrc, kSsrcAttributeCname,
1508 track->cname, message);
1509
1510 // draft-alvestrand-mmusic-msid-00
1511 // a=ssrc:<ssrc-id> msid:identifier [appdata]
deadbeef9d3584c2016-02-16 17:54:10 -08001512 // The appdata consists of the "id" attribute of a MediaStreamTrack,
1513 // which corresponds to the "id" attribute of StreamParams.
1514 const std::string& stream_id = track->sync_label;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001515 InitAttrLine(kAttributeSsrc, &os);
1516 os << kSdpDelimiterColon << ssrc << kSdpDelimiterSpace
deadbeef9d3584c2016-02-16 17:54:10 -08001517 << kSsrcAttributeMsid << kSdpDelimiterColon << stream_id
1518 << kSdpDelimiterSpace << track->id;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001519 AddLine(os.str(), message);
1520
deadbeef9d3584c2016-02-16 17:54:10 -08001521 // TODO(ronghuawu): Remove below code which is for backward
1522 // compatibility.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001523 // draft-alvestrand-rtcweb-mid-01
1524 // a=ssrc:<ssrc-id> mslabel:<value>
1525 // The label isn't yet defined.
1526 // a=ssrc:<ssrc-id> label:<value>
1527 AddSsrcLine(ssrc, kSsrcAttributeMslabel, track->sync_label, message);
1528 AddSsrcLine(ssrc, kSSrcAttributeLabel, track->id, message);
1529 }
1530 }
1531}
1532
1533void WriteFmtpHeader(int payload_type, std::ostringstream* os) {
1534 // fmtp header: a=fmtp:|payload_type| <parameters>
1535 // Add a=fmtp
1536 InitAttrLine(kAttributeFmtp, os);
1537 // Add :|payload_type|
1538 *os << kSdpDelimiterColon << payload_type;
1539}
1540
1541void WriteRtcpFbHeader(int payload_type, std::ostringstream* os) {
1542 // rtcp-fb header: a=rtcp-fb:|payload_type|
1543 // <parameters>/<ccm <ccm_parameters>>
1544 // Add a=rtcp-fb
1545 InitAttrLine(kAttributeRtcpFb, os);
1546 // Add :
1547 *os << kSdpDelimiterColon;
1548 if (payload_type == kWildcardPayloadType) {
1549 *os << "*";
1550 } else {
1551 *os << payload_type;
1552 }
1553}
1554
1555void WriteFmtpParameter(const std::string& parameter_name,
1556 const std::string& parameter_value,
1557 std::ostringstream* os) {
1558 // fmtp parameters: |parameter_name|=|parameter_value|
1559 *os << parameter_name << kSdpDelimiterEqual << parameter_value;
1560}
1561
1562void WriteFmtpParameters(const cricket::CodecParameterMap& parameters,
1563 std::ostringstream* os) {
1564 for (cricket::CodecParameterMap::const_iterator fmtp = parameters.begin();
1565 fmtp != parameters.end(); ++fmtp) {
1566 // Each new parameter, except the first one starts with ";" and " ".
1567 if (fmtp != parameters.begin()) {
1568 *os << kSdpDelimiterSemicolon;
1569 }
1570 *os << kSdpDelimiterSpace;
1571 WriteFmtpParameter(fmtp->first, fmtp->second, os);
1572 }
1573}
1574
1575bool IsFmtpParam(const std::string& name) {
1576 const char* kFmtpParams[] = {
htaa6b99442016-04-12 10:29:17 -07001577 // TODO(hta): Split FMTP parameters apart from parameters in general.
1578 // FMTP parameters are codec specific, not generic.
1579 kCodecParamMinPTime,
1580 kCodecParamSPropStereo,
1581 kCodecParamStereo,
1582 kCodecParamUseInbandFec,
1583 kCodecParamUseDtx,
1584 kCodecParamStartBitrate,
1585 kCodecParamMaxBitrate,
1586 kCodecParamMinBitrate,
1587 kCodecParamMaxQuantization,
1588 kCodecParamSctpProtocol,
1589 kCodecParamSctpStreams,
1590 kCodecParamMaxAverageBitrate,
1591 kCodecParamMaxPlaybackRate,
1592 kCodecParamAssociatedPayloadType,
1593 cricket::kH264FmtpPacketizationMode,
1594 cricket::kH264FmtpLevelAsymmetryAllowed,
1595 cricket::kH264FmtpProfileLevelId};
tfarina5237aaf2015-11-10 23:44:30 -08001596 for (size_t i = 0; i < arraysize(kFmtpParams); ++i) {
htaa6b99442016-04-12 10:29:17 -07001597 if (name.compare(kFmtpParams[i]) == 0) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001598 return true;
1599 }
1600 }
1601 return false;
1602}
1603
1604// Retreives fmtp parameters from |params|, which may contain other parameters
1605// as well, and puts them in |fmtp_parameters|.
1606void GetFmtpParams(const cricket::CodecParameterMap& params,
1607 cricket::CodecParameterMap* fmtp_parameters) {
1608 for (cricket::CodecParameterMap::const_iterator iter = params.begin();
1609 iter != params.end(); ++iter) {
1610 if (IsFmtpParam(iter->first)) {
1611 (*fmtp_parameters)[iter->first] = iter->second;
1612 }
1613 }
1614}
1615
1616template <class T>
1617void AddFmtpLine(const T& codec, std::string* message) {
1618 cricket::CodecParameterMap fmtp_parameters;
1619 GetFmtpParams(codec.params, &fmtp_parameters);
1620 if (fmtp_parameters.empty()) {
1621 // No need to add an fmtp if it will have no (optional) parameters.
1622 return;
1623 }
1624 std::ostringstream os;
1625 WriteFmtpHeader(codec.id, &os);
1626 WriteFmtpParameters(fmtp_parameters, &os);
1627 AddLine(os.str(), message);
1628 return;
1629}
1630
1631template <class T>
1632void AddRtcpFbLines(const T& codec, std::string* message) {
1633 for (std::vector<cricket::FeedbackParam>::const_iterator iter =
1634 codec.feedback_params.params().begin();
1635 iter != codec.feedback_params.params().end(); ++iter) {
1636 std::ostringstream os;
1637 WriteRtcpFbHeader(codec.id, &os);
1638 os << " " << iter->id();
1639 if (!iter->param().empty()) {
1640 os << " " << iter->param();
1641 }
1642 AddLine(os.str(), message);
1643 }
1644}
1645
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00001646bool AddSctpDataCodec(DataContentDescription* media_desc,
1647 int sctp_port) {
1648 if (media_desc->HasCodec(cricket::kGoogleSctpDataCodecId)) {
1649 return ParseFailed("",
1650 "Can't have multiple sctp port attributes.",
1651 NULL);
1652 }
1653 // Add the SCTP Port number as a pseudo-codec "port" parameter
1654 cricket::DataCodec codec_port(
1655 cricket::kGoogleSctpDataCodecId, cricket::kGoogleSctpDataCodecName,
1656 0);
1657 codec_port.SetParam(cricket::kCodecParamPort, sctp_port);
1658 LOG(INFO) << "AddSctpDataCodec: Got SCTP Port Number "
1659 << sctp_port;
1660 media_desc->AddCodec(codec_port);
1661 return true;
1662}
1663
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001664bool GetMinValue(const std::vector<int>& values, int* value) {
1665 if (values.empty()) {
1666 return false;
1667 }
1668 std::vector<int>::const_iterator found =
1669 std::min_element(values.begin(), values.end());
1670 *value = *found;
1671 return true;
1672}
1673
1674bool GetParameter(const std::string& name,
1675 const cricket::CodecParameterMap& params, int* value) {
1676 std::map<std::string, std::string>::const_iterator found =
1677 params.find(name);
1678 if (found == params.end()) {
1679 return false;
1680 }
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001681 if (!rtc::FromString(found->second, value)) {
wu@webrtc.org5e760e72014-04-02 23:19:09 +00001682 return false;
1683 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001684 return true;
1685}
1686
1687void BuildRtpMap(const MediaContentDescription* media_desc,
1688 const MediaType media_type,
1689 std::string* message) {
1690 ASSERT(message != NULL);
1691 ASSERT(media_desc != NULL);
1692 std::ostringstream os;
1693 if (media_type == cricket::MEDIA_TYPE_VIDEO) {
1694 const VideoContentDescription* video_desc =
1695 static_cast<const VideoContentDescription*>(media_desc);
1696 for (std::vector<cricket::VideoCodec>::const_iterator it =
1697 video_desc->codecs().begin();
1698 it != video_desc->codecs().end(); ++it) {
1699 // RFC 4566
1700 // a=rtpmap:<payload type> <encoding name>/<clock rate>
1701 // [/<encodingparameters>]
1702 if (it->id != kWildcardPayloadType) {
1703 InitAttrLine(kAttributeRtpmap, &os);
1704 os << kSdpDelimiterColon << it->id << " " << it->name
1705 << "/" << kDefaultVideoClockrate;
1706 AddLine(os.str(), message);
1707 }
1708 AddRtcpFbLines(*it, message);
1709 AddFmtpLine(*it, message);
1710 }
1711 } else if (media_type == cricket::MEDIA_TYPE_AUDIO) {
1712 const AudioContentDescription* audio_desc =
1713 static_cast<const AudioContentDescription*>(media_desc);
1714 std::vector<int> ptimes;
1715 std::vector<int> maxptimes;
1716 int max_minptime = 0;
1717 for (std::vector<cricket::AudioCodec>::const_iterator it =
1718 audio_desc->codecs().begin();
1719 it != audio_desc->codecs().end(); ++it) {
1720 ASSERT(!it->name.empty());
1721 // RFC 4566
1722 // a=rtpmap:<payload type> <encoding name>/<clock rate>
1723 // [/<encodingparameters>]
1724 InitAttrLine(kAttributeRtpmap, &os);
1725 os << kSdpDelimiterColon << it->id << " ";
1726 os << it->name << "/" << it->clockrate;
1727 if (it->channels != 1) {
1728 os << "/" << it->channels;
1729 }
1730 AddLine(os.str(), message);
1731 AddRtcpFbLines(*it, message);
1732 AddFmtpLine(*it, message);
1733 int minptime = 0;
1734 if (GetParameter(kCodecParamMinPTime, it->params, &minptime)) {
1735 max_minptime = std::max(minptime, max_minptime);
1736 }
1737 int ptime;
1738 if (GetParameter(kCodecParamPTime, it->params, &ptime)) {
1739 ptimes.push_back(ptime);
1740 }
1741 int maxptime;
1742 if (GetParameter(kCodecParamMaxPTime, it->params, &maxptime)) {
1743 maxptimes.push_back(maxptime);
1744 }
1745 }
1746 // Populate the maxptime attribute with the smallest maxptime of all codecs
1747 // under the same m-line.
1748 int min_maxptime = INT_MAX;
1749 if (GetMinValue(maxptimes, &min_maxptime)) {
1750 AddAttributeLine(kCodecParamMaxPTime, min_maxptime, message);
1751 }
1752 ASSERT(min_maxptime > max_minptime);
1753 // Populate the ptime attribute with the smallest ptime or the largest
1754 // minptime, whichever is the largest, for all codecs under the same m-line.
1755 int ptime = INT_MAX;
1756 if (GetMinValue(ptimes, &ptime)) {
1757 ptime = std::min(ptime, min_maxptime);
1758 ptime = std::max(ptime, max_minptime);
1759 AddAttributeLine(kCodecParamPTime, ptime, message);
1760 }
1761 } else if (media_type == cricket::MEDIA_TYPE_DATA) {
1762 const DataContentDescription* data_desc =
1763 static_cast<const DataContentDescription*>(media_desc);
1764 for (std::vector<cricket::DataCodec>::const_iterator it =
1765 data_desc->codecs().begin();
1766 it != data_desc->codecs().end(); ++it) {
1767 // RFC 4566
1768 // a=rtpmap:<payload type> <encoding name>/<clock rate>
1769 // [/<encodingparameters>]
1770 InitAttrLine(kAttributeRtpmap, &os);
1771 os << kSdpDelimiterColon << it->id << " "
1772 << it->name << "/" << it->clockrate;
1773 AddLine(os.str(), message);
1774 }
1775 }
1776}
1777
1778void BuildCandidate(const std::vector<Candidate>& candidates,
honghaiza54a0802015-12-16 18:37:23 -08001779 bool include_ufrag,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001780 std::string* message) {
1781 std::ostringstream os;
1782
1783 for (std::vector<Candidate>::const_iterator it = candidates.begin();
1784 it != candidates.end(); ++it) {
1785 // RFC 5245
1786 // a=candidate:<foundation> <component-id> <transport> <priority>
1787 // <connection-address> <port> typ <candidate-types>
1788 // [raddr <connection-address>] [rport <port>]
1789 // *(SP extension-att-name SP extension-att-value)
1790 std::string type;
1791 // Map the cricket candidate type to "host" / "srflx" / "prflx" / "relay"
1792 if (it->type() == cricket::LOCAL_PORT_TYPE) {
1793 type = kCandidateHost;
1794 } else if (it->type() == cricket::STUN_PORT_TYPE) {
1795 type = kCandidateSrflx;
1796 } else if (it->type() == cricket::RELAY_PORT_TYPE) {
1797 type = kCandidateRelay;
Honghai Zhang7fb69db2016-03-14 11:59:18 -07001798 } else if (it->type() == cricket::PRFLX_PORT_TYPE) {
1799 type = kCandidatePrflx;
1800 // Peer reflexive candidate may be signaled for being removed.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001801 } else {
1802 ASSERT(false);
Peter Thatcher019087f2015-04-28 09:06:26 -07001803 // Never write out candidates if we don't know the type.
1804 continue;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001805 }
1806
1807 InitAttrLine(kAttributeCandidate, &os);
1808 os << kSdpDelimiterColon
mallinath@webrtc.org2d60c5e2014-08-08 22:29:20 +00001809 << it->foundation() << " "
1810 << it->component() << " "
1811 << it->protocol() << " "
1812 << it->priority() << " "
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001813 << it->address().ipaddr().ToString() << " "
1814 << it->address().PortAsString() << " "
mallinath@webrtc.org2d60c5e2014-08-08 22:29:20 +00001815 << kAttributeCandidateTyp << " "
1816 << type << " ";
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001817
1818 // Related address
1819 if (!it->related_address().IsNil()) {
1820 os << kAttributeCandidateRaddr << " "
1821 << it->related_address().ipaddr().ToString() << " "
1822 << kAttributeCandidateRport << " "
1823 << it->related_address().PortAsString() << " ";
1824 }
1825
mallinath@webrtc.org2d60c5e2014-08-08 22:29:20 +00001826 if (it->protocol() == cricket::TCP_PROTOCOL_NAME) {
mallinath@webrtc.orge999bd02014-08-13 06:05:55 +00001827 os << kTcpCandidateType << " " << it->tcptype() << " ";
mallinath@webrtc.org2d60c5e2014-08-08 22:29:20 +00001828 }
1829
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001830 // Extensions
1831 os << kAttributeCandidateGeneration << " " << it->generation();
honghaiza54a0802015-12-16 18:37:23 -08001832 if (include_ufrag && !it->username().empty()) {
1833 os << " " << kAttributeCandidateUfrag << " " << it->username();
1834 }
honghaiza0c44ea2016-03-23 16:07:48 -07001835 if (it->network_id() > 0) {
1836 os << " " << kAttributeCandidateNetworkId << " " << it->network_id();
1837 }
honghaize1a0c942016-02-16 14:54:56 -08001838 if (it->network_cost() > 0) {
1839 os << " " << kAttributeCandidateNetworkCost << " " << it->network_cost();
1840 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001841
1842 AddLine(os.str(), message);
1843 }
1844}
1845
1846void BuildIceOptions(const std::vector<std::string>& transport_options,
1847 std::string* message) {
1848 if (!transport_options.empty()) {
1849 std::ostringstream os;
1850 InitAttrLine(kAttributeIceOption, &os);
1851 os << kSdpDelimiterColon << transport_options[0];
1852 for (size_t i = 1; i < transport_options.size(); ++i) {
1853 os << kSdpDelimiterSpace << transport_options[i];
1854 }
1855 AddLine(os.str(), message);
1856 }
1857}
1858
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00001859bool IsRtp(const std::string& protocol) {
1860 return protocol.empty() ||
1861 (protocol.find(cricket::kMediaProtocolRtpPrefix) != std::string::npos);
1862}
1863
1864bool IsDtlsSctp(const std::string& protocol) {
1865 // This intentionally excludes "SCTP" and "SCTP/DTLS".
lally@webrtc.orga7470932015-02-24 20:19:21 +00001866 return protocol.find(cricket::kMediaProtocolDtlsSctp) != std::string::npos;
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00001867}
1868
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001869bool ParseSessionDescription(const std::string& message, size_t* pos,
1870 std::string* session_id,
1871 std::string* session_version,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001872 TransportDescription* session_td,
1873 RtpHeaderExtensions* session_extmaps,
1874 cricket::SessionDescription* desc,
1875 SdpParseError* error) {
1876 std::string line;
1877
deadbeefc80741f2015-10-22 13:14:45 -07001878 desc->set_msid_supported(false);
1879
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001880 // RFC 4566
1881 // v= (protocol version)
1882 if (!GetLineWithType(message, pos, &line, kLineTypeVersion)) {
1883 return ParseFailedExpectLine(message, *pos, kLineTypeVersion,
1884 std::string(), error);
1885 }
1886 // RFC 4566
1887 // o=<username> <sess-id> <sess-version> <nettype> <addrtype>
1888 // <unicast-address>
1889 if (!GetLineWithType(message, pos, &line, kLineTypeOrigin)) {
1890 return ParseFailedExpectLine(message, *pos, kLineTypeOrigin,
1891 std::string(), error);
1892 }
1893 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001894 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001895 kSdpDelimiterSpace, &fields);
1896 const size_t expected_fields = 6;
1897 if (fields.size() != expected_fields) {
1898 return ParseFailedExpectFieldNum(line, expected_fields, error);
1899 }
1900 *session_id = fields[1];
1901 *session_version = fields[2];
1902
1903 // RFC 4566
1904 // s= (session name)
1905 if (!GetLineWithType(message, pos, &line, kLineTypeSessionName)) {
1906 return ParseFailedExpectLine(message, *pos, kLineTypeSessionName,
1907 std::string(), error);
1908 }
1909
1910 // Optional lines
1911 // Those are the optional lines, so shouldn't return false if not present.
1912 // RFC 4566
1913 // i=* (session information)
1914 GetLineWithType(message, pos, &line, kLineTypeSessionInfo);
1915
1916 // RFC 4566
1917 // u=* (URI of description)
1918 GetLineWithType(message, pos, &line, kLineTypeSessionUri);
1919
1920 // RFC 4566
1921 // e=* (email address)
1922 GetLineWithType(message, pos, &line, kLineTypeSessionEmail);
1923
1924 // RFC 4566
1925 // p=* (phone number)
1926 GetLineWithType(message, pos, &line, kLineTypeSessionPhone);
1927
1928 // RFC 4566
1929 // c=* (connection information -- not required if included in
1930 // all media)
1931 GetLineWithType(message, pos, &line, kLineTypeConnection);
1932
1933 // RFC 4566
1934 // b=* (zero or more bandwidth information lines)
1935 while (GetLineWithType(message, pos, &line, kLineTypeSessionBandwidth)) {
1936 // By pass zero or more b lines.
1937 }
1938
1939 // RFC 4566
1940 // One or more time descriptions ("t=" and "r=" lines; see below)
1941 // t= (time the session is active)
1942 // r=* (zero or more repeat times)
1943 // Ensure there's at least one time description
1944 if (!GetLineWithType(message, pos, &line, kLineTypeTiming)) {
1945 return ParseFailedExpectLine(message, *pos, kLineTypeTiming, std::string(),
1946 error);
1947 }
1948
1949 while (GetLineWithType(message, pos, &line, kLineTypeRepeatTimes)) {
1950 // By pass zero or more r lines.
1951 }
1952
1953 // Go through the rest of the time descriptions
1954 while (GetLineWithType(message, pos, &line, kLineTypeTiming)) {
1955 while (GetLineWithType(message, pos, &line, kLineTypeRepeatTimes)) {
1956 // By pass zero or more r lines.
1957 }
1958 }
1959
1960 // RFC 4566
1961 // z=* (time zone adjustments)
1962 GetLineWithType(message, pos, &line, kLineTypeTimeZone);
1963
1964 // RFC 4566
1965 // k=* (encryption key)
1966 GetLineWithType(message, pos, &line, kLineTypeEncryptionKey);
1967
1968 // RFC 4566
1969 // a=* (zero or more session attribute lines)
1970 while (GetLineWithType(message, pos, &line, kLineTypeAttributes)) {
1971 if (HasAttribute(line, kAttributeGroup)) {
1972 if (!ParseGroupAttribute(line, desc, error)) {
1973 return false;
1974 }
1975 } else if (HasAttribute(line, kAttributeIceUfrag)) {
1976 if (!GetValue(line, kAttributeIceUfrag,
1977 &(session_td->ice_ufrag), error)) {
1978 return false;
1979 }
1980 } else if (HasAttribute(line, kAttributeIcePwd)) {
1981 if (!GetValue(line, kAttributeIcePwd, &(session_td->ice_pwd), error)) {
1982 return false;
1983 }
1984 } else if (HasAttribute(line, kAttributeIceLite)) {
1985 session_td->ice_mode = cricket::ICEMODE_LITE;
1986 } else if (HasAttribute(line, kAttributeIceOption)) {
1987 if (!ParseIceOptions(line, &(session_td->transport_options), error)) {
1988 return false;
1989 }
1990 } else if (HasAttribute(line, kAttributeFingerprint)) {
1991 if (session_td->identity_fingerprint.get()) {
1992 return ParseFailed(
1993 line,
1994 "Can't have multiple fingerprint attributes at the same level.",
1995 error);
1996 }
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001997 rtc::SSLFingerprint* fingerprint = NULL;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001998 if (!ParseFingerprintAttribute(line, &fingerprint, error)) {
1999 return false;
2000 }
2001 session_td->identity_fingerprint.reset(fingerprint);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00002002 } else if (HasAttribute(line, kAttributeSetup)) {
2003 if (!ParseDtlsSetup(line, &(session_td->connection_role), error)) {
2004 return false;
2005 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002006 } else if (HasAttribute(line, kAttributeMsidSemantics)) {
2007 std::string semantics;
2008 if (!GetValue(line, kAttributeMsidSemantics, &semantics, error)) {
2009 return false;
2010 }
deadbeefc80741f2015-10-22 13:14:45 -07002011 desc->set_msid_supported(
2012 CaseInsensitiveFind(semantics, kMediaStreamSemantic));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002013 } else if (HasAttribute(line, kAttributeExtmap)) {
2014 RtpHeaderExtension extmap;
2015 if (!ParseExtmap(line, &extmap, error)) {
2016 return false;
2017 }
2018 session_extmaps->push_back(extmap);
2019 }
2020 }
2021
2022 return true;
2023}
2024
2025bool ParseGroupAttribute(const std::string& line,
2026 cricket::SessionDescription* desc,
2027 SdpParseError* error) {
2028 ASSERT(desc != NULL);
2029
2030 // RFC 5888 and draft-holmberg-mmusic-sdp-bundle-negotiation-00
2031 // a=group:BUNDLE video voice
2032 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002033 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002034 kSdpDelimiterSpace, &fields);
2035 std::string semantics;
2036 if (!GetValue(fields[0], kAttributeGroup, &semantics, error)) {
2037 return false;
2038 }
2039 cricket::ContentGroup group(semantics);
2040 for (size_t i = 1; i < fields.size(); ++i) {
2041 group.AddContentName(fields[i]);
2042 }
2043 desc->AddGroup(group);
2044 return true;
2045}
2046
2047static bool ParseFingerprintAttribute(const std::string& line,
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002048 rtc::SSLFingerprint** fingerprint,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002049 SdpParseError* error) {
2050 if (!IsLineType(line, kLineTypeAttributes) ||
2051 !HasAttribute(line, kAttributeFingerprint)) {
2052 return ParseFailedExpectLine(line, 0, kLineTypeAttributes,
2053 kAttributeFingerprint, error);
2054 }
2055
2056 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002057 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002058 kSdpDelimiterSpace, &fields);
2059 const size_t expected_fields = 2;
2060 if (fields.size() != expected_fields) {
2061 return ParseFailedExpectFieldNum(line, expected_fields, error);
2062 }
2063
2064 // The first field here is "fingerprint:<hash>.
2065 std::string algorithm;
2066 if (!GetValue(fields[0], kAttributeFingerprint, &algorithm, error)) {
2067 return false;
2068 }
2069
2070 // Downcase the algorithm. Note that we don't need to downcase the
2071 // fingerprint because hex_decode can handle upper-case.
2072 std::transform(algorithm.begin(), algorithm.end(), algorithm.begin(),
2073 ::tolower);
2074
2075 // The second field is the digest value. De-hexify it.
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002076 *fingerprint = rtc::SSLFingerprint::CreateFromRfc4572(
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002077 algorithm, fields[1]);
2078 if (!*fingerprint) {
2079 return ParseFailed(line,
2080 "Failed to create fingerprint from the digest.",
2081 error);
2082 }
2083
2084 return true;
2085}
2086
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00002087static bool ParseDtlsSetup(const std::string& line,
2088 cricket::ConnectionRole* role,
2089 SdpParseError* error) {
2090 // setup-attr = "a=setup:" role
2091 // role = "active" / "passive" / "actpass" / "holdconn"
2092 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002093 rtc::split(line.substr(kLinePrefixLength), kSdpDelimiterColon, &fields);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00002094 const size_t expected_fields = 2;
2095 if (fields.size() != expected_fields) {
2096 return ParseFailedExpectFieldNum(line, expected_fields, error);
2097 }
2098 std::string role_str = fields[1];
2099 if (!cricket::StringToConnectionRole(role_str, role)) {
2100 return ParseFailed(line, "Invalid attribute value.", error);
2101 }
2102 return true;
2103}
2104
deadbeef9d3584c2016-02-16 17:54:10 -08002105static bool ParseMsidAttribute(const std::string& line,
2106 std::string* stream_id,
2107 std::string* track_id,
2108 SdpParseError* error) {
2109 // draft-ietf-mmusic-msid-11
2110 // a=msid:<stream id> <track id>
2111 // msid-value = msid-id [ SP msid-appdata ]
2112 // msid-id = 1*64token-char ; see RFC 4566
2113 // msid-appdata = 1*64token-char ; see RFC 4566
2114 std::string field1;
2115 if (!rtc::tokenize_first(line.substr(kLinePrefixLength), kSdpDelimiterSpace,
2116 &field1, track_id)) {
2117 const size_t expected_fields = 2;
2118 return ParseFailedExpectFieldNum(line, expected_fields, error);
2119 }
2120
2121 // msid:<msid-id>
2122 if (!GetValue(field1, kAttributeMsid, stream_id, error)) {
2123 return false;
2124 }
2125 return true;
2126}
2127
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002128// RFC 3551
2129// PT encoding media type clock rate channels
2130// name (Hz)
2131// 0 PCMU A 8,000 1
2132// 1 reserved A
2133// 2 reserved A
2134// 3 GSM A 8,000 1
2135// 4 G723 A 8,000 1
2136// 5 DVI4 A 8,000 1
2137// 6 DVI4 A 16,000 1
2138// 7 LPC A 8,000 1
2139// 8 PCMA A 8,000 1
2140// 9 G722 A 8,000 1
2141// 10 L16 A 44,100 2
2142// 11 L16 A 44,100 1
2143// 12 QCELP A 8,000 1
2144// 13 CN A 8,000 1
2145// 14 MPA A 90,000 (see text)
2146// 15 G728 A 8,000 1
2147// 16 DVI4 A 11,025 1
2148// 17 DVI4 A 22,050 1
2149// 18 G729 A 8,000 1
2150struct StaticPayloadAudioCodec {
2151 const char* name;
2152 int clockrate;
Peter Kasting69558702016-01-12 16:26:35 -08002153 size_t channels;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002154};
2155static const StaticPayloadAudioCodec kStaticPayloadAudioCodecs[] = {
2156 { "PCMU", 8000, 1 },
2157 { "reserved", 0, 0 },
2158 { "reserved", 0, 0 },
2159 { "GSM", 8000, 1 },
2160 { "G723", 8000, 1 },
2161 { "DVI4", 8000, 1 },
2162 { "DVI4", 16000, 1 },
2163 { "LPC", 8000, 1 },
2164 { "PCMA", 8000, 1 },
2165 { "G722", 8000, 1 },
2166 { "L16", 44100, 2 },
2167 { "L16", 44100, 1 },
2168 { "QCELP", 8000, 1 },
2169 { "CN", 8000, 1 },
2170 { "MPA", 90000, 1 },
2171 { "G728", 8000, 1 },
2172 { "DVI4", 11025, 1 },
2173 { "DVI4", 22050, 1 },
2174 { "G729", 8000, 1 },
2175};
2176
2177void MaybeCreateStaticPayloadAudioCodecs(
2178 const std::vector<int>& fmts, AudioContentDescription* media_desc) {
2179 if (!media_desc) {
2180 return;
2181 }
henrike@webrtc.org28654cb2013-07-22 21:07:49 +00002182 int preference = static_cast<int>(fmts.size());
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002183 std::vector<int>::const_iterator it = fmts.begin();
2184 bool add_new_codec = false;
2185 for (; it != fmts.end(); ++it) {
2186 int payload_type = *it;
2187 if (!media_desc->HasCodec(payload_type) &&
2188 payload_type >= 0 &&
tfarina5237aaf2015-11-10 23:44:30 -08002189 payload_type < arraysize(kStaticPayloadAudioCodecs)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002190 std::string encoding_name = kStaticPayloadAudioCodecs[payload_type].name;
2191 int clock_rate = kStaticPayloadAudioCodecs[payload_type].clockrate;
Peter Kasting69558702016-01-12 16:26:35 -08002192 size_t channels = kStaticPayloadAudioCodecs[payload_type].channels;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002193 media_desc->AddCodec(cricket::AudioCodec(payload_type, encoding_name,
2194 clock_rate, 0, channels,
2195 preference));
2196 add_new_codec = true;
2197 }
2198 --preference;
2199 }
2200 if (add_new_codec) {
2201 media_desc->SortCodecs();
2202 }
2203}
2204
2205template <class C>
2206static C* ParseContentDescription(const std::string& message,
2207 const MediaType media_type,
2208 int mline_index,
2209 const std::string& protocol,
2210 const std::vector<int>& codec_preference,
2211 size_t* pos,
2212 std::string* content_name,
2213 TransportDescription* transport,
2214 std::vector<JsepIceCandidate*>* candidates,
2215 webrtc::SdpParseError* error) {
2216 C* media_desc = new C();
2217 switch (media_type) {
2218 case cricket::MEDIA_TYPE_AUDIO:
2219 *content_name = cricket::CN_AUDIO;
2220 break;
2221 case cricket::MEDIA_TYPE_VIDEO:
2222 *content_name = cricket::CN_VIDEO;
2223 break;
2224 case cricket::MEDIA_TYPE_DATA:
2225 *content_name = cricket::CN_DATA;
2226 break;
2227 default:
2228 ASSERT(false);
2229 break;
2230 }
2231 if (!ParseContent(message, media_type, mline_index, protocol,
2232 codec_preference, pos, content_name,
2233 media_desc, transport, candidates, error)) {
2234 delete media_desc;
2235 return NULL;
2236 }
2237 // Sort the codecs according to the m-line fmt list.
2238 media_desc->SortCodecs();
2239 return media_desc;
2240}
2241
2242bool ParseMediaDescription(const std::string& message,
2243 const TransportDescription& session_td,
2244 const RtpHeaderExtensions& session_extmaps,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002245 size_t* pos,
2246 cricket::SessionDescription* desc,
2247 std::vector<JsepIceCandidate*>* candidates,
2248 SdpParseError* error) {
2249 ASSERT(desc != NULL);
2250 std::string line;
2251 int mline_index = -1;
2252
2253 // Zero or more media descriptions
2254 // RFC 4566
2255 // m=<media> <port> <proto> <fmt>
2256 while (GetLineWithType(message, pos, &line, kLineTypeMedia)) {
2257 ++mline_index;
2258
2259 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002260 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002261 kSdpDelimiterSpace, &fields);
2262 const size_t expected_min_fields = 4;
2263 if (fields.size() < expected_min_fields) {
2264 return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
2265 }
2266 bool rejected = false;
2267 // RFC 3264
2268 // To reject an offered stream, the port number in the corresponding stream
2269 // in the answer MUST be set to zero.
2270 if (fields[1] == kMediaPortRejected) {
2271 rejected = true;
2272 }
2273
2274 std::string protocol = fields[2];
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002275
2276 // <fmt>
2277 std::vector<int> codec_preference;
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00002278 if (IsRtp(protocol)) {
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00002279 for (size_t j = 3 ; j < fields.size(); ++j) {
2280 // TODO(wu): Remove when below bug is fixed.
2281 // https://bugzilla.mozilla.org/show_bug.cgi?id=996329
pbosbb36fdf2015-07-09 07:48:14 -07002282 if (fields[j].empty() && j == fields.size() - 1) {
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00002283 continue;
2284 }
wu@webrtc.org36eda7c2014-04-15 20:37:30 +00002285
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00002286 int pl = 0;
pkasting@chromium.orge9facf82015-02-17 20:36:28 +00002287 if (!GetPayloadTypeFromString(line, fields[j], &pl, error)) {
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00002288 return false;
2289 }
2290 codec_preference.push_back(pl);
wu@webrtc.org5e760e72014-04-02 23:19:09 +00002291 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002292 }
2293
2294 // Make a temporary TransportDescription based on |session_td|.
2295 // Some of this gets overwritten by ParseContent.
deadbeef46eed762016-01-28 13:24:37 -08002296 TransportDescription transport(
2297 session_td.transport_options, session_td.ice_ufrag, session_td.ice_pwd,
2298 session_td.ice_mode, session_td.connection_role,
2299 session_td.identity_fingerprint.get());
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002300
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002301 rtc::scoped_ptr<MediaContentDescription> content;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002302 std::string content_name;
2303 if (HasAttribute(line, kMediaTypeVideo)) {
2304 content.reset(ParseContentDescription<VideoContentDescription>(
2305 message, cricket::MEDIA_TYPE_VIDEO, mline_index, protocol,
2306 codec_preference, pos, &content_name,
2307 &transport, candidates, error));
2308 } else if (HasAttribute(line, kMediaTypeAudio)) {
2309 content.reset(ParseContentDescription<AudioContentDescription>(
2310 message, cricket::MEDIA_TYPE_AUDIO, mline_index, protocol,
2311 codec_preference, pos, &content_name,
2312 &transport, candidates, error));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002313 } else if (HasAttribute(line, kMediaTypeData)) {
jiayl@webrtc.org0e527722014-09-08 21:43:43 +00002314 DataContentDescription* data_desc =
wu@webrtc.org78187522013-10-07 23:32:02 +00002315 ParseContentDescription<DataContentDescription>(
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002316 message, cricket::MEDIA_TYPE_DATA, mline_index, protocol,
2317 codec_preference, pos, &content_name,
wu@webrtc.org78187522013-10-07 23:32:02 +00002318 &transport, candidates, error);
jiayl@webrtc.org0e527722014-09-08 21:43:43 +00002319 content.reset(data_desc);
wu@webrtc.org78187522013-10-07 23:32:02 +00002320
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00002321 int p;
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00002322 if (data_desc && IsDtlsSctp(protocol) && rtc::FromString(fields[3], &p)) {
jiayl@webrtc.org0e527722014-09-08 21:43:43 +00002323 if (!AddSctpDataCodec(data_desc, p))
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00002324 return false;
wu@webrtc.org78187522013-10-07 23:32:02 +00002325 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002326 } else {
2327 LOG(LS_WARNING) << "Unsupported media type: " << line;
2328 continue;
2329 }
2330 if (!content.get()) {
2331 // ParseContentDescription returns NULL if failed.
2332 return false;
2333 }
2334
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00002335 if (IsRtp(protocol)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002336 // Set the extmap.
2337 if (!session_extmaps.empty() &&
2338 !content->rtp_header_extensions().empty()) {
2339 return ParseFailed("",
2340 "The a=extmap MUST be either all session level or "
2341 "all media level.",
2342 error);
2343 }
2344 for (size_t i = 0; i < session_extmaps.size(); ++i) {
2345 content->AddRtpHeaderExtension(session_extmaps[i]);
2346 }
2347 }
2348 content->set_protocol(protocol);
2349 desc->AddContent(content_name,
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00002350 IsDtlsSctp(protocol) ? cricket::NS_JINGLE_DRAFT_SCTP :
2351 cricket::NS_JINGLE_RTP,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002352 rejected,
2353 content.release());
2354 // Create TransportInfo with the media level "ice-pwd" and "ice-ufrag".
2355 TransportInfo transport_info(content_name, transport);
2356
2357 if (!desc->AddTransportInfo(transport_info)) {
2358 std::ostringstream description;
2359 description << "Failed to AddTransportInfo with content name: "
2360 << content_name;
2361 return ParseFailed("", description.str(), error);
2362 }
2363 }
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00002364
2365 size_t end_of_message = message.size();
2366 if (mline_index == -1 && *pos != end_of_message) {
2367 ParseFailed(message, *pos, "Expects m line.", error);
2368 return false;
2369 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002370 return true;
2371}
2372
2373bool VerifyCodec(const cricket::Codec& codec) {
2374 // Codec has not been populated correctly unless the name has been set. This
2375 // can happen if an SDP has an fmtp or rtcp-fb with a payload type but doesn't
2376 // have a corresponding "rtpmap" line.
2377 cricket::Codec default_codec;
2378 return default_codec.name != codec.name;
2379}
2380
2381bool VerifyAudioCodecs(const AudioContentDescription* audio_desc) {
2382 const std::vector<cricket::AudioCodec>& codecs = audio_desc->codecs();
2383 for (std::vector<cricket::AudioCodec>::const_iterator iter = codecs.begin();
2384 iter != codecs.end(); ++iter) {
2385 if (!VerifyCodec(*iter)) {
2386 return false;
2387 }
2388 }
2389 return true;
2390}
2391
2392bool VerifyVideoCodecs(const VideoContentDescription* video_desc) {
2393 const std::vector<cricket::VideoCodec>& codecs = video_desc->codecs();
2394 for (std::vector<cricket::VideoCodec>::const_iterator iter = codecs.begin();
2395 iter != codecs.end(); ++iter) {
2396 if (!VerifyCodec(*iter)) {
2397 return false;
2398 }
2399 }
2400 return true;
2401}
2402
2403void AddParameters(const cricket::CodecParameterMap& parameters,
2404 cricket::Codec* codec) {
2405 for (cricket::CodecParameterMap::const_iterator iter =
2406 parameters.begin(); iter != parameters.end(); ++iter) {
2407 codec->SetParam(iter->first, iter->second);
2408 }
2409}
2410
2411void AddFeedbackParameter(const cricket::FeedbackParam& feedback_param,
2412 cricket::Codec* codec) {
2413 codec->AddFeedbackParam(feedback_param);
2414}
2415
2416void AddFeedbackParameters(const cricket::FeedbackParams& feedback_params,
2417 cricket::Codec* codec) {
2418 for (std::vector<cricket::FeedbackParam>::const_iterator iter =
2419 feedback_params.params().begin();
2420 iter != feedback_params.params().end(); ++iter) {
2421 codec->AddFeedbackParam(*iter);
2422 }
2423}
2424
2425// Gets the current codec setting associated with |payload_type|. If there
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +00002426// is no Codec associated with that payload type it returns an empty codec
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002427// with that payload type.
2428template <class T>
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +00002429T GetCodecWithPayloadType(const std::vector<T>& codecs, int payload_type) {
2430 T ret_val;
2431 if (!FindCodecById(codecs, payload_type, &ret_val)) {
2432 ret_val.id = payload_type;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002433 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002434 return ret_val;
2435}
2436
2437// Updates or creates a new codec entry in the audio description.
2438template <class T, class U>
2439void AddOrReplaceCodec(MediaContentDescription* content_desc, const U& codec) {
2440 T* desc = static_cast<T*>(content_desc);
2441 std::vector<U> codecs = desc->codecs();
2442 bool found = false;
2443
2444 typename std::vector<U>::iterator iter;
2445 for (iter = codecs.begin(); iter != codecs.end(); ++iter) {
2446 if (iter->id == codec.id) {
2447 *iter = codec;
2448 found = true;
2449 break;
2450 }
2451 }
2452 if (!found) {
2453 desc->AddCodec(codec);
2454 return;
2455 }
2456 desc->set_codecs(codecs);
2457}
2458
2459// Adds or updates existing codec corresponding to |payload_type| according
2460// to |parameters|.
2461template <class T, class U>
2462void UpdateCodec(MediaContentDescription* content_desc, int payload_type,
2463 const cricket::CodecParameterMap& parameters) {
2464 // Codec might already have been populated (from rtpmap).
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +00002465 U new_codec = GetCodecWithPayloadType(static_cast<T*>(content_desc)->codecs(),
2466 payload_type);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002467 AddParameters(parameters, &new_codec);
2468 AddOrReplaceCodec<T, U>(content_desc, new_codec);
2469}
2470
2471// Adds or updates existing codec corresponding to |payload_type| according
2472// to |feedback_param|.
2473template <class T, class U>
2474void UpdateCodec(MediaContentDescription* content_desc, int payload_type,
2475 const cricket::FeedbackParam& feedback_param) {
2476 // Codec might already have been populated (from rtpmap).
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +00002477 U new_codec = GetCodecWithPayloadType(static_cast<T*>(content_desc)->codecs(),
2478 payload_type);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002479 AddFeedbackParameter(feedback_param, &new_codec);
2480 AddOrReplaceCodec<T, U>(content_desc, new_codec);
2481}
2482
jlmiller@webrtc.orga744a282015-02-18 21:37:46 +00002483template <class T>
2484bool PopWildcardCodec(std::vector<T>* codecs, T* wildcard_codec) {
2485 for (auto iter = codecs->begin(); iter != codecs->end(); ++iter) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002486 if (iter->id == kWildcardPayloadType) {
2487 *wildcard_codec = *iter;
2488 codecs->erase(iter);
2489 return true;
2490 }
2491 }
2492 return false;
2493}
2494
jlmiller@webrtc.orga744a282015-02-18 21:37:46 +00002495template<class T>
2496void UpdateFromWildcardCodecs(cricket::MediaContentDescriptionImpl<T>* desc) {
2497 auto codecs = desc->codecs();
2498 T wildcard_codec;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002499 if (!PopWildcardCodec(&codecs, &wildcard_codec)) {
2500 return;
2501 }
jlmiller@webrtc.orga744a282015-02-18 21:37:46 +00002502 for (auto& codec : codecs) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002503 AddFeedbackParameters(wildcard_codec.feedback_params, &codec);
2504 }
jlmiller@webrtc.orga744a282015-02-18 21:37:46 +00002505 desc->set_codecs(codecs);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002506}
2507
2508void AddAudioAttribute(const std::string& name, const std::string& value,
2509 AudioContentDescription* audio_desc) {
2510 if (value.empty()) {
2511 return;
2512 }
2513 std::vector<cricket::AudioCodec> codecs = audio_desc->codecs();
2514 for (std::vector<cricket::AudioCodec>::iterator iter = codecs.begin();
2515 iter != codecs.end(); ++iter) {
2516 iter->params[name] = value;
2517 }
2518 audio_desc->set_codecs(codecs);
2519}
2520
2521bool ParseContent(const std::string& message,
2522 const MediaType media_type,
2523 int mline_index,
2524 const std::string& protocol,
2525 const std::vector<int>& codec_preference,
2526 size_t* pos,
2527 std::string* content_name,
2528 MediaContentDescription* media_desc,
2529 TransportDescription* transport,
2530 std::vector<JsepIceCandidate*>* candidates,
2531 SdpParseError* error) {
2532 ASSERT(media_desc != NULL);
2533 ASSERT(content_name != NULL);
2534 ASSERT(transport != NULL);
2535
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +00002536 if (media_type == cricket::MEDIA_TYPE_AUDIO) {
2537 MaybeCreateStaticPayloadAudioCodecs(
2538 codec_preference, static_cast<AudioContentDescription*>(media_desc));
2539 }
2540
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002541 // The media level "ice-ufrag" and "ice-pwd".
2542 // The candidates before update the media level "ice-pwd" and "ice-ufrag".
2543 Candidates candidates_orig;
2544 std::string line;
2545 std::string mline_id;
2546 // Tracks created out of the ssrc attributes.
2547 StreamParamsVec tracks;
2548 SsrcInfoVec ssrc_infos;
2549 SsrcGroupVec ssrc_groups;
2550 std::string maxptime_as_string;
2551 std::string ptime_as_string;
deadbeef9d3584c2016-02-16 17:54:10 -08002552 std::string stream_id;
2553 std::string track_id;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002554
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002555 // Loop until the next m line
2556 while (!IsLineType(message, kLineTypeMedia, *pos)) {
2557 if (!GetLine(message, pos, &line)) {
2558 if (*pos >= message.size()) {
2559 break; // Done parsing
2560 } else {
sergeyu@chromium.org5bc25c42013-12-05 00:24:06 +00002561 return ParseFailed(message, *pos, "Invalid SDP line.", error);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002562 }
2563 }
2564
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002565 // RFC 4566
2566 // b=* (zero or more bandwidth information lines)
2567 if (IsLineType(line, kLineTypeSessionBandwidth)) {
2568 std::string bandwidth;
2569 if (HasAttribute(line, kApplicationSpecificMaximum)) {
2570 if (!GetValue(line, kApplicationSpecificMaximum, &bandwidth, error)) {
2571 return false;
2572 } else {
wu@webrtc.org5e760e72014-04-02 23:19:09 +00002573 int b = 0;
2574 if (!GetValueFromString(line, bandwidth, &b, error)) {
2575 return false;
2576 }
Peter Thatcherc0c3a862015-06-24 15:31:25 -07002577 // We should never use more than the default bandwidth for RTP-based
2578 // data channels. Don't allow SDP to set the bandwidth, because
2579 // that would give JS the opportunity to "break the Internet".
2580 // See: https://code.google.com/p/chromium/issues/detail?id=280726
2581 if (media_type == cricket::MEDIA_TYPE_DATA && IsRtp(protocol) &&
2582 b > cricket::kDataMaxBandwidth / 1000) {
2583 std::ostringstream description;
2584 description << "RTP-based data channels may not send more than "
2585 << cricket::kDataMaxBandwidth / 1000 << "kbps.";
2586 return ParseFailed(line, description.str(), error);
2587 }
wu@webrtc.org5e760e72014-04-02 23:19:09 +00002588 media_desc->set_bandwidth(b * 1000);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002589 }
2590 }
2591 continue;
2592 }
2593
2594 if (!IsLineType(line, kLineTypeAttributes)) {
2595 // TODO: Handle other lines if needed.
2596 LOG(LS_INFO) << "Ignored line: " << line;
2597 continue;
2598 }
2599
2600 // Handle attributes common to SCTP and RTP.
2601 if (HasAttribute(line, kAttributeMid)) {
2602 // RFC 3388
2603 // mid-attribute = "a=mid:" identification-tag
2604 // identification-tag = token
2605 // Use the mid identification-tag as the content name.
2606 if (!GetValue(line, kAttributeMid, &mline_id, error)) {
2607 return false;
2608 }
2609 *content_name = mline_id;
2610 } else if (HasAttribute(line, kAttributeCandidate)) {
2611 Candidate candidate;
2612 if (!ParseCandidate(line, &candidate, error, false)) {
2613 return false;
2614 }
2615 candidates_orig.push_back(candidate);
2616 } else if (HasAttribute(line, kAttributeIceUfrag)) {
2617 if (!GetValue(line, kAttributeIceUfrag, &transport->ice_ufrag, error)) {
2618 return false;
2619 }
2620 } else if (HasAttribute(line, kAttributeIcePwd)) {
2621 if (!GetValue(line, kAttributeIcePwd, &transport->ice_pwd, error)) {
2622 return false;
2623 }
2624 } else if (HasAttribute(line, kAttributeIceOption)) {
2625 if (!ParseIceOptions(line, &transport->transport_options, error)) {
2626 return false;
2627 }
2628 } else if (HasAttribute(line, kAttributeFmtp)) {
2629 if (!ParseFmtpAttributes(line, media_type, media_desc, error)) {
2630 return false;
2631 }
2632 } else if (HasAttribute(line, kAttributeFingerprint)) {
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002633 rtc::SSLFingerprint* fingerprint = NULL;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002634
2635 if (!ParseFingerprintAttribute(line, &fingerprint, error)) {
2636 return false;
2637 }
2638 transport->identity_fingerprint.reset(fingerprint);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00002639 } else if (HasAttribute(line, kAttributeSetup)) {
2640 if (!ParseDtlsSetup(line, &(transport->connection_role), error)) {
2641 return false;
2642 }
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00002643 } else if (IsDtlsSctp(protocol) && HasAttribute(line, kAttributeSctpPort)) {
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00002644 int sctp_port;
2645 if (!ParseSctpPort(line, &sctp_port, error)) {
2646 return false;
2647 }
2648 if (!AddSctpDataCodec(static_cast<DataContentDescription*>(media_desc),
2649 sctp_port)) {
2650 return false;
2651 }
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00002652 } else if (IsRtp(protocol)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002653 //
2654 // RTP specific attrubtes
2655 //
2656 if (HasAttribute(line, kAttributeRtcpMux)) {
2657 media_desc->set_rtcp_mux(true);
deadbeef13871492015-12-09 12:37:51 -08002658 } else if (HasAttribute(line, kAttributeRtcpReducedSize)) {
2659 media_desc->set_rtcp_reduced_size(true);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002660 } else if (HasAttribute(line, kAttributeSsrcGroup)) {
2661 if (!ParseSsrcGroupAttribute(line, &ssrc_groups, error)) {
2662 return false;
2663 }
2664 } else if (HasAttribute(line, kAttributeSsrc)) {
2665 if (!ParseSsrcAttribute(line, &ssrc_infos, error)) {
2666 return false;
2667 }
2668 } else if (HasAttribute(line, kAttributeCrypto)) {
2669 if (!ParseCryptoAttribute(line, media_desc, error)) {
2670 return false;
2671 }
2672 } else if (HasAttribute(line, kAttributeRtpmap)) {
2673 if (!ParseRtpmapAttribute(line, media_type, codec_preference,
2674 media_desc, error)) {
2675 return false;
2676 }
2677 } else if (HasAttribute(line, kCodecParamMaxPTime)) {
2678 if (!GetValue(line, kCodecParamMaxPTime, &maxptime_as_string, error)) {
2679 return false;
2680 }
2681 } else if (HasAttribute(line, kAttributeRtcpFb)) {
2682 if (!ParseRtcpFbAttribute(line, media_type, media_desc, error)) {
2683 return false;
2684 }
2685 } else if (HasAttribute(line, kCodecParamPTime)) {
2686 if (!GetValue(line, kCodecParamPTime, &ptime_as_string, error)) {
2687 return false;
2688 }
2689 } else if (HasAttribute(line, kAttributeSendOnly)) {
2690 media_desc->set_direction(cricket::MD_SENDONLY);
2691 } else if (HasAttribute(line, kAttributeRecvOnly)) {
2692 media_desc->set_direction(cricket::MD_RECVONLY);
2693 } else if (HasAttribute(line, kAttributeInactive)) {
2694 media_desc->set_direction(cricket::MD_INACTIVE);
2695 } else if (HasAttribute(line, kAttributeSendRecv)) {
2696 media_desc->set_direction(cricket::MD_SENDRECV);
2697 } else if (HasAttribute(line, kAttributeExtmap)) {
2698 RtpHeaderExtension extmap;
2699 if (!ParseExtmap(line, &extmap, error)) {
2700 return false;
2701 }
2702 media_desc->AddRtpHeaderExtension(extmap);
2703 } else if (HasAttribute(line, kAttributeXGoogleFlag)) {
2704 // Experimental attribute. Conference mode activates more aggressive
2705 // AEC and NS settings.
2706 // TODO: expose API to set these directly.
2707 std::string flag_value;
2708 if (!GetValue(line, kAttributeXGoogleFlag, &flag_value, error)) {
2709 return false;
2710 }
2711 if (flag_value.compare(kValueConference) == 0)
2712 media_desc->set_conference_mode(true);
deadbeef9d3584c2016-02-16 17:54:10 -08002713 } else if (HasAttribute(line, kAttributeMsid)) {
2714 if (!ParseMsidAttribute(line, &stream_id, &track_id, error)) {
2715 return false;
2716 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002717 }
2718 } else {
2719 // Only parse lines that we are interested of.
2720 LOG(LS_INFO) << "Ignored line: " << line;
2721 continue;
2722 }
2723 }
2724
2725 // Create tracks from the |ssrc_infos|.
Taylor Brandstetter5de6b752016-03-09 17:02:30 -08002726 // If the stream_id/track_id for all SSRCS are identical, one StreamParams
2727 // will be created in CreateTracksFromSsrcInfos, containing all the SSRCs from
2728 // the m= section.
2729 CreateTracksFromSsrcInfos(ssrc_infos, stream_id, track_id, &tracks);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002730
2731 // Add the ssrc group to the track.
2732 for (SsrcGroupVec::iterator ssrc_group = ssrc_groups.begin();
2733 ssrc_group != ssrc_groups.end(); ++ssrc_group) {
2734 if (ssrc_group->ssrcs.empty()) {
2735 continue;
2736 }
Peter Boström0c4e06b2015-10-07 12:23:21 +02002737 uint32_t ssrc = ssrc_group->ssrcs.front();
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002738 for (StreamParamsVec::iterator track = tracks.begin();
2739 track != tracks.end(); ++track) {
2740 if (track->has_ssrc(ssrc)) {
2741 track->ssrc_groups.push_back(*ssrc_group);
2742 }
2743 }
2744 }
2745
2746 // Add the new tracks to the |media_desc|.
deadbeef9d3584c2016-02-16 17:54:10 -08002747 for (StreamParams& track : tracks) {
2748 media_desc->AddStream(track);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002749 }
2750
2751 if (media_type == cricket::MEDIA_TYPE_AUDIO) {
2752 AudioContentDescription* audio_desc =
2753 static_cast<AudioContentDescription*>(media_desc);
jlmiller@webrtc.orga744a282015-02-18 21:37:46 +00002754 UpdateFromWildcardCodecs(audio_desc);
2755
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002756 // Verify audio codec ensures that no audio codec has been populated with
2757 // only fmtp.
2758 if (!VerifyAudioCodecs(audio_desc)) {
2759 return ParseFailed("Failed to parse audio codecs correctly.", error);
2760 }
2761 AddAudioAttribute(kCodecParamMaxPTime, maxptime_as_string, audio_desc);
2762 AddAudioAttribute(kCodecParamPTime, ptime_as_string, audio_desc);
2763 }
2764
2765 if (media_type == cricket::MEDIA_TYPE_VIDEO) {
jlmiller@webrtc.orga744a282015-02-18 21:37:46 +00002766 VideoContentDescription* video_desc =
2767 static_cast<VideoContentDescription*>(media_desc);
2768 UpdateFromWildcardCodecs(video_desc);
2769 // Verify video codec ensures that no video codec has been populated with
2770 // only rtcp-fb.
2771 if (!VerifyVideoCodecs(video_desc)) {
2772 return ParseFailed("Failed to parse video codecs correctly.", error);
2773 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002774 }
2775
2776 // RFC 5245
2777 // Update the candidates with the media level "ice-pwd" and "ice-ufrag".
2778 for (Candidates::iterator it = candidates_orig.begin();
2779 it != candidates_orig.end(); ++it) {
honghaiza54a0802015-12-16 18:37:23 -08002780 ASSERT((*it).username().empty() ||
2781 (*it).username() == transport->ice_ufrag);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002782 (*it).set_username(transport->ice_ufrag);
2783 ASSERT((*it).password().empty());
2784 (*it).set_password(transport->ice_pwd);
2785 candidates->push_back(
2786 new JsepIceCandidate(mline_id, mline_index, *it));
2787 }
2788 return true;
2789}
2790
2791bool ParseSsrcAttribute(const std::string& line, SsrcInfoVec* ssrc_infos,
2792 SdpParseError* error) {
2793 ASSERT(ssrc_infos != NULL);
2794 // RFC 5576
2795 // a=ssrc:<ssrc-id> <attribute>
2796 // a=ssrc:<ssrc-id> <attribute>:<value>
2797 std::string field1, field2;
Donald Curtis144d0182015-05-15 13:14:24 -07002798 if (!rtc::tokenize_first(line.substr(kLinePrefixLength), kSdpDelimiterSpace,
2799 &field1, &field2)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002800 const size_t expected_fields = 2;
2801 return ParseFailedExpectFieldNum(line, expected_fields, error);
2802 }
2803
2804 // ssrc:<ssrc-id>
2805 std::string ssrc_id_s;
2806 if (!GetValue(field1, kAttributeSsrc, &ssrc_id_s, error)) {
2807 return false;
2808 }
Peter Boström0c4e06b2015-10-07 12:23:21 +02002809 uint32_t ssrc_id = 0;
wu@webrtc.org5e760e72014-04-02 23:19:09 +00002810 if (!GetValueFromString(line, ssrc_id_s, &ssrc_id, error)) {
2811 return false;
2812 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002813
2814 std::string attribute;
2815 std::string value;
Donald Curtis144d0182015-05-15 13:14:24 -07002816 if (!rtc::tokenize_first(field2, kSdpDelimiterColon, &attribute, &value)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002817 std::ostringstream description;
2818 description << "Failed to get the ssrc attribute value from " << field2
2819 << ". Expected format <attribute>:<value>.";
2820 return ParseFailed(line, description.str(), error);
2821 }
2822
2823 // Check if there's already an item for this |ssrc_id|. Create a new one if
2824 // there isn't.
2825 SsrcInfoVec::iterator ssrc_info = ssrc_infos->begin();
2826 for (; ssrc_info != ssrc_infos->end(); ++ssrc_info) {
2827 if (ssrc_info->ssrc_id == ssrc_id) {
2828 break;
2829 }
2830 }
2831 if (ssrc_info == ssrc_infos->end()) {
2832 SsrcInfo info;
2833 info.ssrc_id = ssrc_id;
2834 ssrc_infos->push_back(info);
2835 ssrc_info = ssrc_infos->end() - 1;
2836 }
2837
2838 // Store the info to the |ssrc_info|.
2839 if (attribute == kSsrcAttributeCname) {
2840 // RFC 5576
2841 // cname:<value>
2842 ssrc_info->cname = value;
2843 } else if (attribute == kSsrcAttributeMsid) {
2844 // draft-alvestrand-mmusic-msid-00
2845 // "msid:" identifier [ " " appdata ]
2846 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002847 rtc::split(value, kSdpDelimiterSpace, &fields);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002848 if (fields.size() < 1 || fields.size() > 2) {
2849 return ParseFailed(line,
2850 "Expected format \"msid:<identifier>[ <appdata>]\".",
2851 error);
2852 }
deadbeef9d3584c2016-02-16 17:54:10 -08002853 ssrc_info->stream_id = fields[0];
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002854 if (fields.size() == 2) {
deadbeef9d3584c2016-02-16 17:54:10 -08002855 ssrc_info->track_id = fields[1];
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002856 }
2857 } else if (attribute == kSsrcAttributeMslabel) {
2858 // draft-alvestrand-rtcweb-mid-01
2859 // mslabel:<value>
2860 ssrc_info->mslabel = value;
2861 } else if (attribute == kSSrcAttributeLabel) {
2862 // The label isn't defined.
2863 // label:<value>
2864 ssrc_info->label = value;
2865 }
2866 return true;
2867}
2868
2869bool ParseSsrcGroupAttribute(const std::string& line,
2870 SsrcGroupVec* ssrc_groups,
2871 SdpParseError* error) {
2872 ASSERT(ssrc_groups != NULL);
2873 // RFC 5576
2874 // a=ssrc-group:<semantics> <ssrc-id> ...
2875 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002876 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002877 kSdpDelimiterSpace, &fields);
2878 const size_t expected_min_fields = 2;
2879 if (fields.size() < expected_min_fields) {
2880 return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
2881 }
2882 std::string semantics;
2883 if (!GetValue(fields[0], kAttributeSsrcGroup, &semantics, error)) {
2884 return false;
2885 }
Peter Boström0c4e06b2015-10-07 12:23:21 +02002886 std::vector<uint32_t> ssrcs;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002887 for (size_t i = 1; i < fields.size(); ++i) {
Peter Boström0c4e06b2015-10-07 12:23:21 +02002888 uint32_t ssrc = 0;
wu@webrtc.org5e760e72014-04-02 23:19:09 +00002889 if (!GetValueFromString(line, fields[i], &ssrc, error)) {
2890 return false;
2891 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002892 ssrcs.push_back(ssrc);
2893 }
2894 ssrc_groups->push_back(SsrcGroup(semantics, ssrcs));
2895 return true;
2896}
2897
2898bool ParseCryptoAttribute(const std::string& line,
2899 MediaContentDescription* media_desc,
2900 SdpParseError* error) {
2901 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002902 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002903 kSdpDelimiterSpace, &fields);
2904 // RFC 4568
2905 // a=crypto:<tag> <crypto-suite> <key-params> [<session-params>]
2906 const size_t expected_min_fields = 3;
2907 if (fields.size() < expected_min_fields) {
2908 return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
2909 }
2910 std::string tag_value;
2911 if (!GetValue(fields[0], kAttributeCrypto, &tag_value, error)) {
2912 return false;
2913 }
wu@webrtc.org5e760e72014-04-02 23:19:09 +00002914 int tag = 0;
2915 if (!GetValueFromString(line, tag_value, &tag, error)) {
2916 return false;
2917 }
jbauch083b73f2015-07-16 02:46:32 -07002918 const std::string& crypto_suite = fields[1];
2919 const std::string& key_params = fields[2];
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002920 std::string session_params;
2921 if (fields.size() > 3) {
2922 session_params = fields[3];
2923 }
2924 media_desc->AddCrypto(CryptoParams(tag, crypto_suite, key_params,
2925 session_params));
2926 return true;
2927}
2928
2929// Updates or creates a new codec entry in the audio description with according
2930// to |name|, |clockrate|, |bitrate|, |channels| and |preference|.
2931void UpdateCodec(int payload_type, const std::string& name, int clockrate,
Peter Kasting69558702016-01-12 16:26:35 -08002932 int bitrate, size_t channels, int preference,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002933 AudioContentDescription* audio_desc) {
2934 // Codec may already be populated with (only) optional parameters
2935 // (from an fmtp).
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +00002936 cricket::AudioCodec codec =
2937 GetCodecWithPayloadType(audio_desc->codecs(), payload_type);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002938 codec.name = name;
2939 codec.clockrate = clockrate;
2940 codec.bitrate = bitrate;
2941 codec.channels = channels;
2942 codec.preference = preference;
2943 AddOrReplaceCodec<AudioContentDescription, cricket::AudioCodec>(audio_desc,
2944 codec);
2945}
2946
2947// Updates or creates a new codec entry in the video description according to
2948// |name|, |width|, |height|, |framerate| and |preference|.
2949void UpdateCodec(int payload_type, const std::string& name, int width,
2950 int height, int framerate, int preference,
2951 VideoContentDescription* video_desc) {
2952 // Codec may already be populated with (only) optional parameters
2953 // (from an fmtp).
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +00002954 cricket::VideoCodec codec =
2955 GetCodecWithPayloadType(video_desc->codecs(), payload_type);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002956 codec.name = name;
2957 codec.width = width;
2958 codec.height = height;
2959 codec.framerate = framerate;
2960 codec.preference = preference;
2961 AddOrReplaceCodec<VideoContentDescription, cricket::VideoCodec>(video_desc,
2962 codec);
2963}
2964
2965bool ParseRtpmapAttribute(const std::string& line,
2966 const MediaType media_type,
2967 const std::vector<int>& codec_preference,
2968 MediaContentDescription* media_desc,
2969 SdpParseError* error) {
2970 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002971 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002972 kSdpDelimiterSpace, &fields);
2973 // RFC 4566
2974 // a=rtpmap:<payload type> <encoding name>/<clock rate>[/<encodingparameters>]
2975 const size_t expected_min_fields = 2;
2976 if (fields.size() < expected_min_fields) {
2977 return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
2978 }
2979 std::string payload_type_value;
2980 if (!GetValue(fields[0], kAttributeRtpmap, &payload_type_value, error)) {
2981 return false;
2982 }
wu@webrtc.org5e760e72014-04-02 23:19:09 +00002983 int payload_type = 0;
pkasting@chromium.orge9facf82015-02-17 20:36:28 +00002984 if (!GetPayloadTypeFromString(line, payload_type_value, &payload_type,
2985 error)) {
wu@webrtc.org5e760e72014-04-02 23:19:09 +00002986 return false;
2987 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002988
2989 // Set the preference order depending on the order of the pl type in the
2990 // <fmt> of the m-line.
2991 const int preference = codec_preference.end() -
2992 std::find(codec_preference.begin(), codec_preference.end(),
2993 payload_type);
2994 if (preference == 0) {
2995 LOG(LS_WARNING) << "Ignore rtpmap line that did not appear in the "
2996 << "<fmt> of the m-line: " << line;
2997 return true;
2998 }
jbauch083b73f2015-07-16 02:46:32 -07002999 const std::string& encoder = fields[1];
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003000 std::vector<std::string> codec_params;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00003001 rtc::split(encoder, '/', &codec_params);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003002 // <encoding name>/<clock rate>[/<encodingparameters>]
3003 // 2 mandatory fields
3004 if (codec_params.size() < 2 || codec_params.size() > 3) {
3005 return ParseFailed(line,
3006 "Expected format \"<encoding name>/<clock rate>"
3007 "[/<encodingparameters>]\".",
3008 error);
3009 }
jbauch083b73f2015-07-16 02:46:32 -07003010 const std::string& encoding_name = codec_params[0];
wu@webrtc.org5e760e72014-04-02 23:19:09 +00003011 int clock_rate = 0;
3012 if (!GetValueFromString(line, codec_params[1], &clock_rate, error)) {
3013 return false;
3014 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003015 if (media_type == cricket::MEDIA_TYPE_VIDEO) {
3016 VideoContentDescription* video_desc =
3017 static_cast<VideoContentDescription*>(media_desc);
3018 // TODO: We will send resolution in SDP. For now use
3019 // JsepSessionDescription::kMaxVideoCodecWidth and kMaxVideoCodecHeight.
3020 UpdateCodec(payload_type, encoding_name,
3021 JsepSessionDescription::kMaxVideoCodecWidth,
3022 JsepSessionDescription::kMaxVideoCodecHeight,
3023 JsepSessionDescription::kDefaultVideoCodecFramerate,
3024 preference, video_desc);
3025 } else if (media_type == cricket::MEDIA_TYPE_AUDIO) {
3026 // RFC 4566
3027 // For audio streams, <encoding parameters> indicates the number
3028 // of audio channels. This parameter is OPTIONAL and may be
3029 // omitted if the number of channels is one, provided that no
3030 // additional parameters are needed.
Peter Kasting69558702016-01-12 16:26:35 -08003031 size_t channels = 1;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003032 if (codec_params.size() == 3) {
wu@webrtc.org5e760e72014-04-02 23:19:09 +00003033 if (!GetValueFromString(line, codec_params[2], &channels, error)) {
3034 return false;
3035 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003036 }
3037 int bitrate = 0;
3038 // The default behavior for ISAC (bitrate == 0) in webrtcvoiceengine.cc
3039 // (specifically FindWebRtcCodec) is bandwidth-adaptive variable bitrate.
3040 // The bandwidth adaptation doesn't always work well, so this code
3041 // sets a fixed target bitrate instead.
3042 if (_stricmp(encoding_name.c_str(), kIsacCodecName) == 0) {
3043 if (clock_rate <= 16000) {
3044 bitrate = kIsacWbDefaultRate;
3045 } else {
3046 bitrate = kIsacSwbDefaultRate;
3047 }
3048 }
3049 AudioContentDescription* audio_desc =
3050 static_cast<AudioContentDescription*>(media_desc);
3051 UpdateCodec(payload_type, encoding_name, clock_rate, bitrate, channels,
3052 preference, audio_desc);
3053 } else if (media_type == cricket::MEDIA_TYPE_DATA) {
3054 DataContentDescription* data_desc =
3055 static_cast<DataContentDescription*>(media_desc);
3056 data_desc->AddCodec(cricket::DataCodec(payload_type, encoding_name,
3057 preference));
3058 }
3059 return true;
3060}
3061
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003062bool ParseFmtpParam(const std::string& line, std::string* parameter,
3063 std::string* value, SdpParseError* error) {
Donald Curtis0e07f922015-05-15 09:21:23 -07003064 if (!rtc::tokenize_first(line, kSdpDelimiterEqual, parameter, value)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003065 ParseFailed(line, "Unable to parse fmtp parameter. \'=\' missing.", error);
3066 return false;
3067 }
3068 // a=fmtp:<payload_type> <param1>=<value1>; <param2>=<value2>; ...
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003069 return true;
3070}
3071
3072bool ParseFmtpAttributes(const std::string& line, const MediaType media_type,
3073 MediaContentDescription* media_desc,
3074 SdpParseError* error) {
3075 if (media_type != cricket::MEDIA_TYPE_AUDIO &&
3076 media_type != cricket::MEDIA_TYPE_VIDEO) {
3077 return true;
3078 }
Donald Curtis0e07f922015-05-15 09:21:23 -07003079
3080 std::string line_payload;
3081 std::string line_params;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003082
3083 // RFC 5576
3084 // a=fmtp:<format> <format specific parameters>
3085 // At least two fields, whereas the second one is any of the optional
3086 // parameters.
Donald Curtis144d0182015-05-15 13:14:24 -07003087 if (!rtc::tokenize_first(line.substr(kLinePrefixLength), kSdpDelimiterSpace,
3088 &line_payload, &line_params)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003089 ParseFailedExpectMinFieldNum(line, 2, error);
3090 return false;
3091 }
3092
Donald Curtis0e07f922015-05-15 09:21:23 -07003093 // Parse out the payload information.
pkasting@chromium.orgd3245462015-02-23 21:28:22 +00003094 std::string payload_type_str;
Donald Curtis0e07f922015-05-15 09:21:23 -07003095 if (!GetValue(line_payload, kAttributeFmtp, &payload_type_str, error)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003096 return false;
3097 }
3098
Donald Curtis0e07f922015-05-15 09:21:23 -07003099 int payload_type = 0;
Donald Curtis144d0182015-05-15 13:14:24 -07003100 if (!GetPayloadTypeFromString(line_payload, payload_type_str, &payload_type,
3101 error)) {
Donald Curtis0e07f922015-05-15 09:21:23 -07003102 return false;
3103 }
3104
3105 // Parse out format specific parameters.
3106 std::vector<std::string> fields;
3107 rtc::split(line_params, kSdpDelimiterSemicolon, &fields);
3108
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003109 cricket::CodecParameterMap codec_params;
Donald Curtis144d0182015-05-15 13:14:24 -07003110 for (auto& iter : fields) {
Donald Curtis0e07f922015-05-15 09:21:23 -07003111 if (iter.find(kSdpDelimiterEqual) == std::string::npos) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003112 // Only fmtps with equals are currently supported. Other fmtp types
3113 // should be ignored. Unknown fmtps do not constitute an error.
3114 continue;
3115 }
Donald Curtis0e07f922015-05-15 09:21:23 -07003116
3117 std::string name;
3118 std::string value;
3119 if (!ParseFmtpParam(rtc::string_trim(iter), &name, &value, error)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003120 return false;
3121 }
3122 codec_params[name] = value;
3123 }
3124
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003125 if (media_type == cricket::MEDIA_TYPE_AUDIO) {
3126 UpdateCodec<AudioContentDescription, cricket::AudioCodec>(
pkasting@chromium.orgd3245462015-02-23 21:28:22 +00003127 media_desc, payload_type, codec_params);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003128 } else if (media_type == cricket::MEDIA_TYPE_VIDEO) {
3129 UpdateCodec<VideoContentDescription, cricket::VideoCodec>(
pkasting@chromium.orgd3245462015-02-23 21:28:22 +00003130 media_desc, payload_type, codec_params);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003131 }
3132 return true;
3133}
3134
3135bool ParseRtcpFbAttribute(const std::string& line, const MediaType media_type,
3136 MediaContentDescription* media_desc,
3137 SdpParseError* error) {
3138 if (media_type != cricket::MEDIA_TYPE_AUDIO &&
3139 media_type != cricket::MEDIA_TYPE_VIDEO) {
3140 return true;
3141 }
3142 std::vector<std::string> rtcp_fb_fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00003143 rtc::split(line.c_str(), kSdpDelimiterSpace, &rtcp_fb_fields);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003144 if (rtcp_fb_fields.size() < 2) {
3145 return ParseFailedGetValue(line, kAttributeRtcpFb, error);
3146 }
3147 std::string payload_type_string;
3148 if (!GetValue(rtcp_fb_fields[0], kAttributeRtcpFb, &payload_type_string,
3149 error)) {
3150 return false;
3151 }
wu@webrtc.org5e760e72014-04-02 23:19:09 +00003152 int payload_type = kWildcardPayloadType;
3153 if (payload_type_string != "*") {
pkasting@chromium.orge9facf82015-02-17 20:36:28 +00003154 if (!GetPayloadTypeFromString(line, payload_type_string, &payload_type,
3155 error)) {
wu@webrtc.org5e760e72014-04-02 23:19:09 +00003156 return false;
3157 }
3158 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003159 std::string id = rtcp_fb_fields[1];
3160 std::string param = "";
3161 for (std::vector<std::string>::iterator iter = rtcp_fb_fields.begin() + 2;
3162 iter != rtcp_fb_fields.end(); ++iter) {
3163 param.append(*iter);
3164 }
3165 const cricket::FeedbackParam feedback_param(id, param);
3166
3167 if (media_type == cricket::MEDIA_TYPE_AUDIO) {
pkasting@chromium.orgd3245462015-02-23 21:28:22 +00003168 UpdateCodec<AudioContentDescription, cricket::AudioCodec>(
3169 media_desc, payload_type, feedback_param);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003170 } else if (media_type == cricket::MEDIA_TYPE_VIDEO) {
pkasting@chromium.orgd3245462015-02-23 21:28:22 +00003171 UpdateCodec<VideoContentDescription, cricket::VideoCodec>(
3172 media_desc, payload_type, feedback_param);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003173 }
3174 return true;
3175}
3176
3177} // namespace webrtc