blob: fa052419c0f3dd49c683f5ff51b85f2cd4bd5e77 [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>
kwibergd1fe2812016-04-27 06:47:29 -070016
henrike@webrtc.org28e20752013-07-10 00:45:36 +000017#include <algorithm>
kwibergd1fe2812016-04-27 06:47:29 -070018#include <memory>
henrike@webrtc.org28e20752013-07-10 00:45:36 +000019#include <string>
deadbeef67cf2c12016-04-13 10:07:16 -070020#include <unordered_map>
henrike@webrtc.org28e20752013-07-10 00:45:36 +000021#include <vector>
22
Henrik Kjellander15583c12016-02-10 10:53:12 +010023#include "webrtc/api/jsepicecandidate.h"
24#include "webrtc/api/jsepsessiondescription.h"
tfarina5237aaf2015-11-10 23:44:30 -080025#include "webrtc/base/arraysize.h"
buildbot@webrtc.orga09a9992014-08-13 17:26:08 +000026#include "webrtc/base/common.h"
27#include "webrtc/base/logging.h"
28#include "webrtc/base/messagedigest.h"
29#include "webrtc/base/stringutils.h"
kjellandera96e2d72016-02-04 23:52:28 -080030#include "webrtc/media/base/codec.h"
kjellandera96e2d72016-02-04 23:52:28 -080031#include "webrtc/media/base/cryptoparams.h"
kjellanderf4752772016-03-02 05:42:30 -080032#include "webrtc/media/base/mediaconstants.h"
kjellandera96e2d72016-02-04 23:52:28 -080033#include "webrtc/media/base/rtputils.h"
34#include "webrtc/media/sctp/sctpdataengine.h"
35#include "webrtc/p2p/base/candidate.h"
kjellanderf4752772016-03-02 05:42:30 -080036#include "webrtc/p2p/base/p2pconstants.h"
kjellandera96e2d72016-02-04 23:52:28 -080037#include "webrtc/p2p/base/port.h"
kjellander@webrtc.org9b8df252016-02-12 06:47:59 +010038#include "webrtc/pc/mediasession.h"
henrike@webrtc.org28e20752013-07-10 00:45:36 +000039
40using cricket::AudioContentDescription;
41using cricket::Candidate;
42using cricket::Candidates;
43using cricket::ContentDescription;
44using cricket::ContentInfo;
45using cricket::CryptoParams;
46using cricket::DataContentDescription;
47using cricket::ICE_CANDIDATE_COMPONENT_RTP;
48using cricket::ICE_CANDIDATE_COMPONENT_RTCP;
49using cricket::kCodecParamMaxBitrate;
50using cricket::kCodecParamMaxPTime;
51using cricket::kCodecParamMaxQuantization;
52using cricket::kCodecParamMinBitrate;
53using cricket::kCodecParamMinPTime;
54using cricket::kCodecParamPTime;
55using cricket::kCodecParamSPropStereo;
buildbot@webrtc.orged97bb02014-05-07 11:15:20 +000056using cricket::kCodecParamStartBitrate;
henrike@webrtc.org28e20752013-07-10 00:45:36 +000057using cricket::kCodecParamStereo;
58using cricket::kCodecParamUseInbandFec;
Minyue Li7100dcd2015-03-27 05:05:59 +010059using cricket::kCodecParamUseDtx;
henrike@webrtc.org28e20752013-07-10 00:45:36 +000060using cricket::kCodecParamSctpProtocol;
61using cricket::kCodecParamSctpStreams;
henrike@webrtc.org1e09a712013-07-26 19:17:59 +000062using cricket::kCodecParamMaxAverageBitrate;
buildbot@webrtc.org5d639b32014-09-10 07:57:12 +000063using cricket::kCodecParamMaxPlaybackRate;
stefan@webrtc.org85d27942014-06-09 12:51:39 +000064using cricket::kCodecParamAssociatedPayloadType;
henrike@webrtc.org28e20752013-07-10 00:45:36 +000065using cricket::MediaContentDescription;
66using cricket::MediaType;
henrike@webrtc.org28e20752013-07-10 00:45:36 +000067using cricket::RtpHeaderExtension;
68using cricket::SsrcGroup;
69using cricket::StreamParams;
70using cricket::StreamParamsVec;
71using cricket::TransportDescription;
72using cricket::TransportInfo;
73using cricket::VideoContentDescription;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +000074using rtc::SocketAddress;
henrike@webrtc.org28e20752013-07-10 00:45:36 +000075
76typedef std::vector<RtpHeaderExtension> RtpHeaderExtensions;
77
78namespace cricket {
79class SessionDescription;
80}
81
82namespace webrtc {
83
84// Line type
85// RFC 4566
86// An SDP session description consists of a number of lines of text of
87// the form:
88// <type>=<value>
89// where <type> MUST be exactly one case-significant character.
deadbeef9d3584c2016-02-16 17:54:10 -080090static const int kLinePrefixLength = 2; // Length of <type>=
henrike@webrtc.org28e20752013-07-10 00:45:36 +000091static const char kLineTypeVersion = 'v';
92static const char kLineTypeOrigin = 'o';
93static const char kLineTypeSessionName = 's';
94static const char kLineTypeSessionInfo = 'i';
95static const char kLineTypeSessionUri = 'u';
96static const char kLineTypeSessionEmail = 'e';
97static const char kLineTypeSessionPhone = 'p';
98static const char kLineTypeSessionBandwidth = 'b';
99static const char kLineTypeTiming = 't';
100static const char kLineTypeRepeatTimes = 'r';
101static const char kLineTypeTimeZone = 'z';
102static const char kLineTypeEncryptionKey = 'k';
103static const char kLineTypeMedia = 'm';
104static const char kLineTypeConnection = 'c';
105static const char kLineTypeAttributes = 'a';
106
107// Attributes
108static const char kAttributeGroup[] = "group";
109static const char kAttributeMid[] = "mid";
deadbeef9d3584c2016-02-16 17:54:10 -0800110static const char kAttributeMsid[] = "msid";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000111static const char kAttributeRtcpMux[] = "rtcp-mux";
deadbeef13871492015-12-09 12:37:51 -0800112static const char kAttributeRtcpReducedSize[] = "rtcp-rsize";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000113static const char kAttributeSsrc[] = "ssrc";
114static const char kSsrcAttributeCname[] = "cname";
115static const char kAttributeExtmap[] = "extmap";
116// draft-alvestrand-mmusic-msid-01
117// a=msid-semantic: WMS
118static const char kAttributeMsidSemantics[] = "msid-semantic";
119static const char kMediaStreamSemantic[] = "WMS";
120static const char kSsrcAttributeMsid[] = "msid";
121static const char kDefaultMsid[] = "default";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000122static const char kSsrcAttributeMslabel[] = "mslabel";
123static const char kSSrcAttributeLabel[] = "label";
124static const char kAttributeSsrcGroup[] = "ssrc-group";
125static const char kAttributeCrypto[] = "crypto";
126static const char kAttributeCandidate[] = "candidate";
127static const char kAttributeCandidateTyp[] = "typ";
128static const char kAttributeCandidateRaddr[] = "raddr";
129static const char kAttributeCandidateRport[] = "rport";
honghaiza54a0802015-12-16 18:37:23 -0800130static const char kAttributeCandidateUfrag[] = "ufrag";
131static const char kAttributeCandidatePwd[] = "pwd";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000132static const char kAttributeCandidateGeneration[] = "generation";
honghaiza0c44ea2016-03-23 16:07:48 -0700133static const char kAttributeCandidateNetworkId[] = "network-id";
honghaize1a0c942016-02-16 14:54:56 -0800134static const char kAttributeCandidateNetworkCost[] = "network-cost";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000135static const char kAttributeFingerprint[] = "fingerprint";
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +0000136static const char kAttributeSetup[] = "setup";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000137static const char kAttributeFmtp[] = "fmtp";
138static const char kAttributeRtpmap[] = "rtpmap";
wu@webrtc.org78187522013-10-07 23:32:02 +0000139static const char kAttributeSctpmap[] = "sctpmap";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000140static const char kAttributeRtcp[] = "rtcp";
141static const char kAttributeIceUfrag[] = "ice-ufrag";
142static const char kAttributeIcePwd[] = "ice-pwd";
143static const char kAttributeIceLite[] = "ice-lite";
144static const char kAttributeIceOption[] = "ice-options";
145static const char kAttributeSendOnly[] = "sendonly";
146static const char kAttributeRecvOnly[] = "recvonly";
147static const char kAttributeRtcpFb[] = "rtcp-fb";
148static const char kAttributeSendRecv[] = "sendrecv";
149static const char kAttributeInactive[] = "inactive";
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +0000150// draft-ietf-mmusic-sctp-sdp-07
151// a=sctp-port
152static const char kAttributeSctpPort[] = "sctp-port";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000153
154// Experimental flags
155static const char kAttributeXGoogleFlag[] = "x-google-flag";
156static const char kValueConference[] = "conference";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000157
158// Candidate
159static const char kCandidateHost[] = "host";
160static const char kCandidateSrflx[] = "srflx";
Honghai Zhang7fb69db2016-03-14 11:59:18 -0700161static const char kCandidatePrflx[] = "prflx";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000162static const char kCandidateRelay[] = "relay";
mallinath@webrtc.org2d60c5e2014-08-08 22:29:20 +0000163static const char kTcpCandidateType[] = "tcptype";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000164
165static const char kSdpDelimiterEqual = '=';
166static const char kSdpDelimiterSpace = ' ';
167static const char kSdpDelimiterColon = ':';
168static const char kSdpDelimiterSemicolon = ';';
169static const char kSdpDelimiterSlash = '/';
170static const char kNewLine = '\n';
171static const char kReturn = '\r';
172static const char kLineBreak[] = "\r\n";
173
174// TODO: Generate the Session and Time description
175// instead of hardcoding.
176static const char kSessionVersion[] = "v=0";
177// RFC 4566
178static const char kSessionOriginUsername[] = "-";
179static const char kSessionOriginSessionId[] = "0";
180static const char kSessionOriginSessionVersion[] = "0";
181static const char kSessionOriginNettype[] = "IN";
182static const char kSessionOriginAddrtype[] = "IP4";
183static const char kSessionOriginAddress[] = "127.0.0.1";
184static const char kSessionName[] = "s=-";
185static const char kTimeDescription[] = "t=0 0";
186static const char kAttrGroup[] = "a=group:BUNDLE";
187static const char kConnectionNettype[] = "IN";
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000188static const char kConnectionIpv4Addrtype[] = "IP4";
189static const char kConnectionIpv6Addrtype[] = "IP6";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000190static const char kMediaTypeVideo[] = "video";
191static const char kMediaTypeAudio[] = "audio";
192static const char kMediaTypeData[] = "application";
193static const char kMediaPortRejected[] = "0";
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000194// draft-ietf-mmusic-trickle-ice-01
195// When no candidates have been gathered, set the connection
196// address to IP6 ::.
perkj@webrtc.orgd105cc82014-11-07 11:22:06 +0000197// TODO(perkj): FF can not parse IP6 ::. See http://crbug/430333
198// Use IPV4 per default.
199static const char kDummyAddress[] = "0.0.0.0";
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000200static const char kDummyPort[] = "9";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000201// RFC 3556
202static const char kApplicationSpecificMaximum[] = "AS";
203
204static const int kDefaultVideoClockrate = 90000;
205
206// ISAC special-case.
207static const char kIsacCodecName[] = "ISAC"; // From webrtcvoiceengine.cc
208static const int kIsacWbDefaultRate = 32000; // From acm_common_defs.h
209static const int kIsacSwbDefaultRate = 56000; // From acm_common_defs.h
210
wu@webrtc.org78187522013-10-07 23:32:02 +0000211static const char kDefaultSctpmapProtocol[] = "webrtc-datachannel";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000212
pkasting@chromium.orgd3245462015-02-23 21:28:22 +0000213// RTP payload type is in the 0-127 range. Use -1 to indicate "all" payload
214// types.
215const int kWildcardPayloadType = -1;
216
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000217struct SsrcInfo {
Peter Boström0c4e06b2015-10-07 12:23:21 +0200218 uint32_t ssrc_id;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000219 std::string cname;
deadbeef9d3584c2016-02-16 17:54:10 -0800220 std::string stream_id;
221 std::string track_id;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000222
223 // For backward compatibility.
224 // TODO(ronghuawu): Remove below 2 fields once all the clients support msid.
225 std::string label;
226 std::string mslabel;
227};
228typedef std::vector<SsrcInfo> SsrcInfoVec;
229typedef std::vector<SsrcGroup> SsrcGroupVec;
230
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000231template <class T>
232static void AddFmtpLine(const T& codec, std::string* message);
233static void BuildMediaDescription(const ContentInfo* content_info,
234 const TransportInfo* transport_info,
235 const MediaType media_type,
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000236 const std::vector<Candidate>& candidates,
deadbeef9d3584c2016-02-16 17:54:10 -0800237 bool unified_plan_sdp,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000238 std::string* message);
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +0000239static void BuildSctpContentAttributes(std::string* message, int sctp_port);
deadbeef9d3584c2016-02-16 17:54:10 -0800240static void BuildRtpContentAttributes(const MediaContentDescription* media_desc,
241 const MediaType media_type,
242 bool unified_plan_sdp,
243 std::string* message);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000244static void BuildRtpMap(const MediaContentDescription* media_desc,
245 const MediaType media_type,
246 std::string* message);
247static void BuildCandidate(const std::vector<Candidate>& candidates,
honghaiza54a0802015-12-16 18:37:23 -0800248 bool include_ufrag,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000249 std::string* message);
250static void BuildIceOptions(const std::vector<std::string>& transport_options,
251 std::string* message);
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +0000252static bool IsRtp(const std::string& protocol);
253static bool IsDtlsSctp(const std::string& protocol);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000254static bool ParseSessionDescription(const std::string& message, size_t* pos,
255 std::string* session_id,
256 std::string* session_version,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000257 TransportDescription* session_td,
258 RtpHeaderExtensions* session_extmaps,
259 cricket::SessionDescription* desc,
260 SdpParseError* error);
261static bool ParseGroupAttribute(const std::string& line,
262 cricket::SessionDescription* desc,
263 SdpParseError* error);
264static bool ParseMediaDescription(
265 const std::string& message,
266 const TransportDescription& session_td,
267 const RtpHeaderExtensions& session_extmaps,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000268 size_t* pos, cricket::SessionDescription* desc,
269 std::vector<JsepIceCandidate*>* candidates,
270 SdpParseError* error);
271static bool ParseContent(const std::string& message,
272 const MediaType media_type,
273 int mline_index,
274 const std::string& protocol,
deadbeef67cf2c12016-04-13 10:07:16 -0700275 const std::vector<int>& payload_types,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000276 size_t* pos,
277 std::string* content_name,
278 MediaContentDescription* media_desc,
279 TransportDescription* transport,
280 std::vector<JsepIceCandidate*>* candidates,
281 SdpParseError* error);
282static bool ParseSsrcAttribute(const std::string& line,
283 SsrcInfoVec* ssrc_infos,
284 SdpParseError* error);
285static bool ParseSsrcGroupAttribute(const std::string& line,
286 SsrcGroupVec* ssrc_groups,
287 SdpParseError* error);
288static bool ParseCryptoAttribute(const std::string& line,
289 MediaContentDescription* media_desc,
290 SdpParseError* error);
291static bool ParseRtpmapAttribute(const std::string& line,
292 const MediaType media_type,
deadbeef67cf2c12016-04-13 10:07:16 -0700293 const std::vector<int>& payload_types,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000294 MediaContentDescription* media_desc,
295 SdpParseError* error);
296static bool ParseFmtpAttributes(const std::string& line,
297 const MediaType media_type,
298 MediaContentDescription* media_desc,
299 SdpParseError* error);
300static bool ParseFmtpParam(const std::string& line, std::string* parameter,
301 std::string* value, SdpParseError* error);
302static bool ParseCandidate(const std::string& message, Candidate* candidate,
303 SdpParseError* error, bool is_raw);
304static bool ParseRtcpFbAttribute(const std::string& line,
305 const MediaType media_type,
306 MediaContentDescription* media_desc,
307 SdpParseError* error);
308static bool ParseIceOptions(const std::string& line,
309 std::vector<std::string>* transport_options,
310 SdpParseError* error);
311static bool ParseExtmap(const std::string& line,
312 RtpHeaderExtension* extmap,
313 SdpParseError* error);
314static bool ParseFingerprintAttribute(const std::string& line,
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000315 rtc::SSLFingerprint** fingerprint,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000316 SdpParseError* error);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +0000317static bool ParseDtlsSetup(const std::string& line,
318 cricket::ConnectionRole* role,
319 SdpParseError* error);
deadbeef9d3584c2016-02-16 17:54:10 -0800320static bool ParseMsidAttribute(const std::string& line,
321 std::string* stream_id,
322 std::string* track_id,
323 SdpParseError* error);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000324
325// Helper functions
326
327// Below ParseFailed*** functions output the line that caused the parsing
328// failure and the detailed reason (|description|) of the failure to |error|.
329// The functions always return false so that they can be used directly in the
330// following way when error happens:
331// "return ParseFailed***(...);"
332
333// The line starting at |line_start| of |message| is the failing line.
334// The reason for the failure should be provided in the |description|.
335// An example of a description could be "unknown character".
336static bool ParseFailed(const std::string& message,
337 size_t line_start,
338 const std::string& description,
339 SdpParseError* error) {
340 // Get the first line of |message| from |line_start|.
sergeyu@chromium.org5bc25c42013-12-05 00:24:06 +0000341 std::string first_line;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000342 size_t line_end = message.find(kNewLine, line_start);
343 if (line_end != std::string::npos) {
344 if (line_end > 0 && (message.at(line_end - 1) == kReturn)) {
345 --line_end;
346 }
347 first_line = message.substr(line_start, (line_end - line_start));
sergeyu@chromium.org5bc25c42013-12-05 00:24:06 +0000348 } else {
349 first_line = message.substr(line_start);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000350 }
351
352 if (error) {
353 error->line = first_line;
354 error->description = description;
355 }
356 LOG(LS_ERROR) << "Failed to parse: \"" << first_line
357 << "\". Reason: " << description;
358 return false;
359}
360
361// |line| is the failing line. The reason for the failure should be
362// provided in the |description|.
363static bool ParseFailed(const std::string& line,
364 const std::string& description,
365 SdpParseError* error) {
366 return ParseFailed(line, 0, description, error);
367}
368
369// Parses failure where the failing SDP line isn't know or there are multiple
370// failing lines.
371static bool ParseFailed(const std::string& description,
372 SdpParseError* error) {
373 return ParseFailed("", description, error);
374}
375
376// |line| is the failing line. The failure is due to the fact that |line|
377// doesn't have |expected_fields| fields.
378static bool ParseFailedExpectFieldNum(const std::string& line,
379 int expected_fields,
380 SdpParseError* error) {
381 std::ostringstream description;
382 description << "Expects " << expected_fields << " fields.";
383 return ParseFailed(line, description.str(), error);
384}
385
386// |line| is the failing line. The failure is due to the fact that |line| has
387// less than |expected_min_fields| fields.
388static bool ParseFailedExpectMinFieldNum(const std::string& line,
389 int expected_min_fields,
390 SdpParseError* error) {
391 std::ostringstream description;
392 description << "Expects at least " << expected_min_fields << " fields.";
393 return ParseFailed(line, description.str(), error);
394}
395
396// |line| is the failing line. The failure is due to the fact that it failed to
397// get the value of |attribute|.
398static bool ParseFailedGetValue(const std::string& line,
399 const std::string& attribute,
400 SdpParseError* error) {
401 std::ostringstream description;
402 description << "Failed to get the value of attribute: " << attribute;
403 return ParseFailed(line, description.str(), error);
404}
405
406// The line starting at |line_start| of |message| is the failing line. The
407// failure is due to the line type (e.g. the "m" part of the "m-line")
408// not matching what is expected. The expected line type should be
409// provided as |line_type|.
410static bool ParseFailedExpectLine(const std::string& message,
411 size_t line_start,
412 const char line_type,
413 const std::string& line_value,
414 SdpParseError* error) {
415 std::ostringstream description;
416 description << "Expect line: " << line_type << "=" << line_value;
417 return ParseFailed(message, line_start, description.str(), error);
418}
419
420static bool AddLine(const std::string& line, std::string* message) {
421 if (!message)
422 return false;
423
424 message->append(line);
425 message->append(kLineBreak);
426 return true;
427}
428
429static bool GetLine(const std::string& message,
430 size_t* pos,
431 std::string* line) {
432 size_t line_begin = *pos;
433 size_t line_end = message.find(kNewLine, line_begin);
434 if (line_end == std::string::npos) {
435 return false;
436 }
437 // Update the new start position
438 *pos = line_end + 1;
439 if (line_end > 0 && (message.at(line_end - 1) == kReturn)) {
440 --line_end;
441 }
442 *line = message.substr(line_begin, (line_end - line_begin));
443 const char* cline = line->c_str();
444 // RFC 4566
445 // An SDP session description consists of a number of lines of text of
446 // the form:
447 // <type>=<value>
448 // where <type> MUST be exactly one case-significant character and
449 // <value> is structured text whose format depends on <type>.
450 // Whitespace MUST NOT be used on either side of the "=" sign.
decurtis@webrtc.org8af11042015-01-07 19:15:51 +0000451 if (line->length() < 3 ||
452 !islower(cline[0]) ||
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000453 cline[1] != kSdpDelimiterEqual ||
454 cline[2] == kSdpDelimiterSpace) {
455 *pos = line_begin;
456 return false;
457 }
458 return true;
459}
460
461// Init |os| to "|type|=|value|".
462static void InitLine(const char type,
463 const std::string& value,
464 std::ostringstream* os) {
465 os->str("");
466 *os << type << kSdpDelimiterEqual << value;
467}
468
469// Init |os| to "a=|attribute|".
470static void InitAttrLine(const std::string& attribute, std::ostringstream* os) {
471 InitLine(kLineTypeAttributes, attribute, os);
472}
473
474// Writes a SDP attribute line based on |attribute| and |value| to |message|.
475static void AddAttributeLine(const std::string& attribute, int value,
476 std::string* message) {
477 std::ostringstream os;
478 InitAttrLine(attribute, &os);
479 os << kSdpDelimiterColon << value;
480 AddLine(os.str(), message);
481}
482
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000483static bool IsLineType(const std::string& message,
484 const char type,
485 size_t line_start) {
486 if (message.size() < line_start + kLinePrefixLength) {
487 return false;
488 }
489 const char* cmessage = message.c_str();
490 return (cmessage[line_start] == type &&
491 cmessage[line_start + 1] == kSdpDelimiterEqual);
492}
493
494static bool IsLineType(const std::string& line,
495 const char type) {
496 return IsLineType(line, type, 0);
497}
498
499static bool GetLineWithType(const std::string& message, size_t* pos,
500 std::string* line, const char type) {
501 if (!IsLineType(message, type, *pos)) {
502 return false;
503 }
504
505 if (!GetLine(message, pos, line))
506 return false;
507
508 return true;
509}
510
511static bool HasAttribute(const std::string& line,
512 const std::string& attribute) {
513 return (line.compare(kLinePrefixLength, attribute.size(), attribute) == 0);
514}
515
Peter Boström0c4e06b2015-10-07 12:23:21 +0200516static bool AddSsrcLine(uint32_t ssrc_id,
517 const std::string& attribute,
518 const std::string& value,
519 std::string* message) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000520 // RFC 5576
521 // a=ssrc:<ssrc-id> <attribute>:<value>
522 std::ostringstream os;
523 InitAttrLine(kAttributeSsrc, &os);
524 os << kSdpDelimiterColon << ssrc_id << kSdpDelimiterSpace
525 << attribute << kSdpDelimiterColon << value;
526 return AddLine(os.str(), message);
527}
528
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000529// Get value only from <attribute>:<value>.
530static bool GetValue(const std::string& message, const std::string& attribute,
531 std::string* value, SdpParseError* error) {
532 std::string leftpart;
Donald Curtis0e07f922015-05-15 09:21:23 -0700533 if (!rtc::tokenize_first(message, kSdpDelimiterColon, &leftpart, value)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000534 return ParseFailedGetValue(message, attribute, error);
535 }
536 // The left part should end with the expected attribute.
537 if (leftpart.length() < attribute.length() ||
538 leftpart.compare(leftpart.length() - attribute.length(),
539 attribute.length(), attribute) != 0) {
540 return ParseFailedGetValue(message, attribute, error);
541 }
542 return true;
543}
544
545static bool CaseInsensitiveFind(std::string str1, std::string str2) {
546 std::transform(str1.begin(), str1.end(), str1.begin(),
547 ::tolower);
548 std::transform(str2.begin(), str2.end(), str2.begin(),
549 ::tolower);
550 return str1.find(str2) != std::string::npos;
551}
552
wu@webrtc.org5e760e72014-04-02 23:19:09 +0000553template <class T>
554static bool GetValueFromString(const std::string& line,
555 const std::string& s,
556 T* t,
557 SdpParseError* error) {
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000558 if (!rtc::FromString(s, t)) {
wu@webrtc.org5e760e72014-04-02 23:19:09 +0000559 std::ostringstream description;
560 description << "Invalid value: " << s << ".";
561 return ParseFailed(line, description.str(), error);
562 }
563 return true;
564}
565
pkasting@chromium.orge9facf82015-02-17 20:36:28 +0000566static bool GetPayloadTypeFromString(const std::string& line,
567 const std::string& s,
568 int* payload_type,
569 SdpParseError* error) {
570 return GetValueFromString(line, s, payload_type, error) &&
571 cricket::IsValidRtpPayloadType(*payload_type);
572}
573
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800574// |msid_stream_id| and |msid_track_id| represent the stream/track ID from the
575// "a=msid" attribute, if it exists. They are empty if the attribute does not
576// exist.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000577void CreateTracksFromSsrcInfos(const SsrcInfoVec& ssrc_infos,
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800578 const std::string& msid_stream_id,
579 const std::string& msid_track_id,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000580 StreamParamsVec* tracks) {
581 ASSERT(tracks != NULL);
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800582 ASSERT(msid_stream_id.empty() == msid_track_id.empty());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000583 for (SsrcInfoVec::const_iterator ssrc_info = ssrc_infos.begin();
584 ssrc_info != ssrc_infos.end(); ++ssrc_info) {
585 if (ssrc_info->cname.empty()) {
586 continue;
587 }
588
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800589 std::string stream_id;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000590 std::string track_id;
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800591 if (ssrc_info->stream_id.empty() && !ssrc_info->mslabel.empty()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000592 // If there's no msid and there's mslabel, we consider this is a sdp from
593 // a older version of client that doesn't support msid.
594 // In that case, we use the mslabel and label to construct the track.
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800595 stream_id = ssrc_info->mslabel;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000596 track_id = ssrc_info->label;
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800597 } else if (ssrc_info->stream_id.empty() && !msid_stream_id.empty()) {
598 // If there's no msid in the SSRC attributes, but there's a global one
599 // (from a=msid), use that. This is the case with unified plan SDP.
600 stream_id = msid_stream_id;
601 track_id = msid_track_id;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000602 } else {
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800603 stream_id = ssrc_info->stream_id;
deadbeef9d3584c2016-02-16 17:54:10 -0800604 track_id = ssrc_info->track_id;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000605 }
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800606 // If a stream/track ID wasn't populated from the SSRC attributes OR the
607 // msid attribute, use default/random values.
608 if (stream_id.empty()) {
609 stream_id = kDefaultMsid;
610 }
611 if (track_id.empty()) {
612 // TODO(ronghuawu): What should we do if the track id doesn't appear?
613 // Create random string (which will be used as track label later)?
614 track_id = rtc::CreateRandomString(8);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000615 }
616
617 StreamParamsVec::iterator track = tracks->begin();
618 for (; track != tracks->end(); ++track) {
619 if (track->id == track_id) {
620 break;
621 }
622 }
623 if (track == tracks->end()) {
624 // If we don't find an existing track, create a new one.
625 tracks->push_back(StreamParams());
626 track = tracks->end() - 1;
627 }
628 track->add_ssrc(ssrc_info->ssrc_id);
629 track->cname = ssrc_info->cname;
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800630 track->sync_label = stream_id;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000631 track->id = track_id;
632 }
633}
634
635void GetMediaStreamLabels(const ContentInfo* content,
636 std::set<std::string>* labels) {
637 const MediaContentDescription* media_desc =
henrike@webrtc.org28654cb2013-07-22 21:07:49 +0000638 static_cast<const MediaContentDescription*>(
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000639 content->description);
640 const cricket::StreamParamsVec& streams = media_desc->streams();
641 for (cricket::StreamParamsVec::const_iterator it = streams.begin();
642 it != streams.end(); ++it) {
643 labels->insert(it->sync_label);
644 }
645}
646
647// RFC 5245
648// It is RECOMMENDED that default candidates be chosen based on the
649// likelihood of those candidates to work with the peer that is being
650// contacted. It is RECOMMENDED that relayed > reflexive > host.
651static const int kPreferenceUnknown = 0;
652static const int kPreferenceHost = 1;
653static const int kPreferenceReflexive = 2;
654static const int kPreferenceRelayed = 3;
655
656static int GetCandidatePreferenceFromType(const std::string& type) {
657 int preference = kPreferenceUnknown;
658 if (type == cricket::LOCAL_PORT_TYPE) {
659 preference = kPreferenceHost;
660 } else if (type == cricket::STUN_PORT_TYPE) {
661 preference = kPreferenceReflexive;
662 } else if (type == cricket::RELAY_PORT_TYPE) {
663 preference = kPreferenceRelayed;
664 } else {
665 ASSERT(false);
666 }
667 return preference;
668}
669
guoweis@webrtc.org57ac2c82015-02-06 00:45:13 +0000670// Get ip and port of the default destination from the |candidates| with the
671// given value of |component_id|. The default candidate should be the one most
672// likely to work, typically IPv4 relay.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000673// RFC 5245
674// The value of |component_id| currently supported are 1 (RTP) and 2 (RTCP).
675// TODO: Decide the default destination in webrtcsession and
676// pass it down via SessionDescription.
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000677static void GetDefaultDestination(
678 const std::vector<Candidate>& candidates,
679 int component_id, std::string* port,
680 std::string* ip, std::string* addr_type) {
perkj@webrtc.orgd105cc82014-11-07 11:22:06 +0000681 *addr_type = kConnectionIpv4Addrtype;
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000682 *port = kDummyPort;
683 *ip = kDummyAddress;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000684 int current_preference = kPreferenceUnknown;
guoweis@webrtc.org57ac2c82015-02-06 00:45:13 +0000685 int current_family = AF_UNSPEC;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000686 for (std::vector<Candidate>::const_iterator it = candidates.begin();
687 it != candidates.end(); ++it) {
688 if (it->component() != component_id) {
689 continue;
690 }
guoweis@webrtc.org57ac2c82015-02-06 00:45:13 +0000691 // Default destination should be UDP only.
692 if (it->protocol() != cricket::UDP_PROTOCOL_NAME) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000693 continue;
694 }
guoweis@webrtc.org57ac2c82015-02-06 00:45:13 +0000695 const int preference = GetCandidatePreferenceFromType(it->type());
696 const int family = it->address().ipaddr().family();
697 // See if this candidate is more preferable then the current one if it's the
698 // same family. Or if the current family is IPv4 already so we could safely
699 // ignore all IPv6 ones. WebRTC bug 4269.
700 // http://code.google.com/p/webrtc/issues/detail?id=4269
701 if ((preference <= current_preference && current_family == family) ||
702 (current_family == AF_INET && family == AF_INET6)) {
703 continue;
704 }
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000705 if (family == AF_INET) {
706 addr_type->assign(kConnectionIpv4Addrtype);
707 } else if (family == AF_INET6) {
708 addr_type->assign(kConnectionIpv6Addrtype);
709 }
guoweis@webrtc.org57ac2c82015-02-06 00:45:13 +0000710 current_preference = preference;
711 current_family = family;
712 *port = it->address().PortAsString();
713 *ip = it->address().ipaddr().ToString();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000714 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000715}
716
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000717// Update |mline|'s default destination and append a c line after it.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000718static void UpdateMediaDefaultDestination(
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000719 const std::vector<Candidate>& candidates,
jbauch083b73f2015-07-16 02:46:32 -0700720 const std::string& mline,
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000721 std::string* message) {
722 std::string new_lines;
723 AddLine(mline, &new_lines);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000724 // RFC 4566
725 // m=<media> <port> <proto> <fmt> ...
726 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000727 rtc::split(mline, kSdpDelimiterSpace, &fields);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000728 if (fields.size() < 3) {
729 return;
730 }
731
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000732 std::ostringstream os;
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000733 std::string rtp_port, rtp_ip, addr_type;
734 GetDefaultDestination(candidates, ICE_CANDIDATE_COMPONENT_RTP,
735 &rtp_port, &rtp_ip, &addr_type);
736 // Found default RTP candidate.
737 // RFC 5245
738 // The default candidates are added to the SDP as the default
739 // destination for media. For streams based on RTP, this is done by
740 // placing the IP address and port of the RTP candidate into the c and m
741 // lines, respectively.
742 // Update the port in the m line.
743 // If this is a m-line with port equal to 0, we don't change it.
744 if (fields[1] != kMediaPortRejected) {
745 new_lines.replace(fields[0].size() + 1,
746 fields[1].size(),
747 rtp_port);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000748 }
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000749 // Add the c line.
750 // RFC 4566
751 // c=<nettype> <addrtype> <connection-address>
752 InitLine(kLineTypeConnection, kConnectionNettype, &os);
753 os << " " << addr_type << " " << rtp_ip;
754 AddLine(os.str(), &new_lines);
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000755 message->append(new_lines);
756}
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000757
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000758// Gets "a=rtcp" line if found default RTCP candidate from |candidates|.
759static std::string GetRtcpLine(const std::vector<Candidate>& candidates) {
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000760 std::string rtcp_line, rtcp_port, rtcp_ip, addr_type;
761 GetDefaultDestination(candidates, ICE_CANDIDATE_COMPONENT_RTCP,
762 &rtcp_port, &rtcp_ip, &addr_type);
763 // Found default RTCP candidate.
764 // RFC 5245
765 // If the agent is utilizing RTCP, it MUST encode the RTCP candidate
766 // using the a=rtcp attribute as defined in RFC 3605.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000767
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000768 // RFC 3605
769 // rtcp-attribute = "a=rtcp:" port [nettype space addrtype space
770 // connection-address] CRLF
771 std::ostringstream os;
772 InitAttrLine(kAttributeRtcp, &os);
773 os << kSdpDelimiterColon
774 << rtcp_port << " "
775 << kConnectionNettype << " "
776 << addr_type << " "
777 << rtcp_ip;
778 rtcp_line = os.str();
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000779 return rtcp_line;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000780}
781
782// Get candidates according to the mline index from SessionDescriptionInterface.
783static void GetCandidatesByMindex(const SessionDescriptionInterface& desci,
784 int mline_index,
785 std::vector<Candidate>* candidates) {
786 if (!candidates) {
787 return;
788 }
789 const IceCandidateCollection* cc = desci.candidates(mline_index);
790 for (size_t i = 0; i < cc->count(); ++i) {
791 const IceCandidateInterface* candidate = cc->at(i);
792 candidates->push_back(candidate->candidate());
793 }
794}
795
deadbeef9d3584c2016-02-16 17:54:10 -0800796std::string SdpSerialize(const JsepSessionDescription& jdesc,
797 bool unified_plan_sdp) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000798 const cricket::SessionDescription* desc = jdesc.description();
799 if (!desc) {
800 return "";
801 }
802
803 std::string message;
804
805 // Session Description.
806 AddLine(kSessionVersion, &message);
807 // Session Origin
808 // RFC 4566
809 // o=<username> <sess-id> <sess-version> <nettype> <addrtype>
810 // <unicast-address>
811 std::ostringstream os;
812 InitLine(kLineTypeOrigin, kSessionOriginUsername, &os);
jbauch083b73f2015-07-16 02:46:32 -0700813 const std::string& session_id = jdesc.session_id().empty() ?
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000814 kSessionOriginSessionId : jdesc.session_id();
jbauch083b73f2015-07-16 02:46:32 -0700815 const std::string& session_version = jdesc.session_version().empty() ?
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000816 kSessionOriginSessionVersion : jdesc.session_version();
817 os << " " << session_id << " " << session_version << " "
818 << kSessionOriginNettype << " " << kSessionOriginAddrtype << " "
819 << kSessionOriginAddress;
820 AddLine(os.str(), &message);
821 AddLine(kSessionName, &message);
822
823 // Time Description.
824 AddLine(kTimeDescription, &message);
825
826 // Group
827 if (desc->HasGroup(cricket::GROUP_TYPE_BUNDLE)) {
828 std::string group_line = kAttrGroup;
829 const cricket::ContentGroup* group =
830 desc->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
831 ASSERT(group != NULL);
832 const cricket::ContentNames& content_names = group->content_names();
833 for (cricket::ContentNames::const_iterator it = content_names.begin();
834 it != content_names.end(); ++it) {
835 group_line.append(" ");
836 group_line.append(*it);
837 }
838 AddLine(group_line, &message);
839 }
840
841 // MediaStream semantics
842 InitAttrLine(kAttributeMsidSemantics, &os);
843 os << kSdpDelimiterColon << " " << kMediaStreamSemantic;
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000844
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000845 std::set<std::string> media_stream_labels;
846 const ContentInfo* audio_content = GetFirstAudioContent(desc);
847 if (audio_content)
848 GetMediaStreamLabels(audio_content, &media_stream_labels);
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000849
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000850 const ContentInfo* video_content = GetFirstVideoContent(desc);
851 if (video_content)
852 GetMediaStreamLabels(video_content, &media_stream_labels);
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000853
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000854 for (std::set<std::string>::const_iterator it =
855 media_stream_labels.begin(); it != media_stream_labels.end(); ++it) {
856 os << " " << *it;
857 }
858 AddLine(os.str(), &message);
859
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000860 // Preserve the order of the media contents.
861 int mline_index = -1;
862 for (cricket::ContentInfos::const_iterator it = desc->contents().begin();
863 it != desc->contents().end(); ++it) {
864 const MediaContentDescription* mdesc =
865 static_cast<const MediaContentDescription*>(it->description);
866 std::vector<Candidate> candidates;
867 GetCandidatesByMindex(jdesc, ++mline_index, &candidates);
deadbeef9d3584c2016-02-16 17:54:10 -0800868 BuildMediaDescription(&*it, desc->GetTransportInfoByName(it->name),
869 mdesc->type(), candidates, unified_plan_sdp,
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000870 &message);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000871 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000872 return message;
873}
874
875// Serializes the passed in IceCandidateInterface to a SDP string.
876// candidate - The candidate to be serialized.
Honghai Zhang7fb69db2016-03-14 11:59:18 -0700877std::string SdpSerializeCandidate(const IceCandidateInterface& candidate) {
878 return SdpSerializeCandidate(candidate.candidate());
879}
880
881// Serializes a cricket Candidate.
882std::string SdpSerializeCandidate(const cricket::Candidate& candidate) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000883 std::string message;
Honghai Zhang7fb69db2016-03-14 11:59:18 -0700884 std::vector<cricket::Candidate> candidates(1, candidate);
honghaiza54a0802015-12-16 18:37:23 -0800885 BuildCandidate(candidates, true, &message);
wu@webrtc.orgec9f5fb2014-06-24 17:05:10 +0000886 // From WebRTC draft section 4.8.1.1 candidate-attribute will be
887 // just candidate:<candidate> not a=candidate:<blah>CRLF
888 ASSERT(message.find("a=") == 0);
889 message.erase(0, 2);
890 ASSERT(message.find(kLineBreak) == message.size() - 2);
891 message.resize(message.size() - 2);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000892 return message;
893}
894
895bool SdpDeserialize(const std::string& message,
896 JsepSessionDescription* jdesc,
897 SdpParseError* error) {
898 std::string session_id;
899 std::string session_version;
Peter Thatcher7cbd1882015-09-17 18:54:52 -0700900 TransportDescription session_td("", "");
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000901 RtpHeaderExtensions session_extmaps;
902 cricket::SessionDescription* desc = new cricket::SessionDescription();
903 std::vector<JsepIceCandidate*> candidates;
904 size_t current_pos = 0;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000905
906 // Session Description
907 if (!ParseSessionDescription(message, &current_pos, &session_id,
deadbeefc80741f2015-10-22 13:14:45 -0700908 &session_version, &session_td, &session_extmaps,
909 desc, error)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000910 delete desc;
911 return false;
912 }
913
914 // Media Description
deadbeefc80741f2015-10-22 13:14:45 -0700915 if (!ParseMediaDescription(message, session_td, session_extmaps, &current_pos,
916 desc, &candidates, error)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000917 delete desc;
918 for (std::vector<JsepIceCandidate*>::const_iterator
919 it = candidates.begin(); it != candidates.end(); ++it) {
920 delete *it;
921 }
922 return false;
923 }
924
925 jdesc->Initialize(desc, session_id, session_version);
926
927 for (std::vector<JsepIceCandidate*>::const_iterator
928 it = candidates.begin(); it != candidates.end(); ++it) {
929 jdesc->AddCandidate(*it);
930 delete *it;
931 }
932 return true;
933}
934
935bool SdpDeserializeCandidate(const std::string& message,
936 JsepIceCandidate* jcandidate,
937 SdpParseError* error) {
938 ASSERT(jcandidate != NULL);
939 Candidate candidate;
940 if (!ParseCandidate(message, &candidate, error, true)) {
941 return false;
942 }
943 jcandidate->SetCandidate(candidate);
944 return true;
945}
946
Honghai Zhang7fb69db2016-03-14 11:59:18 -0700947bool SdpDeserializeCandidate(const std::string& transport_name,
948 const std::string& message,
949 cricket::Candidate* candidate,
950 SdpParseError* error) {
951 ASSERT(candidate != nullptr);
952 if (!ParseCandidate(message, candidate, error, true)) {
953 return false;
954 }
955 candidate->set_transport_name(transport_name);
956 return true;
957}
958
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000959bool ParseCandidate(const std::string& message, Candidate* candidate,
960 SdpParseError* error, bool is_raw) {
961 ASSERT(candidate != NULL);
962
963 // Get the first line from |message|.
jiayl@webrtc.org7ec3f9f2014-08-08 23:09:15 +0000964 std::string first_line = message;
965 size_t pos = 0;
966 GetLine(message, &pos, &first_line);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000967
jiayl@webrtc.org7ec3f9f2014-08-08 23:09:15 +0000968 // Makes sure |message| contains only one line.
969 if (message.size() > first_line.size()) {
970 std::string left, right;
Donald Curtis0e07f922015-05-15 09:21:23 -0700971 if (rtc::tokenize_first(message, kNewLine, &left, &right) &&
972 !right.empty()) {
jiayl@webrtc.org7ec3f9f2014-08-08 23:09:15 +0000973 return ParseFailed(message, 0, "Expect one line only", error);
974 }
975 }
976
977 // From WebRTC draft section 4.8.1.1 candidate-attribute should be
978 // candidate:<candidate> when trickled, but we still support
979 // a=candidate:<blah>CRLF for backward compatibility and for parsing a line
980 // from the SDP.
981 if (IsLineType(first_line, kLineTypeAttributes)) {
982 first_line = first_line.substr(kLinePrefixLength);
983 }
984
985 std::string attribute_candidate;
986 std::string candidate_value;
987
988 // |first_line| must be in the form of "candidate:<value>".
Donald Curtis144d0182015-05-15 13:14:24 -0700989 if (!rtc::tokenize_first(first_line, kSdpDelimiterColon, &attribute_candidate,
990 &candidate_value) ||
jiayl@webrtc.org7ec3f9f2014-08-08 23:09:15 +0000991 attribute_candidate != kAttributeCandidate) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000992 if (is_raw) {
993 std::ostringstream description;
jiayl@webrtc.org7ec3f9f2014-08-08 23:09:15 +0000994 description << "Expect line: " << kAttributeCandidate
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000995 << ":" << "<candidate-str>";
996 return ParseFailed(first_line, 0, description.str(), error);
997 } else {
998 return ParseFailedExpectLine(first_line, 0, kLineTypeAttributes,
999 kAttributeCandidate, error);
1000 }
1001 }
1002
1003 std::vector<std::string> fields;
jiayl@webrtc.org7ec3f9f2014-08-08 23:09:15 +00001004 rtc::split(candidate_value, kSdpDelimiterSpace, &fields);
1005
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001006 // RFC 5245
1007 // a=candidate:<foundation> <component-id> <transport> <priority>
1008 // <connection-address> <port> typ <candidate-types>
1009 // [raddr <connection-address>] [rport <port>]
1010 // *(SP extension-att-name SP extension-att-value)
1011 const size_t expected_min_fields = 8;
1012 if (fields.size() < expected_min_fields ||
1013 (fields[6] != kAttributeCandidateTyp)) {
1014 return ParseFailedExpectMinFieldNum(first_line, expected_min_fields, error);
1015 }
jbauch083b73f2015-07-16 02:46:32 -07001016 const std::string& foundation = fields[0];
jiayl@webrtc.org7ec3f9f2014-08-08 23:09:15 +00001017
wu@webrtc.org5e760e72014-04-02 23:19:09 +00001018 int component_id = 0;
1019 if (!GetValueFromString(first_line, fields[1], &component_id, error)) {
1020 return false;
1021 }
jbauch083b73f2015-07-16 02:46:32 -07001022 const std::string& transport = fields[2];
Peter Boström0c4e06b2015-10-07 12:23:21 +02001023 uint32_t priority = 0;
wu@webrtc.org5e760e72014-04-02 23:19:09 +00001024 if (!GetValueFromString(first_line, fields[3], &priority, error)) {
1025 return false;
1026 }
jbauch083b73f2015-07-16 02:46:32 -07001027 const std::string& connection_address = fields[4];
wu@webrtc.org5e760e72014-04-02 23:19:09 +00001028 int port = 0;
1029 if (!GetValueFromString(first_line, fields[5], &port, error)) {
1030 return false;
1031 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001032 SocketAddress address(connection_address, port);
1033
1034 cricket::ProtocolType protocol;
1035 if (!StringToProto(transport.c_str(), &protocol)) {
1036 return ParseFailed(first_line, "Unsupported transport type.", error);
1037 }
1038
1039 std::string candidate_type;
jbauch083b73f2015-07-16 02:46:32 -07001040 const std::string& type = fields[7];
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001041 if (type == kCandidateHost) {
1042 candidate_type = cricket::LOCAL_PORT_TYPE;
1043 } else if (type == kCandidateSrflx) {
1044 candidate_type = cricket::STUN_PORT_TYPE;
1045 } else if (type == kCandidateRelay) {
1046 candidate_type = cricket::RELAY_PORT_TYPE;
Honghai Zhang7fb69db2016-03-14 11:59:18 -07001047 } else if (type == kCandidatePrflx) {
1048 candidate_type = cricket::PRFLX_PORT_TYPE;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001049 } else {
1050 return ParseFailed(first_line, "Unsupported candidate type.", error);
1051 }
1052
1053 size_t current_position = expected_min_fields;
1054 SocketAddress related_address;
1055 // The 2 optional fields for related address
1056 // [raddr <connection-address>] [rport <port>]
1057 if (fields.size() >= (current_position + 2) &&
1058 fields[current_position] == kAttributeCandidateRaddr) {
1059 related_address.SetIP(fields[++current_position]);
1060 ++current_position;
1061 }
1062 if (fields.size() >= (current_position + 2) &&
1063 fields[current_position] == kAttributeCandidateRport) {
wu@webrtc.org5e760e72014-04-02 23:19:09 +00001064 int port = 0;
1065 if (!GetValueFromString(
1066 first_line, fields[++current_position], &port, error)) {
1067 return false;
1068 }
1069 related_address.SetPort(port);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001070 ++current_position;
1071 }
1072
mallinath@webrtc.org2d60c5e2014-08-08 22:29:20 +00001073 // If this is a TCP candidate, it has additional extension as defined in
1074 // RFC 6544.
1075 std::string tcptype;
1076 if (fields.size() >= (current_position + 2) &&
1077 fields[current_position] == kTcpCandidateType) {
1078 tcptype = fields[++current_position];
1079 ++current_position;
1080
1081 if (tcptype != cricket::TCPTYPE_ACTIVE_STR &&
1082 tcptype != cricket::TCPTYPE_PASSIVE_STR &&
1083 tcptype != cricket::TCPTYPE_SIMOPEN_STR) {
1084 return ParseFailed(first_line, "Invalid TCP candidate type.", error);
1085 }
1086
1087 if (protocol != cricket::PROTO_TCP) {
1088 return ParseFailed(first_line, "Invalid non-TCP candidate", error);
1089 }
1090 }
1091
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001092 // Extension
honghaiza54a0802015-12-16 18:37:23 -08001093 // Though non-standard, we support the ICE ufrag and pwd being signaled on
1094 // the candidate to avoid issues with confusing which generation a candidate
1095 // belongs to when trickling multiple generations at the same time.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001096 std::string username;
1097 std::string password;
Peter Boström0c4e06b2015-10-07 12:23:21 +02001098 uint32_t generation = 0;
honghaiza0c44ea2016-03-23 16:07:48 -07001099 uint16_t network_id = 0;
1100 uint16_t network_cost = 0;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001101 for (size_t i = current_position; i + 1 < fields.size(); ++i) {
1102 // RFC 5245
1103 // *(SP extension-att-name SP extension-att-value)
1104 if (fields[i] == kAttributeCandidateGeneration) {
wu@webrtc.org5e760e72014-04-02 23:19:09 +00001105 if (!GetValueFromString(first_line, fields[++i], &generation, error)) {
1106 return false;
1107 }
honghaiza54a0802015-12-16 18:37:23 -08001108 } else if (fields[i] == kAttributeCandidateUfrag) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001109 username = fields[++i];
honghaiza54a0802015-12-16 18:37:23 -08001110 } else if (fields[i] == kAttributeCandidatePwd) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001111 password = fields[++i];
honghaiza0c44ea2016-03-23 16:07:48 -07001112 } else if (fields[i] == kAttributeCandidateNetworkId) {
1113 if (!GetValueFromString(first_line, fields[++i], &network_id, error)) {
1114 return false;
1115 }
honghaize1a0c942016-02-16 14:54:56 -08001116 } else if (fields[i] == kAttributeCandidateNetworkCost) {
1117 if (!GetValueFromString(first_line, fields[++i], &network_cost, error)) {
1118 return false;
1119 }
Honghai Zhang351d77b2016-05-20 15:08:29 -07001120 network_cost = std::min(network_cost, rtc::kNetworkCostMax);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001121 } else {
1122 // Skip the unknown extension.
1123 ++i;
1124 }
1125 }
1126
guoweis@webrtc.org61c12472015-01-15 06:53:07 +00001127 *candidate = Candidate(component_id, cricket::ProtoToString(protocol),
guoweis@webrtc.org950c5182014-12-16 23:01:31 +00001128 address, priority, username, password, candidate_type,
honghaiza0c44ea2016-03-23 16:07:48 -07001129 generation, foundation, network_id, network_cost);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001130 candidate->set_related_address(related_address);
mallinath@webrtc.org2d60c5e2014-08-08 22:29:20 +00001131 candidate->set_tcptype(tcptype);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001132 return true;
1133}
1134
1135bool ParseIceOptions(const std::string& line,
1136 std::vector<std::string>* transport_options,
1137 SdpParseError* error) {
1138 std::string ice_options;
1139 if (!GetValue(line, kAttributeIceOption, &ice_options, error)) {
1140 return false;
1141 }
1142 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001143 rtc::split(ice_options, kSdpDelimiterSpace, &fields);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001144 for (size_t i = 0; i < fields.size(); ++i) {
1145 transport_options->push_back(fields[i]);
1146 }
1147 return true;
1148}
1149
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00001150bool ParseSctpPort(const std::string& line,
1151 int* sctp_port,
1152 SdpParseError* error) {
1153 // draft-ietf-mmusic-sctp-sdp-07
1154 // a=sctp-port
1155 std::vector<std::string> fields;
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00001156 const size_t expected_min_fields = 2;
lally69f57602015-10-08 10:15:04 -07001157 rtc::split(line.substr(kLinePrefixLength), kSdpDelimiterColon, &fields);
1158 if (fields.size() < expected_min_fields) {
1159 fields.resize(0);
1160 rtc::split(line.substr(kLinePrefixLength), kSdpDelimiterSpace, &fields);
1161 }
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00001162 if (fields.size() < expected_min_fields) {
1163 return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
1164 }
1165 if (!rtc::FromString(fields[1], sctp_port)) {
lally69f57602015-10-08 10:15:04 -07001166 return ParseFailed(line, "Invalid sctp port value.", error);
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00001167 }
1168 return true;
1169}
1170
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001171bool ParseExtmap(const std::string& line, RtpHeaderExtension* extmap,
1172 SdpParseError* error) {
1173 // RFC 5285
1174 // a=extmap:<value>["/"<direction>] <URI> <extensionattributes>
1175 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001176 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001177 kSdpDelimiterSpace, &fields);
1178 const size_t expected_min_fields = 2;
1179 if (fields.size() < expected_min_fields) {
1180 return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
1181 }
1182 std::string uri = fields[1];
1183
1184 std::string value_direction;
1185 if (!GetValue(fields[0], kAttributeExtmap, &value_direction, error)) {
1186 return false;
1187 }
1188 std::vector<std::string> sub_fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001189 rtc::split(value_direction, kSdpDelimiterSlash, &sub_fields);
wu@webrtc.org5e760e72014-04-02 23:19:09 +00001190 int value = 0;
1191 if (!GetValueFromString(line, sub_fields[0], &value, error)) {
1192 return false;
1193 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001194
1195 *extmap = RtpHeaderExtension(uri, value);
1196 return true;
1197}
1198
1199void BuildMediaDescription(const ContentInfo* content_info,
1200 const TransportInfo* transport_info,
1201 const MediaType media_type,
wu@webrtc.org4c3e9912014-07-16 21:03:13 +00001202 const std::vector<Candidate>& candidates,
deadbeef9d3584c2016-02-16 17:54:10 -08001203 bool unified_plan_sdp,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001204 std::string* message) {
1205 ASSERT(message != NULL);
1206 if (content_info == NULL || message == NULL) {
1207 return;
1208 }
1209 // TODO: Rethink if we should use sprintfn instead of stringstream.
1210 // According to the style guide, streams should only be used for logging.
1211 // http://google-styleguide.googlecode.com/svn/
1212 // trunk/cppguide.xml?showone=Streams#Streams
1213 std::ostringstream os;
1214 const MediaContentDescription* media_desc =
henrike@webrtc.org28654cb2013-07-22 21:07:49 +00001215 static_cast<const MediaContentDescription*>(
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001216 content_info->description);
1217 ASSERT(media_desc != NULL);
1218
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +00001219 int sctp_port = cricket::kSctpDefaultPort;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001220
1221 // RFC 4566
1222 // m=<media> <port> <proto> <fmt>
1223 // fmt is a list of payload type numbers that MAY be used in the session.
1224 const char* type = NULL;
1225 if (media_type == cricket::MEDIA_TYPE_AUDIO)
1226 type = kMediaTypeAudio;
1227 else if (media_type == cricket::MEDIA_TYPE_VIDEO)
1228 type = kMediaTypeVideo;
1229 else if (media_type == cricket::MEDIA_TYPE_DATA)
1230 type = kMediaTypeData;
1231 else
1232 ASSERT(false);
1233
1234 std::string fmt;
1235 if (media_type == cricket::MEDIA_TYPE_VIDEO) {
1236 const VideoContentDescription* video_desc =
1237 static_cast<const VideoContentDescription*>(media_desc);
1238 for (std::vector<cricket::VideoCodec>::const_iterator it =
1239 video_desc->codecs().begin();
1240 it != video_desc->codecs().end(); ++it) {
1241 fmt.append(" ");
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001242 fmt.append(rtc::ToString<int>(it->id));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001243 }
1244 } else if (media_type == cricket::MEDIA_TYPE_AUDIO) {
1245 const AudioContentDescription* audio_desc =
1246 static_cast<const AudioContentDescription*>(media_desc);
1247 for (std::vector<cricket::AudioCodec>::const_iterator it =
1248 audio_desc->codecs().begin();
1249 it != audio_desc->codecs().end(); ++it) {
1250 fmt.append(" ");
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001251 fmt.append(rtc::ToString<int>(it->id));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001252 }
1253 } else if (media_type == cricket::MEDIA_TYPE_DATA) {
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +00001254 const DataContentDescription* data_desc =
1255 static_cast<const DataContentDescription*>(media_desc);
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00001256 if (IsDtlsSctp(media_desc->protocol())) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001257 fmt.append(" ");
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +00001258
1259 for (std::vector<cricket::DataCodec>::const_iterator it =
1260 data_desc->codecs().begin();
1261 it != data_desc->codecs().end(); ++it) {
1262 if (it->id == cricket::kGoogleSctpDataCodecId &&
1263 it->GetParam(cricket::kCodecParamPort, &sctp_port)) {
1264 break;
1265 }
1266 }
1267
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001268 fmt.append(rtc::ToString<int>(sctp_port));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001269 } else {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001270 for (std::vector<cricket::DataCodec>::const_iterator it =
1271 data_desc->codecs().begin();
1272 it != data_desc->codecs().end(); ++it) {
1273 fmt.append(" ");
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001274 fmt.append(rtc::ToString<int>(it->id));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001275 }
1276 }
1277 }
1278 // The fmt must never be empty. If no codecs are found, set the fmt attribute
1279 // to 0.
1280 if (fmt.empty()) {
1281 fmt = " 0";
1282 }
1283
1284 // The port number in the m line will be updated later when associate with
1285 // the candidates.
1286 // RFC 3264
1287 // To reject an offered stream, the port number in the corresponding stream in
1288 // the answer MUST be set to zero.
jbauch083b73f2015-07-16 02:46:32 -07001289 const std::string& port = content_info->rejected ?
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +00001290 kMediaPortRejected : kDummyPort;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001291
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001292 rtc::SSLFingerprint* fp = (transport_info) ?
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001293 transport_info->description.identity_fingerprint.get() : NULL;
1294
wu@webrtc.org4c3e9912014-07-16 21:03:13 +00001295 // Add the m and c lines.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001296 InitLine(kLineTypeMedia, type, &os);
1297 os << " " << port << " " << media_desc->protocol() << fmt;
wu@webrtc.org4c3e9912014-07-16 21:03:13 +00001298 std::string mline = os.str();
1299 UpdateMediaDefaultDestination(candidates, mline, message);
1300
1301 // RFC 4566
1302 // b=AS:<bandwidth>
Peter Thatcherc0c3a862015-06-24 15:31:25 -07001303 if (media_desc->bandwidth() >= 1000) {
wu@webrtc.org4c3e9912014-07-16 21:03:13 +00001304 InitLine(kLineTypeSessionBandwidth, kApplicationSpecificMaximum, &os);
1305 os << kSdpDelimiterColon << (media_desc->bandwidth() / 1000);
1306 AddLine(os.str(), message);
1307 }
1308
1309 // Add the a=rtcp line.
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00001310 if (IsRtp(media_desc->protocol())) {
wu@webrtc.org4c3e9912014-07-16 21:03:13 +00001311 std::string rtcp_line = GetRtcpLine(candidates);
1312 if (!rtcp_line.empty()) {
1313 AddLine(rtcp_line, message);
1314 }
1315 }
1316
honghaiza54a0802015-12-16 18:37:23 -08001317 // Build the a=candidate lines. We don't include ufrag and pwd in the
1318 // candidates in the SDP to avoid redundancy.
1319 BuildCandidate(candidates, false, message);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001320
1321 // Use the transport_info to build the media level ice-ufrag and ice-pwd.
1322 if (transport_info) {
1323 // RFC 5245
1324 // ice-pwd-att = "ice-pwd" ":" password
1325 // ice-ufrag-att = "ice-ufrag" ":" ufrag
1326 // ice-ufrag
deadbeef3f7219b2015-12-28 15:17:14 -08001327 if (!transport_info->description.ice_ufrag.empty()) {
1328 InitAttrLine(kAttributeIceUfrag, &os);
1329 os << kSdpDelimiterColon << transport_info->description.ice_ufrag;
1330 AddLine(os.str(), message);
1331 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001332 // ice-pwd
deadbeef3f7219b2015-12-28 15:17:14 -08001333 if (!transport_info->description.ice_pwd.empty()) {
1334 InitAttrLine(kAttributeIcePwd, &os);
1335 os << kSdpDelimiterColon << transport_info->description.ice_pwd;
1336 AddLine(os.str(), message);
1337 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001338
1339 // draft-petithuguenin-mmusic-ice-attributes-level-03
1340 BuildIceOptions(transport_info->description.transport_options, message);
1341
1342 // RFC 4572
1343 // fingerprint-attribute =
1344 // "fingerprint" ":" hash-func SP fingerprint
1345 if (fp) {
1346 // Insert the fingerprint attribute.
1347 InitAttrLine(kAttributeFingerprint, &os);
1348 os << kSdpDelimiterColon
1349 << fp->algorithm << kSdpDelimiterSpace
1350 << fp->GetRfc4572Fingerprint();
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001351 AddLine(os.str(), message);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001352
1353 // Inserting setup attribute.
1354 if (transport_info->description.connection_role !=
1355 cricket::CONNECTIONROLE_NONE) {
1356 // Making sure we are not using "passive" mode.
1357 cricket::ConnectionRole role =
1358 transport_info->description.connection_role;
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +00001359 std::string dtls_role_str;
1360 VERIFY(cricket::ConnectionRoleToString(role, &dtls_role_str));
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001361 InitAttrLine(kAttributeSetup, &os);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001362 os << kSdpDelimiterColon << dtls_role_str;
1363 AddLine(os.str(), message);
1364 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001365 }
1366 }
1367
1368 // RFC 3388
1369 // mid-attribute = "a=mid:" identification-tag
1370 // identification-tag = token
1371 // Use the content name as the mid identification-tag.
1372 InitAttrLine(kAttributeMid, &os);
1373 os << kSdpDelimiterColon << content_info->name;
1374 AddLine(os.str(), message);
1375
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00001376 if (IsDtlsSctp(media_desc->protocol())) {
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +00001377 BuildSctpContentAttributes(message, sctp_port);
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00001378 } else if (IsRtp(media_desc->protocol())) {
deadbeef9d3584c2016-02-16 17:54:10 -08001379 BuildRtpContentAttributes(media_desc, media_type, unified_plan_sdp,
1380 message);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001381 }
1382}
1383
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +00001384void BuildSctpContentAttributes(std::string* message, int sctp_port) {
wu@webrtc.org78187522013-10-07 23:32:02 +00001385 // draft-ietf-mmusic-sctp-sdp-04
1386 // a=sctpmap:sctpmap-number protocol [streams]
lally69f57602015-10-08 10:15:04 -07001387 // TODO(lally): switch this over to mmusic-sctp-sdp-12 (or later), with
1388 // 'a=sctp-port:'
wu@webrtc.org78187522013-10-07 23:32:02 +00001389 std::ostringstream os;
1390 InitAttrLine(kAttributeSctpmap, &os);
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +00001391 os << kSdpDelimiterColon << sctp_port << kSdpDelimiterSpace
wu@webrtc.org78187522013-10-07 23:32:02 +00001392 << kDefaultSctpmapProtocol << kSdpDelimiterSpace
1393 << (cricket::kMaxSctpSid + 1);
1394 AddLine(os.str(), message);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001395}
1396
deadbeef9d3584c2016-02-16 17:54:10 -08001397// If unified_plan_sdp is true, will use "a=msid".
1398void BuildRtpContentAttributes(const MediaContentDescription* media_desc,
1399 const MediaType media_type,
1400 bool unified_plan_sdp,
1401 std::string* message) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001402 std::ostringstream os;
1403 // RFC 5285
1404 // a=extmap:<value>["/"<direction>] <URI> <extensionattributes>
1405 // The definitions MUST be either all session level or all media level. This
1406 // implementation uses all media level.
1407 for (size_t i = 0; i < media_desc->rtp_header_extensions().size(); ++i) {
1408 InitAttrLine(kAttributeExtmap, &os);
1409 os << kSdpDelimiterColon << media_desc->rtp_header_extensions()[i].id
1410 << kSdpDelimiterSpace << media_desc->rtp_header_extensions()[i].uri;
1411 AddLine(os.str(), message);
1412 }
1413
1414 // RFC 3264
1415 // a=sendrecv || a=sendonly || a=sendrecv || a=inactive
deadbeefc80741f2015-10-22 13:14:45 -07001416 switch (media_desc->direction()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001417 case cricket::MD_INACTIVE:
1418 InitAttrLine(kAttributeInactive, &os);
1419 break;
1420 case cricket::MD_SENDONLY:
1421 InitAttrLine(kAttributeSendOnly, &os);
1422 break;
1423 case cricket::MD_RECVONLY:
1424 InitAttrLine(kAttributeRecvOnly, &os);
1425 break;
1426 case cricket::MD_SENDRECV:
1427 default:
1428 InitAttrLine(kAttributeSendRecv, &os);
1429 break;
1430 }
1431 AddLine(os.str(), message);
1432
deadbeef9d3584c2016-02-16 17:54:10 -08001433 // draft-ietf-mmusic-msid-11
1434 // a=msid:<stream id> <track id>
1435 if (unified_plan_sdp && !media_desc->streams().empty()) {
1436 if (media_desc->streams().size() > 1u) {
1437 LOG(LS_WARNING) << "Trying to serialize unified plan SDP with more than "
1438 << "one track in a media section. Omitting 'a=msid'.";
1439 } else {
1440 auto track = media_desc->streams().begin();
1441 const std::string& stream_id = track->sync_label;
deadbeef9d3584c2016-02-16 17:54:10 -08001442 InitAttrLine(kAttributeMsid, &os);
1443 os << kSdpDelimiterColon << stream_id << kSdpDelimiterSpace << track->id;
1444 AddLine(os.str(), message);
1445 }
1446 }
1447
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001448 // RFC 5761
1449 // a=rtcp-mux
1450 if (media_desc->rtcp_mux()) {
1451 InitAttrLine(kAttributeRtcpMux, &os);
1452 AddLine(os.str(), message);
1453 }
1454
deadbeef13871492015-12-09 12:37:51 -08001455 // RFC 5506
1456 // a=rtcp-rsize
1457 if (media_desc->rtcp_reduced_size()) {
1458 InitAttrLine(kAttributeRtcpReducedSize, &os);
1459 AddLine(os.str(), message);
1460 }
1461
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001462 // RFC 4568
1463 // a=crypto:<tag> <crypto-suite> <key-params> [<session-params>]
1464 for (std::vector<CryptoParams>::const_iterator it =
1465 media_desc->cryptos().begin();
1466 it != media_desc->cryptos().end(); ++it) {
1467 InitAttrLine(kAttributeCrypto, &os);
1468 os << kSdpDelimiterColon << it->tag << " " << it->cipher_suite << " "
1469 << it->key_params;
1470 if (!it->session_params.empty()) {
1471 os << " " << it->session_params;
1472 }
1473 AddLine(os.str(), message);
1474 }
1475
1476 // RFC 4566
1477 // a=rtpmap:<payload type> <encoding name>/<clock rate>
1478 // [/<encodingparameters>]
1479 BuildRtpMap(media_desc, media_type, message);
1480
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001481 for (StreamParamsVec::const_iterator track = media_desc->streams().begin();
1482 track != media_desc->streams().end(); ++track) {
1483 // Require that the track belongs to a media stream,
1484 // ie the sync_label is set. This extra check is necessary since the
1485 // MediaContentDescription always contains a streamparam with an ssrc even
1486 // if no track or media stream have been created.
1487 if (track->sync_label.empty()) continue;
1488
1489 // Build the ssrc-group lines.
1490 for (size_t i = 0; i < track->ssrc_groups.size(); ++i) {
1491 // RFC 5576
1492 // a=ssrc-group:<semantics> <ssrc-id> ...
1493 if (track->ssrc_groups[i].ssrcs.empty()) {
1494 continue;
1495 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001496 InitAttrLine(kAttributeSsrcGroup, &os);
1497 os << kSdpDelimiterColon << track->ssrc_groups[i].semantics;
Peter Boström0c4e06b2015-10-07 12:23:21 +02001498 std::vector<uint32_t>::const_iterator ssrc =
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001499 track->ssrc_groups[i].ssrcs.begin();
1500 for (; ssrc != track->ssrc_groups[i].ssrcs.end(); ++ssrc) {
Peter Boström0c4e06b2015-10-07 12:23:21 +02001501 os << kSdpDelimiterSpace << rtc::ToString<uint32_t>(*ssrc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001502 }
1503 AddLine(os.str(), message);
1504 }
1505 // Build the ssrc lines for each ssrc.
1506 for (size_t i = 0; i < track->ssrcs.size(); ++i) {
Peter Boström0c4e06b2015-10-07 12:23:21 +02001507 uint32_t ssrc = track->ssrcs[i];
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001508 // RFC 5576
1509 // a=ssrc:<ssrc-id> cname:<value>
1510 AddSsrcLine(ssrc, kSsrcAttributeCname,
1511 track->cname, message);
1512
1513 // draft-alvestrand-mmusic-msid-00
1514 // a=ssrc:<ssrc-id> msid:identifier [appdata]
deadbeef9d3584c2016-02-16 17:54:10 -08001515 // The appdata consists of the "id" attribute of a MediaStreamTrack,
1516 // which corresponds to the "id" attribute of StreamParams.
1517 const std::string& stream_id = track->sync_label;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001518 InitAttrLine(kAttributeSsrc, &os);
1519 os << kSdpDelimiterColon << ssrc << kSdpDelimiterSpace
deadbeef9d3584c2016-02-16 17:54:10 -08001520 << kSsrcAttributeMsid << kSdpDelimiterColon << stream_id
1521 << kSdpDelimiterSpace << track->id;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001522 AddLine(os.str(), message);
1523
deadbeef9d3584c2016-02-16 17:54:10 -08001524 // TODO(ronghuawu): Remove below code which is for backward
1525 // compatibility.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001526 // draft-alvestrand-rtcweb-mid-01
1527 // a=ssrc:<ssrc-id> mslabel:<value>
1528 // The label isn't yet defined.
1529 // a=ssrc:<ssrc-id> label:<value>
1530 AddSsrcLine(ssrc, kSsrcAttributeMslabel, track->sync_label, message);
1531 AddSsrcLine(ssrc, kSSrcAttributeLabel, track->id, message);
1532 }
1533 }
1534}
1535
1536void WriteFmtpHeader(int payload_type, std::ostringstream* os) {
1537 // fmtp header: a=fmtp:|payload_type| <parameters>
1538 // Add a=fmtp
1539 InitAttrLine(kAttributeFmtp, os);
1540 // Add :|payload_type|
1541 *os << kSdpDelimiterColon << payload_type;
1542}
1543
1544void WriteRtcpFbHeader(int payload_type, std::ostringstream* os) {
1545 // rtcp-fb header: a=rtcp-fb:|payload_type|
1546 // <parameters>/<ccm <ccm_parameters>>
1547 // Add a=rtcp-fb
1548 InitAttrLine(kAttributeRtcpFb, os);
1549 // Add :
1550 *os << kSdpDelimiterColon;
1551 if (payload_type == kWildcardPayloadType) {
1552 *os << "*";
1553 } else {
1554 *os << payload_type;
1555 }
1556}
1557
1558void WriteFmtpParameter(const std::string& parameter_name,
1559 const std::string& parameter_value,
1560 std::ostringstream* os) {
1561 // fmtp parameters: |parameter_name|=|parameter_value|
1562 *os << parameter_name << kSdpDelimiterEqual << parameter_value;
1563}
1564
1565void WriteFmtpParameters(const cricket::CodecParameterMap& parameters,
1566 std::ostringstream* os) {
1567 for (cricket::CodecParameterMap::const_iterator fmtp = parameters.begin();
1568 fmtp != parameters.end(); ++fmtp) {
hta62a216e2016-04-15 11:02:14 -07001569 // Parameters are a semicolon-separated list, no spaces.
1570 // The list is separated from the header by a space.
1571 if (fmtp == parameters.begin()) {
1572 *os << kSdpDelimiterSpace;
1573 } else {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001574 *os << kSdpDelimiterSemicolon;
1575 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001576 WriteFmtpParameter(fmtp->first, fmtp->second, os);
1577 }
1578}
1579
1580bool IsFmtpParam(const std::string& name) {
1581 const char* kFmtpParams[] = {
htaa6b99442016-04-12 10:29:17 -07001582 // TODO(hta): Split FMTP parameters apart from parameters in general.
1583 // FMTP parameters are codec specific, not generic.
1584 kCodecParamMinPTime,
1585 kCodecParamSPropStereo,
1586 kCodecParamStereo,
1587 kCodecParamUseInbandFec,
1588 kCodecParamUseDtx,
1589 kCodecParamStartBitrate,
1590 kCodecParamMaxBitrate,
1591 kCodecParamMinBitrate,
1592 kCodecParamMaxQuantization,
1593 kCodecParamSctpProtocol,
1594 kCodecParamSctpStreams,
1595 kCodecParamMaxAverageBitrate,
1596 kCodecParamMaxPlaybackRate,
1597 kCodecParamAssociatedPayloadType,
1598 cricket::kH264FmtpPacketizationMode,
1599 cricket::kH264FmtpLevelAsymmetryAllowed,
1600 cricket::kH264FmtpProfileLevelId};
tfarina5237aaf2015-11-10 23:44:30 -08001601 for (size_t i = 0; i < arraysize(kFmtpParams); ++i) {
htaa6b99442016-04-12 10:29:17 -07001602 if (name.compare(kFmtpParams[i]) == 0) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001603 return true;
1604 }
1605 }
1606 return false;
1607}
1608
1609// Retreives fmtp parameters from |params|, which may contain other parameters
1610// as well, and puts them in |fmtp_parameters|.
1611void GetFmtpParams(const cricket::CodecParameterMap& params,
1612 cricket::CodecParameterMap* fmtp_parameters) {
1613 for (cricket::CodecParameterMap::const_iterator iter = params.begin();
1614 iter != params.end(); ++iter) {
1615 if (IsFmtpParam(iter->first)) {
1616 (*fmtp_parameters)[iter->first] = iter->second;
1617 }
1618 }
1619}
1620
1621template <class T>
1622void AddFmtpLine(const T& codec, std::string* message) {
1623 cricket::CodecParameterMap fmtp_parameters;
1624 GetFmtpParams(codec.params, &fmtp_parameters);
1625 if (fmtp_parameters.empty()) {
1626 // No need to add an fmtp if it will have no (optional) parameters.
1627 return;
1628 }
1629 std::ostringstream os;
1630 WriteFmtpHeader(codec.id, &os);
1631 WriteFmtpParameters(fmtp_parameters, &os);
1632 AddLine(os.str(), message);
1633 return;
1634}
1635
1636template <class T>
1637void AddRtcpFbLines(const T& codec, std::string* message) {
1638 for (std::vector<cricket::FeedbackParam>::const_iterator iter =
1639 codec.feedback_params.params().begin();
1640 iter != codec.feedback_params.params().end(); ++iter) {
1641 std::ostringstream os;
1642 WriteRtcpFbHeader(codec.id, &os);
1643 os << " " << iter->id();
1644 if (!iter->param().empty()) {
1645 os << " " << iter->param();
1646 }
1647 AddLine(os.str(), message);
1648 }
1649}
1650
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00001651bool AddSctpDataCodec(DataContentDescription* media_desc,
1652 int sctp_port) {
1653 if (media_desc->HasCodec(cricket::kGoogleSctpDataCodecId)) {
1654 return ParseFailed("",
1655 "Can't have multiple sctp port attributes.",
1656 NULL);
1657 }
1658 // Add the SCTP Port number as a pseudo-codec "port" parameter
deadbeef67cf2c12016-04-13 10:07:16 -07001659 cricket::DataCodec codec_port(cricket::kGoogleSctpDataCodecId,
1660 cricket::kGoogleSctpDataCodecName);
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00001661 codec_port.SetParam(cricket::kCodecParamPort, sctp_port);
1662 LOG(INFO) << "AddSctpDataCodec: Got SCTP Port Number "
1663 << sctp_port;
1664 media_desc->AddCodec(codec_port);
1665 return true;
1666}
1667
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001668bool GetMinValue(const std::vector<int>& values, int* value) {
1669 if (values.empty()) {
1670 return false;
1671 }
1672 std::vector<int>::const_iterator found =
1673 std::min_element(values.begin(), values.end());
1674 *value = *found;
1675 return true;
1676}
1677
1678bool GetParameter(const std::string& name,
1679 const cricket::CodecParameterMap& params, int* value) {
1680 std::map<std::string, std::string>::const_iterator found =
1681 params.find(name);
1682 if (found == params.end()) {
1683 return false;
1684 }
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001685 if (!rtc::FromString(found->second, value)) {
wu@webrtc.org5e760e72014-04-02 23:19:09 +00001686 return false;
1687 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001688 return true;
1689}
1690
1691void BuildRtpMap(const MediaContentDescription* media_desc,
1692 const MediaType media_type,
1693 std::string* message) {
1694 ASSERT(message != NULL);
1695 ASSERT(media_desc != NULL);
1696 std::ostringstream os;
1697 if (media_type == cricket::MEDIA_TYPE_VIDEO) {
1698 const VideoContentDescription* video_desc =
1699 static_cast<const VideoContentDescription*>(media_desc);
1700 for (std::vector<cricket::VideoCodec>::const_iterator it =
1701 video_desc->codecs().begin();
1702 it != video_desc->codecs().end(); ++it) {
1703 // RFC 4566
1704 // a=rtpmap:<payload type> <encoding name>/<clock rate>
1705 // [/<encodingparameters>]
1706 if (it->id != kWildcardPayloadType) {
1707 InitAttrLine(kAttributeRtpmap, &os);
1708 os << kSdpDelimiterColon << it->id << " " << it->name
1709 << "/" << kDefaultVideoClockrate;
1710 AddLine(os.str(), message);
1711 }
1712 AddRtcpFbLines(*it, message);
1713 AddFmtpLine(*it, message);
1714 }
1715 } else if (media_type == cricket::MEDIA_TYPE_AUDIO) {
1716 const AudioContentDescription* audio_desc =
1717 static_cast<const AudioContentDescription*>(media_desc);
1718 std::vector<int> ptimes;
1719 std::vector<int> maxptimes;
1720 int max_minptime = 0;
1721 for (std::vector<cricket::AudioCodec>::const_iterator it =
1722 audio_desc->codecs().begin();
1723 it != audio_desc->codecs().end(); ++it) {
1724 ASSERT(!it->name.empty());
1725 // RFC 4566
1726 // a=rtpmap:<payload type> <encoding name>/<clock rate>
1727 // [/<encodingparameters>]
1728 InitAttrLine(kAttributeRtpmap, &os);
1729 os << kSdpDelimiterColon << it->id << " ";
1730 os << it->name << "/" << it->clockrate;
1731 if (it->channels != 1) {
1732 os << "/" << it->channels;
1733 }
1734 AddLine(os.str(), message);
1735 AddRtcpFbLines(*it, message);
1736 AddFmtpLine(*it, message);
1737 int minptime = 0;
1738 if (GetParameter(kCodecParamMinPTime, it->params, &minptime)) {
1739 max_minptime = std::max(minptime, max_minptime);
1740 }
1741 int ptime;
1742 if (GetParameter(kCodecParamPTime, it->params, &ptime)) {
1743 ptimes.push_back(ptime);
1744 }
1745 int maxptime;
1746 if (GetParameter(kCodecParamMaxPTime, it->params, &maxptime)) {
1747 maxptimes.push_back(maxptime);
1748 }
1749 }
1750 // Populate the maxptime attribute with the smallest maxptime of all codecs
1751 // under the same m-line.
1752 int min_maxptime = INT_MAX;
1753 if (GetMinValue(maxptimes, &min_maxptime)) {
1754 AddAttributeLine(kCodecParamMaxPTime, min_maxptime, message);
1755 }
1756 ASSERT(min_maxptime > max_minptime);
1757 // Populate the ptime attribute with the smallest ptime or the largest
1758 // minptime, whichever is the largest, for all codecs under the same m-line.
1759 int ptime = INT_MAX;
1760 if (GetMinValue(ptimes, &ptime)) {
1761 ptime = std::min(ptime, min_maxptime);
1762 ptime = std::max(ptime, max_minptime);
1763 AddAttributeLine(kCodecParamPTime, ptime, message);
1764 }
1765 } else if (media_type == cricket::MEDIA_TYPE_DATA) {
1766 const DataContentDescription* data_desc =
1767 static_cast<const DataContentDescription*>(media_desc);
1768 for (std::vector<cricket::DataCodec>::const_iterator it =
1769 data_desc->codecs().begin();
1770 it != data_desc->codecs().end(); ++it) {
1771 // RFC 4566
1772 // a=rtpmap:<payload type> <encoding name>/<clock rate>
1773 // [/<encodingparameters>]
1774 InitAttrLine(kAttributeRtpmap, &os);
1775 os << kSdpDelimiterColon << it->id << " "
1776 << it->name << "/" << it->clockrate;
1777 AddLine(os.str(), message);
1778 }
1779 }
1780}
1781
1782void BuildCandidate(const std::vector<Candidate>& candidates,
honghaiza54a0802015-12-16 18:37:23 -08001783 bool include_ufrag,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001784 std::string* message) {
1785 std::ostringstream os;
1786
1787 for (std::vector<Candidate>::const_iterator it = candidates.begin();
1788 it != candidates.end(); ++it) {
1789 // RFC 5245
1790 // a=candidate:<foundation> <component-id> <transport> <priority>
1791 // <connection-address> <port> typ <candidate-types>
1792 // [raddr <connection-address>] [rport <port>]
1793 // *(SP extension-att-name SP extension-att-value)
1794 std::string type;
1795 // Map the cricket candidate type to "host" / "srflx" / "prflx" / "relay"
1796 if (it->type() == cricket::LOCAL_PORT_TYPE) {
1797 type = kCandidateHost;
1798 } else if (it->type() == cricket::STUN_PORT_TYPE) {
1799 type = kCandidateSrflx;
1800 } else if (it->type() == cricket::RELAY_PORT_TYPE) {
1801 type = kCandidateRelay;
Honghai Zhang7fb69db2016-03-14 11:59:18 -07001802 } else if (it->type() == cricket::PRFLX_PORT_TYPE) {
1803 type = kCandidatePrflx;
1804 // Peer reflexive candidate may be signaled for being removed.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001805 } else {
1806 ASSERT(false);
Peter Thatcher019087f2015-04-28 09:06:26 -07001807 // Never write out candidates if we don't know the type.
1808 continue;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001809 }
1810
1811 InitAttrLine(kAttributeCandidate, &os);
1812 os << kSdpDelimiterColon
mallinath@webrtc.org2d60c5e2014-08-08 22:29:20 +00001813 << it->foundation() << " "
1814 << it->component() << " "
1815 << it->protocol() << " "
1816 << it->priority() << " "
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001817 << it->address().ipaddr().ToString() << " "
1818 << it->address().PortAsString() << " "
mallinath@webrtc.org2d60c5e2014-08-08 22:29:20 +00001819 << kAttributeCandidateTyp << " "
1820 << type << " ";
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001821
1822 // Related address
1823 if (!it->related_address().IsNil()) {
1824 os << kAttributeCandidateRaddr << " "
1825 << it->related_address().ipaddr().ToString() << " "
1826 << kAttributeCandidateRport << " "
1827 << it->related_address().PortAsString() << " ";
1828 }
1829
mallinath@webrtc.org2d60c5e2014-08-08 22:29:20 +00001830 if (it->protocol() == cricket::TCP_PROTOCOL_NAME) {
mallinath@webrtc.orge999bd02014-08-13 06:05:55 +00001831 os << kTcpCandidateType << " " << it->tcptype() << " ";
mallinath@webrtc.org2d60c5e2014-08-08 22:29:20 +00001832 }
1833
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001834 // Extensions
1835 os << kAttributeCandidateGeneration << " " << it->generation();
honghaiza54a0802015-12-16 18:37:23 -08001836 if (include_ufrag && !it->username().empty()) {
1837 os << " " << kAttributeCandidateUfrag << " " << it->username();
1838 }
honghaiza0c44ea2016-03-23 16:07:48 -07001839 if (it->network_id() > 0) {
1840 os << " " << kAttributeCandidateNetworkId << " " << it->network_id();
1841 }
honghaize1a0c942016-02-16 14:54:56 -08001842 if (it->network_cost() > 0) {
1843 os << " " << kAttributeCandidateNetworkCost << " " << it->network_cost();
1844 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001845
1846 AddLine(os.str(), message);
1847 }
1848}
1849
1850void BuildIceOptions(const std::vector<std::string>& transport_options,
1851 std::string* message) {
1852 if (!transport_options.empty()) {
1853 std::ostringstream os;
1854 InitAttrLine(kAttributeIceOption, &os);
1855 os << kSdpDelimiterColon << transport_options[0];
1856 for (size_t i = 1; i < transport_options.size(); ++i) {
1857 os << kSdpDelimiterSpace << transport_options[i];
1858 }
1859 AddLine(os.str(), message);
1860 }
1861}
1862
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00001863bool IsRtp(const std::string& protocol) {
1864 return protocol.empty() ||
1865 (protocol.find(cricket::kMediaProtocolRtpPrefix) != std::string::npos);
1866}
1867
1868bool IsDtlsSctp(const std::string& protocol) {
1869 // This intentionally excludes "SCTP" and "SCTP/DTLS".
lally@webrtc.orga7470932015-02-24 20:19:21 +00001870 return protocol.find(cricket::kMediaProtocolDtlsSctp) != std::string::npos;
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00001871}
1872
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001873bool ParseSessionDescription(const std::string& message, size_t* pos,
1874 std::string* session_id,
1875 std::string* session_version,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001876 TransportDescription* session_td,
1877 RtpHeaderExtensions* session_extmaps,
1878 cricket::SessionDescription* desc,
1879 SdpParseError* error) {
1880 std::string line;
1881
deadbeefc80741f2015-10-22 13:14:45 -07001882 desc->set_msid_supported(false);
1883
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001884 // RFC 4566
1885 // v= (protocol version)
1886 if (!GetLineWithType(message, pos, &line, kLineTypeVersion)) {
1887 return ParseFailedExpectLine(message, *pos, kLineTypeVersion,
1888 std::string(), error);
1889 }
1890 // RFC 4566
1891 // o=<username> <sess-id> <sess-version> <nettype> <addrtype>
1892 // <unicast-address>
1893 if (!GetLineWithType(message, pos, &line, kLineTypeOrigin)) {
1894 return ParseFailedExpectLine(message, *pos, kLineTypeOrigin,
1895 std::string(), error);
1896 }
1897 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001898 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001899 kSdpDelimiterSpace, &fields);
1900 const size_t expected_fields = 6;
1901 if (fields.size() != expected_fields) {
1902 return ParseFailedExpectFieldNum(line, expected_fields, error);
1903 }
1904 *session_id = fields[1];
1905 *session_version = fields[2];
1906
1907 // RFC 4566
1908 // s= (session name)
1909 if (!GetLineWithType(message, pos, &line, kLineTypeSessionName)) {
1910 return ParseFailedExpectLine(message, *pos, kLineTypeSessionName,
1911 std::string(), error);
1912 }
1913
1914 // Optional lines
1915 // Those are the optional lines, so shouldn't return false if not present.
1916 // RFC 4566
1917 // i=* (session information)
1918 GetLineWithType(message, pos, &line, kLineTypeSessionInfo);
1919
1920 // RFC 4566
1921 // u=* (URI of description)
1922 GetLineWithType(message, pos, &line, kLineTypeSessionUri);
1923
1924 // RFC 4566
1925 // e=* (email address)
1926 GetLineWithType(message, pos, &line, kLineTypeSessionEmail);
1927
1928 // RFC 4566
1929 // p=* (phone number)
1930 GetLineWithType(message, pos, &line, kLineTypeSessionPhone);
1931
1932 // RFC 4566
1933 // c=* (connection information -- not required if included in
1934 // all media)
1935 GetLineWithType(message, pos, &line, kLineTypeConnection);
1936
1937 // RFC 4566
1938 // b=* (zero or more bandwidth information lines)
1939 while (GetLineWithType(message, pos, &line, kLineTypeSessionBandwidth)) {
1940 // By pass zero or more b lines.
1941 }
1942
1943 // RFC 4566
1944 // One or more time descriptions ("t=" and "r=" lines; see below)
1945 // t= (time the session is active)
1946 // r=* (zero or more repeat times)
1947 // Ensure there's at least one time description
1948 if (!GetLineWithType(message, pos, &line, kLineTypeTiming)) {
1949 return ParseFailedExpectLine(message, *pos, kLineTypeTiming, std::string(),
1950 error);
1951 }
1952
1953 while (GetLineWithType(message, pos, &line, kLineTypeRepeatTimes)) {
1954 // By pass zero or more r lines.
1955 }
1956
1957 // Go through the rest of the time descriptions
1958 while (GetLineWithType(message, pos, &line, kLineTypeTiming)) {
1959 while (GetLineWithType(message, pos, &line, kLineTypeRepeatTimes)) {
1960 // By pass zero or more r lines.
1961 }
1962 }
1963
1964 // RFC 4566
1965 // z=* (time zone adjustments)
1966 GetLineWithType(message, pos, &line, kLineTypeTimeZone);
1967
1968 // RFC 4566
1969 // k=* (encryption key)
1970 GetLineWithType(message, pos, &line, kLineTypeEncryptionKey);
1971
1972 // RFC 4566
1973 // a=* (zero or more session attribute lines)
1974 while (GetLineWithType(message, pos, &line, kLineTypeAttributes)) {
1975 if (HasAttribute(line, kAttributeGroup)) {
1976 if (!ParseGroupAttribute(line, desc, error)) {
1977 return false;
1978 }
1979 } else if (HasAttribute(line, kAttributeIceUfrag)) {
1980 if (!GetValue(line, kAttributeIceUfrag,
1981 &(session_td->ice_ufrag), error)) {
1982 return false;
1983 }
1984 } else if (HasAttribute(line, kAttributeIcePwd)) {
1985 if (!GetValue(line, kAttributeIcePwd, &(session_td->ice_pwd), error)) {
1986 return false;
1987 }
1988 } else if (HasAttribute(line, kAttributeIceLite)) {
1989 session_td->ice_mode = cricket::ICEMODE_LITE;
1990 } else if (HasAttribute(line, kAttributeIceOption)) {
1991 if (!ParseIceOptions(line, &(session_td->transport_options), error)) {
1992 return false;
1993 }
1994 } else if (HasAttribute(line, kAttributeFingerprint)) {
1995 if (session_td->identity_fingerprint.get()) {
1996 return ParseFailed(
1997 line,
1998 "Can't have multiple fingerprint attributes at the same level.",
1999 error);
2000 }
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002001 rtc::SSLFingerprint* fingerprint = NULL;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002002 if (!ParseFingerprintAttribute(line, &fingerprint, error)) {
2003 return false;
2004 }
2005 session_td->identity_fingerprint.reset(fingerprint);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00002006 } else if (HasAttribute(line, kAttributeSetup)) {
2007 if (!ParseDtlsSetup(line, &(session_td->connection_role), error)) {
2008 return false;
2009 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002010 } else if (HasAttribute(line, kAttributeMsidSemantics)) {
2011 std::string semantics;
2012 if (!GetValue(line, kAttributeMsidSemantics, &semantics, error)) {
2013 return false;
2014 }
deadbeefc80741f2015-10-22 13:14:45 -07002015 desc->set_msid_supported(
2016 CaseInsensitiveFind(semantics, kMediaStreamSemantic));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002017 } else if (HasAttribute(line, kAttributeExtmap)) {
2018 RtpHeaderExtension extmap;
2019 if (!ParseExtmap(line, &extmap, error)) {
2020 return false;
2021 }
2022 session_extmaps->push_back(extmap);
2023 }
2024 }
2025
2026 return true;
2027}
2028
2029bool ParseGroupAttribute(const std::string& line,
2030 cricket::SessionDescription* desc,
2031 SdpParseError* error) {
2032 ASSERT(desc != NULL);
2033
2034 // RFC 5888 and draft-holmberg-mmusic-sdp-bundle-negotiation-00
2035 // a=group:BUNDLE video voice
2036 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002037 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002038 kSdpDelimiterSpace, &fields);
2039 std::string semantics;
2040 if (!GetValue(fields[0], kAttributeGroup, &semantics, error)) {
2041 return false;
2042 }
2043 cricket::ContentGroup group(semantics);
2044 for (size_t i = 1; i < fields.size(); ++i) {
2045 group.AddContentName(fields[i]);
2046 }
2047 desc->AddGroup(group);
2048 return true;
2049}
2050
2051static bool ParseFingerprintAttribute(const std::string& line,
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002052 rtc::SSLFingerprint** fingerprint,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002053 SdpParseError* error) {
2054 if (!IsLineType(line, kLineTypeAttributes) ||
2055 !HasAttribute(line, kAttributeFingerprint)) {
2056 return ParseFailedExpectLine(line, 0, kLineTypeAttributes,
2057 kAttributeFingerprint, error);
2058 }
2059
2060 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002061 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002062 kSdpDelimiterSpace, &fields);
2063 const size_t expected_fields = 2;
2064 if (fields.size() != expected_fields) {
2065 return ParseFailedExpectFieldNum(line, expected_fields, error);
2066 }
2067
2068 // The first field here is "fingerprint:<hash>.
2069 std::string algorithm;
2070 if (!GetValue(fields[0], kAttributeFingerprint, &algorithm, error)) {
2071 return false;
2072 }
2073
2074 // Downcase the algorithm. Note that we don't need to downcase the
2075 // fingerprint because hex_decode can handle upper-case.
2076 std::transform(algorithm.begin(), algorithm.end(), algorithm.begin(),
2077 ::tolower);
2078
2079 // The second field is the digest value. De-hexify it.
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002080 *fingerprint = rtc::SSLFingerprint::CreateFromRfc4572(
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002081 algorithm, fields[1]);
2082 if (!*fingerprint) {
2083 return ParseFailed(line,
2084 "Failed to create fingerprint from the digest.",
2085 error);
2086 }
2087
2088 return true;
2089}
2090
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00002091static bool ParseDtlsSetup(const std::string& line,
2092 cricket::ConnectionRole* role,
2093 SdpParseError* error) {
2094 // setup-attr = "a=setup:" role
2095 // role = "active" / "passive" / "actpass" / "holdconn"
2096 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002097 rtc::split(line.substr(kLinePrefixLength), kSdpDelimiterColon, &fields);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00002098 const size_t expected_fields = 2;
2099 if (fields.size() != expected_fields) {
2100 return ParseFailedExpectFieldNum(line, expected_fields, error);
2101 }
2102 std::string role_str = fields[1];
2103 if (!cricket::StringToConnectionRole(role_str, role)) {
2104 return ParseFailed(line, "Invalid attribute value.", error);
2105 }
2106 return true;
2107}
2108
deadbeef9d3584c2016-02-16 17:54:10 -08002109static bool ParseMsidAttribute(const std::string& line,
2110 std::string* stream_id,
2111 std::string* track_id,
2112 SdpParseError* error) {
2113 // draft-ietf-mmusic-msid-11
2114 // a=msid:<stream id> <track id>
2115 // msid-value = msid-id [ SP msid-appdata ]
2116 // msid-id = 1*64token-char ; see RFC 4566
2117 // msid-appdata = 1*64token-char ; see RFC 4566
2118 std::string field1;
2119 if (!rtc::tokenize_first(line.substr(kLinePrefixLength), kSdpDelimiterSpace,
2120 &field1, track_id)) {
2121 const size_t expected_fields = 2;
2122 return ParseFailedExpectFieldNum(line, expected_fields, error);
2123 }
2124
2125 // msid:<msid-id>
2126 if (!GetValue(field1, kAttributeMsid, stream_id, error)) {
2127 return false;
2128 }
2129 return true;
2130}
2131
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002132// RFC 3551
2133// PT encoding media type clock rate channels
2134// name (Hz)
2135// 0 PCMU A 8,000 1
2136// 1 reserved A
2137// 2 reserved A
2138// 3 GSM A 8,000 1
2139// 4 G723 A 8,000 1
2140// 5 DVI4 A 8,000 1
2141// 6 DVI4 A 16,000 1
2142// 7 LPC A 8,000 1
2143// 8 PCMA A 8,000 1
2144// 9 G722 A 8,000 1
2145// 10 L16 A 44,100 2
2146// 11 L16 A 44,100 1
2147// 12 QCELP A 8,000 1
2148// 13 CN A 8,000 1
2149// 14 MPA A 90,000 (see text)
2150// 15 G728 A 8,000 1
2151// 16 DVI4 A 11,025 1
2152// 17 DVI4 A 22,050 1
2153// 18 G729 A 8,000 1
2154struct StaticPayloadAudioCodec {
2155 const char* name;
2156 int clockrate;
Peter Kasting69558702016-01-12 16:26:35 -08002157 size_t channels;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002158};
2159static const StaticPayloadAudioCodec kStaticPayloadAudioCodecs[] = {
2160 { "PCMU", 8000, 1 },
2161 { "reserved", 0, 0 },
2162 { "reserved", 0, 0 },
2163 { "GSM", 8000, 1 },
2164 { "G723", 8000, 1 },
2165 { "DVI4", 8000, 1 },
2166 { "DVI4", 16000, 1 },
2167 { "LPC", 8000, 1 },
2168 { "PCMA", 8000, 1 },
2169 { "G722", 8000, 1 },
2170 { "L16", 44100, 2 },
2171 { "L16", 44100, 1 },
2172 { "QCELP", 8000, 1 },
2173 { "CN", 8000, 1 },
2174 { "MPA", 90000, 1 },
2175 { "G728", 8000, 1 },
2176 { "DVI4", 11025, 1 },
2177 { "DVI4", 22050, 1 },
2178 { "G729", 8000, 1 },
2179};
2180
2181void MaybeCreateStaticPayloadAudioCodecs(
2182 const std::vector<int>& fmts, AudioContentDescription* media_desc) {
2183 if (!media_desc) {
2184 return;
2185 }
deadbeef67cf2c12016-04-13 10:07:16 -07002186 RTC_DCHECK(media_desc->codecs().empty());
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002187 std::vector<int>::const_iterator it = fmts.begin();
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002188 for (; it != fmts.end(); ++it) {
2189 int payload_type = *it;
2190 if (!media_desc->HasCodec(payload_type) &&
2191 payload_type >= 0 &&
tfarina5237aaf2015-11-10 23:44:30 -08002192 payload_type < arraysize(kStaticPayloadAudioCodecs)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002193 std::string encoding_name = kStaticPayloadAudioCodecs[payload_type].name;
2194 int clock_rate = kStaticPayloadAudioCodecs[payload_type].clockrate;
Peter Kasting69558702016-01-12 16:26:35 -08002195 size_t channels = kStaticPayloadAudioCodecs[payload_type].channels;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002196 media_desc->AddCodec(cricket::AudioCodec(payload_type, encoding_name,
deadbeef67cf2c12016-04-13 10:07:16 -07002197 clock_rate, 0, channels));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002198 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002199 }
2200}
2201
2202template <class C>
2203static C* ParseContentDescription(const std::string& message,
2204 const MediaType media_type,
2205 int mline_index,
2206 const std::string& protocol,
deadbeef67cf2c12016-04-13 10:07:16 -07002207 const std::vector<int>& payload_types,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002208 size_t* pos,
2209 std::string* content_name,
2210 TransportDescription* transport,
2211 std::vector<JsepIceCandidate*>* candidates,
2212 webrtc::SdpParseError* error) {
2213 C* media_desc = new C();
2214 switch (media_type) {
2215 case cricket::MEDIA_TYPE_AUDIO:
2216 *content_name = cricket::CN_AUDIO;
2217 break;
2218 case cricket::MEDIA_TYPE_VIDEO:
2219 *content_name = cricket::CN_VIDEO;
2220 break;
2221 case cricket::MEDIA_TYPE_DATA:
2222 *content_name = cricket::CN_DATA;
2223 break;
2224 default:
2225 ASSERT(false);
2226 break;
2227 }
deadbeef67cf2c12016-04-13 10:07:16 -07002228 if (!ParseContent(message, media_type, mline_index, protocol, payload_types,
2229 pos, content_name, media_desc, transport, candidates,
2230 error)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002231 delete media_desc;
2232 return NULL;
2233 }
2234 // Sort the codecs according to the m-line fmt list.
deadbeef67cf2c12016-04-13 10:07:16 -07002235 std::unordered_map<int, int> payload_type_preferences;
2236 // "size + 1" so that the lowest preference payload type has a preference of
2237 // 1, which is greater than the default (0) for payload types not in the fmt
2238 // list.
2239 int preference = static_cast<int>(payload_types.size() + 1);
2240 for (int pt : payload_types) {
2241 payload_type_preferences[pt] = preference--;
2242 }
2243 std::vector<typename C::CodecType> codecs = media_desc->codecs();
2244 std::sort(codecs.begin(), codecs.end(), [&payload_type_preferences](
2245 const typename C::CodecType& a,
2246 const typename C::CodecType& b) {
2247 return payload_type_preferences[a.id] > payload_type_preferences[b.id];
2248 });
2249 media_desc->set_codecs(codecs);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002250 return media_desc;
2251}
2252
2253bool ParseMediaDescription(const std::string& message,
2254 const TransportDescription& session_td,
2255 const RtpHeaderExtensions& session_extmaps,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002256 size_t* pos,
2257 cricket::SessionDescription* desc,
2258 std::vector<JsepIceCandidate*>* candidates,
2259 SdpParseError* error) {
2260 ASSERT(desc != NULL);
2261 std::string line;
2262 int mline_index = -1;
2263
2264 // Zero or more media descriptions
2265 // RFC 4566
2266 // m=<media> <port> <proto> <fmt>
2267 while (GetLineWithType(message, pos, &line, kLineTypeMedia)) {
2268 ++mline_index;
2269
2270 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002271 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002272 kSdpDelimiterSpace, &fields);
2273 const size_t expected_min_fields = 4;
2274 if (fields.size() < expected_min_fields) {
2275 return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
2276 }
2277 bool rejected = false;
2278 // RFC 3264
2279 // To reject an offered stream, the port number in the corresponding stream
2280 // in the answer MUST be set to zero.
2281 if (fields[1] == kMediaPortRejected) {
2282 rejected = true;
2283 }
2284
2285 std::string protocol = fields[2];
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002286
2287 // <fmt>
deadbeef67cf2c12016-04-13 10:07:16 -07002288 std::vector<int> payload_types;
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00002289 if (IsRtp(protocol)) {
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00002290 for (size_t j = 3 ; j < fields.size(); ++j) {
2291 // TODO(wu): Remove when below bug is fixed.
2292 // https://bugzilla.mozilla.org/show_bug.cgi?id=996329
pbosbb36fdf2015-07-09 07:48:14 -07002293 if (fields[j].empty() && j == fields.size() - 1) {
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00002294 continue;
2295 }
wu@webrtc.org36eda7c2014-04-15 20:37:30 +00002296
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00002297 int pl = 0;
pkasting@chromium.orge9facf82015-02-17 20:36:28 +00002298 if (!GetPayloadTypeFromString(line, fields[j], &pl, error)) {
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00002299 return false;
2300 }
deadbeef67cf2c12016-04-13 10:07:16 -07002301 payload_types.push_back(pl);
wu@webrtc.org5e760e72014-04-02 23:19:09 +00002302 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002303 }
2304
2305 // Make a temporary TransportDescription based on |session_td|.
2306 // Some of this gets overwritten by ParseContent.
deadbeef46eed762016-01-28 13:24:37 -08002307 TransportDescription transport(
2308 session_td.transport_options, session_td.ice_ufrag, session_td.ice_pwd,
2309 session_td.ice_mode, session_td.connection_role,
2310 session_td.identity_fingerprint.get());
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002311
kwibergd1fe2812016-04-27 06:47:29 -07002312 std::unique_ptr<MediaContentDescription> content;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002313 std::string content_name;
2314 if (HasAttribute(line, kMediaTypeVideo)) {
2315 content.reset(ParseContentDescription<VideoContentDescription>(
deadbeef67cf2c12016-04-13 10:07:16 -07002316 message, cricket::MEDIA_TYPE_VIDEO, mline_index, protocol,
2317 payload_types, pos, &content_name, &transport, candidates, error));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002318 } else if (HasAttribute(line, kMediaTypeAudio)) {
2319 content.reset(ParseContentDescription<AudioContentDescription>(
deadbeef67cf2c12016-04-13 10:07:16 -07002320 message, cricket::MEDIA_TYPE_AUDIO, mline_index, protocol,
2321 payload_types, pos, &content_name, &transport, candidates, error));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002322 } else if (HasAttribute(line, kMediaTypeData)) {
jiayl@webrtc.org0e527722014-09-08 21:43:43 +00002323 DataContentDescription* data_desc =
wu@webrtc.org78187522013-10-07 23:32:02 +00002324 ParseContentDescription<DataContentDescription>(
deadbeef67cf2c12016-04-13 10:07:16 -07002325 message, cricket::MEDIA_TYPE_DATA, mline_index, protocol,
2326 payload_types, pos, &content_name, &transport, candidates, error);
jiayl@webrtc.org0e527722014-09-08 21:43:43 +00002327 content.reset(data_desc);
wu@webrtc.org78187522013-10-07 23:32:02 +00002328
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00002329 int p;
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00002330 if (data_desc && IsDtlsSctp(protocol) && rtc::FromString(fields[3], &p)) {
jiayl@webrtc.org0e527722014-09-08 21:43:43 +00002331 if (!AddSctpDataCodec(data_desc, p))
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00002332 return false;
wu@webrtc.org78187522013-10-07 23:32:02 +00002333 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002334 } else {
2335 LOG(LS_WARNING) << "Unsupported media type: " << line;
2336 continue;
2337 }
2338 if (!content.get()) {
2339 // ParseContentDescription returns NULL if failed.
2340 return false;
2341 }
2342
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00002343 if (IsRtp(protocol)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002344 // Set the extmap.
2345 if (!session_extmaps.empty() &&
2346 !content->rtp_header_extensions().empty()) {
2347 return ParseFailed("",
2348 "The a=extmap MUST be either all session level or "
2349 "all media level.",
2350 error);
2351 }
2352 for (size_t i = 0; i < session_extmaps.size(); ++i) {
2353 content->AddRtpHeaderExtension(session_extmaps[i]);
2354 }
2355 }
2356 content->set_protocol(protocol);
2357 desc->AddContent(content_name,
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00002358 IsDtlsSctp(protocol) ? cricket::NS_JINGLE_DRAFT_SCTP :
2359 cricket::NS_JINGLE_RTP,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002360 rejected,
2361 content.release());
2362 // Create TransportInfo with the media level "ice-pwd" and "ice-ufrag".
2363 TransportInfo transport_info(content_name, transport);
2364
2365 if (!desc->AddTransportInfo(transport_info)) {
2366 std::ostringstream description;
2367 description << "Failed to AddTransportInfo with content name: "
2368 << content_name;
2369 return ParseFailed("", description.str(), error);
2370 }
2371 }
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00002372
2373 size_t end_of_message = message.size();
2374 if (mline_index == -1 && *pos != end_of_message) {
2375 ParseFailed(message, *pos, "Expects m line.", error);
2376 return false;
2377 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002378 return true;
2379}
2380
2381bool VerifyCodec(const cricket::Codec& codec) {
2382 // Codec has not been populated correctly unless the name has been set. This
2383 // can happen if an SDP has an fmtp or rtcp-fb with a payload type but doesn't
2384 // have a corresponding "rtpmap" line.
2385 cricket::Codec default_codec;
2386 return default_codec.name != codec.name;
2387}
2388
2389bool VerifyAudioCodecs(const AudioContentDescription* audio_desc) {
2390 const std::vector<cricket::AudioCodec>& codecs = audio_desc->codecs();
2391 for (std::vector<cricket::AudioCodec>::const_iterator iter = codecs.begin();
2392 iter != codecs.end(); ++iter) {
2393 if (!VerifyCodec(*iter)) {
2394 return false;
2395 }
2396 }
2397 return true;
2398}
2399
2400bool VerifyVideoCodecs(const VideoContentDescription* video_desc) {
2401 const std::vector<cricket::VideoCodec>& codecs = video_desc->codecs();
2402 for (std::vector<cricket::VideoCodec>::const_iterator iter = codecs.begin();
2403 iter != codecs.end(); ++iter) {
2404 if (!VerifyCodec(*iter)) {
2405 return false;
2406 }
2407 }
2408 return true;
2409}
2410
2411void AddParameters(const cricket::CodecParameterMap& parameters,
2412 cricket::Codec* codec) {
2413 for (cricket::CodecParameterMap::const_iterator iter =
2414 parameters.begin(); iter != parameters.end(); ++iter) {
2415 codec->SetParam(iter->first, iter->second);
2416 }
2417}
2418
2419void AddFeedbackParameter(const cricket::FeedbackParam& feedback_param,
2420 cricket::Codec* codec) {
2421 codec->AddFeedbackParam(feedback_param);
2422}
2423
2424void AddFeedbackParameters(const cricket::FeedbackParams& feedback_params,
2425 cricket::Codec* codec) {
2426 for (std::vector<cricket::FeedbackParam>::const_iterator iter =
2427 feedback_params.params().begin();
2428 iter != feedback_params.params().end(); ++iter) {
2429 codec->AddFeedbackParam(*iter);
2430 }
2431}
2432
2433// Gets the current codec setting associated with |payload_type|. If there
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +00002434// is no Codec associated with that payload type it returns an empty codec
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002435// with that payload type.
2436template <class T>
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +00002437T GetCodecWithPayloadType(const std::vector<T>& codecs, int payload_type) {
2438 T ret_val;
2439 if (!FindCodecById(codecs, payload_type, &ret_val)) {
2440 ret_val.id = payload_type;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002441 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002442 return ret_val;
2443}
2444
2445// Updates or creates a new codec entry in the audio description.
2446template <class T, class U>
2447void AddOrReplaceCodec(MediaContentDescription* content_desc, const U& codec) {
2448 T* desc = static_cast<T*>(content_desc);
2449 std::vector<U> codecs = desc->codecs();
2450 bool found = false;
2451
2452 typename std::vector<U>::iterator iter;
2453 for (iter = codecs.begin(); iter != codecs.end(); ++iter) {
2454 if (iter->id == codec.id) {
2455 *iter = codec;
2456 found = true;
2457 break;
2458 }
2459 }
2460 if (!found) {
2461 desc->AddCodec(codec);
2462 return;
2463 }
2464 desc->set_codecs(codecs);
2465}
2466
2467// Adds or updates existing codec corresponding to |payload_type| according
2468// to |parameters|.
2469template <class T, class U>
2470void UpdateCodec(MediaContentDescription* content_desc, int payload_type,
2471 const cricket::CodecParameterMap& parameters) {
2472 // Codec might already have been populated (from rtpmap).
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +00002473 U new_codec = GetCodecWithPayloadType(static_cast<T*>(content_desc)->codecs(),
2474 payload_type);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002475 AddParameters(parameters, &new_codec);
2476 AddOrReplaceCodec<T, U>(content_desc, new_codec);
2477}
2478
2479// Adds or updates existing codec corresponding to |payload_type| according
2480// to |feedback_param|.
2481template <class T, class U>
2482void UpdateCodec(MediaContentDescription* content_desc, int payload_type,
2483 const cricket::FeedbackParam& feedback_param) {
2484 // Codec might already have been populated (from rtpmap).
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +00002485 U new_codec = GetCodecWithPayloadType(static_cast<T*>(content_desc)->codecs(),
2486 payload_type);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002487 AddFeedbackParameter(feedback_param, &new_codec);
2488 AddOrReplaceCodec<T, U>(content_desc, new_codec);
2489}
2490
jlmiller@webrtc.orga744a282015-02-18 21:37:46 +00002491template <class T>
2492bool PopWildcardCodec(std::vector<T>* codecs, T* wildcard_codec) {
2493 for (auto iter = codecs->begin(); iter != codecs->end(); ++iter) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002494 if (iter->id == kWildcardPayloadType) {
2495 *wildcard_codec = *iter;
2496 codecs->erase(iter);
2497 return true;
2498 }
2499 }
2500 return false;
2501}
2502
jlmiller@webrtc.orga744a282015-02-18 21:37:46 +00002503template<class T>
2504void UpdateFromWildcardCodecs(cricket::MediaContentDescriptionImpl<T>* desc) {
2505 auto codecs = desc->codecs();
2506 T wildcard_codec;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002507 if (!PopWildcardCodec(&codecs, &wildcard_codec)) {
2508 return;
2509 }
jlmiller@webrtc.orga744a282015-02-18 21:37:46 +00002510 for (auto& codec : codecs) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002511 AddFeedbackParameters(wildcard_codec.feedback_params, &codec);
2512 }
jlmiller@webrtc.orga744a282015-02-18 21:37:46 +00002513 desc->set_codecs(codecs);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002514}
2515
2516void AddAudioAttribute(const std::string& name, const std::string& value,
2517 AudioContentDescription* audio_desc) {
2518 if (value.empty()) {
2519 return;
2520 }
2521 std::vector<cricket::AudioCodec> codecs = audio_desc->codecs();
2522 for (std::vector<cricket::AudioCodec>::iterator iter = codecs.begin();
2523 iter != codecs.end(); ++iter) {
2524 iter->params[name] = value;
2525 }
2526 audio_desc->set_codecs(codecs);
2527}
2528
2529bool ParseContent(const std::string& message,
2530 const MediaType media_type,
2531 int mline_index,
2532 const std::string& protocol,
deadbeef67cf2c12016-04-13 10:07:16 -07002533 const std::vector<int>& payload_types,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002534 size_t* pos,
2535 std::string* content_name,
2536 MediaContentDescription* media_desc,
2537 TransportDescription* transport,
2538 std::vector<JsepIceCandidate*>* candidates,
2539 SdpParseError* error) {
2540 ASSERT(media_desc != NULL);
2541 ASSERT(content_name != NULL);
2542 ASSERT(transport != NULL);
2543
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +00002544 if (media_type == cricket::MEDIA_TYPE_AUDIO) {
2545 MaybeCreateStaticPayloadAudioCodecs(
deadbeef67cf2c12016-04-13 10:07:16 -07002546 payload_types, static_cast<AudioContentDescription*>(media_desc));
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +00002547 }
2548
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002549 // The media level "ice-ufrag" and "ice-pwd".
2550 // The candidates before update the media level "ice-pwd" and "ice-ufrag".
2551 Candidates candidates_orig;
2552 std::string line;
2553 std::string mline_id;
2554 // Tracks created out of the ssrc attributes.
2555 StreamParamsVec tracks;
2556 SsrcInfoVec ssrc_infos;
2557 SsrcGroupVec ssrc_groups;
2558 std::string maxptime_as_string;
2559 std::string ptime_as_string;
deadbeef9d3584c2016-02-16 17:54:10 -08002560 std::string stream_id;
2561 std::string track_id;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002562
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002563 // Loop until the next m line
2564 while (!IsLineType(message, kLineTypeMedia, *pos)) {
2565 if (!GetLine(message, pos, &line)) {
2566 if (*pos >= message.size()) {
2567 break; // Done parsing
2568 } else {
sergeyu@chromium.org5bc25c42013-12-05 00:24:06 +00002569 return ParseFailed(message, *pos, "Invalid SDP line.", error);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002570 }
2571 }
2572
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002573 // RFC 4566
2574 // b=* (zero or more bandwidth information lines)
2575 if (IsLineType(line, kLineTypeSessionBandwidth)) {
2576 std::string bandwidth;
2577 if (HasAttribute(line, kApplicationSpecificMaximum)) {
2578 if (!GetValue(line, kApplicationSpecificMaximum, &bandwidth, error)) {
2579 return false;
2580 } else {
wu@webrtc.org5e760e72014-04-02 23:19:09 +00002581 int b = 0;
2582 if (!GetValueFromString(line, bandwidth, &b, error)) {
2583 return false;
2584 }
Peter Thatcherc0c3a862015-06-24 15:31:25 -07002585 // We should never use more than the default bandwidth for RTP-based
2586 // data channels. Don't allow SDP to set the bandwidth, because
2587 // that would give JS the opportunity to "break the Internet".
2588 // See: https://code.google.com/p/chromium/issues/detail?id=280726
2589 if (media_type == cricket::MEDIA_TYPE_DATA && IsRtp(protocol) &&
2590 b > cricket::kDataMaxBandwidth / 1000) {
2591 std::ostringstream description;
2592 description << "RTP-based data channels may not send more than "
2593 << cricket::kDataMaxBandwidth / 1000 << "kbps.";
2594 return ParseFailed(line, description.str(), error);
2595 }
wu@webrtc.org5e760e72014-04-02 23:19:09 +00002596 media_desc->set_bandwidth(b * 1000);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002597 }
2598 }
2599 continue;
2600 }
2601
2602 if (!IsLineType(line, kLineTypeAttributes)) {
2603 // TODO: Handle other lines if needed.
2604 LOG(LS_INFO) << "Ignored line: " << line;
2605 continue;
2606 }
2607
2608 // Handle attributes common to SCTP and RTP.
2609 if (HasAttribute(line, kAttributeMid)) {
2610 // RFC 3388
2611 // mid-attribute = "a=mid:" identification-tag
2612 // identification-tag = token
2613 // Use the mid identification-tag as the content name.
2614 if (!GetValue(line, kAttributeMid, &mline_id, error)) {
2615 return false;
2616 }
2617 *content_name = mline_id;
2618 } else if (HasAttribute(line, kAttributeCandidate)) {
2619 Candidate candidate;
2620 if (!ParseCandidate(line, &candidate, error, false)) {
2621 return false;
2622 }
2623 candidates_orig.push_back(candidate);
2624 } else if (HasAttribute(line, kAttributeIceUfrag)) {
2625 if (!GetValue(line, kAttributeIceUfrag, &transport->ice_ufrag, error)) {
2626 return false;
2627 }
2628 } else if (HasAttribute(line, kAttributeIcePwd)) {
2629 if (!GetValue(line, kAttributeIcePwd, &transport->ice_pwd, error)) {
2630 return false;
2631 }
2632 } else if (HasAttribute(line, kAttributeIceOption)) {
2633 if (!ParseIceOptions(line, &transport->transport_options, error)) {
2634 return false;
2635 }
2636 } else if (HasAttribute(line, kAttributeFmtp)) {
2637 if (!ParseFmtpAttributes(line, media_type, media_desc, error)) {
2638 return false;
2639 }
2640 } else if (HasAttribute(line, kAttributeFingerprint)) {
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002641 rtc::SSLFingerprint* fingerprint = NULL;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002642
2643 if (!ParseFingerprintAttribute(line, &fingerprint, error)) {
2644 return false;
2645 }
2646 transport->identity_fingerprint.reset(fingerprint);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00002647 } else if (HasAttribute(line, kAttributeSetup)) {
2648 if (!ParseDtlsSetup(line, &(transport->connection_role), error)) {
2649 return false;
2650 }
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00002651 } else if (IsDtlsSctp(protocol) && HasAttribute(line, kAttributeSctpPort)) {
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00002652 int sctp_port;
2653 if (!ParseSctpPort(line, &sctp_port, error)) {
2654 return false;
2655 }
2656 if (!AddSctpDataCodec(static_cast<DataContentDescription*>(media_desc),
2657 sctp_port)) {
2658 return false;
2659 }
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00002660 } else if (IsRtp(protocol)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002661 //
2662 // RTP specific attrubtes
2663 //
2664 if (HasAttribute(line, kAttributeRtcpMux)) {
2665 media_desc->set_rtcp_mux(true);
deadbeef13871492015-12-09 12:37:51 -08002666 } else if (HasAttribute(line, kAttributeRtcpReducedSize)) {
2667 media_desc->set_rtcp_reduced_size(true);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002668 } else if (HasAttribute(line, kAttributeSsrcGroup)) {
2669 if (!ParseSsrcGroupAttribute(line, &ssrc_groups, error)) {
2670 return false;
2671 }
2672 } else if (HasAttribute(line, kAttributeSsrc)) {
2673 if (!ParseSsrcAttribute(line, &ssrc_infos, error)) {
2674 return false;
2675 }
2676 } else if (HasAttribute(line, kAttributeCrypto)) {
2677 if (!ParseCryptoAttribute(line, media_desc, error)) {
2678 return false;
2679 }
2680 } else if (HasAttribute(line, kAttributeRtpmap)) {
deadbeef67cf2c12016-04-13 10:07:16 -07002681 if (!ParseRtpmapAttribute(line, media_type, payload_types, media_desc,
2682 error)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002683 return false;
2684 }
2685 } else if (HasAttribute(line, kCodecParamMaxPTime)) {
2686 if (!GetValue(line, kCodecParamMaxPTime, &maxptime_as_string, error)) {
2687 return false;
2688 }
2689 } else if (HasAttribute(line, kAttributeRtcpFb)) {
2690 if (!ParseRtcpFbAttribute(line, media_type, media_desc, error)) {
2691 return false;
2692 }
2693 } else if (HasAttribute(line, kCodecParamPTime)) {
2694 if (!GetValue(line, kCodecParamPTime, &ptime_as_string, error)) {
2695 return false;
2696 }
2697 } else if (HasAttribute(line, kAttributeSendOnly)) {
2698 media_desc->set_direction(cricket::MD_SENDONLY);
2699 } else if (HasAttribute(line, kAttributeRecvOnly)) {
2700 media_desc->set_direction(cricket::MD_RECVONLY);
2701 } else if (HasAttribute(line, kAttributeInactive)) {
2702 media_desc->set_direction(cricket::MD_INACTIVE);
2703 } else if (HasAttribute(line, kAttributeSendRecv)) {
2704 media_desc->set_direction(cricket::MD_SENDRECV);
2705 } else if (HasAttribute(line, kAttributeExtmap)) {
2706 RtpHeaderExtension extmap;
2707 if (!ParseExtmap(line, &extmap, error)) {
2708 return false;
2709 }
2710 media_desc->AddRtpHeaderExtension(extmap);
2711 } else if (HasAttribute(line, kAttributeXGoogleFlag)) {
2712 // Experimental attribute. Conference mode activates more aggressive
2713 // AEC and NS settings.
2714 // TODO: expose API to set these directly.
2715 std::string flag_value;
2716 if (!GetValue(line, kAttributeXGoogleFlag, &flag_value, error)) {
2717 return false;
2718 }
2719 if (flag_value.compare(kValueConference) == 0)
2720 media_desc->set_conference_mode(true);
deadbeef9d3584c2016-02-16 17:54:10 -08002721 } else if (HasAttribute(line, kAttributeMsid)) {
2722 if (!ParseMsidAttribute(line, &stream_id, &track_id, error)) {
2723 return false;
2724 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002725 }
2726 } else {
2727 // Only parse lines that we are interested of.
2728 LOG(LS_INFO) << "Ignored line: " << line;
2729 continue;
2730 }
2731 }
2732
2733 // Create tracks from the |ssrc_infos|.
Taylor Brandstetter5de6b752016-03-09 17:02:30 -08002734 // If the stream_id/track_id for all SSRCS are identical, one StreamParams
2735 // will be created in CreateTracksFromSsrcInfos, containing all the SSRCs from
2736 // the m= section.
2737 CreateTracksFromSsrcInfos(ssrc_infos, stream_id, track_id, &tracks);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002738
2739 // Add the ssrc group to the track.
2740 for (SsrcGroupVec::iterator ssrc_group = ssrc_groups.begin();
2741 ssrc_group != ssrc_groups.end(); ++ssrc_group) {
2742 if (ssrc_group->ssrcs.empty()) {
2743 continue;
2744 }
Peter Boström0c4e06b2015-10-07 12:23:21 +02002745 uint32_t ssrc = ssrc_group->ssrcs.front();
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002746 for (StreamParamsVec::iterator track = tracks.begin();
2747 track != tracks.end(); ++track) {
2748 if (track->has_ssrc(ssrc)) {
2749 track->ssrc_groups.push_back(*ssrc_group);
2750 }
2751 }
2752 }
2753
2754 // Add the new tracks to the |media_desc|.
deadbeef9d3584c2016-02-16 17:54:10 -08002755 for (StreamParams& track : tracks) {
2756 media_desc->AddStream(track);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002757 }
2758
2759 if (media_type == cricket::MEDIA_TYPE_AUDIO) {
2760 AudioContentDescription* audio_desc =
2761 static_cast<AudioContentDescription*>(media_desc);
jlmiller@webrtc.orga744a282015-02-18 21:37:46 +00002762 UpdateFromWildcardCodecs(audio_desc);
2763
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002764 // Verify audio codec ensures that no audio codec has been populated with
2765 // only fmtp.
2766 if (!VerifyAudioCodecs(audio_desc)) {
2767 return ParseFailed("Failed to parse audio codecs correctly.", error);
2768 }
2769 AddAudioAttribute(kCodecParamMaxPTime, maxptime_as_string, audio_desc);
2770 AddAudioAttribute(kCodecParamPTime, ptime_as_string, audio_desc);
2771 }
2772
2773 if (media_type == cricket::MEDIA_TYPE_VIDEO) {
jlmiller@webrtc.orga744a282015-02-18 21:37:46 +00002774 VideoContentDescription* video_desc =
2775 static_cast<VideoContentDescription*>(media_desc);
2776 UpdateFromWildcardCodecs(video_desc);
2777 // Verify video codec ensures that no video codec has been populated with
2778 // only rtcp-fb.
2779 if (!VerifyVideoCodecs(video_desc)) {
2780 return ParseFailed("Failed to parse video codecs correctly.", error);
2781 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002782 }
2783
2784 // RFC 5245
2785 // Update the candidates with the media level "ice-pwd" and "ice-ufrag".
2786 for (Candidates::iterator it = candidates_orig.begin();
2787 it != candidates_orig.end(); ++it) {
honghaiza54a0802015-12-16 18:37:23 -08002788 ASSERT((*it).username().empty() ||
2789 (*it).username() == transport->ice_ufrag);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002790 (*it).set_username(transport->ice_ufrag);
2791 ASSERT((*it).password().empty());
2792 (*it).set_password(transport->ice_pwd);
2793 candidates->push_back(
2794 new JsepIceCandidate(mline_id, mline_index, *it));
2795 }
2796 return true;
2797}
2798
2799bool ParseSsrcAttribute(const std::string& line, SsrcInfoVec* ssrc_infos,
2800 SdpParseError* error) {
2801 ASSERT(ssrc_infos != NULL);
2802 // RFC 5576
2803 // a=ssrc:<ssrc-id> <attribute>
2804 // a=ssrc:<ssrc-id> <attribute>:<value>
2805 std::string field1, field2;
Donald Curtis144d0182015-05-15 13:14:24 -07002806 if (!rtc::tokenize_first(line.substr(kLinePrefixLength), kSdpDelimiterSpace,
2807 &field1, &field2)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002808 const size_t expected_fields = 2;
2809 return ParseFailedExpectFieldNum(line, expected_fields, error);
2810 }
2811
2812 // ssrc:<ssrc-id>
2813 std::string ssrc_id_s;
2814 if (!GetValue(field1, kAttributeSsrc, &ssrc_id_s, error)) {
2815 return false;
2816 }
Peter Boström0c4e06b2015-10-07 12:23:21 +02002817 uint32_t ssrc_id = 0;
wu@webrtc.org5e760e72014-04-02 23:19:09 +00002818 if (!GetValueFromString(line, ssrc_id_s, &ssrc_id, error)) {
2819 return false;
2820 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002821
2822 std::string attribute;
2823 std::string value;
Donald Curtis144d0182015-05-15 13:14:24 -07002824 if (!rtc::tokenize_first(field2, kSdpDelimiterColon, &attribute, &value)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002825 std::ostringstream description;
2826 description << "Failed to get the ssrc attribute value from " << field2
2827 << ". Expected format <attribute>:<value>.";
2828 return ParseFailed(line, description.str(), error);
2829 }
2830
2831 // Check if there's already an item for this |ssrc_id|. Create a new one if
2832 // there isn't.
2833 SsrcInfoVec::iterator ssrc_info = ssrc_infos->begin();
2834 for (; ssrc_info != ssrc_infos->end(); ++ssrc_info) {
2835 if (ssrc_info->ssrc_id == ssrc_id) {
2836 break;
2837 }
2838 }
2839 if (ssrc_info == ssrc_infos->end()) {
2840 SsrcInfo info;
2841 info.ssrc_id = ssrc_id;
2842 ssrc_infos->push_back(info);
2843 ssrc_info = ssrc_infos->end() - 1;
2844 }
2845
2846 // Store the info to the |ssrc_info|.
2847 if (attribute == kSsrcAttributeCname) {
2848 // RFC 5576
2849 // cname:<value>
2850 ssrc_info->cname = value;
2851 } else if (attribute == kSsrcAttributeMsid) {
2852 // draft-alvestrand-mmusic-msid-00
2853 // "msid:" identifier [ " " appdata ]
2854 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002855 rtc::split(value, kSdpDelimiterSpace, &fields);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002856 if (fields.size() < 1 || fields.size() > 2) {
2857 return ParseFailed(line,
2858 "Expected format \"msid:<identifier>[ <appdata>]\".",
2859 error);
2860 }
deadbeef9d3584c2016-02-16 17:54:10 -08002861 ssrc_info->stream_id = fields[0];
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002862 if (fields.size() == 2) {
deadbeef9d3584c2016-02-16 17:54:10 -08002863 ssrc_info->track_id = fields[1];
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002864 }
2865 } else if (attribute == kSsrcAttributeMslabel) {
2866 // draft-alvestrand-rtcweb-mid-01
2867 // mslabel:<value>
2868 ssrc_info->mslabel = value;
2869 } else if (attribute == kSSrcAttributeLabel) {
2870 // The label isn't defined.
2871 // label:<value>
2872 ssrc_info->label = value;
2873 }
2874 return true;
2875}
2876
2877bool ParseSsrcGroupAttribute(const std::string& line,
2878 SsrcGroupVec* ssrc_groups,
2879 SdpParseError* error) {
2880 ASSERT(ssrc_groups != NULL);
2881 // RFC 5576
2882 // a=ssrc-group:<semantics> <ssrc-id> ...
2883 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002884 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002885 kSdpDelimiterSpace, &fields);
2886 const size_t expected_min_fields = 2;
2887 if (fields.size() < expected_min_fields) {
2888 return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
2889 }
2890 std::string semantics;
2891 if (!GetValue(fields[0], kAttributeSsrcGroup, &semantics, error)) {
2892 return false;
2893 }
Peter Boström0c4e06b2015-10-07 12:23:21 +02002894 std::vector<uint32_t> ssrcs;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002895 for (size_t i = 1; i < fields.size(); ++i) {
Peter Boström0c4e06b2015-10-07 12:23:21 +02002896 uint32_t ssrc = 0;
wu@webrtc.org5e760e72014-04-02 23:19:09 +00002897 if (!GetValueFromString(line, fields[i], &ssrc, error)) {
2898 return false;
2899 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002900 ssrcs.push_back(ssrc);
2901 }
2902 ssrc_groups->push_back(SsrcGroup(semantics, ssrcs));
2903 return true;
2904}
2905
2906bool ParseCryptoAttribute(const std::string& line,
2907 MediaContentDescription* media_desc,
2908 SdpParseError* error) {
2909 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002910 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002911 kSdpDelimiterSpace, &fields);
2912 // RFC 4568
2913 // a=crypto:<tag> <crypto-suite> <key-params> [<session-params>]
2914 const size_t expected_min_fields = 3;
2915 if (fields.size() < expected_min_fields) {
2916 return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
2917 }
2918 std::string tag_value;
2919 if (!GetValue(fields[0], kAttributeCrypto, &tag_value, error)) {
2920 return false;
2921 }
wu@webrtc.org5e760e72014-04-02 23:19:09 +00002922 int tag = 0;
2923 if (!GetValueFromString(line, tag_value, &tag, error)) {
2924 return false;
2925 }
jbauch083b73f2015-07-16 02:46:32 -07002926 const std::string& crypto_suite = fields[1];
2927 const std::string& key_params = fields[2];
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002928 std::string session_params;
2929 if (fields.size() > 3) {
2930 session_params = fields[3];
2931 }
2932 media_desc->AddCrypto(CryptoParams(tag, crypto_suite, key_params,
2933 session_params));
2934 return true;
2935}
2936
2937// Updates or creates a new codec entry in the audio description with according
deadbeef67cf2c12016-04-13 10:07:16 -07002938// to |name|, |clockrate|, |bitrate|, and |channels|.
2939void UpdateCodec(int payload_type,
2940 const std::string& name,
2941 int clockrate,
2942 int bitrate,
2943 size_t channels,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002944 AudioContentDescription* audio_desc) {
2945 // Codec may already be populated with (only) optional parameters
2946 // (from an fmtp).
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +00002947 cricket::AudioCodec codec =
2948 GetCodecWithPayloadType(audio_desc->codecs(), payload_type);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002949 codec.name = name;
2950 codec.clockrate = clockrate;
2951 codec.bitrate = bitrate;
2952 codec.channels = channels;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002953 AddOrReplaceCodec<AudioContentDescription, cricket::AudioCodec>(audio_desc,
2954 codec);
2955}
2956
2957// Updates or creates a new codec entry in the video description according to
deadbeef67cf2c12016-04-13 10:07:16 -07002958// |name|, |width|, |height|, and |framerate|.
2959void UpdateCodec(int payload_type,
2960 const std::string& name,
2961 int width,
2962 int height,
2963 int framerate,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002964 VideoContentDescription* video_desc) {
2965 // Codec may already be populated with (only) optional parameters
2966 // (from an fmtp).
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +00002967 cricket::VideoCodec codec =
2968 GetCodecWithPayloadType(video_desc->codecs(), payload_type);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002969 codec.name = name;
2970 codec.width = width;
2971 codec.height = height;
2972 codec.framerate = framerate;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002973 AddOrReplaceCodec<VideoContentDescription, cricket::VideoCodec>(video_desc,
2974 codec);
2975}
2976
2977bool ParseRtpmapAttribute(const std::string& line,
2978 const MediaType media_type,
deadbeef67cf2c12016-04-13 10:07:16 -07002979 const std::vector<int>& payload_types,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002980 MediaContentDescription* media_desc,
2981 SdpParseError* error) {
2982 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002983 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002984 kSdpDelimiterSpace, &fields);
2985 // RFC 4566
2986 // a=rtpmap:<payload type> <encoding name>/<clock rate>[/<encodingparameters>]
2987 const size_t expected_min_fields = 2;
2988 if (fields.size() < expected_min_fields) {
2989 return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
2990 }
2991 std::string payload_type_value;
2992 if (!GetValue(fields[0], kAttributeRtpmap, &payload_type_value, error)) {
2993 return false;
2994 }
wu@webrtc.org5e760e72014-04-02 23:19:09 +00002995 int payload_type = 0;
pkasting@chromium.orge9facf82015-02-17 20:36:28 +00002996 if (!GetPayloadTypeFromString(line, payload_type_value, &payload_type,
2997 error)) {
wu@webrtc.org5e760e72014-04-02 23:19:09 +00002998 return false;
2999 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003000
deadbeef67cf2c12016-04-13 10:07:16 -07003001 if (std::find(payload_types.begin(), payload_types.end(), payload_type) ==
3002 payload_types.end()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003003 LOG(LS_WARNING) << "Ignore rtpmap line that did not appear in the "
3004 << "<fmt> of the m-line: " << line;
3005 return true;
3006 }
jbauch083b73f2015-07-16 02:46:32 -07003007 const std::string& encoder = fields[1];
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003008 std::vector<std::string> codec_params;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00003009 rtc::split(encoder, '/', &codec_params);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003010 // <encoding name>/<clock rate>[/<encodingparameters>]
3011 // 2 mandatory fields
3012 if (codec_params.size() < 2 || codec_params.size() > 3) {
3013 return ParseFailed(line,
3014 "Expected format \"<encoding name>/<clock rate>"
3015 "[/<encodingparameters>]\".",
3016 error);
3017 }
jbauch083b73f2015-07-16 02:46:32 -07003018 const std::string& encoding_name = codec_params[0];
wu@webrtc.org5e760e72014-04-02 23:19:09 +00003019 int clock_rate = 0;
3020 if (!GetValueFromString(line, codec_params[1], &clock_rate, error)) {
3021 return false;
3022 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003023 if (media_type == cricket::MEDIA_TYPE_VIDEO) {
3024 VideoContentDescription* video_desc =
3025 static_cast<VideoContentDescription*>(media_desc);
3026 // TODO: We will send resolution in SDP. For now use
3027 // JsepSessionDescription::kMaxVideoCodecWidth and kMaxVideoCodecHeight.
3028 UpdateCodec(payload_type, encoding_name,
3029 JsepSessionDescription::kMaxVideoCodecWidth,
3030 JsepSessionDescription::kMaxVideoCodecHeight,
3031 JsepSessionDescription::kDefaultVideoCodecFramerate,
deadbeef67cf2c12016-04-13 10:07:16 -07003032 video_desc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003033 } else if (media_type == cricket::MEDIA_TYPE_AUDIO) {
3034 // RFC 4566
3035 // For audio streams, <encoding parameters> indicates the number
3036 // of audio channels. This parameter is OPTIONAL and may be
3037 // omitted if the number of channels is one, provided that no
3038 // additional parameters are needed.
Peter Kasting69558702016-01-12 16:26:35 -08003039 size_t channels = 1;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003040 if (codec_params.size() == 3) {
wu@webrtc.org5e760e72014-04-02 23:19:09 +00003041 if (!GetValueFromString(line, codec_params[2], &channels, error)) {
3042 return false;
3043 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003044 }
3045 int bitrate = 0;
3046 // The default behavior for ISAC (bitrate == 0) in webrtcvoiceengine.cc
3047 // (specifically FindWebRtcCodec) is bandwidth-adaptive variable bitrate.
3048 // The bandwidth adaptation doesn't always work well, so this code
3049 // sets a fixed target bitrate instead.
3050 if (_stricmp(encoding_name.c_str(), kIsacCodecName) == 0) {
3051 if (clock_rate <= 16000) {
3052 bitrate = kIsacWbDefaultRate;
3053 } else {
3054 bitrate = kIsacSwbDefaultRate;
3055 }
3056 }
3057 AudioContentDescription* audio_desc =
3058 static_cast<AudioContentDescription*>(media_desc);
3059 UpdateCodec(payload_type, encoding_name, clock_rate, bitrate, channels,
deadbeef67cf2c12016-04-13 10:07:16 -07003060 audio_desc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003061 } else if (media_type == cricket::MEDIA_TYPE_DATA) {
3062 DataContentDescription* data_desc =
3063 static_cast<DataContentDescription*>(media_desc);
deadbeef67cf2c12016-04-13 10:07:16 -07003064 data_desc->AddCodec(cricket::DataCodec(payload_type, encoding_name));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003065 }
3066 return true;
3067}
3068
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003069bool ParseFmtpParam(const std::string& line, std::string* parameter,
3070 std::string* value, SdpParseError* error) {
Donald Curtis0e07f922015-05-15 09:21:23 -07003071 if (!rtc::tokenize_first(line, kSdpDelimiterEqual, parameter, value)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003072 ParseFailed(line, "Unable to parse fmtp parameter. \'=\' missing.", error);
3073 return false;
3074 }
3075 // a=fmtp:<payload_type> <param1>=<value1>; <param2>=<value2>; ...
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003076 return true;
3077}
3078
3079bool ParseFmtpAttributes(const std::string& line, const MediaType media_type,
3080 MediaContentDescription* media_desc,
3081 SdpParseError* error) {
3082 if (media_type != cricket::MEDIA_TYPE_AUDIO &&
3083 media_type != cricket::MEDIA_TYPE_VIDEO) {
3084 return true;
3085 }
Donald Curtis0e07f922015-05-15 09:21:23 -07003086
3087 std::string line_payload;
3088 std::string line_params;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003089
3090 // RFC 5576
3091 // a=fmtp:<format> <format specific parameters>
3092 // At least two fields, whereas the second one is any of the optional
3093 // parameters.
Donald Curtis144d0182015-05-15 13:14:24 -07003094 if (!rtc::tokenize_first(line.substr(kLinePrefixLength), kSdpDelimiterSpace,
3095 &line_payload, &line_params)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003096 ParseFailedExpectMinFieldNum(line, 2, error);
3097 return false;
3098 }
3099
Donald Curtis0e07f922015-05-15 09:21:23 -07003100 // Parse out the payload information.
pkasting@chromium.orgd3245462015-02-23 21:28:22 +00003101 std::string payload_type_str;
Donald Curtis0e07f922015-05-15 09:21:23 -07003102 if (!GetValue(line_payload, kAttributeFmtp, &payload_type_str, error)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003103 return false;
3104 }
3105
Donald Curtis0e07f922015-05-15 09:21:23 -07003106 int payload_type = 0;
Donald Curtis144d0182015-05-15 13:14:24 -07003107 if (!GetPayloadTypeFromString(line_payload, payload_type_str, &payload_type,
3108 error)) {
Donald Curtis0e07f922015-05-15 09:21:23 -07003109 return false;
3110 }
3111
3112 // Parse out format specific parameters.
3113 std::vector<std::string> fields;
3114 rtc::split(line_params, kSdpDelimiterSemicolon, &fields);
3115
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003116 cricket::CodecParameterMap codec_params;
Donald Curtis144d0182015-05-15 13:14:24 -07003117 for (auto& iter : fields) {
Donald Curtis0e07f922015-05-15 09:21:23 -07003118 if (iter.find(kSdpDelimiterEqual) == std::string::npos) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003119 // Only fmtps with equals are currently supported. Other fmtp types
3120 // should be ignored. Unknown fmtps do not constitute an error.
3121 continue;
3122 }
Donald Curtis0e07f922015-05-15 09:21:23 -07003123
3124 std::string name;
3125 std::string value;
3126 if (!ParseFmtpParam(rtc::string_trim(iter), &name, &value, error)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003127 return false;
3128 }
3129 codec_params[name] = value;
3130 }
3131
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003132 if (media_type == cricket::MEDIA_TYPE_AUDIO) {
3133 UpdateCodec<AudioContentDescription, cricket::AudioCodec>(
pkasting@chromium.orgd3245462015-02-23 21:28:22 +00003134 media_desc, payload_type, codec_params);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003135 } else if (media_type == cricket::MEDIA_TYPE_VIDEO) {
3136 UpdateCodec<VideoContentDescription, cricket::VideoCodec>(
pkasting@chromium.orgd3245462015-02-23 21:28:22 +00003137 media_desc, payload_type, codec_params);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003138 }
3139 return true;
3140}
3141
3142bool ParseRtcpFbAttribute(const std::string& line, const MediaType media_type,
3143 MediaContentDescription* media_desc,
3144 SdpParseError* error) {
3145 if (media_type != cricket::MEDIA_TYPE_AUDIO &&
3146 media_type != cricket::MEDIA_TYPE_VIDEO) {
3147 return true;
3148 }
3149 std::vector<std::string> rtcp_fb_fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00003150 rtc::split(line.c_str(), kSdpDelimiterSpace, &rtcp_fb_fields);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003151 if (rtcp_fb_fields.size() < 2) {
3152 return ParseFailedGetValue(line, kAttributeRtcpFb, error);
3153 }
3154 std::string payload_type_string;
3155 if (!GetValue(rtcp_fb_fields[0], kAttributeRtcpFb, &payload_type_string,
3156 error)) {
3157 return false;
3158 }
wu@webrtc.org5e760e72014-04-02 23:19:09 +00003159 int payload_type = kWildcardPayloadType;
3160 if (payload_type_string != "*") {
pkasting@chromium.orge9facf82015-02-17 20:36:28 +00003161 if (!GetPayloadTypeFromString(line, payload_type_string, &payload_type,
3162 error)) {
wu@webrtc.org5e760e72014-04-02 23:19:09 +00003163 return false;
3164 }
3165 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003166 std::string id = rtcp_fb_fields[1];
3167 std::string param = "";
3168 for (std::vector<std::string>::iterator iter = rtcp_fb_fields.begin() + 2;
3169 iter != rtcp_fb_fields.end(); ++iter) {
3170 param.append(*iter);
3171 }
3172 const cricket::FeedbackParam feedback_param(id, param);
3173
3174 if (media_type == cricket::MEDIA_TYPE_AUDIO) {
pkasting@chromium.orgd3245462015-02-23 21:28:22 +00003175 UpdateCodec<AudioContentDescription, cricket::AudioCodec>(
3176 media_desc, payload_type, feedback_param);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003177 } else if (media_type == cricket::MEDIA_TYPE_VIDEO) {
pkasting@chromium.orgd3245462015-02-23 21:28:22 +00003178 UpdateCodec<VideoContentDescription, cricket::VideoCodec>(
3179 media_desc, payload_type, feedback_param);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003180 }
3181 return true;
3182}
3183
3184} // namespace webrtc