blob: 8497f4c5fa9bbc5f79e16b85d26079963e55cd89 [file] [log] [blame]
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001/*
kjellanderb24317b2016-02-10 07:54:43 -08002 * Copyright 2011 The WebRTC project authors. All Rights Reserved.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003 *
kjellanderb24317b2016-02-10 07:54:43 -08004 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00009 */
10
Henrik Kjellander15583c12016-02-10 10:53:12 +010011#include "webrtc/api/webrtcsdp.h"
henrike@webrtc.org28e20752013-07-10 00:45:36 +000012
kjellandera96e2d72016-02-04 23:52:28 -080013#include <ctype.h>
henrike@webrtc.org28e20752013-07-10 00:45:36 +000014#include <limits.h>
15#include <stdio.h>
16#include <algorithm>
17#include <string>
deadbeef67cf2c12016-04-13 10:07:16 -070018#include <unordered_map>
henrike@webrtc.org28e20752013-07-10 00:45:36 +000019#include <vector>
20
Henrik Kjellander15583c12016-02-10 10:53:12 +010021#include "webrtc/api/jsepicecandidate.h"
22#include "webrtc/api/jsepsessiondescription.h"
tfarina5237aaf2015-11-10 23:44:30 -080023#include "webrtc/base/arraysize.h"
buildbot@webrtc.orga09a9992014-08-13 17:26:08 +000024#include "webrtc/base/common.h"
25#include "webrtc/base/logging.h"
26#include "webrtc/base/messagedigest.h"
27#include "webrtc/base/stringutils.h"
kjellandera96e2d72016-02-04 23:52:28 -080028#include "webrtc/media/base/codec.h"
kjellandera96e2d72016-02-04 23:52:28 -080029#include "webrtc/media/base/cryptoparams.h"
kjellanderf4752772016-03-02 05:42:30 -080030#include "webrtc/media/base/mediaconstants.h"
kjellandera96e2d72016-02-04 23:52:28 -080031#include "webrtc/media/base/rtputils.h"
32#include "webrtc/media/sctp/sctpdataengine.h"
33#include "webrtc/p2p/base/candidate.h"
kjellanderf4752772016-03-02 05:42:30 -080034#include "webrtc/p2p/base/p2pconstants.h"
kjellandera96e2d72016-02-04 23:52:28 -080035#include "webrtc/p2p/base/port.h"
kjellander@webrtc.org9b8df252016-02-12 06:47:59 +010036#include "webrtc/pc/mediasession.h"
henrike@webrtc.org28e20752013-07-10 00:45:36 +000037
38using cricket::AudioContentDescription;
39using cricket::Candidate;
40using cricket::Candidates;
41using cricket::ContentDescription;
42using cricket::ContentInfo;
43using cricket::CryptoParams;
44using cricket::DataContentDescription;
45using cricket::ICE_CANDIDATE_COMPONENT_RTP;
46using cricket::ICE_CANDIDATE_COMPONENT_RTCP;
47using cricket::kCodecParamMaxBitrate;
48using cricket::kCodecParamMaxPTime;
49using cricket::kCodecParamMaxQuantization;
50using cricket::kCodecParamMinBitrate;
51using cricket::kCodecParamMinPTime;
52using cricket::kCodecParamPTime;
53using cricket::kCodecParamSPropStereo;
buildbot@webrtc.orged97bb02014-05-07 11:15:20 +000054using cricket::kCodecParamStartBitrate;
henrike@webrtc.org28e20752013-07-10 00:45:36 +000055using cricket::kCodecParamStereo;
56using cricket::kCodecParamUseInbandFec;
Minyue Li7100dcd2015-03-27 05:05:59 +010057using cricket::kCodecParamUseDtx;
henrike@webrtc.org28e20752013-07-10 00:45:36 +000058using cricket::kCodecParamSctpProtocol;
59using cricket::kCodecParamSctpStreams;
henrike@webrtc.org1e09a712013-07-26 19:17:59 +000060using cricket::kCodecParamMaxAverageBitrate;
buildbot@webrtc.org5d639b32014-09-10 07:57:12 +000061using cricket::kCodecParamMaxPlaybackRate;
stefan@webrtc.org85d27942014-06-09 12:51:39 +000062using cricket::kCodecParamAssociatedPayloadType;
henrike@webrtc.org28e20752013-07-10 00:45:36 +000063using cricket::MediaContentDescription;
64using cricket::MediaType;
henrike@webrtc.org28e20752013-07-10 00:45:36 +000065using cricket::RtpHeaderExtension;
66using cricket::SsrcGroup;
67using cricket::StreamParams;
68using cricket::StreamParamsVec;
69using cricket::TransportDescription;
70using cricket::TransportInfo;
71using cricket::VideoContentDescription;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +000072using rtc::SocketAddress;
henrike@webrtc.org28e20752013-07-10 00:45:36 +000073
74typedef std::vector<RtpHeaderExtension> RtpHeaderExtensions;
75
76namespace cricket {
77class SessionDescription;
78}
79
80namespace webrtc {
81
82// Line type
83// RFC 4566
84// An SDP session description consists of a number of lines of text of
85// the form:
86// <type>=<value>
87// where <type> MUST be exactly one case-significant character.
deadbeef9d3584c2016-02-16 17:54:10 -080088static const int kLinePrefixLength = 2; // Length of <type>=
henrike@webrtc.org28e20752013-07-10 00:45:36 +000089static const char kLineTypeVersion = 'v';
90static const char kLineTypeOrigin = 'o';
91static const char kLineTypeSessionName = 's';
92static const char kLineTypeSessionInfo = 'i';
93static const char kLineTypeSessionUri = 'u';
94static const char kLineTypeSessionEmail = 'e';
95static const char kLineTypeSessionPhone = 'p';
96static const char kLineTypeSessionBandwidth = 'b';
97static const char kLineTypeTiming = 't';
98static const char kLineTypeRepeatTimes = 'r';
99static const char kLineTypeTimeZone = 'z';
100static const char kLineTypeEncryptionKey = 'k';
101static const char kLineTypeMedia = 'm';
102static const char kLineTypeConnection = 'c';
103static const char kLineTypeAttributes = 'a';
104
105// Attributes
106static const char kAttributeGroup[] = "group";
107static const char kAttributeMid[] = "mid";
deadbeef9d3584c2016-02-16 17:54:10 -0800108static const char kAttributeMsid[] = "msid";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000109static const char kAttributeRtcpMux[] = "rtcp-mux";
deadbeef13871492015-12-09 12:37:51 -0800110static const char kAttributeRtcpReducedSize[] = "rtcp-rsize";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000111static const char kAttributeSsrc[] = "ssrc";
112static const char kSsrcAttributeCname[] = "cname";
113static const char kAttributeExtmap[] = "extmap";
114// draft-alvestrand-mmusic-msid-01
115// a=msid-semantic: WMS
116static const char kAttributeMsidSemantics[] = "msid-semantic";
117static const char kMediaStreamSemantic[] = "WMS";
118static const char kSsrcAttributeMsid[] = "msid";
119static const char kDefaultMsid[] = "default";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000120static const char kSsrcAttributeMslabel[] = "mslabel";
121static const char kSSrcAttributeLabel[] = "label";
122static const char kAttributeSsrcGroup[] = "ssrc-group";
123static const char kAttributeCrypto[] = "crypto";
124static const char kAttributeCandidate[] = "candidate";
125static const char kAttributeCandidateTyp[] = "typ";
126static const char kAttributeCandidateRaddr[] = "raddr";
127static const char kAttributeCandidateRport[] = "rport";
honghaiza54a0802015-12-16 18:37:23 -0800128static const char kAttributeCandidateUfrag[] = "ufrag";
129static const char kAttributeCandidatePwd[] = "pwd";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000130static const char kAttributeCandidateGeneration[] = "generation";
honghaiza0c44ea2016-03-23 16:07:48 -0700131static const char kAttributeCandidateNetworkId[] = "network-id";
honghaize1a0c942016-02-16 14:54:56 -0800132static const char kAttributeCandidateNetworkCost[] = "network-cost";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000133static const char kAttributeFingerprint[] = "fingerprint";
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +0000134static const char kAttributeSetup[] = "setup";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000135static const char kAttributeFmtp[] = "fmtp";
136static const char kAttributeRtpmap[] = "rtpmap";
wu@webrtc.org78187522013-10-07 23:32:02 +0000137static const char kAttributeSctpmap[] = "sctpmap";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000138static const char kAttributeRtcp[] = "rtcp";
139static const char kAttributeIceUfrag[] = "ice-ufrag";
140static const char kAttributeIcePwd[] = "ice-pwd";
141static const char kAttributeIceLite[] = "ice-lite";
142static const char kAttributeIceOption[] = "ice-options";
143static const char kAttributeSendOnly[] = "sendonly";
144static const char kAttributeRecvOnly[] = "recvonly";
145static const char kAttributeRtcpFb[] = "rtcp-fb";
146static const char kAttributeSendRecv[] = "sendrecv";
147static const char kAttributeInactive[] = "inactive";
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +0000148// draft-ietf-mmusic-sctp-sdp-07
149// a=sctp-port
150static const char kAttributeSctpPort[] = "sctp-port";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000151
152// Experimental flags
153static const char kAttributeXGoogleFlag[] = "x-google-flag";
154static const char kValueConference[] = "conference";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000155
156// Candidate
157static const char kCandidateHost[] = "host";
158static const char kCandidateSrflx[] = "srflx";
Honghai Zhang7fb69db2016-03-14 11:59:18 -0700159static const char kCandidatePrflx[] = "prflx";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000160static const char kCandidateRelay[] = "relay";
mallinath@webrtc.org2d60c5e2014-08-08 22:29:20 +0000161static const char kTcpCandidateType[] = "tcptype";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000162
163static const char kSdpDelimiterEqual = '=';
164static const char kSdpDelimiterSpace = ' ';
165static const char kSdpDelimiterColon = ':';
166static const char kSdpDelimiterSemicolon = ';';
167static const char kSdpDelimiterSlash = '/';
168static const char kNewLine = '\n';
169static const char kReturn = '\r';
170static const char kLineBreak[] = "\r\n";
171
172// TODO: Generate the Session and Time description
173// instead of hardcoding.
174static const char kSessionVersion[] = "v=0";
175// RFC 4566
176static const char kSessionOriginUsername[] = "-";
177static const char kSessionOriginSessionId[] = "0";
178static const char kSessionOriginSessionVersion[] = "0";
179static const char kSessionOriginNettype[] = "IN";
180static const char kSessionOriginAddrtype[] = "IP4";
181static const char kSessionOriginAddress[] = "127.0.0.1";
182static const char kSessionName[] = "s=-";
183static const char kTimeDescription[] = "t=0 0";
184static const char kAttrGroup[] = "a=group:BUNDLE";
185static const char kConnectionNettype[] = "IN";
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000186static const char kConnectionIpv4Addrtype[] = "IP4";
187static const char kConnectionIpv6Addrtype[] = "IP6";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000188static const char kMediaTypeVideo[] = "video";
189static const char kMediaTypeAudio[] = "audio";
190static const char kMediaTypeData[] = "application";
191static const char kMediaPortRejected[] = "0";
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000192// draft-ietf-mmusic-trickle-ice-01
193// When no candidates have been gathered, set the connection
194// address to IP6 ::.
perkj@webrtc.orgd105cc82014-11-07 11:22:06 +0000195// TODO(perkj): FF can not parse IP6 ::. See http://crbug/430333
196// Use IPV4 per default.
197static const char kDummyAddress[] = "0.0.0.0";
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000198static const char kDummyPort[] = "9";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000199// RFC 3556
200static const char kApplicationSpecificMaximum[] = "AS";
201
202static const int kDefaultVideoClockrate = 90000;
203
204// ISAC special-case.
205static const char kIsacCodecName[] = "ISAC"; // From webrtcvoiceengine.cc
206static const int kIsacWbDefaultRate = 32000; // From acm_common_defs.h
207static const int kIsacSwbDefaultRate = 56000; // From acm_common_defs.h
208
wu@webrtc.org78187522013-10-07 23:32:02 +0000209static const char kDefaultSctpmapProtocol[] = "webrtc-datachannel";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000210
pkasting@chromium.orgd3245462015-02-23 21:28:22 +0000211// RTP payload type is in the 0-127 range. Use -1 to indicate "all" payload
212// types.
213const int kWildcardPayloadType = -1;
214
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000215struct SsrcInfo {
Peter Boström0c4e06b2015-10-07 12:23:21 +0200216 uint32_t ssrc_id;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000217 std::string cname;
deadbeef9d3584c2016-02-16 17:54:10 -0800218 std::string stream_id;
219 std::string track_id;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000220
221 // For backward compatibility.
222 // TODO(ronghuawu): Remove below 2 fields once all the clients support msid.
223 std::string label;
224 std::string mslabel;
225};
226typedef std::vector<SsrcInfo> SsrcInfoVec;
227typedef std::vector<SsrcGroup> SsrcGroupVec;
228
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000229template <class T>
230static void AddFmtpLine(const T& codec, std::string* message);
231static void BuildMediaDescription(const ContentInfo* content_info,
232 const TransportInfo* transport_info,
233 const MediaType media_type,
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000234 const std::vector<Candidate>& candidates,
deadbeef9d3584c2016-02-16 17:54:10 -0800235 bool unified_plan_sdp,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000236 std::string* message);
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +0000237static void BuildSctpContentAttributes(std::string* message, int sctp_port);
deadbeef9d3584c2016-02-16 17:54:10 -0800238static void BuildRtpContentAttributes(const MediaContentDescription* media_desc,
239 const MediaType media_type,
240 bool unified_plan_sdp,
241 std::string* message);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000242static void BuildRtpMap(const MediaContentDescription* media_desc,
243 const MediaType media_type,
244 std::string* message);
245static void BuildCandidate(const std::vector<Candidate>& candidates,
honghaiza54a0802015-12-16 18:37:23 -0800246 bool include_ufrag,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000247 std::string* message);
248static void BuildIceOptions(const std::vector<std::string>& transport_options,
249 std::string* message);
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +0000250static bool IsRtp(const std::string& protocol);
251static bool IsDtlsSctp(const std::string& protocol);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000252static bool ParseSessionDescription(const std::string& message, size_t* pos,
253 std::string* session_id,
254 std::string* session_version,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000255 TransportDescription* session_td,
256 RtpHeaderExtensions* session_extmaps,
257 cricket::SessionDescription* desc,
258 SdpParseError* error);
259static bool ParseGroupAttribute(const std::string& line,
260 cricket::SessionDescription* desc,
261 SdpParseError* error);
262static bool ParseMediaDescription(
263 const std::string& message,
264 const TransportDescription& session_td,
265 const RtpHeaderExtensions& session_extmaps,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000266 size_t* pos, cricket::SessionDescription* desc,
267 std::vector<JsepIceCandidate*>* candidates,
268 SdpParseError* error);
269static bool ParseContent(const std::string& message,
270 const MediaType media_type,
271 int mline_index,
272 const std::string& protocol,
deadbeef67cf2c12016-04-13 10:07:16 -0700273 const std::vector<int>& payload_types,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000274 size_t* pos,
275 std::string* content_name,
276 MediaContentDescription* media_desc,
277 TransportDescription* transport,
278 std::vector<JsepIceCandidate*>* candidates,
279 SdpParseError* error);
280static bool ParseSsrcAttribute(const std::string& line,
281 SsrcInfoVec* ssrc_infos,
282 SdpParseError* error);
283static bool ParseSsrcGroupAttribute(const std::string& line,
284 SsrcGroupVec* ssrc_groups,
285 SdpParseError* error);
286static bool ParseCryptoAttribute(const std::string& line,
287 MediaContentDescription* media_desc,
288 SdpParseError* error);
289static bool ParseRtpmapAttribute(const std::string& line,
290 const MediaType media_type,
deadbeef67cf2c12016-04-13 10:07:16 -0700291 const std::vector<int>& payload_types,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000292 MediaContentDescription* media_desc,
293 SdpParseError* error);
294static bool ParseFmtpAttributes(const std::string& line,
295 const MediaType media_type,
296 MediaContentDescription* media_desc,
297 SdpParseError* error);
298static bool ParseFmtpParam(const std::string& line, std::string* parameter,
299 std::string* value, SdpParseError* error);
300static bool ParseCandidate(const std::string& message, Candidate* candidate,
301 SdpParseError* error, bool is_raw);
302static bool ParseRtcpFbAttribute(const std::string& line,
303 const MediaType media_type,
304 MediaContentDescription* media_desc,
305 SdpParseError* error);
306static bool ParseIceOptions(const std::string& line,
307 std::vector<std::string>* transport_options,
308 SdpParseError* error);
309static bool ParseExtmap(const std::string& line,
310 RtpHeaderExtension* extmap,
311 SdpParseError* error);
312static bool ParseFingerprintAttribute(const std::string& line,
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000313 rtc::SSLFingerprint** fingerprint,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000314 SdpParseError* error);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +0000315static bool ParseDtlsSetup(const std::string& line,
316 cricket::ConnectionRole* role,
317 SdpParseError* error);
deadbeef9d3584c2016-02-16 17:54:10 -0800318static bool ParseMsidAttribute(const std::string& line,
319 std::string* stream_id,
320 std::string* track_id,
321 SdpParseError* error);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000322
323// Helper functions
324
325// Below ParseFailed*** functions output the line that caused the parsing
326// failure and the detailed reason (|description|) of the failure to |error|.
327// The functions always return false so that they can be used directly in the
328// following way when error happens:
329// "return ParseFailed***(...);"
330
331// The line starting at |line_start| of |message| is the failing line.
332// The reason for the failure should be provided in the |description|.
333// An example of a description could be "unknown character".
334static bool ParseFailed(const std::string& message,
335 size_t line_start,
336 const std::string& description,
337 SdpParseError* error) {
338 // Get the first line of |message| from |line_start|.
sergeyu@chromium.org5bc25c42013-12-05 00:24:06 +0000339 std::string first_line;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000340 size_t line_end = message.find(kNewLine, line_start);
341 if (line_end != std::string::npos) {
342 if (line_end > 0 && (message.at(line_end - 1) == kReturn)) {
343 --line_end;
344 }
345 first_line = message.substr(line_start, (line_end - line_start));
sergeyu@chromium.org5bc25c42013-12-05 00:24:06 +0000346 } else {
347 first_line = message.substr(line_start);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000348 }
349
350 if (error) {
351 error->line = first_line;
352 error->description = description;
353 }
354 LOG(LS_ERROR) << "Failed to parse: \"" << first_line
355 << "\". Reason: " << description;
356 return false;
357}
358
359// |line| is the failing line. The reason for the failure should be
360// provided in the |description|.
361static bool ParseFailed(const std::string& line,
362 const std::string& description,
363 SdpParseError* error) {
364 return ParseFailed(line, 0, description, error);
365}
366
367// Parses failure where the failing SDP line isn't know or there are multiple
368// failing lines.
369static bool ParseFailed(const std::string& description,
370 SdpParseError* error) {
371 return ParseFailed("", description, error);
372}
373
374// |line| is the failing line. The failure is due to the fact that |line|
375// doesn't have |expected_fields| fields.
376static bool ParseFailedExpectFieldNum(const std::string& line,
377 int expected_fields,
378 SdpParseError* error) {
379 std::ostringstream description;
380 description << "Expects " << expected_fields << " fields.";
381 return ParseFailed(line, description.str(), error);
382}
383
384// |line| is the failing line. The failure is due to the fact that |line| has
385// less than |expected_min_fields| fields.
386static bool ParseFailedExpectMinFieldNum(const std::string& line,
387 int expected_min_fields,
388 SdpParseError* error) {
389 std::ostringstream description;
390 description << "Expects at least " << expected_min_fields << " fields.";
391 return ParseFailed(line, description.str(), error);
392}
393
394// |line| is the failing line. The failure is due to the fact that it failed to
395// get the value of |attribute|.
396static bool ParseFailedGetValue(const std::string& line,
397 const std::string& attribute,
398 SdpParseError* error) {
399 std::ostringstream description;
400 description << "Failed to get the value of attribute: " << attribute;
401 return ParseFailed(line, description.str(), error);
402}
403
404// The line starting at |line_start| of |message| is the failing line. The
405// failure is due to the line type (e.g. the "m" part of the "m-line")
406// not matching what is expected. The expected line type should be
407// provided as |line_type|.
408static bool ParseFailedExpectLine(const std::string& message,
409 size_t line_start,
410 const char line_type,
411 const std::string& line_value,
412 SdpParseError* error) {
413 std::ostringstream description;
414 description << "Expect line: " << line_type << "=" << line_value;
415 return ParseFailed(message, line_start, description.str(), error);
416}
417
418static bool AddLine(const std::string& line, std::string* message) {
419 if (!message)
420 return false;
421
422 message->append(line);
423 message->append(kLineBreak);
424 return true;
425}
426
427static bool GetLine(const std::string& message,
428 size_t* pos,
429 std::string* line) {
430 size_t line_begin = *pos;
431 size_t line_end = message.find(kNewLine, line_begin);
432 if (line_end == std::string::npos) {
433 return false;
434 }
435 // Update the new start position
436 *pos = line_end + 1;
437 if (line_end > 0 && (message.at(line_end - 1) == kReturn)) {
438 --line_end;
439 }
440 *line = message.substr(line_begin, (line_end - line_begin));
441 const char* cline = line->c_str();
442 // RFC 4566
443 // An SDP session description consists of a number of lines of text of
444 // the form:
445 // <type>=<value>
446 // where <type> MUST be exactly one case-significant character and
447 // <value> is structured text whose format depends on <type>.
448 // Whitespace MUST NOT be used on either side of the "=" sign.
decurtis@webrtc.org8af11042015-01-07 19:15:51 +0000449 if (line->length() < 3 ||
450 !islower(cline[0]) ||
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000451 cline[1] != kSdpDelimiterEqual ||
452 cline[2] == kSdpDelimiterSpace) {
453 *pos = line_begin;
454 return false;
455 }
456 return true;
457}
458
459// Init |os| to "|type|=|value|".
460static void InitLine(const char type,
461 const std::string& value,
462 std::ostringstream* os) {
463 os->str("");
464 *os << type << kSdpDelimiterEqual << value;
465}
466
467// Init |os| to "a=|attribute|".
468static void InitAttrLine(const std::string& attribute, std::ostringstream* os) {
469 InitLine(kLineTypeAttributes, attribute, os);
470}
471
472// Writes a SDP attribute line based on |attribute| and |value| to |message|.
473static void AddAttributeLine(const std::string& attribute, int value,
474 std::string* message) {
475 std::ostringstream os;
476 InitAttrLine(attribute, &os);
477 os << kSdpDelimiterColon << value;
478 AddLine(os.str(), message);
479}
480
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000481static bool IsLineType(const std::string& message,
482 const char type,
483 size_t line_start) {
484 if (message.size() < line_start + kLinePrefixLength) {
485 return false;
486 }
487 const char* cmessage = message.c_str();
488 return (cmessage[line_start] == type &&
489 cmessage[line_start + 1] == kSdpDelimiterEqual);
490}
491
492static bool IsLineType(const std::string& line,
493 const char type) {
494 return IsLineType(line, type, 0);
495}
496
497static bool GetLineWithType(const std::string& message, size_t* pos,
498 std::string* line, const char type) {
499 if (!IsLineType(message, type, *pos)) {
500 return false;
501 }
502
503 if (!GetLine(message, pos, line))
504 return false;
505
506 return true;
507}
508
509static bool HasAttribute(const std::string& line,
510 const std::string& attribute) {
511 return (line.compare(kLinePrefixLength, attribute.size(), attribute) == 0);
512}
513
Peter Boström0c4e06b2015-10-07 12:23:21 +0200514static bool AddSsrcLine(uint32_t ssrc_id,
515 const std::string& attribute,
516 const std::string& value,
517 std::string* message) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000518 // RFC 5576
519 // a=ssrc:<ssrc-id> <attribute>:<value>
520 std::ostringstream os;
521 InitAttrLine(kAttributeSsrc, &os);
522 os << kSdpDelimiterColon << ssrc_id << kSdpDelimiterSpace
523 << attribute << kSdpDelimiterColon << value;
524 return AddLine(os.str(), message);
525}
526
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000527// Get value only from <attribute>:<value>.
528static bool GetValue(const std::string& message, const std::string& attribute,
529 std::string* value, SdpParseError* error) {
530 std::string leftpart;
Donald Curtis0e07f922015-05-15 09:21:23 -0700531 if (!rtc::tokenize_first(message, kSdpDelimiterColon, &leftpart, value)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000532 return ParseFailedGetValue(message, attribute, error);
533 }
534 // The left part should end with the expected attribute.
535 if (leftpart.length() < attribute.length() ||
536 leftpart.compare(leftpart.length() - attribute.length(),
537 attribute.length(), attribute) != 0) {
538 return ParseFailedGetValue(message, attribute, error);
539 }
540 return true;
541}
542
543static bool CaseInsensitiveFind(std::string str1, std::string str2) {
544 std::transform(str1.begin(), str1.end(), str1.begin(),
545 ::tolower);
546 std::transform(str2.begin(), str2.end(), str2.begin(),
547 ::tolower);
548 return str1.find(str2) != std::string::npos;
549}
550
wu@webrtc.org5e760e72014-04-02 23:19:09 +0000551template <class T>
552static bool GetValueFromString(const std::string& line,
553 const std::string& s,
554 T* t,
555 SdpParseError* error) {
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000556 if (!rtc::FromString(s, t)) {
wu@webrtc.org5e760e72014-04-02 23:19:09 +0000557 std::ostringstream description;
558 description << "Invalid value: " << s << ".";
559 return ParseFailed(line, description.str(), error);
560 }
561 return true;
562}
563
pkasting@chromium.orge9facf82015-02-17 20:36:28 +0000564static bool GetPayloadTypeFromString(const std::string& line,
565 const std::string& s,
566 int* payload_type,
567 SdpParseError* error) {
568 return GetValueFromString(line, s, payload_type, error) &&
569 cricket::IsValidRtpPayloadType(*payload_type);
570}
571
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800572// |msid_stream_id| and |msid_track_id| represent the stream/track ID from the
573// "a=msid" attribute, if it exists. They are empty if the attribute does not
574// exist.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000575void CreateTracksFromSsrcInfos(const SsrcInfoVec& ssrc_infos,
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800576 const std::string& msid_stream_id,
577 const std::string& msid_track_id,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000578 StreamParamsVec* tracks) {
579 ASSERT(tracks != NULL);
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800580 ASSERT(msid_stream_id.empty() == msid_track_id.empty());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000581 for (SsrcInfoVec::const_iterator ssrc_info = ssrc_infos.begin();
582 ssrc_info != ssrc_infos.end(); ++ssrc_info) {
583 if (ssrc_info->cname.empty()) {
584 continue;
585 }
586
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800587 std::string stream_id;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000588 std::string track_id;
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800589 if (ssrc_info->stream_id.empty() && !ssrc_info->mslabel.empty()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000590 // If there's no msid and there's mslabel, we consider this is a sdp from
591 // a older version of client that doesn't support msid.
592 // In that case, we use the mslabel and label to construct the track.
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800593 stream_id = ssrc_info->mslabel;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000594 track_id = ssrc_info->label;
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800595 } else if (ssrc_info->stream_id.empty() && !msid_stream_id.empty()) {
596 // If there's no msid in the SSRC attributes, but there's a global one
597 // (from a=msid), use that. This is the case with unified plan SDP.
598 stream_id = msid_stream_id;
599 track_id = msid_track_id;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000600 } else {
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800601 stream_id = ssrc_info->stream_id;
deadbeef9d3584c2016-02-16 17:54:10 -0800602 track_id = ssrc_info->track_id;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000603 }
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800604 // If a stream/track ID wasn't populated from the SSRC attributes OR the
605 // msid attribute, use default/random values.
606 if (stream_id.empty()) {
607 stream_id = kDefaultMsid;
608 }
609 if (track_id.empty()) {
610 // TODO(ronghuawu): What should we do if the track id doesn't appear?
611 // Create random string (which will be used as track label later)?
612 track_id = rtc::CreateRandomString(8);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000613 }
614
615 StreamParamsVec::iterator track = tracks->begin();
616 for (; track != tracks->end(); ++track) {
617 if (track->id == track_id) {
618 break;
619 }
620 }
621 if (track == tracks->end()) {
622 // If we don't find an existing track, create a new one.
623 tracks->push_back(StreamParams());
624 track = tracks->end() - 1;
625 }
626 track->add_ssrc(ssrc_info->ssrc_id);
627 track->cname = ssrc_info->cname;
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800628 track->sync_label = stream_id;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000629 track->id = track_id;
630 }
631}
632
633void GetMediaStreamLabels(const ContentInfo* content,
634 std::set<std::string>* labels) {
635 const MediaContentDescription* media_desc =
henrike@webrtc.org28654cb2013-07-22 21:07:49 +0000636 static_cast<const MediaContentDescription*>(
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000637 content->description);
638 const cricket::StreamParamsVec& streams = media_desc->streams();
639 for (cricket::StreamParamsVec::const_iterator it = streams.begin();
640 it != streams.end(); ++it) {
641 labels->insert(it->sync_label);
642 }
643}
644
645// RFC 5245
646// It is RECOMMENDED that default candidates be chosen based on the
647// likelihood of those candidates to work with the peer that is being
648// contacted. It is RECOMMENDED that relayed > reflexive > host.
649static const int kPreferenceUnknown = 0;
650static const int kPreferenceHost = 1;
651static const int kPreferenceReflexive = 2;
652static const int kPreferenceRelayed = 3;
653
654static int GetCandidatePreferenceFromType(const std::string& type) {
655 int preference = kPreferenceUnknown;
656 if (type == cricket::LOCAL_PORT_TYPE) {
657 preference = kPreferenceHost;
658 } else if (type == cricket::STUN_PORT_TYPE) {
659 preference = kPreferenceReflexive;
660 } else if (type == cricket::RELAY_PORT_TYPE) {
661 preference = kPreferenceRelayed;
662 } else {
663 ASSERT(false);
664 }
665 return preference;
666}
667
guoweis@webrtc.org57ac2c82015-02-06 00:45:13 +0000668// Get ip and port of the default destination from the |candidates| with the
669// given value of |component_id|. The default candidate should be the one most
670// likely to work, typically IPv4 relay.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000671// RFC 5245
672// The value of |component_id| currently supported are 1 (RTP) and 2 (RTCP).
673// TODO: Decide the default destination in webrtcsession and
674// pass it down via SessionDescription.
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000675static void GetDefaultDestination(
676 const std::vector<Candidate>& candidates,
677 int component_id, std::string* port,
678 std::string* ip, std::string* addr_type) {
perkj@webrtc.orgd105cc82014-11-07 11:22:06 +0000679 *addr_type = kConnectionIpv4Addrtype;
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000680 *port = kDummyPort;
681 *ip = kDummyAddress;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000682 int current_preference = kPreferenceUnknown;
guoweis@webrtc.org57ac2c82015-02-06 00:45:13 +0000683 int current_family = AF_UNSPEC;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000684 for (std::vector<Candidate>::const_iterator it = candidates.begin();
685 it != candidates.end(); ++it) {
686 if (it->component() != component_id) {
687 continue;
688 }
guoweis@webrtc.org57ac2c82015-02-06 00:45:13 +0000689 // Default destination should be UDP only.
690 if (it->protocol() != cricket::UDP_PROTOCOL_NAME) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000691 continue;
692 }
guoweis@webrtc.org57ac2c82015-02-06 00:45:13 +0000693 const int preference = GetCandidatePreferenceFromType(it->type());
694 const int family = it->address().ipaddr().family();
695 // See if this candidate is more preferable then the current one if it's the
696 // same family. Or if the current family is IPv4 already so we could safely
697 // ignore all IPv6 ones. WebRTC bug 4269.
698 // http://code.google.com/p/webrtc/issues/detail?id=4269
699 if ((preference <= current_preference && current_family == family) ||
700 (current_family == AF_INET && family == AF_INET6)) {
701 continue;
702 }
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000703 if (family == AF_INET) {
704 addr_type->assign(kConnectionIpv4Addrtype);
705 } else if (family == AF_INET6) {
706 addr_type->assign(kConnectionIpv6Addrtype);
707 }
guoweis@webrtc.org57ac2c82015-02-06 00:45:13 +0000708 current_preference = preference;
709 current_family = family;
710 *port = it->address().PortAsString();
711 *ip = it->address().ipaddr().ToString();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000712 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000713}
714
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000715// Update |mline|'s default destination and append a c line after it.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000716static void UpdateMediaDefaultDestination(
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000717 const std::vector<Candidate>& candidates,
jbauch083b73f2015-07-16 02:46:32 -0700718 const std::string& mline,
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000719 std::string* message) {
720 std::string new_lines;
721 AddLine(mline, &new_lines);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000722 // RFC 4566
723 // m=<media> <port> <proto> <fmt> ...
724 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000725 rtc::split(mline, kSdpDelimiterSpace, &fields);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000726 if (fields.size() < 3) {
727 return;
728 }
729
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000730 std::ostringstream os;
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000731 std::string rtp_port, rtp_ip, addr_type;
732 GetDefaultDestination(candidates, ICE_CANDIDATE_COMPONENT_RTP,
733 &rtp_port, &rtp_ip, &addr_type);
734 // Found default RTP candidate.
735 // RFC 5245
736 // The default candidates are added to the SDP as the default
737 // destination for media. For streams based on RTP, this is done by
738 // placing the IP address and port of the RTP candidate into the c and m
739 // lines, respectively.
740 // Update the port in the m line.
741 // If this is a m-line with port equal to 0, we don't change it.
742 if (fields[1] != kMediaPortRejected) {
743 new_lines.replace(fields[0].size() + 1,
744 fields[1].size(),
745 rtp_port);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000746 }
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000747 // Add the c line.
748 // RFC 4566
749 // c=<nettype> <addrtype> <connection-address>
750 InitLine(kLineTypeConnection, kConnectionNettype, &os);
751 os << " " << addr_type << " " << rtp_ip;
752 AddLine(os.str(), &new_lines);
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000753 message->append(new_lines);
754}
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000755
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000756// Gets "a=rtcp" line if found default RTCP candidate from |candidates|.
757static std::string GetRtcpLine(const std::vector<Candidate>& candidates) {
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000758 std::string rtcp_line, rtcp_port, rtcp_ip, addr_type;
759 GetDefaultDestination(candidates, ICE_CANDIDATE_COMPONENT_RTCP,
760 &rtcp_port, &rtcp_ip, &addr_type);
761 // Found default RTCP candidate.
762 // RFC 5245
763 // If the agent is utilizing RTCP, it MUST encode the RTCP candidate
764 // using the a=rtcp attribute as defined in RFC 3605.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000765
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000766 // RFC 3605
767 // rtcp-attribute = "a=rtcp:" port [nettype space addrtype space
768 // connection-address] CRLF
769 std::ostringstream os;
770 InitAttrLine(kAttributeRtcp, &os);
771 os << kSdpDelimiterColon
772 << rtcp_port << " "
773 << kConnectionNettype << " "
774 << addr_type << " "
775 << rtcp_ip;
776 rtcp_line = os.str();
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000777 return rtcp_line;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000778}
779
780// Get candidates according to the mline index from SessionDescriptionInterface.
781static void GetCandidatesByMindex(const SessionDescriptionInterface& desci,
782 int mline_index,
783 std::vector<Candidate>* candidates) {
784 if (!candidates) {
785 return;
786 }
787 const IceCandidateCollection* cc = desci.candidates(mline_index);
788 for (size_t i = 0; i < cc->count(); ++i) {
789 const IceCandidateInterface* candidate = cc->at(i);
790 candidates->push_back(candidate->candidate());
791 }
792}
793
deadbeef9d3584c2016-02-16 17:54:10 -0800794std::string SdpSerialize(const JsepSessionDescription& jdesc,
795 bool unified_plan_sdp) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000796 const cricket::SessionDescription* desc = jdesc.description();
797 if (!desc) {
798 return "";
799 }
800
801 std::string message;
802
803 // Session Description.
804 AddLine(kSessionVersion, &message);
805 // Session Origin
806 // RFC 4566
807 // o=<username> <sess-id> <sess-version> <nettype> <addrtype>
808 // <unicast-address>
809 std::ostringstream os;
810 InitLine(kLineTypeOrigin, kSessionOriginUsername, &os);
jbauch083b73f2015-07-16 02:46:32 -0700811 const std::string& session_id = jdesc.session_id().empty() ?
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000812 kSessionOriginSessionId : jdesc.session_id();
jbauch083b73f2015-07-16 02:46:32 -0700813 const std::string& session_version = jdesc.session_version().empty() ?
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000814 kSessionOriginSessionVersion : jdesc.session_version();
815 os << " " << session_id << " " << session_version << " "
816 << kSessionOriginNettype << " " << kSessionOriginAddrtype << " "
817 << kSessionOriginAddress;
818 AddLine(os.str(), &message);
819 AddLine(kSessionName, &message);
820
821 // Time Description.
822 AddLine(kTimeDescription, &message);
823
824 // Group
825 if (desc->HasGroup(cricket::GROUP_TYPE_BUNDLE)) {
826 std::string group_line = kAttrGroup;
827 const cricket::ContentGroup* group =
828 desc->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
829 ASSERT(group != NULL);
830 const cricket::ContentNames& content_names = group->content_names();
831 for (cricket::ContentNames::const_iterator it = content_names.begin();
832 it != content_names.end(); ++it) {
833 group_line.append(" ");
834 group_line.append(*it);
835 }
836 AddLine(group_line, &message);
837 }
838
839 // MediaStream semantics
840 InitAttrLine(kAttributeMsidSemantics, &os);
841 os << kSdpDelimiterColon << " " << kMediaStreamSemantic;
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000842
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000843 std::set<std::string> media_stream_labels;
844 const ContentInfo* audio_content = GetFirstAudioContent(desc);
845 if (audio_content)
846 GetMediaStreamLabels(audio_content, &media_stream_labels);
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000847
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000848 const ContentInfo* video_content = GetFirstVideoContent(desc);
849 if (video_content)
850 GetMediaStreamLabels(video_content, &media_stream_labels);
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000851
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000852 for (std::set<std::string>::const_iterator it =
853 media_stream_labels.begin(); it != media_stream_labels.end(); ++it) {
854 os << " " << *it;
855 }
856 AddLine(os.str(), &message);
857
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000858 // Preserve the order of the media contents.
859 int mline_index = -1;
860 for (cricket::ContentInfos::const_iterator it = desc->contents().begin();
861 it != desc->contents().end(); ++it) {
862 const MediaContentDescription* mdesc =
863 static_cast<const MediaContentDescription*>(it->description);
864 std::vector<Candidate> candidates;
865 GetCandidatesByMindex(jdesc, ++mline_index, &candidates);
deadbeef9d3584c2016-02-16 17:54:10 -0800866 BuildMediaDescription(&*it, desc->GetTransportInfoByName(it->name),
867 mdesc->type(), candidates, unified_plan_sdp,
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000868 &message);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000869 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000870 return message;
871}
872
873// Serializes the passed in IceCandidateInterface to a SDP string.
874// candidate - The candidate to be serialized.
Honghai Zhang7fb69db2016-03-14 11:59:18 -0700875std::string SdpSerializeCandidate(const IceCandidateInterface& candidate) {
876 return SdpSerializeCandidate(candidate.candidate());
877}
878
879// Serializes a cricket Candidate.
880std::string SdpSerializeCandidate(const cricket::Candidate& candidate) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000881 std::string message;
Honghai Zhang7fb69db2016-03-14 11:59:18 -0700882 std::vector<cricket::Candidate> candidates(1, candidate);
honghaiza54a0802015-12-16 18:37:23 -0800883 BuildCandidate(candidates, true, &message);
wu@webrtc.orgec9f5fb2014-06-24 17:05:10 +0000884 // From WebRTC draft section 4.8.1.1 candidate-attribute will be
885 // just candidate:<candidate> not a=candidate:<blah>CRLF
886 ASSERT(message.find("a=") == 0);
887 message.erase(0, 2);
888 ASSERT(message.find(kLineBreak) == message.size() - 2);
889 message.resize(message.size() - 2);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000890 return message;
891}
892
893bool SdpDeserialize(const std::string& message,
894 JsepSessionDescription* jdesc,
895 SdpParseError* error) {
896 std::string session_id;
897 std::string session_version;
Peter Thatcher7cbd1882015-09-17 18:54:52 -0700898 TransportDescription session_td("", "");
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000899 RtpHeaderExtensions session_extmaps;
900 cricket::SessionDescription* desc = new cricket::SessionDescription();
901 std::vector<JsepIceCandidate*> candidates;
902 size_t current_pos = 0;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000903
904 // Session Description
905 if (!ParseSessionDescription(message, &current_pos, &session_id,
deadbeefc80741f2015-10-22 13:14:45 -0700906 &session_version, &session_td, &session_extmaps,
907 desc, error)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000908 delete desc;
909 return false;
910 }
911
912 // Media Description
deadbeefc80741f2015-10-22 13:14:45 -0700913 if (!ParseMediaDescription(message, session_td, session_extmaps, &current_pos,
914 desc, &candidates, error)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000915 delete desc;
916 for (std::vector<JsepIceCandidate*>::const_iterator
917 it = candidates.begin(); it != candidates.end(); ++it) {
918 delete *it;
919 }
920 return false;
921 }
922
923 jdesc->Initialize(desc, session_id, session_version);
924
925 for (std::vector<JsepIceCandidate*>::const_iterator
926 it = candidates.begin(); it != candidates.end(); ++it) {
927 jdesc->AddCandidate(*it);
928 delete *it;
929 }
930 return true;
931}
932
933bool SdpDeserializeCandidate(const std::string& message,
934 JsepIceCandidate* jcandidate,
935 SdpParseError* error) {
936 ASSERT(jcandidate != NULL);
937 Candidate candidate;
938 if (!ParseCandidate(message, &candidate, error, true)) {
939 return false;
940 }
941 jcandidate->SetCandidate(candidate);
942 return true;
943}
944
Honghai Zhang7fb69db2016-03-14 11:59:18 -0700945bool SdpDeserializeCandidate(const std::string& transport_name,
946 const std::string& message,
947 cricket::Candidate* candidate,
948 SdpParseError* error) {
949 ASSERT(candidate != nullptr);
950 if (!ParseCandidate(message, candidate, error, true)) {
951 return false;
952 }
953 candidate->set_transport_name(transport_name);
954 return true;
955}
956
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000957bool ParseCandidate(const std::string& message, Candidate* candidate,
958 SdpParseError* error, bool is_raw) {
959 ASSERT(candidate != NULL);
960
961 // Get the first line from |message|.
jiayl@webrtc.org7ec3f9f2014-08-08 23:09:15 +0000962 std::string first_line = message;
963 size_t pos = 0;
964 GetLine(message, &pos, &first_line);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000965
jiayl@webrtc.org7ec3f9f2014-08-08 23:09:15 +0000966 // Makes sure |message| contains only one line.
967 if (message.size() > first_line.size()) {
968 std::string left, right;
Donald Curtis0e07f922015-05-15 09:21:23 -0700969 if (rtc::tokenize_first(message, kNewLine, &left, &right) &&
970 !right.empty()) {
jiayl@webrtc.org7ec3f9f2014-08-08 23:09:15 +0000971 return ParseFailed(message, 0, "Expect one line only", error);
972 }
973 }
974
975 // From WebRTC draft section 4.8.1.1 candidate-attribute should be
976 // candidate:<candidate> when trickled, but we still support
977 // a=candidate:<blah>CRLF for backward compatibility and for parsing a line
978 // from the SDP.
979 if (IsLineType(first_line, kLineTypeAttributes)) {
980 first_line = first_line.substr(kLinePrefixLength);
981 }
982
983 std::string attribute_candidate;
984 std::string candidate_value;
985
986 // |first_line| must be in the form of "candidate:<value>".
Donald Curtis144d0182015-05-15 13:14:24 -0700987 if (!rtc::tokenize_first(first_line, kSdpDelimiterColon, &attribute_candidate,
988 &candidate_value) ||
jiayl@webrtc.org7ec3f9f2014-08-08 23:09:15 +0000989 attribute_candidate != kAttributeCandidate) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000990 if (is_raw) {
991 std::ostringstream description;
jiayl@webrtc.org7ec3f9f2014-08-08 23:09:15 +0000992 description << "Expect line: " << kAttributeCandidate
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000993 << ":" << "<candidate-str>";
994 return ParseFailed(first_line, 0, description.str(), error);
995 } else {
996 return ParseFailedExpectLine(first_line, 0, kLineTypeAttributes,
997 kAttributeCandidate, error);
998 }
999 }
1000
1001 std::vector<std::string> fields;
jiayl@webrtc.org7ec3f9f2014-08-08 23:09:15 +00001002 rtc::split(candidate_value, kSdpDelimiterSpace, &fields);
1003
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001004 // RFC 5245
1005 // a=candidate:<foundation> <component-id> <transport> <priority>
1006 // <connection-address> <port> typ <candidate-types>
1007 // [raddr <connection-address>] [rport <port>]
1008 // *(SP extension-att-name SP extension-att-value)
1009 const size_t expected_min_fields = 8;
1010 if (fields.size() < expected_min_fields ||
1011 (fields[6] != kAttributeCandidateTyp)) {
1012 return ParseFailedExpectMinFieldNum(first_line, expected_min_fields, error);
1013 }
jbauch083b73f2015-07-16 02:46:32 -07001014 const std::string& foundation = fields[0];
jiayl@webrtc.org7ec3f9f2014-08-08 23:09:15 +00001015
wu@webrtc.org5e760e72014-04-02 23:19:09 +00001016 int component_id = 0;
1017 if (!GetValueFromString(first_line, fields[1], &component_id, error)) {
1018 return false;
1019 }
jbauch083b73f2015-07-16 02:46:32 -07001020 const std::string& transport = fields[2];
Peter Boström0c4e06b2015-10-07 12:23:21 +02001021 uint32_t priority = 0;
wu@webrtc.org5e760e72014-04-02 23:19:09 +00001022 if (!GetValueFromString(first_line, fields[3], &priority, error)) {
1023 return false;
1024 }
jbauch083b73f2015-07-16 02:46:32 -07001025 const std::string& connection_address = fields[4];
wu@webrtc.org5e760e72014-04-02 23:19:09 +00001026 int port = 0;
1027 if (!GetValueFromString(first_line, fields[5], &port, error)) {
1028 return false;
1029 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001030 SocketAddress address(connection_address, port);
1031
1032 cricket::ProtocolType protocol;
1033 if (!StringToProto(transport.c_str(), &protocol)) {
1034 return ParseFailed(first_line, "Unsupported transport type.", error);
1035 }
1036
1037 std::string candidate_type;
jbauch083b73f2015-07-16 02:46:32 -07001038 const std::string& type = fields[7];
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001039 if (type == kCandidateHost) {
1040 candidate_type = cricket::LOCAL_PORT_TYPE;
1041 } else if (type == kCandidateSrflx) {
1042 candidate_type = cricket::STUN_PORT_TYPE;
1043 } else if (type == kCandidateRelay) {
1044 candidate_type = cricket::RELAY_PORT_TYPE;
Honghai Zhang7fb69db2016-03-14 11:59:18 -07001045 } else if (type == kCandidatePrflx) {
1046 candidate_type = cricket::PRFLX_PORT_TYPE;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001047 } else {
1048 return ParseFailed(first_line, "Unsupported candidate type.", error);
1049 }
1050
1051 size_t current_position = expected_min_fields;
1052 SocketAddress related_address;
1053 // The 2 optional fields for related address
1054 // [raddr <connection-address>] [rport <port>]
1055 if (fields.size() >= (current_position + 2) &&
1056 fields[current_position] == kAttributeCandidateRaddr) {
1057 related_address.SetIP(fields[++current_position]);
1058 ++current_position;
1059 }
1060 if (fields.size() >= (current_position + 2) &&
1061 fields[current_position] == kAttributeCandidateRport) {
wu@webrtc.org5e760e72014-04-02 23:19:09 +00001062 int port = 0;
1063 if (!GetValueFromString(
1064 first_line, fields[++current_position], &port, error)) {
1065 return false;
1066 }
1067 related_address.SetPort(port);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001068 ++current_position;
1069 }
1070
mallinath@webrtc.org2d60c5e2014-08-08 22:29:20 +00001071 // If this is a TCP candidate, it has additional extension as defined in
1072 // RFC 6544.
1073 std::string tcptype;
1074 if (fields.size() >= (current_position + 2) &&
1075 fields[current_position] == kTcpCandidateType) {
1076 tcptype = fields[++current_position];
1077 ++current_position;
1078
1079 if (tcptype != cricket::TCPTYPE_ACTIVE_STR &&
1080 tcptype != cricket::TCPTYPE_PASSIVE_STR &&
1081 tcptype != cricket::TCPTYPE_SIMOPEN_STR) {
1082 return ParseFailed(first_line, "Invalid TCP candidate type.", error);
1083 }
1084
1085 if (protocol != cricket::PROTO_TCP) {
1086 return ParseFailed(first_line, "Invalid non-TCP candidate", error);
1087 }
1088 }
1089
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001090 // Extension
honghaiza54a0802015-12-16 18:37:23 -08001091 // Though non-standard, we support the ICE ufrag and pwd being signaled on
1092 // the candidate to avoid issues with confusing which generation a candidate
1093 // belongs to when trickling multiple generations at the same time.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001094 std::string username;
1095 std::string password;
Peter Boström0c4e06b2015-10-07 12:23:21 +02001096 uint32_t generation = 0;
honghaiza0c44ea2016-03-23 16:07:48 -07001097 uint16_t network_id = 0;
1098 uint16_t network_cost = 0;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001099 for (size_t i = current_position; i + 1 < fields.size(); ++i) {
1100 // RFC 5245
1101 // *(SP extension-att-name SP extension-att-value)
1102 if (fields[i] == kAttributeCandidateGeneration) {
wu@webrtc.org5e760e72014-04-02 23:19:09 +00001103 if (!GetValueFromString(first_line, fields[++i], &generation, error)) {
1104 return false;
1105 }
honghaiza54a0802015-12-16 18:37:23 -08001106 } else if (fields[i] == kAttributeCandidateUfrag) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001107 username = fields[++i];
honghaiza54a0802015-12-16 18:37:23 -08001108 } else if (fields[i] == kAttributeCandidatePwd) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001109 password = fields[++i];
honghaiza0c44ea2016-03-23 16:07:48 -07001110 } else if (fields[i] == kAttributeCandidateNetworkId) {
1111 if (!GetValueFromString(first_line, fields[++i], &network_id, error)) {
1112 return false;
1113 }
honghaize1a0c942016-02-16 14:54:56 -08001114 } else if (fields[i] == kAttributeCandidateNetworkCost) {
1115 if (!GetValueFromString(first_line, fields[++i], &network_cost, error)) {
1116 return false;
1117 }
honghaiza0c44ea2016-03-23 16:07:48 -07001118 network_cost = std::min(network_cost, cricket::kMaxNetworkCost);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001119 } else {
1120 // Skip the unknown extension.
1121 ++i;
1122 }
1123 }
1124
guoweis@webrtc.org61c12472015-01-15 06:53:07 +00001125 *candidate = Candidate(component_id, cricket::ProtoToString(protocol),
guoweis@webrtc.org950c5182014-12-16 23:01:31 +00001126 address, priority, username, password, candidate_type,
honghaiza0c44ea2016-03-23 16:07:48 -07001127 generation, foundation, network_id, network_cost);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001128 candidate->set_related_address(related_address);
mallinath@webrtc.org2d60c5e2014-08-08 22:29:20 +00001129 candidate->set_tcptype(tcptype);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001130 return true;
1131}
1132
1133bool ParseIceOptions(const std::string& line,
1134 std::vector<std::string>* transport_options,
1135 SdpParseError* error) {
1136 std::string ice_options;
1137 if (!GetValue(line, kAttributeIceOption, &ice_options, error)) {
1138 return false;
1139 }
1140 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001141 rtc::split(ice_options, kSdpDelimiterSpace, &fields);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001142 for (size_t i = 0; i < fields.size(); ++i) {
1143 transport_options->push_back(fields[i]);
1144 }
1145 return true;
1146}
1147
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00001148bool ParseSctpPort(const std::string& line,
1149 int* sctp_port,
1150 SdpParseError* error) {
1151 // draft-ietf-mmusic-sctp-sdp-07
1152 // a=sctp-port
1153 std::vector<std::string> fields;
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00001154 const size_t expected_min_fields = 2;
lally69f57602015-10-08 10:15:04 -07001155 rtc::split(line.substr(kLinePrefixLength), kSdpDelimiterColon, &fields);
1156 if (fields.size() < expected_min_fields) {
1157 fields.resize(0);
1158 rtc::split(line.substr(kLinePrefixLength), kSdpDelimiterSpace, &fields);
1159 }
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00001160 if (fields.size() < expected_min_fields) {
1161 return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
1162 }
1163 if (!rtc::FromString(fields[1], sctp_port)) {
lally69f57602015-10-08 10:15:04 -07001164 return ParseFailed(line, "Invalid sctp port value.", error);
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00001165 }
1166 return true;
1167}
1168
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001169bool ParseExtmap(const std::string& line, RtpHeaderExtension* extmap,
1170 SdpParseError* error) {
1171 // RFC 5285
1172 // a=extmap:<value>["/"<direction>] <URI> <extensionattributes>
1173 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001174 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001175 kSdpDelimiterSpace, &fields);
1176 const size_t expected_min_fields = 2;
1177 if (fields.size() < expected_min_fields) {
1178 return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
1179 }
1180 std::string uri = fields[1];
1181
1182 std::string value_direction;
1183 if (!GetValue(fields[0], kAttributeExtmap, &value_direction, error)) {
1184 return false;
1185 }
1186 std::vector<std::string> sub_fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001187 rtc::split(value_direction, kSdpDelimiterSlash, &sub_fields);
wu@webrtc.org5e760e72014-04-02 23:19:09 +00001188 int value = 0;
1189 if (!GetValueFromString(line, sub_fields[0], &value, error)) {
1190 return false;
1191 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001192
1193 *extmap = RtpHeaderExtension(uri, value);
1194 return true;
1195}
1196
1197void BuildMediaDescription(const ContentInfo* content_info,
1198 const TransportInfo* transport_info,
1199 const MediaType media_type,
wu@webrtc.org4c3e9912014-07-16 21:03:13 +00001200 const std::vector<Candidate>& candidates,
deadbeef9d3584c2016-02-16 17:54:10 -08001201 bool unified_plan_sdp,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001202 std::string* message) {
1203 ASSERT(message != NULL);
1204 if (content_info == NULL || message == NULL) {
1205 return;
1206 }
1207 // TODO: Rethink if we should use sprintfn instead of stringstream.
1208 // According to the style guide, streams should only be used for logging.
1209 // http://google-styleguide.googlecode.com/svn/
1210 // trunk/cppguide.xml?showone=Streams#Streams
1211 std::ostringstream os;
1212 const MediaContentDescription* media_desc =
henrike@webrtc.org28654cb2013-07-22 21:07:49 +00001213 static_cast<const MediaContentDescription*>(
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001214 content_info->description);
1215 ASSERT(media_desc != NULL);
1216
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +00001217 int sctp_port = cricket::kSctpDefaultPort;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001218
1219 // RFC 4566
1220 // m=<media> <port> <proto> <fmt>
1221 // fmt is a list of payload type numbers that MAY be used in the session.
1222 const char* type = NULL;
1223 if (media_type == cricket::MEDIA_TYPE_AUDIO)
1224 type = kMediaTypeAudio;
1225 else if (media_type == cricket::MEDIA_TYPE_VIDEO)
1226 type = kMediaTypeVideo;
1227 else if (media_type == cricket::MEDIA_TYPE_DATA)
1228 type = kMediaTypeData;
1229 else
1230 ASSERT(false);
1231
1232 std::string fmt;
1233 if (media_type == cricket::MEDIA_TYPE_VIDEO) {
1234 const VideoContentDescription* video_desc =
1235 static_cast<const VideoContentDescription*>(media_desc);
1236 for (std::vector<cricket::VideoCodec>::const_iterator it =
1237 video_desc->codecs().begin();
1238 it != video_desc->codecs().end(); ++it) {
1239 fmt.append(" ");
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001240 fmt.append(rtc::ToString<int>(it->id));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001241 }
1242 } else if (media_type == cricket::MEDIA_TYPE_AUDIO) {
1243 const AudioContentDescription* audio_desc =
1244 static_cast<const AudioContentDescription*>(media_desc);
1245 for (std::vector<cricket::AudioCodec>::const_iterator it =
1246 audio_desc->codecs().begin();
1247 it != audio_desc->codecs().end(); ++it) {
1248 fmt.append(" ");
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001249 fmt.append(rtc::ToString<int>(it->id));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001250 }
1251 } else if (media_type == cricket::MEDIA_TYPE_DATA) {
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +00001252 const DataContentDescription* data_desc =
1253 static_cast<const DataContentDescription*>(media_desc);
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00001254 if (IsDtlsSctp(media_desc->protocol())) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001255 fmt.append(" ");
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +00001256
1257 for (std::vector<cricket::DataCodec>::const_iterator it =
1258 data_desc->codecs().begin();
1259 it != data_desc->codecs().end(); ++it) {
1260 if (it->id == cricket::kGoogleSctpDataCodecId &&
1261 it->GetParam(cricket::kCodecParamPort, &sctp_port)) {
1262 break;
1263 }
1264 }
1265
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001266 fmt.append(rtc::ToString<int>(sctp_port));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001267 } else {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001268 for (std::vector<cricket::DataCodec>::const_iterator it =
1269 data_desc->codecs().begin();
1270 it != data_desc->codecs().end(); ++it) {
1271 fmt.append(" ");
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001272 fmt.append(rtc::ToString<int>(it->id));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001273 }
1274 }
1275 }
1276 // The fmt must never be empty. If no codecs are found, set the fmt attribute
1277 // to 0.
1278 if (fmt.empty()) {
1279 fmt = " 0";
1280 }
1281
1282 // The port number in the m line will be updated later when associate with
1283 // the candidates.
1284 // RFC 3264
1285 // To reject an offered stream, the port number in the corresponding stream in
1286 // the answer MUST be set to zero.
jbauch083b73f2015-07-16 02:46:32 -07001287 const std::string& port = content_info->rejected ?
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +00001288 kMediaPortRejected : kDummyPort;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001289
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001290 rtc::SSLFingerprint* fp = (transport_info) ?
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001291 transport_info->description.identity_fingerprint.get() : NULL;
1292
wu@webrtc.org4c3e9912014-07-16 21:03:13 +00001293 // Add the m and c lines.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001294 InitLine(kLineTypeMedia, type, &os);
1295 os << " " << port << " " << media_desc->protocol() << fmt;
wu@webrtc.org4c3e9912014-07-16 21:03:13 +00001296 std::string mline = os.str();
1297 UpdateMediaDefaultDestination(candidates, mline, message);
1298
1299 // RFC 4566
1300 // b=AS:<bandwidth>
Peter Thatcherc0c3a862015-06-24 15:31:25 -07001301 if (media_desc->bandwidth() >= 1000) {
wu@webrtc.org4c3e9912014-07-16 21:03:13 +00001302 InitLine(kLineTypeSessionBandwidth, kApplicationSpecificMaximum, &os);
1303 os << kSdpDelimiterColon << (media_desc->bandwidth() / 1000);
1304 AddLine(os.str(), message);
1305 }
1306
1307 // Add the a=rtcp line.
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00001308 if (IsRtp(media_desc->protocol())) {
wu@webrtc.org4c3e9912014-07-16 21:03:13 +00001309 std::string rtcp_line = GetRtcpLine(candidates);
1310 if (!rtcp_line.empty()) {
1311 AddLine(rtcp_line, message);
1312 }
1313 }
1314
honghaiza54a0802015-12-16 18:37:23 -08001315 // Build the a=candidate lines. We don't include ufrag and pwd in the
1316 // candidates in the SDP to avoid redundancy.
1317 BuildCandidate(candidates, false, message);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001318
1319 // Use the transport_info to build the media level ice-ufrag and ice-pwd.
1320 if (transport_info) {
1321 // RFC 5245
1322 // ice-pwd-att = "ice-pwd" ":" password
1323 // ice-ufrag-att = "ice-ufrag" ":" ufrag
1324 // ice-ufrag
deadbeef3f7219b2015-12-28 15:17:14 -08001325 if (!transport_info->description.ice_ufrag.empty()) {
1326 InitAttrLine(kAttributeIceUfrag, &os);
1327 os << kSdpDelimiterColon << transport_info->description.ice_ufrag;
1328 AddLine(os.str(), message);
1329 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001330 // ice-pwd
deadbeef3f7219b2015-12-28 15:17:14 -08001331 if (!transport_info->description.ice_pwd.empty()) {
1332 InitAttrLine(kAttributeIcePwd, &os);
1333 os << kSdpDelimiterColon << transport_info->description.ice_pwd;
1334 AddLine(os.str(), message);
1335 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001336
1337 // draft-petithuguenin-mmusic-ice-attributes-level-03
1338 BuildIceOptions(transport_info->description.transport_options, message);
1339
1340 // RFC 4572
1341 // fingerprint-attribute =
1342 // "fingerprint" ":" hash-func SP fingerprint
1343 if (fp) {
1344 // Insert the fingerprint attribute.
1345 InitAttrLine(kAttributeFingerprint, &os);
1346 os << kSdpDelimiterColon
1347 << fp->algorithm << kSdpDelimiterSpace
1348 << fp->GetRfc4572Fingerprint();
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001349 AddLine(os.str(), message);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001350
1351 // Inserting setup attribute.
1352 if (transport_info->description.connection_role !=
1353 cricket::CONNECTIONROLE_NONE) {
1354 // Making sure we are not using "passive" mode.
1355 cricket::ConnectionRole role =
1356 transport_info->description.connection_role;
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +00001357 std::string dtls_role_str;
1358 VERIFY(cricket::ConnectionRoleToString(role, &dtls_role_str));
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001359 InitAttrLine(kAttributeSetup, &os);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001360 os << kSdpDelimiterColon << dtls_role_str;
1361 AddLine(os.str(), message);
1362 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001363 }
1364 }
1365
1366 // RFC 3388
1367 // mid-attribute = "a=mid:" identification-tag
1368 // identification-tag = token
1369 // Use the content name as the mid identification-tag.
1370 InitAttrLine(kAttributeMid, &os);
1371 os << kSdpDelimiterColon << content_info->name;
1372 AddLine(os.str(), message);
1373
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00001374 if (IsDtlsSctp(media_desc->protocol())) {
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +00001375 BuildSctpContentAttributes(message, sctp_port);
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00001376 } else if (IsRtp(media_desc->protocol())) {
deadbeef9d3584c2016-02-16 17:54:10 -08001377 BuildRtpContentAttributes(media_desc, media_type, unified_plan_sdp,
1378 message);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001379 }
1380}
1381
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +00001382void BuildSctpContentAttributes(std::string* message, int sctp_port) {
wu@webrtc.org78187522013-10-07 23:32:02 +00001383 // draft-ietf-mmusic-sctp-sdp-04
1384 // a=sctpmap:sctpmap-number protocol [streams]
lally69f57602015-10-08 10:15:04 -07001385 // TODO(lally): switch this over to mmusic-sctp-sdp-12 (or later), with
1386 // 'a=sctp-port:'
wu@webrtc.org78187522013-10-07 23:32:02 +00001387 std::ostringstream os;
1388 InitAttrLine(kAttributeSctpmap, &os);
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +00001389 os << kSdpDelimiterColon << sctp_port << kSdpDelimiterSpace
wu@webrtc.org78187522013-10-07 23:32:02 +00001390 << kDefaultSctpmapProtocol << kSdpDelimiterSpace
1391 << (cricket::kMaxSctpSid + 1);
1392 AddLine(os.str(), message);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001393}
1394
deadbeef9d3584c2016-02-16 17:54:10 -08001395// If unified_plan_sdp is true, will use "a=msid".
1396void BuildRtpContentAttributes(const MediaContentDescription* media_desc,
1397 const MediaType media_type,
1398 bool unified_plan_sdp,
1399 std::string* message) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001400 std::ostringstream os;
1401 // RFC 5285
1402 // a=extmap:<value>["/"<direction>] <URI> <extensionattributes>
1403 // The definitions MUST be either all session level or all media level. This
1404 // implementation uses all media level.
1405 for (size_t i = 0; i < media_desc->rtp_header_extensions().size(); ++i) {
1406 InitAttrLine(kAttributeExtmap, &os);
1407 os << kSdpDelimiterColon << media_desc->rtp_header_extensions()[i].id
1408 << kSdpDelimiterSpace << media_desc->rtp_header_extensions()[i].uri;
1409 AddLine(os.str(), message);
1410 }
1411
1412 // RFC 3264
1413 // a=sendrecv || a=sendonly || a=sendrecv || a=inactive
deadbeefc80741f2015-10-22 13:14:45 -07001414 switch (media_desc->direction()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001415 case cricket::MD_INACTIVE:
1416 InitAttrLine(kAttributeInactive, &os);
1417 break;
1418 case cricket::MD_SENDONLY:
1419 InitAttrLine(kAttributeSendOnly, &os);
1420 break;
1421 case cricket::MD_RECVONLY:
1422 InitAttrLine(kAttributeRecvOnly, &os);
1423 break;
1424 case cricket::MD_SENDRECV:
1425 default:
1426 InitAttrLine(kAttributeSendRecv, &os);
1427 break;
1428 }
1429 AddLine(os.str(), message);
1430
deadbeef9d3584c2016-02-16 17:54:10 -08001431 // draft-ietf-mmusic-msid-11
1432 // a=msid:<stream id> <track id>
1433 if (unified_plan_sdp && !media_desc->streams().empty()) {
1434 if (media_desc->streams().size() > 1u) {
1435 LOG(LS_WARNING) << "Trying to serialize unified plan SDP with more than "
1436 << "one track in a media section. Omitting 'a=msid'.";
1437 } else {
1438 auto track = media_desc->streams().begin();
1439 const std::string& stream_id = track->sync_label;
deadbeef9d3584c2016-02-16 17:54:10 -08001440 InitAttrLine(kAttributeMsid, &os);
1441 os << kSdpDelimiterColon << stream_id << kSdpDelimiterSpace << track->id;
1442 AddLine(os.str(), message);
1443 }
1444 }
1445
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001446 // RFC 5761
1447 // a=rtcp-mux
1448 if (media_desc->rtcp_mux()) {
1449 InitAttrLine(kAttributeRtcpMux, &os);
1450 AddLine(os.str(), message);
1451 }
1452
deadbeef13871492015-12-09 12:37:51 -08001453 // RFC 5506
1454 // a=rtcp-rsize
1455 if (media_desc->rtcp_reduced_size()) {
1456 InitAttrLine(kAttributeRtcpReducedSize, &os);
1457 AddLine(os.str(), message);
1458 }
1459
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001460 // RFC 4568
1461 // a=crypto:<tag> <crypto-suite> <key-params> [<session-params>]
1462 for (std::vector<CryptoParams>::const_iterator it =
1463 media_desc->cryptos().begin();
1464 it != media_desc->cryptos().end(); ++it) {
1465 InitAttrLine(kAttributeCrypto, &os);
1466 os << kSdpDelimiterColon << it->tag << " " << it->cipher_suite << " "
1467 << it->key_params;
1468 if (!it->session_params.empty()) {
1469 os << " " << it->session_params;
1470 }
1471 AddLine(os.str(), message);
1472 }
1473
1474 // RFC 4566
1475 // a=rtpmap:<payload type> <encoding name>/<clock rate>
1476 // [/<encodingparameters>]
1477 BuildRtpMap(media_desc, media_type, message);
1478
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001479 for (StreamParamsVec::const_iterator track = media_desc->streams().begin();
1480 track != media_desc->streams().end(); ++track) {
1481 // Require that the track belongs to a media stream,
1482 // ie the sync_label is set. This extra check is necessary since the
1483 // MediaContentDescription always contains a streamparam with an ssrc even
1484 // if no track or media stream have been created.
1485 if (track->sync_label.empty()) continue;
1486
1487 // Build the ssrc-group lines.
1488 for (size_t i = 0; i < track->ssrc_groups.size(); ++i) {
1489 // RFC 5576
1490 // a=ssrc-group:<semantics> <ssrc-id> ...
1491 if (track->ssrc_groups[i].ssrcs.empty()) {
1492 continue;
1493 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001494 InitAttrLine(kAttributeSsrcGroup, &os);
1495 os << kSdpDelimiterColon << track->ssrc_groups[i].semantics;
Peter Boström0c4e06b2015-10-07 12:23:21 +02001496 std::vector<uint32_t>::const_iterator ssrc =
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001497 track->ssrc_groups[i].ssrcs.begin();
1498 for (; ssrc != track->ssrc_groups[i].ssrcs.end(); ++ssrc) {
Peter Boström0c4e06b2015-10-07 12:23:21 +02001499 os << kSdpDelimiterSpace << rtc::ToString<uint32_t>(*ssrc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001500 }
1501 AddLine(os.str(), message);
1502 }
1503 // Build the ssrc lines for each ssrc.
1504 for (size_t i = 0; i < track->ssrcs.size(); ++i) {
Peter Boström0c4e06b2015-10-07 12:23:21 +02001505 uint32_t ssrc = track->ssrcs[i];
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001506 // RFC 5576
1507 // a=ssrc:<ssrc-id> cname:<value>
1508 AddSsrcLine(ssrc, kSsrcAttributeCname,
1509 track->cname, message);
1510
1511 // draft-alvestrand-mmusic-msid-00
1512 // a=ssrc:<ssrc-id> msid:identifier [appdata]
deadbeef9d3584c2016-02-16 17:54:10 -08001513 // The appdata consists of the "id" attribute of a MediaStreamTrack,
1514 // which corresponds to the "id" attribute of StreamParams.
1515 const std::string& stream_id = track->sync_label;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001516 InitAttrLine(kAttributeSsrc, &os);
1517 os << kSdpDelimiterColon << ssrc << kSdpDelimiterSpace
deadbeef9d3584c2016-02-16 17:54:10 -08001518 << kSsrcAttributeMsid << kSdpDelimiterColon << stream_id
1519 << kSdpDelimiterSpace << track->id;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001520 AddLine(os.str(), message);
1521
deadbeef9d3584c2016-02-16 17:54:10 -08001522 // TODO(ronghuawu): Remove below code which is for backward
1523 // compatibility.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001524 // draft-alvestrand-rtcweb-mid-01
1525 // a=ssrc:<ssrc-id> mslabel:<value>
1526 // The label isn't yet defined.
1527 // a=ssrc:<ssrc-id> label:<value>
1528 AddSsrcLine(ssrc, kSsrcAttributeMslabel, track->sync_label, message);
1529 AddSsrcLine(ssrc, kSSrcAttributeLabel, track->id, message);
1530 }
1531 }
1532}
1533
1534void WriteFmtpHeader(int payload_type, std::ostringstream* os) {
1535 // fmtp header: a=fmtp:|payload_type| <parameters>
1536 // Add a=fmtp
1537 InitAttrLine(kAttributeFmtp, os);
1538 // Add :|payload_type|
1539 *os << kSdpDelimiterColon << payload_type;
1540}
1541
1542void WriteRtcpFbHeader(int payload_type, std::ostringstream* os) {
1543 // rtcp-fb header: a=rtcp-fb:|payload_type|
1544 // <parameters>/<ccm <ccm_parameters>>
1545 // Add a=rtcp-fb
1546 InitAttrLine(kAttributeRtcpFb, os);
1547 // Add :
1548 *os << kSdpDelimiterColon;
1549 if (payload_type == kWildcardPayloadType) {
1550 *os << "*";
1551 } else {
1552 *os << payload_type;
1553 }
1554}
1555
1556void WriteFmtpParameter(const std::string& parameter_name,
1557 const std::string& parameter_value,
1558 std::ostringstream* os) {
1559 // fmtp parameters: |parameter_name|=|parameter_value|
1560 *os << parameter_name << kSdpDelimiterEqual << parameter_value;
1561}
1562
1563void WriteFmtpParameters(const cricket::CodecParameterMap& parameters,
1564 std::ostringstream* os) {
1565 for (cricket::CodecParameterMap::const_iterator fmtp = parameters.begin();
1566 fmtp != parameters.end(); ++fmtp) {
1567 // Each new parameter, except the first one starts with ";" and " ".
1568 if (fmtp != parameters.begin()) {
1569 *os << kSdpDelimiterSemicolon;
1570 }
1571 *os << kSdpDelimiterSpace;
1572 WriteFmtpParameter(fmtp->first, fmtp->second, os);
1573 }
1574}
1575
1576bool IsFmtpParam(const std::string& name) {
1577 const char* kFmtpParams[] = {
htaa6b99442016-04-12 10:29:17 -07001578 // TODO(hta): Split FMTP parameters apart from parameters in general.
1579 // FMTP parameters are codec specific, not generic.
1580 kCodecParamMinPTime,
1581 kCodecParamSPropStereo,
1582 kCodecParamStereo,
1583 kCodecParamUseInbandFec,
1584 kCodecParamUseDtx,
1585 kCodecParamStartBitrate,
1586 kCodecParamMaxBitrate,
1587 kCodecParamMinBitrate,
1588 kCodecParamMaxQuantization,
1589 kCodecParamSctpProtocol,
1590 kCodecParamSctpStreams,
1591 kCodecParamMaxAverageBitrate,
1592 kCodecParamMaxPlaybackRate,
1593 kCodecParamAssociatedPayloadType,
1594 cricket::kH264FmtpPacketizationMode,
1595 cricket::kH264FmtpLevelAsymmetryAllowed,
1596 cricket::kH264FmtpProfileLevelId};
tfarina5237aaf2015-11-10 23:44:30 -08001597 for (size_t i = 0; i < arraysize(kFmtpParams); ++i) {
htaa6b99442016-04-12 10:29:17 -07001598 if (name.compare(kFmtpParams[i]) == 0) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001599 return true;
1600 }
1601 }
1602 return false;
1603}
1604
1605// Retreives fmtp parameters from |params|, which may contain other parameters
1606// as well, and puts them in |fmtp_parameters|.
1607void GetFmtpParams(const cricket::CodecParameterMap& params,
1608 cricket::CodecParameterMap* fmtp_parameters) {
1609 for (cricket::CodecParameterMap::const_iterator iter = params.begin();
1610 iter != params.end(); ++iter) {
1611 if (IsFmtpParam(iter->first)) {
1612 (*fmtp_parameters)[iter->first] = iter->second;
1613 }
1614 }
1615}
1616
1617template <class T>
1618void AddFmtpLine(const T& codec, std::string* message) {
1619 cricket::CodecParameterMap fmtp_parameters;
1620 GetFmtpParams(codec.params, &fmtp_parameters);
1621 if (fmtp_parameters.empty()) {
1622 // No need to add an fmtp if it will have no (optional) parameters.
1623 return;
1624 }
1625 std::ostringstream os;
1626 WriteFmtpHeader(codec.id, &os);
1627 WriteFmtpParameters(fmtp_parameters, &os);
1628 AddLine(os.str(), message);
1629 return;
1630}
1631
1632template <class T>
1633void AddRtcpFbLines(const T& codec, std::string* message) {
1634 for (std::vector<cricket::FeedbackParam>::const_iterator iter =
1635 codec.feedback_params.params().begin();
1636 iter != codec.feedback_params.params().end(); ++iter) {
1637 std::ostringstream os;
1638 WriteRtcpFbHeader(codec.id, &os);
1639 os << " " << iter->id();
1640 if (!iter->param().empty()) {
1641 os << " " << iter->param();
1642 }
1643 AddLine(os.str(), message);
1644 }
1645}
1646
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00001647bool AddSctpDataCodec(DataContentDescription* media_desc,
1648 int sctp_port) {
1649 if (media_desc->HasCodec(cricket::kGoogleSctpDataCodecId)) {
1650 return ParseFailed("",
1651 "Can't have multiple sctp port attributes.",
1652 NULL);
1653 }
1654 // Add the SCTP Port number as a pseudo-codec "port" parameter
deadbeef67cf2c12016-04-13 10:07:16 -07001655 cricket::DataCodec codec_port(cricket::kGoogleSctpDataCodecId,
1656 cricket::kGoogleSctpDataCodecName);
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00001657 codec_port.SetParam(cricket::kCodecParamPort, sctp_port);
1658 LOG(INFO) << "AddSctpDataCodec: Got SCTP Port Number "
1659 << sctp_port;
1660 media_desc->AddCodec(codec_port);
1661 return true;
1662}
1663
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001664bool GetMinValue(const std::vector<int>& values, int* value) {
1665 if (values.empty()) {
1666 return false;
1667 }
1668 std::vector<int>::const_iterator found =
1669 std::min_element(values.begin(), values.end());
1670 *value = *found;
1671 return true;
1672}
1673
1674bool GetParameter(const std::string& name,
1675 const cricket::CodecParameterMap& params, int* value) {
1676 std::map<std::string, std::string>::const_iterator found =
1677 params.find(name);
1678 if (found == params.end()) {
1679 return false;
1680 }
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001681 if (!rtc::FromString(found->second, value)) {
wu@webrtc.org5e760e72014-04-02 23:19:09 +00001682 return false;
1683 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001684 return true;
1685}
1686
1687void BuildRtpMap(const MediaContentDescription* media_desc,
1688 const MediaType media_type,
1689 std::string* message) {
1690 ASSERT(message != NULL);
1691 ASSERT(media_desc != NULL);
1692 std::ostringstream os;
1693 if (media_type == cricket::MEDIA_TYPE_VIDEO) {
1694 const VideoContentDescription* video_desc =
1695 static_cast<const VideoContentDescription*>(media_desc);
1696 for (std::vector<cricket::VideoCodec>::const_iterator it =
1697 video_desc->codecs().begin();
1698 it != video_desc->codecs().end(); ++it) {
1699 // RFC 4566
1700 // a=rtpmap:<payload type> <encoding name>/<clock rate>
1701 // [/<encodingparameters>]
1702 if (it->id != kWildcardPayloadType) {
1703 InitAttrLine(kAttributeRtpmap, &os);
1704 os << kSdpDelimiterColon << it->id << " " << it->name
1705 << "/" << kDefaultVideoClockrate;
1706 AddLine(os.str(), message);
1707 }
1708 AddRtcpFbLines(*it, message);
1709 AddFmtpLine(*it, message);
1710 }
1711 } else if (media_type == cricket::MEDIA_TYPE_AUDIO) {
1712 const AudioContentDescription* audio_desc =
1713 static_cast<const AudioContentDescription*>(media_desc);
1714 std::vector<int> ptimes;
1715 std::vector<int> maxptimes;
1716 int max_minptime = 0;
1717 for (std::vector<cricket::AudioCodec>::const_iterator it =
1718 audio_desc->codecs().begin();
1719 it != audio_desc->codecs().end(); ++it) {
1720 ASSERT(!it->name.empty());
1721 // RFC 4566
1722 // a=rtpmap:<payload type> <encoding name>/<clock rate>
1723 // [/<encodingparameters>]
1724 InitAttrLine(kAttributeRtpmap, &os);
1725 os << kSdpDelimiterColon << it->id << " ";
1726 os << it->name << "/" << it->clockrate;
1727 if (it->channels != 1) {
1728 os << "/" << it->channels;
1729 }
1730 AddLine(os.str(), message);
1731 AddRtcpFbLines(*it, message);
1732 AddFmtpLine(*it, message);
1733 int minptime = 0;
1734 if (GetParameter(kCodecParamMinPTime, it->params, &minptime)) {
1735 max_minptime = std::max(minptime, max_minptime);
1736 }
1737 int ptime;
1738 if (GetParameter(kCodecParamPTime, it->params, &ptime)) {
1739 ptimes.push_back(ptime);
1740 }
1741 int maxptime;
1742 if (GetParameter(kCodecParamMaxPTime, it->params, &maxptime)) {
1743 maxptimes.push_back(maxptime);
1744 }
1745 }
1746 // Populate the maxptime attribute with the smallest maxptime of all codecs
1747 // under the same m-line.
1748 int min_maxptime = INT_MAX;
1749 if (GetMinValue(maxptimes, &min_maxptime)) {
1750 AddAttributeLine(kCodecParamMaxPTime, min_maxptime, message);
1751 }
1752 ASSERT(min_maxptime > max_minptime);
1753 // Populate the ptime attribute with the smallest ptime or the largest
1754 // minptime, whichever is the largest, for all codecs under the same m-line.
1755 int ptime = INT_MAX;
1756 if (GetMinValue(ptimes, &ptime)) {
1757 ptime = std::min(ptime, min_maxptime);
1758 ptime = std::max(ptime, max_minptime);
1759 AddAttributeLine(kCodecParamPTime, ptime, message);
1760 }
1761 } else if (media_type == cricket::MEDIA_TYPE_DATA) {
1762 const DataContentDescription* data_desc =
1763 static_cast<const DataContentDescription*>(media_desc);
1764 for (std::vector<cricket::DataCodec>::const_iterator it =
1765 data_desc->codecs().begin();
1766 it != data_desc->codecs().end(); ++it) {
1767 // RFC 4566
1768 // a=rtpmap:<payload type> <encoding name>/<clock rate>
1769 // [/<encodingparameters>]
1770 InitAttrLine(kAttributeRtpmap, &os);
1771 os << kSdpDelimiterColon << it->id << " "
1772 << it->name << "/" << it->clockrate;
1773 AddLine(os.str(), message);
1774 }
1775 }
1776}
1777
1778void BuildCandidate(const std::vector<Candidate>& candidates,
honghaiza54a0802015-12-16 18:37:23 -08001779 bool include_ufrag,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001780 std::string* message) {
1781 std::ostringstream os;
1782
1783 for (std::vector<Candidate>::const_iterator it = candidates.begin();
1784 it != candidates.end(); ++it) {
1785 // RFC 5245
1786 // a=candidate:<foundation> <component-id> <transport> <priority>
1787 // <connection-address> <port> typ <candidate-types>
1788 // [raddr <connection-address>] [rport <port>]
1789 // *(SP extension-att-name SP extension-att-value)
1790 std::string type;
1791 // Map the cricket candidate type to "host" / "srflx" / "prflx" / "relay"
1792 if (it->type() == cricket::LOCAL_PORT_TYPE) {
1793 type = kCandidateHost;
1794 } else if (it->type() == cricket::STUN_PORT_TYPE) {
1795 type = kCandidateSrflx;
1796 } else if (it->type() == cricket::RELAY_PORT_TYPE) {
1797 type = kCandidateRelay;
Honghai Zhang7fb69db2016-03-14 11:59:18 -07001798 } else if (it->type() == cricket::PRFLX_PORT_TYPE) {
1799 type = kCandidatePrflx;
1800 // Peer reflexive candidate may be signaled for being removed.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001801 } else {
1802 ASSERT(false);
Peter Thatcher019087f2015-04-28 09:06:26 -07001803 // Never write out candidates if we don't know the type.
1804 continue;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001805 }
1806
1807 InitAttrLine(kAttributeCandidate, &os);
1808 os << kSdpDelimiterColon
mallinath@webrtc.org2d60c5e2014-08-08 22:29:20 +00001809 << it->foundation() << " "
1810 << it->component() << " "
1811 << it->protocol() << " "
1812 << it->priority() << " "
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001813 << it->address().ipaddr().ToString() << " "
1814 << it->address().PortAsString() << " "
mallinath@webrtc.org2d60c5e2014-08-08 22:29:20 +00001815 << kAttributeCandidateTyp << " "
1816 << type << " ";
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001817
1818 // Related address
1819 if (!it->related_address().IsNil()) {
1820 os << kAttributeCandidateRaddr << " "
1821 << it->related_address().ipaddr().ToString() << " "
1822 << kAttributeCandidateRport << " "
1823 << it->related_address().PortAsString() << " ";
1824 }
1825
mallinath@webrtc.org2d60c5e2014-08-08 22:29:20 +00001826 if (it->protocol() == cricket::TCP_PROTOCOL_NAME) {
mallinath@webrtc.orge999bd02014-08-13 06:05:55 +00001827 os << kTcpCandidateType << " " << it->tcptype() << " ";
mallinath@webrtc.org2d60c5e2014-08-08 22:29:20 +00001828 }
1829
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001830 // Extensions
1831 os << kAttributeCandidateGeneration << " " << it->generation();
honghaiza54a0802015-12-16 18:37:23 -08001832 if (include_ufrag && !it->username().empty()) {
1833 os << " " << kAttributeCandidateUfrag << " " << it->username();
1834 }
honghaiza0c44ea2016-03-23 16:07:48 -07001835 if (it->network_id() > 0) {
1836 os << " " << kAttributeCandidateNetworkId << " " << it->network_id();
1837 }
honghaize1a0c942016-02-16 14:54:56 -08001838 if (it->network_cost() > 0) {
1839 os << " " << kAttributeCandidateNetworkCost << " " << it->network_cost();
1840 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001841
1842 AddLine(os.str(), message);
1843 }
1844}
1845
1846void BuildIceOptions(const std::vector<std::string>& transport_options,
1847 std::string* message) {
1848 if (!transport_options.empty()) {
1849 std::ostringstream os;
1850 InitAttrLine(kAttributeIceOption, &os);
1851 os << kSdpDelimiterColon << transport_options[0];
1852 for (size_t i = 1; i < transport_options.size(); ++i) {
1853 os << kSdpDelimiterSpace << transport_options[i];
1854 }
1855 AddLine(os.str(), message);
1856 }
1857}
1858
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00001859bool IsRtp(const std::string& protocol) {
1860 return protocol.empty() ||
1861 (protocol.find(cricket::kMediaProtocolRtpPrefix) != std::string::npos);
1862}
1863
1864bool IsDtlsSctp(const std::string& protocol) {
1865 // This intentionally excludes "SCTP" and "SCTP/DTLS".
lally@webrtc.orga7470932015-02-24 20:19:21 +00001866 return protocol.find(cricket::kMediaProtocolDtlsSctp) != std::string::npos;
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00001867}
1868
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001869bool ParseSessionDescription(const std::string& message, size_t* pos,
1870 std::string* session_id,
1871 std::string* session_version,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001872 TransportDescription* session_td,
1873 RtpHeaderExtensions* session_extmaps,
1874 cricket::SessionDescription* desc,
1875 SdpParseError* error) {
1876 std::string line;
1877
deadbeefc80741f2015-10-22 13:14:45 -07001878 desc->set_msid_supported(false);
1879
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001880 // RFC 4566
1881 // v= (protocol version)
1882 if (!GetLineWithType(message, pos, &line, kLineTypeVersion)) {
1883 return ParseFailedExpectLine(message, *pos, kLineTypeVersion,
1884 std::string(), error);
1885 }
1886 // RFC 4566
1887 // o=<username> <sess-id> <sess-version> <nettype> <addrtype>
1888 // <unicast-address>
1889 if (!GetLineWithType(message, pos, &line, kLineTypeOrigin)) {
1890 return ParseFailedExpectLine(message, *pos, kLineTypeOrigin,
1891 std::string(), error);
1892 }
1893 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001894 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001895 kSdpDelimiterSpace, &fields);
1896 const size_t expected_fields = 6;
1897 if (fields.size() != expected_fields) {
1898 return ParseFailedExpectFieldNum(line, expected_fields, error);
1899 }
1900 *session_id = fields[1];
1901 *session_version = fields[2];
1902
1903 // RFC 4566
1904 // s= (session name)
1905 if (!GetLineWithType(message, pos, &line, kLineTypeSessionName)) {
1906 return ParseFailedExpectLine(message, *pos, kLineTypeSessionName,
1907 std::string(), error);
1908 }
1909
1910 // Optional lines
1911 // Those are the optional lines, so shouldn't return false if not present.
1912 // RFC 4566
1913 // i=* (session information)
1914 GetLineWithType(message, pos, &line, kLineTypeSessionInfo);
1915
1916 // RFC 4566
1917 // u=* (URI of description)
1918 GetLineWithType(message, pos, &line, kLineTypeSessionUri);
1919
1920 // RFC 4566
1921 // e=* (email address)
1922 GetLineWithType(message, pos, &line, kLineTypeSessionEmail);
1923
1924 // RFC 4566
1925 // p=* (phone number)
1926 GetLineWithType(message, pos, &line, kLineTypeSessionPhone);
1927
1928 // RFC 4566
1929 // c=* (connection information -- not required if included in
1930 // all media)
1931 GetLineWithType(message, pos, &line, kLineTypeConnection);
1932
1933 // RFC 4566
1934 // b=* (zero or more bandwidth information lines)
1935 while (GetLineWithType(message, pos, &line, kLineTypeSessionBandwidth)) {
1936 // By pass zero or more b lines.
1937 }
1938
1939 // RFC 4566
1940 // One or more time descriptions ("t=" and "r=" lines; see below)
1941 // t= (time the session is active)
1942 // r=* (zero or more repeat times)
1943 // Ensure there's at least one time description
1944 if (!GetLineWithType(message, pos, &line, kLineTypeTiming)) {
1945 return ParseFailedExpectLine(message, *pos, kLineTypeTiming, std::string(),
1946 error);
1947 }
1948
1949 while (GetLineWithType(message, pos, &line, kLineTypeRepeatTimes)) {
1950 // By pass zero or more r lines.
1951 }
1952
1953 // Go through the rest of the time descriptions
1954 while (GetLineWithType(message, pos, &line, kLineTypeTiming)) {
1955 while (GetLineWithType(message, pos, &line, kLineTypeRepeatTimes)) {
1956 // By pass zero or more r lines.
1957 }
1958 }
1959
1960 // RFC 4566
1961 // z=* (time zone adjustments)
1962 GetLineWithType(message, pos, &line, kLineTypeTimeZone);
1963
1964 // RFC 4566
1965 // k=* (encryption key)
1966 GetLineWithType(message, pos, &line, kLineTypeEncryptionKey);
1967
1968 // RFC 4566
1969 // a=* (zero or more session attribute lines)
1970 while (GetLineWithType(message, pos, &line, kLineTypeAttributes)) {
1971 if (HasAttribute(line, kAttributeGroup)) {
1972 if (!ParseGroupAttribute(line, desc, error)) {
1973 return false;
1974 }
1975 } else if (HasAttribute(line, kAttributeIceUfrag)) {
1976 if (!GetValue(line, kAttributeIceUfrag,
1977 &(session_td->ice_ufrag), error)) {
1978 return false;
1979 }
1980 } else if (HasAttribute(line, kAttributeIcePwd)) {
1981 if (!GetValue(line, kAttributeIcePwd, &(session_td->ice_pwd), error)) {
1982 return false;
1983 }
1984 } else if (HasAttribute(line, kAttributeIceLite)) {
1985 session_td->ice_mode = cricket::ICEMODE_LITE;
1986 } else if (HasAttribute(line, kAttributeIceOption)) {
1987 if (!ParseIceOptions(line, &(session_td->transport_options), error)) {
1988 return false;
1989 }
1990 } else if (HasAttribute(line, kAttributeFingerprint)) {
1991 if (session_td->identity_fingerprint.get()) {
1992 return ParseFailed(
1993 line,
1994 "Can't have multiple fingerprint attributes at the same level.",
1995 error);
1996 }
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001997 rtc::SSLFingerprint* fingerprint = NULL;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001998 if (!ParseFingerprintAttribute(line, &fingerprint, error)) {
1999 return false;
2000 }
2001 session_td->identity_fingerprint.reset(fingerprint);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00002002 } else if (HasAttribute(line, kAttributeSetup)) {
2003 if (!ParseDtlsSetup(line, &(session_td->connection_role), error)) {
2004 return false;
2005 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002006 } else if (HasAttribute(line, kAttributeMsidSemantics)) {
2007 std::string semantics;
2008 if (!GetValue(line, kAttributeMsidSemantics, &semantics, error)) {
2009 return false;
2010 }
deadbeefc80741f2015-10-22 13:14:45 -07002011 desc->set_msid_supported(
2012 CaseInsensitiveFind(semantics, kMediaStreamSemantic));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002013 } else if (HasAttribute(line, kAttributeExtmap)) {
2014 RtpHeaderExtension extmap;
2015 if (!ParseExtmap(line, &extmap, error)) {
2016 return false;
2017 }
2018 session_extmaps->push_back(extmap);
2019 }
2020 }
2021
2022 return true;
2023}
2024
2025bool ParseGroupAttribute(const std::string& line,
2026 cricket::SessionDescription* desc,
2027 SdpParseError* error) {
2028 ASSERT(desc != NULL);
2029
2030 // RFC 5888 and draft-holmberg-mmusic-sdp-bundle-negotiation-00
2031 // a=group:BUNDLE video voice
2032 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002033 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002034 kSdpDelimiterSpace, &fields);
2035 std::string semantics;
2036 if (!GetValue(fields[0], kAttributeGroup, &semantics, error)) {
2037 return false;
2038 }
2039 cricket::ContentGroup group(semantics);
2040 for (size_t i = 1; i < fields.size(); ++i) {
2041 group.AddContentName(fields[i]);
2042 }
2043 desc->AddGroup(group);
2044 return true;
2045}
2046
2047static bool ParseFingerprintAttribute(const std::string& line,
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002048 rtc::SSLFingerprint** fingerprint,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002049 SdpParseError* error) {
2050 if (!IsLineType(line, kLineTypeAttributes) ||
2051 !HasAttribute(line, kAttributeFingerprint)) {
2052 return ParseFailedExpectLine(line, 0, kLineTypeAttributes,
2053 kAttributeFingerprint, error);
2054 }
2055
2056 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002057 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002058 kSdpDelimiterSpace, &fields);
2059 const size_t expected_fields = 2;
2060 if (fields.size() != expected_fields) {
2061 return ParseFailedExpectFieldNum(line, expected_fields, error);
2062 }
2063
2064 // The first field here is "fingerprint:<hash>.
2065 std::string algorithm;
2066 if (!GetValue(fields[0], kAttributeFingerprint, &algorithm, error)) {
2067 return false;
2068 }
2069
2070 // Downcase the algorithm. Note that we don't need to downcase the
2071 // fingerprint because hex_decode can handle upper-case.
2072 std::transform(algorithm.begin(), algorithm.end(), algorithm.begin(),
2073 ::tolower);
2074
2075 // The second field is the digest value. De-hexify it.
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002076 *fingerprint = rtc::SSLFingerprint::CreateFromRfc4572(
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002077 algorithm, fields[1]);
2078 if (!*fingerprint) {
2079 return ParseFailed(line,
2080 "Failed to create fingerprint from the digest.",
2081 error);
2082 }
2083
2084 return true;
2085}
2086
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00002087static bool ParseDtlsSetup(const std::string& line,
2088 cricket::ConnectionRole* role,
2089 SdpParseError* error) {
2090 // setup-attr = "a=setup:" role
2091 // role = "active" / "passive" / "actpass" / "holdconn"
2092 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002093 rtc::split(line.substr(kLinePrefixLength), kSdpDelimiterColon, &fields);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00002094 const size_t expected_fields = 2;
2095 if (fields.size() != expected_fields) {
2096 return ParseFailedExpectFieldNum(line, expected_fields, error);
2097 }
2098 std::string role_str = fields[1];
2099 if (!cricket::StringToConnectionRole(role_str, role)) {
2100 return ParseFailed(line, "Invalid attribute value.", error);
2101 }
2102 return true;
2103}
2104
deadbeef9d3584c2016-02-16 17:54:10 -08002105static bool ParseMsidAttribute(const std::string& line,
2106 std::string* stream_id,
2107 std::string* track_id,
2108 SdpParseError* error) {
2109 // draft-ietf-mmusic-msid-11
2110 // a=msid:<stream id> <track id>
2111 // msid-value = msid-id [ SP msid-appdata ]
2112 // msid-id = 1*64token-char ; see RFC 4566
2113 // msid-appdata = 1*64token-char ; see RFC 4566
2114 std::string field1;
2115 if (!rtc::tokenize_first(line.substr(kLinePrefixLength), kSdpDelimiterSpace,
2116 &field1, track_id)) {
2117 const size_t expected_fields = 2;
2118 return ParseFailedExpectFieldNum(line, expected_fields, error);
2119 }
2120
2121 // msid:<msid-id>
2122 if (!GetValue(field1, kAttributeMsid, stream_id, error)) {
2123 return false;
2124 }
2125 return true;
2126}
2127
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002128// RFC 3551
2129// PT encoding media type clock rate channels
2130// name (Hz)
2131// 0 PCMU A 8,000 1
2132// 1 reserved A
2133// 2 reserved A
2134// 3 GSM A 8,000 1
2135// 4 G723 A 8,000 1
2136// 5 DVI4 A 8,000 1
2137// 6 DVI4 A 16,000 1
2138// 7 LPC A 8,000 1
2139// 8 PCMA A 8,000 1
2140// 9 G722 A 8,000 1
2141// 10 L16 A 44,100 2
2142// 11 L16 A 44,100 1
2143// 12 QCELP A 8,000 1
2144// 13 CN A 8,000 1
2145// 14 MPA A 90,000 (see text)
2146// 15 G728 A 8,000 1
2147// 16 DVI4 A 11,025 1
2148// 17 DVI4 A 22,050 1
2149// 18 G729 A 8,000 1
2150struct StaticPayloadAudioCodec {
2151 const char* name;
2152 int clockrate;
Peter Kasting69558702016-01-12 16:26:35 -08002153 size_t channels;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002154};
2155static const StaticPayloadAudioCodec kStaticPayloadAudioCodecs[] = {
2156 { "PCMU", 8000, 1 },
2157 { "reserved", 0, 0 },
2158 { "reserved", 0, 0 },
2159 { "GSM", 8000, 1 },
2160 { "G723", 8000, 1 },
2161 { "DVI4", 8000, 1 },
2162 { "DVI4", 16000, 1 },
2163 { "LPC", 8000, 1 },
2164 { "PCMA", 8000, 1 },
2165 { "G722", 8000, 1 },
2166 { "L16", 44100, 2 },
2167 { "L16", 44100, 1 },
2168 { "QCELP", 8000, 1 },
2169 { "CN", 8000, 1 },
2170 { "MPA", 90000, 1 },
2171 { "G728", 8000, 1 },
2172 { "DVI4", 11025, 1 },
2173 { "DVI4", 22050, 1 },
2174 { "G729", 8000, 1 },
2175};
2176
2177void MaybeCreateStaticPayloadAudioCodecs(
2178 const std::vector<int>& fmts, AudioContentDescription* media_desc) {
2179 if (!media_desc) {
2180 return;
2181 }
deadbeef67cf2c12016-04-13 10:07:16 -07002182 RTC_DCHECK(media_desc->codecs().empty());
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002183 std::vector<int>::const_iterator it = fmts.begin();
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002184 for (; it != fmts.end(); ++it) {
2185 int payload_type = *it;
2186 if (!media_desc->HasCodec(payload_type) &&
2187 payload_type >= 0 &&
tfarina5237aaf2015-11-10 23:44:30 -08002188 payload_type < arraysize(kStaticPayloadAudioCodecs)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002189 std::string encoding_name = kStaticPayloadAudioCodecs[payload_type].name;
2190 int clock_rate = kStaticPayloadAudioCodecs[payload_type].clockrate;
Peter Kasting69558702016-01-12 16:26:35 -08002191 size_t channels = kStaticPayloadAudioCodecs[payload_type].channels;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002192 media_desc->AddCodec(cricket::AudioCodec(payload_type, encoding_name,
deadbeef67cf2c12016-04-13 10:07:16 -07002193 clock_rate, 0, channels));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002194 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002195 }
2196}
2197
2198template <class C>
2199static C* ParseContentDescription(const std::string& message,
2200 const MediaType media_type,
2201 int mline_index,
2202 const std::string& protocol,
deadbeef67cf2c12016-04-13 10:07:16 -07002203 const std::vector<int>& payload_types,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002204 size_t* pos,
2205 std::string* content_name,
2206 TransportDescription* transport,
2207 std::vector<JsepIceCandidate*>* candidates,
2208 webrtc::SdpParseError* error) {
2209 C* media_desc = new C();
2210 switch (media_type) {
2211 case cricket::MEDIA_TYPE_AUDIO:
2212 *content_name = cricket::CN_AUDIO;
2213 break;
2214 case cricket::MEDIA_TYPE_VIDEO:
2215 *content_name = cricket::CN_VIDEO;
2216 break;
2217 case cricket::MEDIA_TYPE_DATA:
2218 *content_name = cricket::CN_DATA;
2219 break;
2220 default:
2221 ASSERT(false);
2222 break;
2223 }
deadbeef67cf2c12016-04-13 10:07:16 -07002224 if (!ParseContent(message, media_type, mline_index, protocol, payload_types,
2225 pos, content_name, media_desc, transport, candidates,
2226 error)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002227 delete media_desc;
2228 return NULL;
2229 }
2230 // Sort the codecs according to the m-line fmt list.
deadbeef67cf2c12016-04-13 10:07:16 -07002231 std::unordered_map<int, int> payload_type_preferences;
2232 // "size + 1" so that the lowest preference payload type has a preference of
2233 // 1, which is greater than the default (0) for payload types not in the fmt
2234 // list.
2235 int preference = static_cast<int>(payload_types.size() + 1);
2236 for (int pt : payload_types) {
2237 payload_type_preferences[pt] = preference--;
2238 }
2239 std::vector<typename C::CodecType> codecs = media_desc->codecs();
2240 std::sort(codecs.begin(), codecs.end(), [&payload_type_preferences](
2241 const typename C::CodecType& a,
2242 const typename C::CodecType& b) {
2243 return payload_type_preferences[a.id] > payload_type_preferences[b.id];
2244 });
2245 media_desc->set_codecs(codecs);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002246 return media_desc;
2247}
2248
2249bool ParseMediaDescription(const std::string& message,
2250 const TransportDescription& session_td,
2251 const RtpHeaderExtensions& session_extmaps,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002252 size_t* pos,
2253 cricket::SessionDescription* desc,
2254 std::vector<JsepIceCandidate*>* candidates,
2255 SdpParseError* error) {
2256 ASSERT(desc != NULL);
2257 std::string line;
2258 int mline_index = -1;
2259
2260 // Zero or more media descriptions
2261 // RFC 4566
2262 // m=<media> <port> <proto> <fmt>
2263 while (GetLineWithType(message, pos, &line, kLineTypeMedia)) {
2264 ++mline_index;
2265
2266 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002267 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002268 kSdpDelimiterSpace, &fields);
2269 const size_t expected_min_fields = 4;
2270 if (fields.size() < expected_min_fields) {
2271 return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
2272 }
2273 bool rejected = false;
2274 // RFC 3264
2275 // To reject an offered stream, the port number in the corresponding stream
2276 // in the answer MUST be set to zero.
2277 if (fields[1] == kMediaPortRejected) {
2278 rejected = true;
2279 }
2280
2281 std::string protocol = fields[2];
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002282
2283 // <fmt>
deadbeef67cf2c12016-04-13 10:07:16 -07002284 std::vector<int> payload_types;
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00002285 if (IsRtp(protocol)) {
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00002286 for (size_t j = 3 ; j < fields.size(); ++j) {
2287 // TODO(wu): Remove when below bug is fixed.
2288 // https://bugzilla.mozilla.org/show_bug.cgi?id=996329
pbosbb36fdf2015-07-09 07:48:14 -07002289 if (fields[j].empty() && j == fields.size() - 1) {
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00002290 continue;
2291 }
wu@webrtc.org36eda7c2014-04-15 20:37:30 +00002292
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00002293 int pl = 0;
pkasting@chromium.orge9facf82015-02-17 20:36:28 +00002294 if (!GetPayloadTypeFromString(line, fields[j], &pl, error)) {
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00002295 return false;
2296 }
deadbeef67cf2c12016-04-13 10:07:16 -07002297 payload_types.push_back(pl);
wu@webrtc.org5e760e72014-04-02 23:19:09 +00002298 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002299 }
2300
2301 // Make a temporary TransportDescription based on |session_td|.
2302 // Some of this gets overwritten by ParseContent.
deadbeef46eed762016-01-28 13:24:37 -08002303 TransportDescription transport(
2304 session_td.transport_options, session_td.ice_ufrag, session_td.ice_pwd,
2305 session_td.ice_mode, session_td.connection_role,
2306 session_td.identity_fingerprint.get());
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002307
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002308 rtc::scoped_ptr<MediaContentDescription> content;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002309 std::string content_name;
2310 if (HasAttribute(line, kMediaTypeVideo)) {
2311 content.reset(ParseContentDescription<VideoContentDescription>(
deadbeef67cf2c12016-04-13 10:07:16 -07002312 message, cricket::MEDIA_TYPE_VIDEO, mline_index, protocol,
2313 payload_types, pos, &content_name, &transport, candidates, error));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002314 } else if (HasAttribute(line, kMediaTypeAudio)) {
2315 content.reset(ParseContentDescription<AudioContentDescription>(
deadbeef67cf2c12016-04-13 10:07:16 -07002316 message, cricket::MEDIA_TYPE_AUDIO, 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, kMediaTypeData)) {
jiayl@webrtc.org0e527722014-09-08 21:43:43 +00002319 DataContentDescription* data_desc =
wu@webrtc.org78187522013-10-07 23:32:02 +00002320 ParseContentDescription<DataContentDescription>(
deadbeef67cf2c12016-04-13 10:07:16 -07002321 message, cricket::MEDIA_TYPE_DATA, mline_index, protocol,
2322 payload_types, pos, &content_name, &transport, candidates, error);
jiayl@webrtc.org0e527722014-09-08 21:43:43 +00002323 content.reset(data_desc);
wu@webrtc.org78187522013-10-07 23:32:02 +00002324
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00002325 int p;
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00002326 if (data_desc && IsDtlsSctp(protocol) && rtc::FromString(fields[3], &p)) {
jiayl@webrtc.org0e527722014-09-08 21:43:43 +00002327 if (!AddSctpDataCodec(data_desc, p))
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00002328 return false;
wu@webrtc.org78187522013-10-07 23:32:02 +00002329 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002330 } else {
2331 LOG(LS_WARNING) << "Unsupported media type: " << line;
2332 continue;
2333 }
2334 if (!content.get()) {
2335 // ParseContentDescription returns NULL if failed.
2336 return false;
2337 }
2338
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00002339 if (IsRtp(protocol)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002340 // Set the extmap.
2341 if (!session_extmaps.empty() &&
2342 !content->rtp_header_extensions().empty()) {
2343 return ParseFailed("",
2344 "The a=extmap MUST be either all session level or "
2345 "all media level.",
2346 error);
2347 }
2348 for (size_t i = 0; i < session_extmaps.size(); ++i) {
2349 content->AddRtpHeaderExtension(session_extmaps[i]);
2350 }
2351 }
2352 content->set_protocol(protocol);
2353 desc->AddContent(content_name,
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00002354 IsDtlsSctp(protocol) ? cricket::NS_JINGLE_DRAFT_SCTP :
2355 cricket::NS_JINGLE_RTP,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002356 rejected,
2357 content.release());
2358 // Create TransportInfo with the media level "ice-pwd" and "ice-ufrag".
2359 TransportInfo transport_info(content_name, transport);
2360
2361 if (!desc->AddTransportInfo(transport_info)) {
2362 std::ostringstream description;
2363 description << "Failed to AddTransportInfo with content name: "
2364 << content_name;
2365 return ParseFailed("", description.str(), error);
2366 }
2367 }
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00002368
2369 size_t end_of_message = message.size();
2370 if (mline_index == -1 && *pos != end_of_message) {
2371 ParseFailed(message, *pos, "Expects m line.", error);
2372 return false;
2373 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002374 return true;
2375}
2376
2377bool VerifyCodec(const cricket::Codec& codec) {
2378 // Codec has not been populated correctly unless the name has been set. This
2379 // can happen if an SDP has an fmtp or rtcp-fb with a payload type but doesn't
2380 // have a corresponding "rtpmap" line.
2381 cricket::Codec default_codec;
2382 return default_codec.name != codec.name;
2383}
2384
2385bool VerifyAudioCodecs(const AudioContentDescription* audio_desc) {
2386 const std::vector<cricket::AudioCodec>& codecs = audio_desc->codecs();
2387 for (std::vector<cricket::AudioCodec>::const_iterator iter = codecs.begin();
2388 iter != codecs.end(); ++iter) {
2389 if (!VerifyCodec(*iter)) {
2390 return false;
2391 }
2392 }
2393 return true;
2394}
2395
2396bool VerifyVideoCodecs(const VideoContentDescription* video_desc) {
2397 const std::vector<cricket::VideoCodec>& codecs = video_desc->codecs();
2398 for (std::vector<cricket::VideoCodec>::const_iterator iter = codecs.begin();
2399 iter != codecs.end(); ++iter) {
2400 if (!VerifyCodec(*iter)) {
2401 return false;
2402 }
2403 }
2404 return true;
2405}
2406
2407void AddParameters(const cricket::CodecParameterMap& parameters,
2408 cricket::Codec* codec) {
2409 for (cricket::CodecParameterMap::const_iterator iter =
2410 parameters.begin(); iter != parameters.end(); ++iter) {
2411 codec->SetParam(iter->first, iter->second);
2412 }
2413}
2414
2415void AddFeedbackParameter(const cricket::FeedbackParam& feedback_param,
2416 cricket::Codec* codec) {
2417 codec->AddFeedbackParam(feedback_param);
2418}
2419
2420void AddFeedbackParameters(const cricket::FeedbackParams& feedback_params,
2421 cricket::Codec* codec) {
2422 for (std::vector<cricket::FeedbackParam>::const_iterator iter =
2423 feedback_params.params().begin();
2424 iter != feedback_params.params().end(); ++iter) {
2425 codec->AddFeedbackParam(*iter);
2426 }
2427}
2428
2429// Gets the current codec setting associated with |payload_type|. If there
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +00002430// is no Codec associated with that payload type it returns an empty codec
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002431// with that payload type.
2432template <class T>
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +00002433T GetCodecWithPayloadType(const std::vector<T>& codecs, int payload_type) {
2434 T ret_val;
2435 if (!FindCodecById(codecs, payload_type, &ret_val)) {
2436 ret_val.id = payload_type;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002437 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002438 return ret_val;
2439}
2440
2441// Updates or creates a new codec entry in the audio description.
2442template <class T, class U>
2443void AddOrReplaceCodec(MediaContentDescription* content_desc, const U& codec) {
2444 T* desc = static_cast<T*>(content_desc);
2445 std::vector<U> codecs = desc->codecs();
2446 bool found = false;
2447
2448 typename std::vector<U>::iterator iter;
2449 for (iter = codecs.begin(); iter != codecs.end(); ++iter) {
2450 if (iter->id == codec.id) {
2451 *iter = codec;
2452 found = true;
2453 break;
2454 }
2455 }
2456 if (!found) {
2457 desc->AddCodec(codec);
2458 return;
2459 }
2460 desc->set_codecs(codecs);
2461}
2462
2463// Adds or updates existing codec corresponding to |payload_type| according
2464// to |parameters|.
2465template <class T, class U>
2466void UpdateCodec(MediaContentDescription* content_desc, int payload_type,
2467 const cricket::CodecParameterMap& parameters) {
2468 // Codec might already have been populated (from rtpmap).
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +00002469 U new_codec = GetCodecWithPayloadType(static_cast<T*>(content_desc)->codecs(),
2470 payload_type);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002471 AddParameters(parameters, &new_codec);
2472 AddOrReplaceCodec<T, U>(content_desc, new_codec);
2473}
2474
2475// Adds or updates existing codec corresponding to |payload_type| according
2476// to |feedback_param|.
2477template <class T, class U>
2478void UpdateCodec(MediaContentDescription* content_desc, int payload_type,
2479 const cricket::FeedbackParam& feedback_param) {
2480 // Codec might already have been populated (from rtpmap).
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +00002481 U new_codec = GetCodecWithPayloadType(static_cast<T*>(content_desc)->codecs(),
2482 payload_type);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002483 AddFeedbackParameter(feedback_param, &new_codec);
2484 AddOrReplaceCodec<T, U>(content_desc, new_codec);
2485}
2486
jlmiller@webrtc.orga744a282015-02-18 21:37:46 +00002487template <class T>
2488bool PopWildcardCodec(std::vector<T>* codecs, T* wildcard_codec) {
2489 for (auto iter = codecs->begin(); iter != codecs->end(); ++iter) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002490 if (iter->id == kWildcardPayloadType) {
2491 *wildcard_codec = *iter;
2492 codecs->erase(iter);
2493 return true;
2494 }
2495 }
2496 return false;
2497}
2498
jlmiller@webrtc.orga744a282015-02-18 21:37:46 +00002499template<class T>
2500void UpdateFromWildcardCodecs(cricket::MediaContentDescriptionImpl<T>* desc) {
2501 auto codecs = desc->codecs();
2502 T wildcard_codec;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002503 if (!PopWildcardCodec(&codecs, &wildcard_codec)) {
2504 return;
2505 }
jlmiller@webrtc.orga744a282015-02-18 21:37:46 +00002506 for (auto& codec : codecs) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002507 AddFeedbackParameters(wildcard_codec.feedback_params, &codec);
2508 }
jlmiller@webrtc.orga744a282015-02-18 21:37:46 +00002509 desc->set_codecs(codecs);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002510}
2511
2512void AddAudioAttribute(const std::string& name, const std::string& value,
2513 AudioContentDescription* audio_desc) {
2514 if (value.empty()) {
2515 return;
2516 }
2517 std::vector<cricket::AudioCodec> codecs = audio_desc->codecs();
2518 for (std::vector<cricket::AudioCodec>::iterator iter = codecs.begin();
2519 iter != codecs.end(); ++iter) {
2520 iter->params[name] = value;
2521 }
2522 audio_desc->set_codecs(codecs);
2523}
2524
2525bool ParseContent(const std::string& message,
2526 const MediaType media_type,
2527 int mline_index,
2528 const std::string& protocol,
deadbeef67cf2c12016-04-13 10:07:16 -07002529 const std::vector<int>& payload_types,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002530 size_t* pos,
2531 std::string* content_name,
2532 MediaContentDescription* media_desc,
2533 TransportDescription* transport,
2534 std::vector<JsepIceCandidate*>* candidates,
2535 SdpParseError* error) {
2536 ASSERT(media_desc != NULL);
2537 ASSERT(content_name != NULL);
2538 ASSERT(transport != NULL);
2539
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +00002540 if (media_type == cricket::MEDIA_TYPE_AUDIO) {
2541 MaybeCreateStaticPayloadAudioCodecs(
deadbeef67cf2c12016-04-13 10:07:16 -07002542 payload_types, static_cast<AudioContentDescription*>(media_desc));
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +00002543 }
2544
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002545 // The media level "ice-ufrag" and "ice-pwd".
2546 // The candidates before update the media level "ice-pwd" and "ice-ufrag".
2547 Candidates candidates_orig;
2548 std::string line;
2549 std::string mline_id;
2550 // Tracks created out of the ssrc attributes.
2551 StreamParamsVec tracks;
2552 SsrcInfoVec ssrc_infos;
2553 SsrcGroupVec ssrc_groups;
2554 std::string maxptime_as_string;
2555 std::string ptime_as_string;
deadbeef9d3584c2016-02-16 17:54:10 -08002556 std::string stream_id;
2557 std::string track_id;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002558
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002559 // Loop until the next m line
2560 while (!IsLineType(message, kLineTypeMedia, *pos)) {
2561 if (!GetLine(message, pos, &line)) {
2562 if (*pos >= message.size()) {
2563 break; // Done parsing
2564 } else {
sergeyu@chromium.org5bc25c42013-12-05 00:24:06 +00002565 return ParseFailed(message, *pos, "Invalid SDP line.", error);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002566 }
2567 }
2568
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002569 // RFC 4566
2570 // b=* (zero or more bandwidth information lines)
2571 if (IsLineType(line, kLineTypeSessionBandwidth)) {
2572 std::string bandwidth;
2573 if (HasAttribute(line, kApplicationSpecificMaximum)) {
2574 if (!GetValue(line, kApplicationSpecificMaximum, &bandwidth, error)) {
2575 return false;
2576 } else {
wu@webrtc.org5e760e72014-04-02 23:19:09 +00002577 int b = 0;
2578 if (!GetValueFromString(line, bandwidth, &b, error)) {
2579 return false;
2580 }
Peter Thatcherc0c3a862015-06-24 15:31:25 -07002581 // We should never use more than the default bandwidth for RTP-based
2582 // data channels. Don't allow SDP to set the bandwidth, because
2583 // that would give JS the opportunity to "break the Internet".
2584 // See: https://code.google.com/p/chromium/issues/detail?id=280726
2585 if (media_type == cricket::MEDIA_TYPE_DATA && IsRtp(protocol) &&
2586 b > cricket::kDataMaxBandwidth / 1000) {
2587 std::ostringstream description;
2588 description << "RTP-based data channels may not send more than "
2589 << cricket::kDataMaxBandwidth / 1000 << "kbps.";
2590 return ParseFailed(line, description.str(), error);
2591 }
wu@webrtc.org5e760e72014-04-02 23:19:09 +00002592 media_desc->set_bandwidth(b * 1000);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002593 }
2594 }
2595 continue;
2596 }
2597
2598 if (!IsLineType(line, kLineTypeAttributes)) {
2599 // TODO: Handle other lines if needed.
2600 LOG(LS_INFO) << "Ignored line: " << line;
2601 continue;
2602 }
2603
2604 // Handle attributes common to SCTP and RTP.
2605 if (HasAttribute(line, kAttributeMid)) {
2606 // RFC 3388
2607 // mid-attribute = "a=mid:" identification-tag
2608 // identification-tag = token
2609 // Use the mid identification-tag as the content name.
2610 if (!GetValue(line, kAttributeMid, &mline_id, error)) {
2611 return false;
2612 }
2613 *content_name = mline_id;
2614 } else if (HasAttribute(line, kAttributeCandidate)) {
2615 Candidate candidate;
2616 if (!ParseCandidate(line, &candidate, error, false)) {
2617 return false;
2618 }
2619 candidates_orig.push_back(candidate);
2620 } else if (HasAttribute(line, kAttributeIceUfrag)) {
2621 if (!GetValue(line, kAttributeIceUfrag, &transport->ice_ufrag, error)) {
2622 return false;
2623 }
2624 } else if (HasAttribute(line, kAttributeIcePwd)) {
2625 if (!GetValue(line, kAttributeIcePwd, &transport->ice_pwd, error)) {
2626 return false;
2627 }
2628 } else if (HasAttribute(line, kAttributeIceOption)) {
2629 if (!ParseIceOptions(line, &transport->transport_options, error)) {
2630 return false;
2631 }
2632 } else if (HasAttribute(line, kAttributeFmtp)) {
2633 if (!ParseFmtpAttributes(line, media_type, media_desc, error)) {
2634 return false;
2635 }
2636 } else if (HasAttribute(line, kAttributeFingerprint)) {
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002637 rtc::SSLFingerprint* fingerprint = NULL;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002638
2639 if (!ParseFingerprintAttribute(line, &fingerprint, error)) {
2640 return false;
2641 }
2642 transport->identity_fingerprint.reset(fingerprint);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00002643 } else if (HasAttribute(line, kAttributeSetup)) {
2644 if (!ParseDtlsSetup(line, &(transport->connection_role), error)) {
2645 return false;
2646 }
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00002647 } else if (IsDtlsSctp(protocol) && HasAttribute(line, kAttributeSctpPort)) {
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00002648 int sctp_port;
2649 if (!ParseSctpPort(line, &sctp_port, error)) {
2650 return false;
2651 }
2652 if (!AddSctpDataCodec(static_cast<DataContentDescription*>(media_desc),
2653 sctp_port)) {
2654 return false;
2655 }
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00002656 } else if (IsRtp(protocol)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002657 //
2658 // RTP specific attrubtes
2659 //
2660 if (HasAttribute(line, kAttributeRtcpMux)) {
2661 media_desc->set_rtcp_mux(true);
deadbeef13871492015-12-09 12:37:51 -08002662 } else if (HasAttribute(line, kAttributeRtcpReducedSize)) {
2663 media_desc->set_rtcp_reduced_size(true);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002664 } else if (HasAttribute(line, kAttributeSsrcGroup)) {
2665 if (!ParseSsrcGroupAttribute(line, &ssrc_groups, error)) {
2666 return false;
2667 }
2668 } else if (HasAttribute(line, kAttributeSsrc)) {
2669 if (!ParseSsrcAttribute(line, &ssrc_infos, error)) {
2670 return false;
2671 }
2672 } else if (HasAttribute(line, kAttributeCrypto)) {
2673 if (!ParseCryptoAttribute(line, media_desc, error)) {
2674 return false;
2675 }
2676 } else if (HasAttribute(line, kAttributeRtpmap)) {
deadbeef67cf2c12016-04-13 10:07:16 -07002677 if (!ParseRtpmapAttribute(line, media_type, payload_types, media_desc,
2678 error)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002679 return false;
2680 }
2681 } else if (HasAttribute(line, kCodecParamMaxPTime)) {
2682 if (!GetValue(line, kCodecParamMaxPTime, &maxptime_as_string, error)) {
2683 return false;
2684 }
2685 } else if (HasAttribute(line, kAttributeRtcpFb)) {
2686 if (!ParseRtcpFbAttribute(line, media_type, media_desc, error)) {
2687 return false;
2688 }
2689 } else if (HasAttribute(line, kCodecParamPTime)) {
2690 if (!GetValue(line, kCodecParamPTime, &ptime_as_string, error)) {
2691 return false;
2692 }
2693 } else if (HasAttribute(line, kAttributeSendOnly)) {
2694 media_desc->set_direction(cricket::MD_SENDONLY);
2695 } else if (HasAttribute(line, kAttributeRecvOnly)) {
2696 media_desc->set_direction(cricket::MD_RECVONLY);
2697 } else if (HasAttribute(line, kAttributeInactive)) {
2698 media_desc->set_direction(cricket::MD_INACTIVE);
2699 } else if (HasAttribute(line, kAttributeSendRecv)) {
2700 media_desc->set_direction(cricket::MD_SENDRECV);
2701 } else if (HasAttribute(line, kAttributeExtmap)) {
2702 RtpHeaderExtension extmap;
2703 if (!ParseExtmap(line, &extmap, error)) {
2704 return false;
2705 }
2706 media_desc->AddRtpHeaderExtension(extmap);
2707 } else if (HasAttribute(line, kAttributeXGoogleFlag)) {
2708 // Experimental attribute. Conference mode activates more aggressive
2709 // AEC and NS settings.
2710 // TODO: expose API to set these directly.
2711 std::string flag_value;
2712 if (!GetValue(line, kAttributeXGoogleFlag, &flag_value, error)) {
2713 return false;
2714 }
2715 if (flag_value.compare(kValueConference) == 0)
2716 media_desc->set_conference_mode(true);
deadbeef9d3584c2016-02-16 17:54:10 -08002717 } else if (HasAttribute(line, kAttributeMsid)) {
2718 if (!ParseMsidAttribute(line, &stream_id, &track_id, error)) {
2719 return false;
2720 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002721 }
2722 } else {
2723 // Only parse lines that we are interested of.
2724 LOG(LS_INFO) << "Ignored line: " << line;
2725 continue;
2726 }
2727 }
2728
2729 // Create tracks from the |ssrc_infos|.
Taylor Brandstetter5de6b752016-03-09 17:02:30 -08002730 // If the stream_id/track_id for all SSRCS are identical, one StreamParams
2731 // will be created in CreateTracksFromSsrcInfos, containing all the SSRCs from
2732 // the m= section.
2733 CreateTracksFromSsrcInfos(ssrc_infos, stream_id, track_id, &tracks);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002734
2735 // Add the ssrc group to the track.
2736 for (SsrcGroupVec::iterator ssrc_group = ssrc_groups.begin();
2737 ssrc_group != ssrc_groups.end(); ++ssrc_group) {
2738 if (ssrc_group->ssrcs.empty()) {
2739 continue;
2740 }
Peter Boström0c4e06b2015-10-07 12:23:21 +02002741 uint32_t ssrc = ssrc_group->ssrcs.front();
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002742 for (StreamParamsVec::iterator track = tracks.begin();
2743 track != tracks.end(); ++track) {
2744 if (track->has_ssrc(ssrc)) {
2745 track->ssrc_groups.push_back(*ssrc_group);
2746 }
2747 }
2748 }
2749
2750 // Add the new tracks to the |media_desc|.
deadbeef9d3584c2016-02-16 17:54:10 -08002751 for (StreamParams& track : tracks) {
2752 media_desc->AddStream(track);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002753 }
2754
2755 if (media_type == cricket::MEDIA_TYPE_AUDIO) {
2756 AudioContentDescription* audio_desc =
2757 static_cast<AudioContentDescription*>(media_desc);
jlmiller@webrtc.orga744a282015-02-18 21:37:46 +00002758 UpdateFromWildcardCodecs(audio_desc);
2759
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002760 // Verify audio codec ensures that no audio codec has been populated with
2761 // only fmtp.
2762 if (!VerifyAudioCodecs(audio_desc)) {
2763 return ParseFailed("Failed to parse audio codecs correctly.", error);
2764 }
2765 AddAudioAttribute(kCodecParamMaxPTime, maxptime_as_string, audio_desc);
2766 AddAudioAttribute(kCodecParamPTime, ptime_as_string, audio_desc);
2767 }
2768
2769 if (media_type == cricket::MEDIA_TYPE_VIDEO) {
jlmiller@webrtc.orga744a282015-02-18 21:37:46 +00002770 VideoContentDescription* video_desc =
2771 static_cast<VideoContentDescription*>(media_desc);
2772 UpdateFromWildcardCodecs(video_desc);
2773 // Verify video codec ensures that no video codec has been populated with
2774 // only rtcp-fb.
2775 if (!VerifyVideoCodecs(video_desc)) {
2776 return ParseFailed("Failed to parse video codecs correctly.", error);
2777 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002778 }
2779
2780 // RFC 5245
2781 // Update the candidates with the media level "ice-pwd" and "ice-ufrag".
2782 for (Candidates::iterator it = candidates_orig.begin();
2783 it != candidates_orig.end(); ++it) {
honghaiza54a0802015-12-16 18:37:23 -08002784 ASSERT((*it).username().empty() ||
2785 (*it).username() == transport->ice_ufrag);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002786 (*it).set_username(transport->ice_ufrag);
2787 ASSERT((*it).password().empty());
2788 (*it).set_password(transport->ice_pwd);
2789 candidates->push_back(
2790 new JsepIceCandidate(mline_id, mline_index, *it));
2791 }
2792 return true;
2793}
2794
2795bool ParseSsrcAttribute(const std::string& line, SsrcInfoVec* ssrc_infos,
2796 SdpParseError* error) {
2797 ASSERT(ssrc_infos != NULL);
2798 // RFC 5576
2799 // a=ssrc:<ssrc-id> <attribute>
2800 // a=ssrc:<ssrc-id> <attribute>:<value>
2801 std::string field1, field2;
Donald Curtis144d0182015-05-15 13:14:24 -07002802 if (!rtc::tokenize_first(line.substr(kLinePrefixLength), kSdpDelimiterSpace,
2803 &field1, &field2)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002804 const size_t expected_fields = 2;
2805 return ParseFailedExpectFieldNum(line, expected_fields, error);
2806 }
2807
2808 // ssrc:<ssrc-id>
2809 std::string ssrc_id_s;
2810 if (!GetValue(field1, kAttributeSsrc, &ssrc_id_s, error)) {
2811 return false;
2812 }
Peter Boström0c4e06b2015-10-07 12:23:21 +02002813 uint32_t ssrc_id = 0;
wu@webrtc.org5e760e72014-04-02 23:19:09 +00002814 if (!GetValueFromString(line, ssrc_id_s, &ssrc_id, error)) {
2815 return false;
2816 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002817
2818 std::string attribute;
2819 std::string value;
Donald Curtis144d0182015-05-15 13:14:24 -07002820 if (!rtc::tokenize_first(field2, kSdpDelimiterColon, &attribute, &value)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002821 std::ostringstream description;
2822 description << "Failed to get the ssrc attribute value from " << field2
2823 << ". Expected format <attribute>:<value>.";
2824 return ParseFailed(line, description.str(), error);
2825 }
2826
2827 // Check if there's already an item for this |ssrc_id|. Create a new one if
2828 // there isn't.
2829 SsrcInfoVec::iterator ssrc_info = ssrc_infos->begin();
2830 for (; ssrc_info != ssrc_infos->end(); ++ssrc_info) {
2831 if (ssrc_info->ssrc_id == ssrc_id) {
2832 break;
2833 }
2834 }
2835 if (ssrc_info == ssrc_infos->end()) {
2836 SsrcInfo info;
2837 info.ssrc_id = ssrc_id;
2838 ssrc_infos->push_back(info);
2839 ssrc_info = ssrc_infos->end() - 1;
2840 }
2841
2842 // Store the info to the |ssrc_info|.
2843 if (attribute == kSsrcAttributeCname) {
2844 // RFC 5576
2845 // cname:<value>
2846 ssrc_info->cname = value;
2847 } else if (attribute == kSsrcAttributeMsid) {
2848 // draft-alvestrand-mmusic-msid-00
2849 // "msid:" identifier [ " " appdata ]
2850 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002851 rtc::split(value, kSdpDelimiterSpace, &fields);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002852 if (fields.size() < 1 || fields.size() > 2) {
2853 return ParseFailed(line,
2854 "Expected format \"msid:<identifier>[ <appdata>]\".",
2855 error);
2856 }
deadbeef9d3584c2016-02-16 17:54:10 -08002857 ssrc_info->stream_id = fields[0];
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002858 if (fields.size() == 2) {
deadbeef9d3584c2016-02-16 17:54:10 -08002859 ssrc_info->track_id = fields[1];
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002860 }
2861 } else if (attribute == kSsrcAttributeMslabel) {
2862 // draft-alvestrand-rtcweb-mid-01
2863 // mslabel:<value>
2864 ssrc_info->mslabel = value;
2865 } else if (attribute == kSSrcAttributeLabel) {
2866 // The label isn't defined.
2867 // label:<value>
2868 ssrc_info->label = value;
2869 }
2870 return true;
2871}
2872
2873bool ParseSsrcGroupAttribute(const std::string& line,
2874 SsrcGroupVec* ssrc_groups,
2875 SdpParseError* error) {
2876 ASSERT(ssrc_groups != NULL);
2877 // RFC 5576
2878 // a=ssrc-group:<semantics> <ssrc-id> ...
2879 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002880 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002881 kSdpDelimiterSpace, &fields);
2882 const size_t expected_min_fields = 2;
2883 if (fields.size() < expected_min_fields) {
2884 return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
2885 }
2886 std::string semantics;
2887 if (!GetValue(fields[0], kAttributeSsrcGroup, &semantics, error)) {
2888 return false;
2889 }
Peter Boström0c4e06b2015-10-07 12:23:21 +02002890 std::vector<uint32_t> ssrcs;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002891 for (size_t i = 1; i < fields.size(); ++i) {
Peter Boström0c4e06b2015-10-07 12:23:21 +02002892 uint32_t ssrc = 0;
wu@webrtc.org5e760e72014-04-02 23:19:09 +00002893 if (!GetValueFromString(line, fields[i], &ssrc, error)) {
2894 return false;
2895 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002896 ssrcs.push_back(ssrc);
2897 }
2898 ssrc_groups->push_back(SsrcGroup(semantics, ssrcs));
2899 return true;
2900}
2901
2902bool ParseCryptoAttribute(const std::string& line,
2903 MediaContentDescription* media_desc,
2904 SdpParseError* error) {
2905 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002906 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002907 kSdpDelimiterSpace, &fields);
2908 // RFC 4568
2909 // a=crypto:<tag> <crypto-suite> <key-params> [<session-params>]
2910 const size_t expected_min_fields = 3;
2911 if (fields.size() < expected_min_fields) {
2912 return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
2913 }
2914 std::string tag_value;
2915 if (!GetValue(fields[0], kAttributeCrypto, &tag_value, error)) {
2916 return false;
2917 }
wu@webrtc.org5e760e72014-04-02 23:19:09 +00002918 int tag = 0;
2919 if (!GetValueFromString(line, tag_value, &tag, error)) {
2920 return false;
2921 }
jbauch083b73f2015-07-16 02:46:32 -07002922 const std::string& crypto_suite = fields[1];
2923 const std::string& key_params = fields[2];
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002924 std::string session_params;
2925 if (fields.size() > 3) {
2926 session_params = fields[3];
2927 }
2928 media_desc->AddCrypto(CryptoParams(tag, crypto_suite, key_params,
2929 session_params));
2930 return true;
2931}
2932
2933// Updates or creates a new codec entry in the audio description with according
deadbeef67cf2c12016-04-13 10:07:16 -07002934// to |name|, |clockrate|, |bitrate|, and |channels|.
2935void UpdateCodec(int payload_type,
2936 const std::string& name,
2937 int clockrate,
2938 int bitrate,
2939 size_t channels,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002940 AudioContentDescription* audio_desc) {
2941 // Codec may already be populated with (only) optional parameters
2942 // (from an fmtp).
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +00002943 cricket::AudioCodec codec =
2944 GetCodecWithPayloadType(audio_desc->codecs(), payload_type);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002945 codec.name = name;
2946 codec.clockrate = clockrate;
2947 codec.bitrate = bitrate;
2948 codec.channels = channels;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002949 AddOrReplaceCodec<AudioContentDescription, cricket::AudioCodec>(audio_desc,
2950 codec);
2951}
2952
2953// Updates or creates a new codec entry in the video description according to
deadbeef67cf2c12016-04-13 10:07:16 -07002954// |name|, |width|, |height|, and |framerate|.
2955void UpdateCodec(int payload_type,
2956 const std::string& name,
2957 int width,
2958 int height,
2959 int framerate,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002960 VideoContentDescription* video_desc) {
2961 // Codec may already be populated with (only) optional parameters
2962 // (from an fmtp).
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +00002963 cricket::VideoCodec codec =
2964 GetCodecWithPayloadType(video_desc->codecs(), payload_type);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002965 codec.name = name;
2966 codec.width = width;
2967 codec.height = height;
2968 codec.framerate = framerate;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002969 AddOrReplaceCodec<VideoContentDescription, cricket::VideoCodec>(video_desc,
2970 codec);
2971}
2972
2973bool ParseRtpmapAttribute(const std::string& line,
2974 const MediaType media_type,
deadbeef67cf2c12016-04-13 10:07:16 -07002975 const std::vector<int>& payload_types,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002976 MediaContentDescription* media_desc,
2977 SdpParseError* error) {
2978 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002979 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002980 kSdpDelimiterSpace, &fields);
2981 // RFC 4566
2982 // a=rtpmap:<payload type> <encoding name>/<clock rate>[/<encodingparameters>]
2983 const size_t expected_min_fields = 2;
2984 if (fields.size() < expected_min_fields) {
2985 return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
2986 }
2987 std::string payload_type_value;
2988 if (!GetValue(fields[0], kAttributeRtpmap, &payload_type_value, error)) {
2989 return false;
2990 }
wu@webrtc.org5e760e72014-04-02 23:19:09 +00002991 int payload_type = 0;
pkasting@chromium.orge9facf82015-02-17 20:36:28 +00002992 if (!GetPayloadTypeFromString(line, payload_type_value, &payload_type,
2993 error)) {
wu@webrtc.org5e760e72014-04-02 23:19:09 +00002994 return false;
2995 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002996
deadbeef67cf2c12016-04-13 10:07:16 -07002997 if (std::find(payload_types.begin(), payload_types.end(), payload_type) ==
2998 payload_types.end()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002999 LOG(LS_WARNING) << "Ignore rtpmap line that did not appear in the "
3000 << "<fmt> of the m-line: " << line;
3001 return true;
3002 }
jbauch083b73f2015-07-16 02:46:32 -07003003 const std::string& encoder = fields[1];
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003004 std::vector<std::string> codec_params;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00003005 rtc::split(encoder, '/', &codec_params);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003006 // <encoding name>/<clock rate>[/<encodingparameters>]
3007 // 2 mandatory fields
3008 if (codec_params.size() < 2 || codec_params.size() > 3) {
3009 return ParseFailed(line,
3010 "Expected format \"<encoding name>/<clock rate>"
3011 "[/<encodingparameters>]\".",
3012 error);
3013 }
jbauch083b73f2015-07-16 02:46:32 -07003014 const std::string& encoding_name = codec_params[0];
wu@webrtc.org5e760e72014-04-02 23:19:09 +00003015 int clock_rate = 0;
3016 if (!GetValueFromString(line, codec_params[1], &clock_rate, error)) {
3017 return false;
3018 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003019 if (media_type == cricket::MEDIA_TYPE_VIDEO) {
3020 VideoContentDescription* video_desc =
3021 static_cast<VideoContentDescription*>(media_desc);
3022 // TODO: We will send resolution in SDP. For now use
3023 // JsepSessionDescription::kMaxVideoCodecWidth and kMaxVideoCodecHeight.
3024 UpdateCodec(payload_type, encoding_name,
3025 JsepSessionDescription::kMaxVideoCodecWidth,
3026 JsepSessionDescription::kMaxVideoCodecHeight,
3027 JsepSessionDescription::kDefaultVideoCodecFramerate,
deadbeef67cf2c12016-04-13 10:07:16 -07003028 video_desc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003029 } else if (media_type == cricket::MEDIA_TYPE_AUDIO) {
3030 // RFC 4566
3031 // For audio streams, <encoding parameters> indicates the number
3032 // of audio channels. This parameter is OPTIONAL and may be
3033 // omitted if the number of channels is one, provided that no
3034 // additional parameters are needed.
Peter Kasting69558702016-01-12 16:26:35 -08003035 size_t channels = 1;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003036 if (codec_params.size() == 3) {
wu@webrtc.org5e760e72014-04-02 23:19:09 +00003037 if (!GetValueFromString(line, codec_params[2], &channels, error)) {
3038 return false;
3039 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003040 }
3041 int bitrate = 0;
3042 // The default behavior for ISAC (bitrate == 0) in webrtcvoiceengine.cc
3043 // (specifically FindWebRtcCodec) is bandwidth-adaptive variable bitrate.
3044 // The bandwidth adaptation doesn't always work well, so this code
3045 // sets a fixed target bitrate instead.
3046 if (_stricmp(encoding_name.c_str(), kIsacCodecName) == 0) {
3047 if (clock_rate <= 16000) {
3048 bitrate = kIsacWbDefaultRate;
3049 } else {
3050 bitrate = kIsacSwbDefaultRate;
3051 }
3052 }
3053 AudioContentDescription* audio_desc =
3054 static_cast<AudioContentDescription*>(media_desc);
3055 UpdateCodec(payload_type, encoding_name, clock_rate, bitrate, channels,
deadbeef67cf2c12016-04-13 10:07:16 -07003056 audio_desc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003057 } else if (media_type == cricket::MEDIA_TYPE_DATA) {
3058 DataContentDescription* data_desc =
3059 static_cast<DataContentDescription*>(media_desc);
deadbeef67cf2c12016-04-13 10:07:16 -07003060 data_desc->AddCodec(cricket::DataCodec(payload_type, encoding_name));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003061 }
3062 return true;
3063}
3064
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003065bool ParseFmtpParam(const std::string& line, std::string* parameter,
3066 std::string* value, SdpParseError* error) {
Donald Curtis0e07f922015-05-15 09:21:23 -07003067 if (!rtc::tokenize_first(line, kSdpDelimiterEqual, parameter, value)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003068 ParseFailed(line, "Unable to parse fmtp parameter. \'=\' missing.", error);
3069 return false;
3070 }
3071 // a=fmtp:<payload_type> <param1>=<value1>; <param2>=<value2>; ...
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003072 return true;
3073}
3074
3075bool ParseFmtpAttributes(const std::string& line, const MediaType media_type,
3076 MediaContentDescription* media_desc,
3077 SdpParseError* error) {
3078 if (media_type != cricket::MEDIA_TYPE_AUDIO &&
3079 media_type != cricket::MEDIA_TYPE_VIDEO) {
3080 return true;
3081 }
Donald Curtis0e07f922015-05-15 09:21:23 -07003082
3083 std::string line_payload;
3084 std::string line_params;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003085
3086 // RFC 5576
3087 // a=fmtp:<format> <format specific parameters>
3088 // At least two fields, whereas the second one is any of the optional
3089 // parameters.
Donald Curtis144d0182015-05-15 13:14:24 -07003090 if (!rtc::tokenize_first(line.substr(kLinePrefixLength), kSdpDelimiterSpace,
3091 &line_payload, &line_params)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003092 ParseFailedExpectMinFieldNum(line, 2, error);
3093 return false;
3094 }
3095
Donald Curtis0e07f922015-05-15 09:21:23 -07003096 // Parse out the payload information.
pkasting@chromium.orgd3245462015-02-23 21:28:22 +00003097 std::string payload_type_str;
Donald Curtis0e07f922015-05-15 09:21:23 -07003098 if (!GetValue(line_payload, kAttributeFmtp, &payload_type_str, error)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003099 return false;
3100 }
3101
Donald Curtis0e07f922015-05-15 09:21:23 -07003102 int payload_type = 0;
Donald Curtis144d0182015-05-15 13:14:24 -07003103 if (!GetPayloadTypeFromString(line_payload, payload_type_str, &payload_type,
3104 error)) {
Donald Curtis0e07f922015-05-15 09:21:23 -07003105 return false;
3106 }
3107
3108 // Parse out format specific parameters.
3109 std::vector<std::string> fields;
3110 rtc::split(line_params, kSdpDelimiterSemicolon, &fields);
3111
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003112 cricket::CodecParameterMap codec_params;
Donald Curtis144d0182015-05-15 13:14:24 -07003113 for (auto& iter : fields) {
Donald Curtis0e07f922015-05-15 09:21:23 -07003114 if (iter.find(kSdpDelimiterEqual) == std::string::npos) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003115 // Only fmtps with equals are currently supported. Other fmtp types
3116 // should be ignored. Unknown fmtps do not constitute an error.
3117 continue;
3118 }
Donald Curtis0e07f922015-05-15 09:21:23 -07003119
3120 std::string name;
3121 std::string value;
3122 if (!ParseFmtpParam(rtc::string_trim(iter), &name, &value, error)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003123 return false;
3124 }
3125 codec_params[name] = value;
3126 }
3127
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003128 if (media_type == cricket::MEDIA_TYPE_AUDIO) {
3129 UpdateCodec<AudioContentDescription, cricket::AudioCodec>(
pkasting@chromium.orgd3245462015-02-23 21:28:22 +00003130 media_desc, payload_type, codec_params);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003131 } else if (media_type == cricket::MEDIA_TYPE_VIDEO) {
3132 UpdateCodec<VideoContentDescription, cricket::VideoCodec>(
pkasting@chromium.orgd3245462015-02-23 21:28:22 +00003133 media_desc, payload_type, codec_params);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003134 }
3135 return true;
3136}
3137
3138bool ParseRtcpFbAttribute(const std::string& line, const MediaType media_type,
3139 MediaContentDescription* media_desc,
3140 SdpParseError* error) {
3141 if (media_type != cricket::MEDIA_TYPE_AUDIO &&
3142 media_type != cricket::MEDIA_TYPE_VIDEO) {
3143 return true;
3144 }
3145 std::vector<std::string> rtcp_fb_fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00003146 rtc::split(line.c_str(), kSdpDelimiterSpace, &rtcp_fb_fields);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003147 if (rtcp_fb_fields.size() < 2) {
3148 return ParseFailedGetValue(line, kAttributeRtcpFb, error);
3149 }
3150 std::string payload_type_string;
3151 if (!GetValue(rtcp_fb_fields[0], kAttributeRtcpFb, &payload_type_string,
3152 error)) {
3153 return false;
3154 }
wu@webrtc.org5e760e72014-04-02 23:19:09 +00003155 int payload_type = kWildcardPayloadType;
3156 if (payload_type_string != "*") {
pkasting@chromium.orge9facf82015-02-17 20:36:28 +00003157 if (!GetPayloadTypeFromString(line, payload_type_string, &payload_type,
3158 error)) {
wu@webrtc.org5e760e72014-04-02 23:19:09 +00003159 return false;
3160 }
3161 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003162 std::string id = rtcp_fb_fields[1];
3163 std::string param = "";
3164 for (std::vector<std::string>::iterator iter = rtcp_fb_fields.begin() + 2;
3165 iter != rtcp_fb_fields.end(); ++iter) {
3166 param.append(*iter);
3167 }
3168 const cricket::FeedbackParam feedback_param(id, param);
3169
3170 if (media_type == cricket::MEDIA_TYPE_AUDIO) {
pkasting@chromium.orgd3245462015-02-23 21:28:22 +00003171 UpdateCodec<AudioContentDescription, cricket::AudioCodec>(
3172 media_desc, payload_type, feedback_param);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003173 } else if (media_type == cricket::MEDIA_TYPE_VIDEO) {
pkasting@chromium.orgd3245462015-02-23 21:28:22 +00003174 UpdateCodec<VideoContentDescription, cricket::VideoCodec>(
3175 media_desc, payload_type, feedback_param);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003176 }
3177 return true;
3178}
3179
3180} // namespace webrtc