blob: 10f99224f82d8a045e5fb1a76cce52792021b60f [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) {
hta62a216e2016-04-15 11:02:14 -07001567 // Parameters are a semicolon-separated list, no spaces.
1568 // The list is separated from the header by a space.
1569 if (fmtp == parameters.begin()) {
1570 *os << kSdpDelimiterSpace;
1571 } else {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001572 *os << kSdpDelimiterSemicolon;
1573 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001574 WriteFmtpParameter(fmtp->first, fmtp->second, os);
1575 }
1576}
1577
1578bool IsFmtpParam(const std::string& name) {
1579 const char* kFmtpParams[] = {
htaa6b99442016-04-12 10:29:17 -07001580 // TODO(hta): Split FMTP parameters apart from parameters in general.
1581 // FMTP parameters are codec specific, not generic.
1582 kCodecParamMinPTime,
1583 kCodecParamSPropStereo,
1584 kCodecParamStereo,
1585 kCodecParamUseInbandFec,
1586 kCodecParamUseDtx,
1587 kCodecParamStartBitrate,
1588 kCodecParamMaxBitrate,
1589 kCodecParamMinBitrate,
1590 kCodecParamMaxQuantization,
1591 kCodecParamSctpProtocol,
1592 kCodecParamSctpStreams,
1593 kCodecParamMaxAverageBitrate,
1594 kCodecParamMaxPlaybackRate,
1595 kCodecParamAssociatedPayloadType,
1596 cricket::kH264FmtpPacketizationMode,
1597 cricket::kH264FmtpLevelAsymmetryAllowed,
1598 cricket::kH264FmtpProfileLevelId};
tfarina5237aaf2015-11-10 23:44:30 -08001599 for (size_t i = 0; i < arraysize(kFmtpParams); ++i) {
htaa6b99442016-04-12 10:29:17 -07001600 if (name.compare(kFmtpParams[i]) == 0) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001601 return true;
1602 }
1603 }
1604 return false;
1605}
1606
1607// Retreives fmtp parameters from |params|, which may contain other parameters
1608// as well, and puts them in |fmtp_parameters|.
1609void GetFmtpParams(const cricket::CodecParameterMap& params,
1610 cricket::CodecParameterMap* fmtp_parameters) {
1611 for (cricket::CodecParameterMap::const_iterator iter = params.begin();
1612 iter != params.end(); ++iter) {
1613 if (IsFmtpParam(iter->first)) {
1614 (*fmtp_parameters)[iter->first] = iter->second;
1615 }
1616 }
1617}
1618
1619template <class T>
1620void AddFmtpLine(const T& codec, std::string* message) {
1621 cricket::CodecParameterMap fmtp_parameters;
1622 GetFmtpParams(codec.params, &fmtp_parameters);
1623 if (fmtp_parameters.empty()) {
1624 // No need to add an fmtp if it will have no (optional) parameters.
1625 return;
1626 }
1627 std::ostringstream os;
1628 WriteFmtpHeader(codec.id, &os);
1629 WriteFmtpParameters(fmtp_parameters, &os);
1630 AddLine(os.str(), message);
1631 return;
1632}
1633
1634template <class T>
1635void AddRtcpFbLines(const T& codec, std::string* message) {
1636 for (std::vector<cricket::FeedbackParam>::const_iterator iter =
1637 codec.feedback_params.params().begin();
1638 iter != codec.feedback_params.params().end(); ++iter) {
1639 std::ostringstream os;
1640 WriteRtcpFbHeader(codec.id, &os);
1641 os << " " << iter->id();
1642 if (!iter->param().empty()) {
1643 os << " " << iter->param();
1644 }
1645 AddLine(os.str(), message);
1646 }
1647}
1648
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00001649bool AddSctpDataCodec(DataContentDescription* media_desc,
1650 int sctp_port) {
1651 if (media_desc->HasCodec(cricket::kGoogleSctpDataCodecId)) {
1652 return ParseFailed("",
1653 "Can't have multiple sctp port attributes.",
1654 NULL);
1655 }
1656 // Add the SCTP Port number as a pseudo-codec "port" parameter
deadbeef67cf2c12016-04-13 10:07:16 -07001657 cricket::DataCodec codec_port(cricket::kGoogleSctpDataCodecId,
1658 cricket::kGoogleSctpDataCodecName);
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00001659 codec_port.SetParam(cricket::kCodecParamPort, sctp_port);
1660 LOG(INFO) << "AddSctpDataCodec: Got SCTP Port Number "
1661 << sctp_port;
1662 media_desc->AddCodec(codec_port);
1663 return true;
1664}
1665
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001666bool GetMinValue(const std::vector<int>& values, int* value) {
1667 if (values.empty()) {
1668 return false;
1669 }
1670 std::vector<int>::const_iterator found =
1671 std::min_element(values.begin(), values.end());
1672 *value = *found;
1673 return true;
1674}
1675
1676bool GetParameter(const std::string& name,
1677 const cricket::CodecParameterMap& params, int* value) {
1678 std::map<std::string, std::string>::const_iterator found =
1679 params.find(name);
1680 if (found == params.end()) {
1681 return false;
1682 }
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001683 if (!rtc::FromString(found->second, value)) {
wu@webrtc.org5e760e72014-04-02 23:19:09 +00001684 return false;
1685 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001686 return true;
1687}
1688
1689void BuildRtpMap(const MediaContentDescription* media_desc,
1690 const MediaType media_type,
1691 std::string* message) {
1692 ASSERT(message != NULL);
1693 ASSERT(media_desc != NULL);
1694 std::ostringstream os;
1695 if (media_type == cricket::MEDIA_TYPE_VIDEO) {
1696 const VideoContentDescription* video_desc =
1697 static_cast<const VideoContentDescription*>(media_desc);
1698 for (std::vector<cricket::VideoCodec>::const_iterator it =
1699 video_desc->codecs().begin();
1700 it != video_desc->codecs().end(); ++it) {
1701 // RFC 4566
1702 // a=rtpmap:<payload type> <encoding name>/<clock rate>
1703 // [/<encodingparameters>]
1704 if (it->id != kWildcardPayloadType) {
1705 InitAttrLine(kAttributeRtpmap, &os);
1706 os << kSdpDelimiterColon << it->id << " " << it->name
1707 << "/" << kDefaultVideoClockrate;
1708 AddLine(os.str(), message);
1709 }
1710 AddRtcpFbLines(*it, message);
1711 AddFmtpLine(*it, message);
1712 }
1713 } else if (media_type == cricket::MEDIA_TYPE_AUDIO) {
1714 const AudioContentDescription* audio_desc =
1715 static_cast<const AudioContentDescription*>(media_desc);
1716 std::vector<int> ptimes;
1717 std::vector<int> maxptimes;
1718 int max_minptime = 0;
1719 for (std::vector<cricket::AudioCodec>::const_iterator it =
1720 audio_desc->codecs().begin();
1721 it != audio_desc->codecs().end(); ++it) {
1722 ASSERT(!it->name.empty());
1723 // RFC 4566
1724 // a=rtpmap:<payload type> <encoding name>/<clock rate>
1725 // [/<encodingparameters>]
1726 InitAttrLine(kAttributeRtpmap, &os);
1727 os << kSdpDelimiterColon << it->id << " ";
1728 os << it->name << "/" << it->clockrate;
1729 if (it->channels != 1) {
1730 os << "/" << it->channels;
1731 }
1732 AddLine(os.str(), message);
1733 AddRtcpFbLines(*it, message);
1734 AddFmtpLine(*it, message);
1735 int minptime = 0;
1736 if (GetParameter(kCodecParamMinPTime, it->params, &minptime)) {
1737 max_minptime = std::max(minptime, max_minptime);
1738 }
1739 int ptime;
1740 if (GetParameter(kCodecParamPTime, it->params, &ptime)) {
1741 ptimes.push_back(ptime);
1742 }
1743 int maxptime;
1744 if (GetParameter(kCodecParamMaxPTime, it->params, &maxptime)) {
1745 maxptimes.push_back(maxptime);
1746 }
1747 }
1748 // Populate the maxptime attribute with the smallest maxptime of all codecs
1749 // under the same m-line.
1750 int min_maxptime = INT_MAX;
1751 if (GetMinValue(maxptimes, &min_maxptime)) {
1752 AddAttributeLine(kCodecParamMaxPTime, min_maxptime, message);
1753 }
1754 ASSERT(min_maxptime > max_minptime);
1755 // Populate the ptime attribute with the smallest ptime or the largest
1756 // minptime, whichever is the largest, for all codecs under the same m-line.
1757 int ptime = INT_MAX;
1758 if (GetMinValue(ptimes, &ptime)) {
1759 ptime = std::min(ptime, min_maxptime);
1760 ptime = std::max(ptime, max_minptime);
1761 AddAttributeLine(kCodecParamPTime, ptime, message);
1762 }
1763 } else if (media_type == cricket::MEDIA_TYPE_DATA) {
1764 const DataContentDescription* data_desc =
1765 static_cast<const DataContentDescription*>(media_desc);
1766 for (std::vector<cricket::DataCodec>::const_iterator it =
1767 data_desc->codecs().begin();
1768 it != data_desc->codecs().end(); ++it) {
1769 // RFC 4566
1770 // a=rtpmap:<payload type> <encoding name>/<clock rate>
1771 // [/<encodingparameters>]
1772 InitAttrLine(kAttributeRtpmap, &os);
1773 os << kSdpDelimiterColon << it->id << " "
1774 << it->name << "/" << it->clockrate;
1775 AddLine(os.str(), message);
1776 }
1777 }
1778}
1779
1780void BuildCandidate(const std::vector<Candidate>& candidates,
honghaiza54a0802015-12-16 18:37:23 -08001781 bool include_ufrag,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001782 std::string* message) {
1783 std::ostringstream os;
1784
1785 for (std::vector<Candidate>::const_iterator it = candidates.begin();
1786 it != candidates.end(); ++it) {
1787 // RFC 5245
1788 // a=candidate:<foundation> <component-id> <transport> <priority>
1789 // <connection-address> <port> typ <candidate-types>
1790 // [raddr <connection-address>] [rport <port>]
1791 // *(SP extension-att-name SP extension-att-value)
1792 std::string type;
1793 // Map the cricket candidate type to "host" / "srflx" / "prflx" / "relay"
1794 if (it->type() == cricket::LOCAL_PORT_TYPE) {
1795 type = kCandidateHost;
1796 } else if (it->type() == cricket::STUN_PORT_TYPE) {
1797 type = kCandidateSrflx;
1798 } else if (it->type() == cricket::RELAY_PORT_TYPE) {
1799 type = kCandidateRelay;
Honghai Zhang7fb69db2016-03-14 11:59:18 -07001800 } else if (it->type() == cricket::PRFLX_PORT_TYPE) {
1801 type = kCandidatePrflx;
1802 // Peer reflexive candidate may be signaled for being removed.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001803 } else {
1804 ASSERT(false);
Peter Thatcher019087f2015-04-28 09:06:26 -07001805 // Never write out candidates if we don't know the type.
1806 continue;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001807 }
1808
1809 InitAttrLine(kAttributeCandidate, &os);
1810 os << kSdpDelimiterColon
mallinath@webrtc.org2d60c5e2014-08-08 22:29:20 +00001811 << it->foundation() << " "
1812 << it->component() << " "
1813 << it->protocol() << " "
1814 << it->priority() << " "
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001815 << it->address().ipaddr().ToString() << " "
1816 << it->address().PortAsString() << " "
mallinath@webrtc.org2d60c5e2014-08-08 22:29:20 +00001817 << kAttributeCandidateTyp << " "
1818 << type << " ";
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001819
1820 // Related address
1821 if (!it->related_address().IsNil()) {
1822 os << kAttributeCandidateRaddr << " "
1823 << it->related_address().ipaddr().ToString() << " "
1824 << kAttributeCandidateRport << " "
1825 << it->related_address().PortAsString() << " ";
1826 }
1827
mallinath@webrtc.org2d60c5e2014-08-08 22:29:20 +00001828 if (it->protocol() == cricket::TCP_PROTOCOL_NAME) {
mallinath@webrtc.orge999bd02014-08-13 06:05:55 +00001829 os << kTcpCandidateType << " " << it->tcptype() << " ";
mallinath@webrtc.org2d60c5e2014-08-08 22:29:20 +00001830 }
1831
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001832 // Extensions
1833 os << kAttributeCandidateGeneration << " " << it->generation();
honghaiza54a0802015-12-16 18:37:23 -08001834 if (include_ufrag && !it->username().empty()) {
1835 os << " " << kAttributeCandidateUfrag << " " << it->username();
1836 }
honghaiza0c44ea2016-03-23 16:07:48 -07001837 if (it->network_id() > 0) {
1838 os << " " << kAttributeCandidateNetworkId << " " << it->network_id();
1839 }
honghaize1a0c942016-02-16 14:54:56 -08001840 if (it->network_cost() > 0) {
1841 os << " " << kAttributeCandidateNetworkCost << " " << it->network_cost();
1842 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001843
1844 AddLine(os.str(), message);
1845 }
1846}
1847
1848void BuildIceOptions(const std::vector<std::string>& transport_options,
1849 std::string* message) {
1850 if (!transport_options.empty()) {
1851 std::ostringstream os;
1852 InitAttrLine(kAttributeIceOption, &os);
1853 os << kSdpDelimiterColon << transport_options[0];
1854 for (size_t i = 1; i < transport_options.size(); ++i) {
1855 os << kSdpDelimiterSpace << transport_options[i];
1856 }
1857 AddLine(os.str(), message);
1858 }
1859}
1860
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00001861bool IsRtp(const std::string& protocol) {
1862 return protocol.empty() ||
1863 (protocol.find(cricket::kMediaProtocolRtpPrefix) != std::string::npos);
1864}
1865
1866bool IsDtlsSctp(const std::string& protocol) {
1867 // This intentionally excludes "SCTP" and "SCTP/DTLS".
lally@webrtc.orga7470932015-02-24 20:19:21 +00001868 return protocol.find(cricket::kMediaProtocolDtlsSctp) != std::string::npos;
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00001869}
1870
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001871bool ParseSessionDescription(const std::string& message, size_t* pos,
1872 std::string* session_id,
1873 std::string* session_version,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001874 TransportDescription* session_td,
1875 RtpHeaderExtensions* session_extmaps,
1876 cricket::SessionDescription* desc,
1877 SdpParseError* error) {
1878 std::string line;
1879
deadbeefc80741f2015-10-22 13:14:45 -07001880 desc->set_msid_supported(false);
1881
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001882 // RFC 4566
1883 // v= (protocol version)
1884 if (!GetLineWithType(message, pos, &line, kLineTypeVersion)) {
1885 return ParseFailedExpectLine(message, *pos, kLineTypeVersion,
1886 std::string(), error);
1887 }
1888 // RFC 4566
1889 // o=<username> <sess-id> <sess-version> <nettype> <addrtype>
1890 // <unicast-address>
1891 if (!GetLineWithType(message, pos, &line, kLineTypeOrigin)) {
1892 return ParseFailedExpectLine(message, *pos, kLineTypeOrigin,
1893 std::string(), error);
1894 }
1895 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001896 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001897 kSdpDelimiterSpace, &fields);
1898 const size_t expected_fields = 6;
1899 if (fields.size() != expected_fields) {
1900 return ParseFailedExpectFieldNum(line, expected_fields, error);
1901 }
1902 *session_id = fields[1];
1903 *session_version = fields[2];
1904
1905 // RFC 4566
1906 // s= (session name)
1907 if (!GetLineWithType(message, pos, &line, kLineTypeSessionName)) {
1908 return ParseFailedExpectLine(message, *pos, kLineTypeSessionName,
1909 std::string(), error);
1910 }
1911
1912 // Optional lines
1913 // Those are the optional lines, so shouldn't return false if not present.
1914 // RFC 4566
1915 // i=* (session information)
1916 GetLineWithType(message, pos, &line, kLineTypeSessionInfo);
1917
1918 // RFC 4566
1919 // u=* (URI of description)
1920 GetLineWithType(message, pos, &line, kLineTypeSessionUri);
1921
1922 // RFC 4566
1923 // e=* (email address)
1924 GetLineWithType(message, pos, &line, kLineTypeSessionEmail);
1925
1926 // RFC 4566
1927 // p=* (phone number)
1928 GetLineWithType(message, pos, &line, kLineTypeSessionPhone);
1929
1930 // RFC 4566
1931 // c=* (connection information -- not required if included in
1932 // all media)
1933 GetLineWithType(message, pos, &line, kLineTypeConnection);
1934
1935 // RFC 4566
1936 // b=* (zero or more bandwidth information lines)
1937 while (GetLineWithType(message, pos, &line, kLineTypeSessionBandwidth)) {
1938 // By pass zero or more b lines.
1939 }
1940
1941 // RFC 4566
1942 // One or more time descriptions ("t=" and "r=" lines; see below)
1943 // t= (time the session is active)
1944 // r=* (zero or more repeat times)
1945 // Ensure there's at least one time description
1946 if (!GetLineWithType(message, pos, &line, kLineTypeTiming)) {
1947 return ParseFailedExpectLine(message, *pos, kLineTypeTiming, std::string(),
1948 error);
1949 }
1950
1951 while (GetLineWithType(message, pos, &line, kLineTypeRepeatTimes)) {
1952 // By pass zero or more r lines.
1953 }
1954
1955 // Go through the rest of the time descriptions
1956 while (GetLineWithType(message, pos, &line, kLineTypeTiming)) {
1957 while (GetLineWithType(message, pos, &line, kLineTypeRepeatTimes)) {
1958 // By pass zero or more r lines.
1959 }
1960 }
1961
1962 // RFC 4566
1963 // z=* (time zone adjustments)
1964 GetLineWithType(message, pos, &line, kLineTypeTimeZone);
1965
1966 // RFC 4566
1967 // k=* (encryption key)
1968 GetLineWithType(message, pos, &line, kLineTypeEncryptionKey);
1969
1970 // RFC 4566
1971 // a=* (zero or more session attribute lines)
1972 while (GetLineWithType(message, pos, &line, kLineTypeAttributes)) {
1973 if (HasAttribute(line, kAttributeGroup)) {
1974 if (!ParseGroupAttribute(line, desc, error)) {
1975 return false;
1976 }
1977 } else if (HasAttribute(line, kAttributeIceUfrag)) {
1978 if (!GetValue(line, kAttributeIceUfrag,
1979 &(session_td->ice_ufrag), error)) {
1980 return false;
1981 }
1982 } else if (HasAttribute(line, kAttributeIcePwd)) {
1983 if (!GetValue(line, kAttributeIcePwd, &(session_td->ice_pwd), error)) {
1984 return false;
1985 }
1986 } else if (HasAttribute(line, kAttributeIceLite)) {
1987 session_td->ice_mode = cricket::ICEMODE_LITE;
1988 } else if (HasAttribute(line, kAttributeIceOption)) {
1989 if (!ParseIceOptions(line, &(session_td->transport_options), error)) {
1990 return false;
1991 }
1992 } else if (HasAttribute(line, kAttributeFingerprint)) {
1993 if (session_td->identity_fingerprint.get()) {
1994 return ParseFailed(
1995 line,
1996 "Can't have multiple fingerprint attributes at the same level.",
1997 error);
1998 }
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001999 rtc::SSLFingerprint* fingerprint = NULL;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002000 if (!ParseFingerprintAttribute(line, &fingerprint, error)) {
2001 return false;
2002 }
2003 session_td->identity_fingerprint.reset(fingerprint);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00002004 } else if (HasAttribute(line, kAttributeSetup)) {
2005 if (!ParseDtlsSetup(line, &(session_td->connection_role), error)) {
2006 return false;
2007 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002008 } else if (HasAttribute(line, kAttributeMsidSemantics)) {
2009 std::string semantics;
2010 if (!GetValue(line, kAttributeMsidSemantics, &semantics, error)) {
2011 return false;
2012 }
deadbeefc80741f2015-10-22 13:14:45 -07002013 desc->set_msid_supported(
2014 CaseInsensitiveFind(semantics, kMediaStreamSemantic));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002015 } else if (HasAttribute(line, kAttributeExtmap)) {
2016 RtpHeaderExtension extmap;
2017 if (!ParseExtmap(line, &extmap, error)) {
2018 return false;
2019 }
2020 session_extmaps->push_back(extmap);
2021 }
2022 }
2023
2024 return true;
2025}
2026
2027bool ParseGroupAttribute(const std::string& line,
2028 cricket::SessionDescription* desc,
2029 SdpParseError* error) {
2030 ASSERT(desc != NULL);
2031
2032 // RFC 5888 and draft-holmberg-mmusic-sdp-bundle-negotiation-00
2033 // a=group:BUNDLE video voice
2034 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002035 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002036 kSdpDelimiterSpace, &fields);
2037 std::string semantics;
2038 if (!GetValue(fields[0], kAttributeGroup, &semantics, error)) {
2039 return false;
2040 }
2041 cricket::ContentGroup group(semantics);
2042 for (size_t i = 1; i < fields.size(); ++i) {
2043 group.AddContentName(fields[i]);
2044 }
2045 desc->AddGroup(group);
2046 return true;
2047}
2048
2049static bool ParseFingerprintAttribute(const std::string& line,
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002050 rtc::SSLFingerprint** fingerprint,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002051 SdpParseError* error) {
2052 if (!IsLineType(line, kLineTypeAttributes) ||
2053 !HasAttribute(line, kAttributeFingerprint)) {
2054 return ParseFailedExpectLine(line, 0, kLineTypeAttributes,
2055 kAttributeFingerprint, error);
2056 }
2057
2058 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002059 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002060 kSdpDelimiterSpace, &fields);
2061 const size_t expected_fields = 2;
2062 if (fields.size() != expected_fields) {
2063 return ParseFailedExpectFieldNum(line, expected_fields, error);
2064 }
2065
2066 // The first field here is "fingerprint:<hash>.
2067 std::string algorithm;
2068 if (!GetValue(fields[0], kAttributeFingerprint, &algorithm, error)) {
2069 return false;
2070 }
2071
2072 // Downcase the algorithm. Note that we don't need to downcase the
2073 // fingerprint because hex_decode can handle upper-case.
2074 std::transform(algorithm.begin(), algorithm.end(), algorithm.begin(),
2075 ::tolower);
2076
2077 // The second field is the digest value. De-hexify it.
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002078 *fingerprint = rtc::SSLFingerprint::CreateFromRfc4572(
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002079 algorithm, fields[1]);
2080 if (!*fingerprint) {
2081 return ParseFailed(line,
2082 "Failed to create fingerprint from the digest.",
2083 error);
2084 }
2085
2086 return true;
2087}
2088
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00002089static bool ParseDtlsSetup(const std::string& line,
2090 cricket::ConnectionRole* role,
2091 SdpParseError* error) {
2092 // setup-attr = "a=setup:" role
2093 // role = "active" / "passive" / "actpass" / "holdconn"
2094 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002095 rtc::split(line.substr(kLinePrefixLength), kSdpDelimiterColon, &fields);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00002096 const size_t expected_fields = 2;
2097 if (fields.size() != expected_fields) {
2098 return ParseFailedExpectFieldNum(line, expected_fields, error);
2099 }
2100 std::string role_str = fields[1];
2101 if (!cricket::StringToConnectionRole(role_str, role)) {
2102 return ParseFailed(line, "Invalid attribute value.", error);
2103 }
2104 return true;
2105}
2106
deadbeef9d3584c2016-02-16 17:54:10 -08002107static bool ParseMsidAttribute(const std::string& line,
2108 std::string* stream_id,
2109 std::string* track_id,
2110 SdpParseError* error) {
2111 // draft-ietf-mmusic-msid-11
2112 // a=msid:<stream id> <track id>
2113 // msid-value = msid-id [ SP msid-appdata ]
2114 // msid-id = 1*64token-char ; see RFC 4566
2115 // msid-appdata = 1*64token-char ; see RFC 4566
2116 std::string field1;
2117 if (!rtc::tokenize_first(line.substr(kLinePrefixLength), kSdpDelimiterSpace,
2118 &field1, track_id)) {
2119 const size_t expected_fields = 2;
2120 return ParseFailedExpectFieldNum(line, expected_fields, error);
2121 }
2122
2123 // msid:<msid-id>
2124 if (!GetValue(field1, kAttributeMsid, stream_id, error)) {
2125 return false;
2126 }
2127 return true;
2128}
2129
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002130// RFC 3551
2131// PT encoding media type clock rate channels
2132// name (Hz)
2133// 0 PCMU A 8,000 1
2134// 1 reserved A
2135// 2 reserved A
2136// 3 GSM A 8,000 1
2137// 4 G723 A 8,000 1
2138// 5 DVI4 A 8,000 1
2139// 6 DVI4 A 16,000 1
2140// 7 LPC A 8,000 1
2141// 8 PCMA A 8,000 1
2142// 9 G722 A 8,000 1
2143// 10 L16 A 44,100 2
2144// 11 L16 A 44,100 1
2145// 12 QCELP A 8,000 1
2146// 13 CN A 8,000 1
2147// 14 MPA A 90,000 (see text)
2148// 15 G728 A 8,000 1
2149// 16 DVI4 A 11,025 1
2150// 17 DVI4 A 22,050 1
2151// 18 G729 A 8,000 1
2152struct StaticPayloadAudioCodec {
2153 const char* name;
2154 int clockrate;
Peter Kasting69558702016-01-12 16:26:35 -08002155 size_t channels;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002156};
2157static const StaticPayloadAudioCodec kStaticPayloadAudioCodecs[] = {
2158 { "PCMU", 8000, 1 },
2159 { "reserved", 0, 0 },
2160 { "reserved", 0, 0 },
2161 { "GSM", 8000, 1 },
2162 { "G723", 8000, 1 },
2163 { "DVI4", 8000, 1 },
2164 { "DVI4", 16000, 1 },
2165 { "LPC", 8000, 1 },
2166 { "PCMA", 8000, 1 },
2167 { "G722", 8000, 1 },
2168 { "L16", 44100, 2 },
2169 { "L16", 44100, 1 },
2170 { "QCELP", 8000, 1 },
2171 { "CN", 8000, 1 },
2172 { "MPA", 90000, 1 },
2173 { "G728", 8000, 1 },
2174 { "DVI4", 11025, 1 },
2175 { "DVI4", 22050, 1 },
2176 { "G729", 8000, 1 },
2177};
2178
2179void MaybeCreateStaticPayloadAudioCodecs(
2180 const std::vector<int>& fmts, AudioContentDescription* media_desc) {
2181 if (!media_desc) {
2182 return;
2183 }
deadbeef67cf2c12016-04-13 10:07:16 -07002184 RTC_DCHECK(media_desc->codecs().empty());
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002185 std::vector<int>::const_iterator it = fmts.begin();
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002186 for (; it != fmts.end(); ++it) {
2187 int payload_type = *it;
2188 if (!media_desc->HasCodec(payload_type) &&
2189 payload_type >= 0 &&
tfarina5237aaf2015-11-10 23:44:30 -08002190 payload_type < arraysize(kStaticPayloadAudioCodecs)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002191 std::string encoding_name = kStaticPayloadAudioCodecs[payload_type].name;
2192 int clock_rate = kStaticPayloadAudioCodecs[payload_type].clockrate;
Peter Kasting69558702016-01-12 16:26:35 -08002193 size_t channels = kStaticPayloadAudioCodecs[payload_type].channels;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002194 media_desc->AddCodec(cricket::AudioCodec(payload_type, encoding_name,
deadbeef67cf2c12016-04-13 10:07:16 -07002195 clock_rate, 0, channels));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002196 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002197 }
2198}
2199
2200template <class C>
2201static C* ParseContentDescription(const std::string& message,
2202 const MediaType media_type,
2203 int mline_index,
2204 const std::string& protocol,
deadbeef67cf2c12016-04-13 10:07:16 -07002205 const std::vector<int>& payload_types,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002206 size_t* pos,
2207 std::string* content_name,
2208 TransportDescription* transport,
2209 std::vector<JsepIceCandidate*>* candidates,
2210 webrtc::SdpParseError* error) {
2211 C* media_desc = new C();
2212 switch (media_type) {
2213 case cricket::MEDIA_TYPE_AUDIO:
2214 *content_name = cricket::CN_AUDIO;
2215 break;
2216 case cricket::MEDIA_TYPE_VIDEO:
2217 *content_name = cricket::CN_VIDEO;
2218 break;
2219 case cricket::MEDIA_TYPE_DATA:
2220 *content_name = cricket::CN_DATA;
2221 break;
2222 default:
2223 ASSERT(false);
2224 break;
2225 }
deadbeef67cf2c12016-04-13 10:07:16 -07002226 if (!ParseContent(message, media_type, mline_index, protocol, payload_types,
2227 pos, content_name, media_desc, transport, candidates,
2228 error)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002229 delete media_desc;
2230 return NULL;
2231 }
2232 // Sort the codecs according to the m-line fmt list.
deadbeef67cf2c12016-04-13 10:07:16 -07002233 std::unordered_map<int, int> payload_type_preferences;
2234 // "size + 1" so that the lowest preference payload type has a preference of
2235 // 1, which is greater than the default (0) for payload types not in the fmt
2236 // list.
2237 int preference = static_cast<int>(payload_types.size() + 1);
2238 for (int pt : payload_types) {
2239 payload_type_preferences[pt] = preference--;
2240 }
2241 std::vector<typename C::CodecType> codecs = media_desc->codecs();
2242 std::sort(codecs.begin(), codecs.end(), [&payload_type_preferences](
2243 const typename C::CodecType& a,
2244 const typename C::CodecType& b) {
2245 return payload_type_preferences[a.id] > payload_type_preferences[b.id];
2246 });
2247 media_desc->set_codecs(codecs);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002248 return media_desc;
2249}
2250
2251bool ParseMediaDescription(const std::string& message,
2252 const TransportDescription& session_td,
2253 const RtpHeaderExtensions& session_extmaps,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002254 size_t* pos,
2255 cricket::SessionDescription* desc,
2256 std::vector<JsepIceCandidate*>* candidates,
2257 SdpParseError* error) {
2258 ASSERT(desc != NULL);
2259 std::string line;
2260 int mline_index = -1;
2261
2262 // Zero or more media descriptions
2263 // RFC 4566
2264 // m=<media> <port> <proto> <fmt>
2265 while (GetLineWithType(message, pos, &line, kLineTypeMedia)) {
2266 ++mline_index;
2267
2268 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002269 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002270 kSdpDelimiterSpace, &fields);
2271 const size_t expected_min_fields = 4;
2272 if (fields.size() < expected_min_fields) {
2273 return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
2274 }
2275 bool rejected = false;
2276 // RFC 3264
2277 // To reject an offered stream, the port number in the corresponding stream
2278 // in the answer MUST be set to zero.
2279 if (fields[1] == kMediaPortRejected) {
2280 rejected = true;
2281 }
2282
2283 std::string protocol = fields[2];
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002284
2285 // <fmt>
deadbeef67cf2c12016-04-13 10:07:16 -07002286 std::vector<int> payload_types;
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00002287 if (IsRtp(protocol)) {
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00002288 for (size_t j = 3 ; j < fields.size(); ++j) {
2289 // TODO(wu): Remove when below bug is fixed.
2290 // https://bugzilla.mozilla.org/show_bug.cgi?id=996329
pbosbb36fdf2015-07-09 07:48:14 -07002291 if (fields[j].empty() && j == fields.size() - 1) {
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00002292 continue;
2293 }
wu@webrtc.org36eda7c2014-04-15 20:37:30 +00002294
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00002295 int pl = 0;
pkasting@chromium.orge9facf82015-02-17 20:36:28 +00002296 if (!GetPayloadTypeFromString(line, fields[j], &pl, error)) {
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00002297 return false;
2298 }
deadbeef67cf2c12016-04-13 10:07:16 -07002299 payload_types.push_back(pl);
wu@webrtc.org5e760e72014-04-02 23:19:09 +00002300 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002301 }
2302
2303 // Make a temporary TransportDescription based on |session_td|.
2304 // Some of this gets overwritten by ParseContent.
deadbeef46eed762016-01-28 13:24:37 -08002305 TransportDescription transport(
2306 session_td.transport_options, session_td.ice_ufrag, session_td.ice_pwd,
2307 session_td.ice_mode, session_td.connection_role,
2308 session_td.identity_fingerprint.get());
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002309
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002310 rtc::scoped_ptr<MediaContentDescription> content;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002311 std::string content_name;
2312 if (HasAttribute(line, kMediaTypeVideo)) {
2313 content.reset(ParseContentDescription<VideoContentDescription>(
deadbeef67cf2c12016-04-13 10:07:16 -07002314 message, cricket::MEDIA_TYPE_VIDEO, mline_index, protocol,
2315 payload_types, pos, &content_name, &transport, candidates, error));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002316 } else if (HasAttribute(line, kMediaTypeAudio)) {
2317 content.reset(ParseContentDescription<AudioContentDescription>(
deadbeef67cf2c12016-04-13 10:07:16 -07002318 message, cricket::MEDIA_TYPE_AUDIO, mline_index, protocol,
2319 payload_types, pos, &content_name, &transport, candidates, error));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002320 } else if (HasAttribute(line, kMediaTypeData)) {
jiayl@webrtc.org0e527722014-09-08 21:43:43 +00002321 DataContentDescription* data_desc =
wu@webrtc.org78187522013-10-07 23:32:02 +00002322 ParseContentDescription<DataContentDescription>(
deadbeef67cf2c12016-04-13 10:07:16 -07002323 message, cricket::MEDIA_TYPE_DATA, mline_index, protocol,
2324 payload_types, pos, &content_name, &transport, candidates, error);
jiayl@webrtc.org0e527722014-09-08 21:43:43 +00002325 content.reset(data_desc);
wu@webrtc.org78187522013-10-07 23:32:02 +00002326
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00002327 int p;
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00002328 if (data_desc && IsDtlsSctp(protocol) && rtc::FromString(fields[3], &p)) {
jiayl@webrtc.org0e527722014-09-08 21:43:43 +00002329 if (!AddSctpDataCodec(data_desc, p))
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00002330 return false;
wu@webrtc.org78187522013-10-07 23:32:02 +00002331 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002332 } else {
2333 LOG(LS_WARNING) << "Unsupported media type: " << line;
2334 continue;
2335 }
2336 if (!content.get()) {
2337 // ParseContentDescription returns NULL if failed.
2338 return false;
2339 }
2340
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00002341 if (IsRtp(protocol)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002342 // Set the extmap.
2343 if (!session_extmaps.empty() &&
2344 !content->rtp_header_extensions().empty()) {
2345 return ParseFailed("",
2346 "The a=extmap MUST be either all session level or "
2347 "all media level.",
2348 error);
2349 }
2350 for (size_t i = 0; i < session_extmaps.size(); ++i) {
2351 content->AddRtpHeaderExtension(session_extmaps[i]);
2352 }
2353 }
2354 content->set_protocol(protocol);
2355 desc->AddContent(content_name,
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00002356 IsDtlsSctp(protocol) ? cricket::NS_JINGLE_DRAFT_SCTP :
2357 cricket::NS_JINGLE_RTP,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002358 rejected,
2359 content.release());
2360 // Create TransportInfo with the media level "ice-pwd" and "ice-ufrag".
2361 TransportInfo transport_info(content_name, transport);
2362
2363 if (!desc->AddTransportInfo(transport_info)) {
2364 std::ostringstream description;
2365 description << "Failed to AddTransportInfo with content name: "
2366 << content_name;
2367 return ParseFailed("", description.str(), error);
2368 }
2369 }
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00002370
2371 size_t end_of_message = message.size();
2372 if (mline_index == -1 && *pos != end_of_message) {
2373 ParseFailed(message, *pos, "Expects m line.", error);
2374 return false;
2375 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002376 return true;
2377}
2378
2379bool VerifyCodec(const cricket::Codec& codec) {
2380 // Codec has not been populated correctly unless the name has been set. This
2381 // can happen if an SDP has an fmtp or rtcp-fb with a payload type but doesn't
2382 // have a corresponding "rtpmap" line.
2383 cricket::Codec default_codec;
2384 return default_codec.name != codec.name;
2385}
2386
2387bool VerifyAudioCodecs(const AudioContentDescription* audio_desc) {
2388 const std::vector<cricket::AudioCodec>& codecs = audio_desc->codecs();
2389 for (std::vector<cricket::AudioCodec>::const_iterator iter = codecs.begin();
2390 iter != codecs.end(); ++iter) {
2391 if (!VerifyCodec(*iter)) {
2392 return false;
2393 }
2394 }
2395 return true;
2396}
2397
2398bool VerifyVideoCodecs(const VideoContentDescription* video_desc) {
2399 const std::vector<cricket::VideoCodec>& codecs = video_desc->codecs();
2400 for (std::vector<cricket::VideoCodec>::const_iterator iter = codecs.begin();
2401 iter != codecs.end(); ++iter) {
2402 if (!VerifyCodec(*iter)) {
2403 return false;
2404 }
2405 }
2406 return true;
2407}
2408
2409void AddParameters(const cricket::CodecParameterMap& parameters,
2410 cricket::Codec* codec) {
2411 for (cricket::CodecParameterMap::const_iterator iter =
2412 parameters.begin(); iter != parameters.end(); ++iter) {
2413 codec->SetParam(iter->first, iter->second);
2414 }
2415}
2416
2417void AddFeedbackParameter(const cricket::FeedbackParam& feedback_param,
2418 cricket::Codec* codec) {
2419 codec->AddFeedbackParam(feedback_param);
2420}
2421
2422void AddFeedbackParameters(const cricket::FeedbackParams& feedback_params,
2423 cricket::Codec* codec) {
2424 for (std::vector<cricket::FeedbackParam>::const_iterator iter =
2425 feedback_params.params().begin();
2426 iter != feedback_params.params().end(); ++iter) {
2427 codec->AddFeedbackParam(*iter);
2428 }
2429}
2430
2431// Gets the current codec setting associated with |payload_type|. If there
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +00002432// is no Codec associated with that payload type it returns an empty codec
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002433// with that payload type.
2434template <class T>
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +00002435T GetCodecWithPayloadType(const std::vector<T>& codecs, int payload_type) {
2436 T ret_val;
2437 if (!FindCodecById(codecs, payload_type, &ret_val)) {
2438 ret_val.id = payload_type;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002439 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002440 return ret_val;
2441}
2442
2443// Updates or creates a new codec entry in the audio description.
2444template <class T, class U>
2445void AddOrReplaceCodec(MediaContentDescription* content_desc, const U& codec) {
2446 T* desc = static_cast<T*>(content_desc);
2447 std::vector<U> codecs = desc->codecs();
2448 bool found = false;
2449
2450 typename std::vector<U>::iterator iter;
2451 for (iter = codecs.begin(); iter != codecs.end(); ++iter) {
2452 if (iter->id == codec.id) {
2453 *iter = codec;
2454 found = true;
2455 break;
2456 }
2457 }
2458 if (!found) {
2459 desc->AddCodec(codec);
2460 return;
2461 }
2462 desc->set_codecs(codecs);
2463}
2464
2465// Adds or updates existing codec corresponding to |payload_type| according
2466// to |parameters|.
2467template <class T, class U>
2468void UpdateCodec(MediaContentDescription* content_desc, int payload_type,
2469 const cricket::CodecParameterMap& parameters) {
2470 // Codec might already have been populated (from rtpmap).
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +00002471 U new_codec = GetCodecWithPayloadType(static_cast<T*>(content_desc)->codecs(),
2472 payload_type);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002473 AddParameters(parameters, &new_codec);
2474 AddOrReplaceCodec<T, U>(content_desc, new_codec);
2475}
2476
2477// Adds or updates existing codec corresponding to |payload_type| according
2478// to |feedback_param|.
2479template <class T, class U>
2480void UpdateCodec(MediaContentDescription* content_desc, int payload_type,
2481 const cricket::FeedbackParam& feedback_param) {
2482 // Codec might already have been populated (from rtpmap).
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +00002483 U new_codec = GetCodecWithPayloadType(static_cast<T*>(content_desc)->codecs(),
2484 payload_type);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002485 AddFeedbackParameter(feedback_param, &new_codec);
2486 AddOrReplaceCodec<T, U>(content_desc, new_codec);
2487}
2488
jlmiller@webrtc.orga744a282015-02-18 21:37:46 +00002489template <class T>
2490bool PopWildcardCodec(std::vector<T>* codecs, T* wildcard_codec) {
2491 for (auto iter = codecs->begin(); iter != codecs->end(); ++iter) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002492 if (iter->id == kWildcardPayloadType) {
2493 *wildcard_codec = *iter;
2494 codecs->erase(iter);
2495 return true;
2496 }
2497 }
2498 return false;
2499}
2500
jlmiller@webrtc.orga744a282015-02-18 21:37:46 +00002501template<class T>
2502void UpdateFromWildcardCodecs(cricket::MediaContentDescriptionImpl<T>* desc) {
2503 auto codecs = desc->codecs();
2504 T wildcard_codec;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002505 if (!PopWildcardCodec(&codecs, &wildcard_codec)) {
2506 return;
2507 }
jlmiller@webrtc.orga744a282015-02-18 21:37:46 +00002508 for (auto& codec : codecs) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002509 AddFeedbackParameters(wildcard_codec.feedback_params, &codec);
2510 }
jlmiller@webrtc.orga744a282015-02-18 21:37:46 +00002511 desc->set_codecs(codecs);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002512}
2513
2514void AddAudioAttribute(const std::string& name, const std::string& value,
2515 AudioContentDescription* audio_desc) {
2516 if (value.empty()) {
2517 return;
2518 }
2519 std::vector<cricket::AudioCodec> codecs = audio_desc->codecs();
2520 for (std::vector<cricket::AudioCodec>::iterator iter = codecs.begin();
2521 iter != codecs.end(); ++iter) {
2522 iter->params[name] = value;
2523 }
2524 audio_desc->set_codecs(codecs);
2525}
2526
2527bool ParseContent(const std::string& message,
2528 const MediaType media_type,
2529 int mline_index,
2530 const std::string& protocol,
deadbeef67cf2c12016-04-13 10:07:16 -07002531 const std::vector<int>& payload_types,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002532 size_t* pos,
2533 std::string* content_name,
2534 MediaContentDescription* media_desc,
2535 TransportDescription* transport,
2536 std::vector<JsepIceCandidate*>* candidates,
2537 SdpParseError* error) {
2538 ASSERT(media_desc != NULL);
2539 ASSERT(content_name != NULL);
2540 ASSERT(transport != NULL);
2541
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +00002542 if (media_type == cricket::MEDIA_TYPE_AUDIO) {
2543 MaybeCreateStaticPayloadAudioCodecs(
deadbeef67cf2c12016-04-13 10:07:16 -07002544 payload_types, static_cast<AudioContentDescription*>(media_desc));
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +00002545 }
2546
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002547 // The media level "ice-ufrag" and "ice-pwd".
2548 // The candidates before update the media level "ice-pwd" and "ice-ufrag".
2549 Candidates candidates_orig;
2550 std::string line;
2551 std::string mline_id;
2552 // Tracks created out of the ssrc attributes.
2553 StreamParamsVec tracks;
2554 SsrcInfoVec ssrc_infos;
2555 SsrcGroupVec ssrc_groups;
2556 std::string maxptime_as_string;
2557 std::string ptime_as_string;
deadbeef9d3584c2016-02-16 17:54:10 -08002558 std::string stream_id;
2559 std::string track_id;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002560
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002561 // Loop until the next m line
2562 while (!IsLineType(message, kLineTypeMedia, *pos)) {
2563 if (!GetLine(message, pos, &line)) {
2564 if (*pos >= message.size()) {
2565 break; // Done parsing
2566 } else {
sergeyu@chromium.org5bc25c42013-12-05 00:24:06 +00002567 return ParseFailed(message, *pos, "Invalid SDP line.", error);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002568 }
2569 }
2570
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002571 // RFC 4566
2572 // b=* (zero or more bandwidth information lines)
2573 if (IsLineType(line, kLineTypeSessionBandwidth)) {
2574 std::string bandwidth;
2575 if (HasAttribute(line, kApplicationSpecificMaximum)) {
2576 if (!GetValue(line, kApplicationSpecificMaximum, &bandwidth, error)) {
2577 return false;
2578 } else {
wu@webrtc.org5e760e72014-04-02 23:19:09 +00002579 int b = 0;
2580 if (!GetValueFromString(line, bandwidth, &b, error)) {
2581 return false;
2582 }
Peter Thatcherc0c3a862015-06-24 15:31:25 -07002583 // We should never use more than the default bandwidth for RTP-based
2584 // data channels. Don't allow SDP to set the bandwidth, because
2585 // that would give JS the opportunity to "break the Internet".
2586 // See: https://code.google.com/p/chromium/issues/detail?id=280726
2587 if (media_type == cricket::MEDIA_TYPE_DATA && IsRtp(protocol) &&
2588 b > cricket::kDataMaxBandwidth / 1000) {
2589 std::ostringstream description;
2590 description << "RTP-based data channels may not send more than "
2591 << cricket::kDataMaxBandwidth / 1000 << "kbps.";
2592 return ParseFailed(line, description.str(), error);
2593 }
wu@webrtc.org5e760e72014-04-02 23:19:09 +00002594 media_desc->set_bandwidth(b * 1000);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002595 }
2596 }
2597 continue;
2598 }
2599
2600 if (!IsLineType(line, kLineTypeAttributes)) {
2601 // TODO: Handle other lines if needed.
2602 LOG(LS_INFO) << "Ignored line: " << line;
2603 continue;
2604 }
2605
2606 // Handle attributes common to SCTP and RTP.
2607 if (HasAttribute(line, kAttributeMid)) {
2608 // RFC 3388
2609 // mid-attribute = "a=mid:" identification-tag
2610 // identification-tag = token
2611 // Use the mid identification-tag as the content name.
2612 if (!GetValue(line, kAttributeMid, &mline_id, error)) {
2613 return false;
2614 }
2615 *content_name = mline_id;
2616 } else if (HasAttribute(line, kAttributeCandidate)) {
2617 Candidate candidate;
2618 if (!ParseCandidate(line, &candidate, error, false)) {
2619 return false;
2620 }
2621 candidates_orig.push_back(candidate);
2622 } else if (HasAttribute(line, kAttributeIceUfrag)) {
2623 if (!GetValue(line, kAttributeIceUfrag, &transport->ice_ufrag, error)) {
2624 return false;
2625 }
2626 } else if (HasAttribute(line, kAttributeIcePwd)) {
2627 if (!GetValue(line, kAttributeIcePwd, &transport->ice_pwd, error)) {
2628 return false;
2629 }
2630 } else if (HasAttribute(line, kAttributeIceOption)) {
2631 if (!ParseIceOptions(line, &transport->transport_options, error)) {
2632 return false;
2633 }
2634 } else if (HasAttribute(line, kAttributeFmtp)) {
2635 if (!ParseFmtpAttributes(line, media_type, media_desc, error)) {
2636 return false;
2637 }
2638 } else if (HasAttribute(line, kAttributeFingerprint)) {
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002639 rtc::SSLFingerprint* fingerprint = NULL;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002640
2641 if (!ParseFingerprintAttribute(line, &fingerprint, error)) {
2642 return false;
2643 }
2644 transport->identity_fingerprint.reset(fingerprint);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00002645 } else if (HasAttribute(line, kAttributeSetup)) {
2646 if (!ParseDtlsSetup(line, &(transport->connection_role), error)) {
2647 return false;
2648 }
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00002649 } else if (IsDtlsSctp(protocol) && HasAttribute(line, kAttributeSctpPort)) {
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00002650 int sctp_port;
2651 if (!ParseSctpPort(line, &sctp_port, error)) {
2652 return false;
2653 }
2654 if (!AddSctpDataCodec(static_cast<DataContentDescription*>(media_desc),
2655 sctp_port)) {
2656 return false;
2657 }
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00002658 } else if (IsRtp(protocol)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002659 //
2660 // RTP specific attrubtes
2661 //
2662 if (HasAttribute(line, kAttributeRtcpMux)) {
2663 media_desc->set_rtcp_mux(true);
deadbeef13871492015-12-09 12:37:51 -08002664 } else if (HasAttribute(line, kAttributeRtcpReducedSize)) {
2665 media_desc->set_rtcp_reduced_size(true);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002666 } else if (HasAttribute(line, kAttributeSsrcGroup)) {
2667 if (!ParseSsrcGroupAttribute(line, &ssrc_groups, error)) {
2668 return false;
2669 }
2670 } else if (HasAttribute(line, kAttributeSsrc)) {
2671 if (!ParseSsrcAttribute(line, &ssrc_infos, error)) {
2672 return false;
2673 }
2674 } else if (HasAttribute(line, kAttributeCrypto)) {
2675 if (!ParseCryptoAttribute(line, media_desc, error)) {
2676 return false;
2677 }
2678 } else if (HasAttribute(line, kAttributeRtpmap)) {
deadbeef67cf2c12016-04-13 10:07:16 -07002679 if (!ParseRtpmapAttribute(line, media_type, payload_types, media_desc,
2680 error)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002681 return false;
2682 }
2683 } else if (HasAttribute(line, kCodecParamMaxPTime)) {
2684 if (!GetValue(line, kCodecParamMaxPTime, &maxptime_as_string, error)) {
2685 return false;
2686 }
2687 } else if (HasAttribute(line, kAttributeRtcpFb)) {
2688 if (!ParseRtcpFbAttribute(line, media_type, media_desc, error)) {
2689 return false;
2690 }
2691 } else if (HasAttribute(line, kCodecParamPTime)) {
2692 if (!GetValue(line, kCodecParamPTime, &ptime_as_string, error)) {
2693 return false;
2694 }
2695 } else if (HasAttribute(line, kAttributeSendOnly)) {
2696 media_desc->set_direction(cricket::MD_SENDONLY);
2697 } else if (HasAttribute(line, kAttributeRecvOnly)) {
2698 media_desc->set_direction(cricket::MD_RECVONLY);
2699 } else if (HasAttribute(line, kAttributeInactive)) {
2700 media_desc->set_direction(cricket::MD_INACTIVE);
2701 } else if (HasAttribute(line, kAttributeSendRecv)) {
2702 media_desc->set_direction(cricket::MD_SENDRECV);
2703 } else if (HasAttribute(line, kAttributeExtmap)) {
2704 RtpHeaderExtension extmap;
2705 if (!ParseExtmap(line, &extmap, error)) {
2706 return false;
2707 }
2708 media_desc->AddRtpHeaderExtension(extmap);
2709 } else if (HasAttribute(line, kAttributeXGoogleFlag)) {
2710 // Experimental attribute. Conference mode activates more aggressive
2711 // AEC and NS settings.
2712 // TODO: expose API to set these directly.
2713 std::string flag_value;
2714 if (!GetValue(line, kAttributeXGoogleFlag, &flag_value, error)) {
2715 return false;
2716 }
2717 if (flag_value.compare(kValueConference) == 0)
2718 media_desc->set_conference_mode(true);
deadbeef9d3584c2016-02-16 17:54:10 -08002719 } else if (HasAttribute(line, kAttributeMsid)) {
2720 if (!ParseMsidAttribute(line, &stream_id, &track_id, error)) {
2721 return false;
2722 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002723 }
2724 } else {
2725 // Only parse lines that we are interested of.
2726 LOG(LS_INFO) << "Ignored line: " << line;
2727 continue;
2728 }
2729 }
2730
2731 // Create tracks from the |ssrc_infos|.
Taylor Brandstetter5de6b752016-03-09 17:02:30 -08002732 // If the stream_id/track_id for all SSRCS are identical, one StreamParams
2733 // will be created in CreateTracksFromSsrcInfos, containing all the SSRCs from
2734 // the m= section.
2735 CreateTracksFromSsrcInfos(ssrc_infos, stream_id, track_id, &tracks);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002736
2737 // Add the ssrc group to the track.
2738 for (SsrcGroupVec::iterator ssrc_group = ssrc_groups.begin();
2739 ssrc_group != ssrc_groups.end(); ++ssrc_group) {
2740 if (ssrc_group->ssrcs.empty()) {
2741 continue;
2742 }
Peter Boström0c4e06b2015-10-07 12:23:21 +02002743 uint32_t ssrc = ssrc_group->ssrcs.front();
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002744 for (StreamParamsVec::iterator track = tracks.begin();
2745 track != tracks.end(); ++track) {
2746 if (track->has_ssrc(ssrc)) {
2747 track->ssrc_groups.push_back(*ssrc_group);
2748 }
2749 }
2750 }
2751
2752 // Add the new tracks to the |media_desc|.
deadbeef9d3584c2016-02-16 17:54:10 -08002753 for (StreamParams& track : tracks) {
2754 media_desc->AddStream(track);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002755 }
2756
2757 if (media_type == cricket::MEDIA_TYPE_AUDIO) {
2758 AudioContentDescription* audio_desc =
2759 static_cast<AudioContentDescription*>(media_desc);
jlmiller@webrtc.orga744a282015-02-18 21:37:46 +00002760 UpdateFromWildcardCodecs(audio_desc);
2761
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002762 // Verify audio codec ensures that no audio codec has been populated with
2763 // only fmtp.
2764 if (!VerifyAudioCodecs(audio_desc)) {
2765 return ParseFailed("Failed to parse audio codecs correctly.", error);
2766 }
2767 AddAudioAttribute(kCodecParamMaxPTime, maxptime_as_string, audio_desc);
2768 AddAudioAttribute(kCodecParamPTime, ptime_as_string, audio_desc);
2769 }
2770
2771 if (media_type == cricket::MEDIA_TYPE_VIDEO) {
jlmiller@webrtc.orga744a282015-02-18 21:37:46 +00002772 VideoContentDescription* video_desc =
2773 static_cast<VideoContentDescription*>(media_desc);
2774 UpdateFromWildcardCodecs(video_desc);
2775 // Verify video codec ensures that no video codec has been populated with
2776 // only rtcp-fb.
2777 if (!VerifyVideoCodecs(video_desc)) {
2778 return ParseFailed("Failed to parse video codecs correctly.", error);
2779 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002780 }
2781
2782 // RFC 5245
2783 // Update the candidates with the media level "ice-pwd" and "ice-ufrag".
2784 for (Candidates::iterator it = candidates_orig.begin();
2785 it != candidates_orig.end(); ++it) {
honghaiza54a0802015-12-16 18:37:23 -08002786 ASSERT((*it).username().empty() ||
2787 (*it).username() == transport->ice_ufrag);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002788 (*it).set_username(transport->ice_ufrag);
2789 ASSERT((*it).password().empty());
2790 (*it).set_password(transport->ice_pwd);
2791 candidates->push_back(
2792 new JsepIceCandidate(mline_id, mline_index, *it));
2793 }
2794 return true;
2795}
2796
2797bool ParseSsrcAttribute(const std::string& line, SsrcInfoVec* ssrc_infos,
2798 SdpParseError* error) {
2799 ASSERT(ssrc_infos != NULL);
2800 // RFC 5576
2801 // a=ssrc:<ssrc-id> <attribute>
2802 // a=ssrc:<ssrc-id> <attribute>:<value>
2803 std::string field1, field2;
Donald Curtis144d0182015-05-15 13:14:24 -07002804 if (!rtc::tokenize_first(line.substr(kLinePrefixLength), kSdpDelimiterSpace,
2805 &field1, &field2)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002806 const size_t expected_fields = 2;
2807 return ParseFailedExpectFieldNum(line, expected_fields, error);
2808 }
2809
2810 // ssrc:<ssrc-id>
2811 std::string ssrc_id_s;
2812 if (!GetValue(field1, kAttributeSsrc, &ssrc_id_s, error)) {
2813 return false;
2814 }
Peter Boström0c4e06b2015-10-07 12:23:21 +02002815 uint32_t ssrc_id = 0;
wu@webrtc.org5e760e72014-04-02 23:19:09 +00002816 if (!GetValueFromString(line, ssrc_id_s, &ssrc_id, error)) {
2817 return false;
2818 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002819
2820 std::string attribute;
2821 std::string value;
Donald Curtis144d0182015-05-15 13:14:24 -07002822 if (!rtc::tokenize_first(field2, kSdpDelimiterColon, &attribute, &value)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002823 std::ostringstream description;
2824 description << "Failed to get the ssrc attribute value from " << field2
2825 << ". Expected format <attribute>:<value>.";
2826 return ParseFailed(line, description.str(), error);
2827 }
2828
2829 // Check if there's already an item for this |ssrc_id|. Create a new one if
2830 // there isn't.
2831 SsrcInfoVec::iterator ssrc_info = ssrc_infos->begin();
2832 for (; ssrc_info != ssrc_infos->end(); ++ssrc_info) {
2833 if (ssrc_info->ssrc_id == ssrc_id) {
2834 break;
2835 }
2836 }
2837 if (ssrc_info == ssrc_infos->end()) {
2838 SsrcInfo info;
2839 info.ssrc_id = ssrc_id;
2840 ssrc_infos->push_back(info);
2841 ssrc_info = ssrc_infos->end() - 1;
2842 }
2843
2844 // Store the info to the |ssrc_info|.
2845 if (attribute == kSsrcAttributeCname) {
2846 // RFC 5576
2847 // cname:<value>
2848 ssrc_info->cname = value;
2849 } else if (attribute == kSsrcAttributeMsid) {
2850 // draft-alvestrand-mmusic-msid-00
2851 // "msid:" identifier [ " " appdata ]
2852 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002853 rtc::split(value, kSdpDelimiterSpace, &fields);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002854 if (fields.size() < 1 || fields.size() > 2) {
2855 return ParseFailed(line,
2856 "Expected format \"msid:<identifier>[ <appdata>]\".",
2857 error);
2858 }
deadbeef9d3584c2016-02-16 17:54:10 -08002859 ssrc_info->stream_id = fields[0];
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002860 if (fields.size() == 2) {
deadbeef9d3584c2016-02-16 17:54:10 -08002861 ssrc_info->track_id = fields[1];
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002862 }
2863 } else if (attribute == kSsrcAttributeMslabel) {
2864 // draft-alvestrand-rtcweb-mid-01
2865 // mslabel:<value>
2866 ssrc_info->mslabel = value;
2867 } else if (attribute == kSSrcAttributeLabel) {
2868 // The label isn't defined.
2869 // label:<value>
2870 ssrc_info->label = value;
2871 }
2872 return true;
2873}
2874
2875bool ParseSsrcGroupAttribute(const std::string& line,
2876 SsrcGroupVec* ssrc_groups,
2877 SdpParseError* error) {
2878 ASSERT(ssrc_groups != NULL);
2879 // RFC 5576
2880 // a=ssrc-group:<semantics> <ssrc-id> ...
2881 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002882 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002883 kSdpDelimiterSpace, &fields);
2884 const size_t expected_min_fields = 2;
2885 if (fields.size() < expected_min_fields) {
2886 return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
2887 }
2888 std::string semantics;
2889 if (!GetValue(fields[0], kAttributeSsrcGroup, &semantics, error)) {
2890 return false;
2891 }
Peter Boström0c4e06b2015-10-07 12:23:21 +02002892 std::vector<uint32_t> ssrcs;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002893 for (size_t i = 1; i < fields.size(); ++i) {
Peter Boström0c4e06b2015-10-07 12:23:21 +02002894 uint32_t ssrc = 0;
wu@webrtc.org5e760e72014-04-02 23:19:09 +00002895 if (!GetValueFromString(line, fields[i], &ssrc, error)) {
2896 return false;
2897 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002898 ssrcs.push_back(ssrc);
2899 }
2900 ssrc_groups->push_back(SsrcGroup(semantics, ssrcs));
2901 return true;
2902}
2903
2904bool ParseCryptoAttribute(const std::string& line,
2905 MediaContentDescription* media_desc,
2906 SdpParseError* error) {
2907 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002908 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002909 kSdpDelimiterSpace, &fields);
2910 // RFC 4568
2911 // a=crypto:<tag> <crypto-suite> <key-params> [<session-params>]
2912 const size_t expected_min_fields = 3;
2913 if (fields.size() < expected_min_fields) {
2914 return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
2915 }
2916 std::string tag_value;
2917 if (!GetValue(fields[0], kAttributeCrypto, &tag_value, error)) {
2918 return false;
2919 }
wu@webrtc.org5e760e72014-04-02 23:19:09 +00002920 int tag = 0;
2921 if (!GetValueFromString(line, tag_value, &tag, error)) {
2922 return false;
2923 }
jbauch083b73f2015-07-16 02:46:32 -07002924 const std::string& crypto_suite = fields[1];
2925 const std::string& key_params = fields[2];
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002926 std::string session_params;
2927 if (fields.size() > 3) {
2928 session_params = fields[3];
2929 }
2930 media_desc->AddCrypto(CryptoParams(tag, crypto_suite, key_params,
2931 session_params));
2932 return true;
2933}
2934
2935// Updates or creates a new codec entry in the audio description with according
deadbeef67cf2c12016-04-13 10:07:16 -07002936// to |name|, |clockrate|, |bitrate|, and |channels|.
2937void UpdateCodec(int payload_type,
2938 const std::string& name,
2939 int clockrate,
2940 int bitrate,
2941 size_t channels,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002942 AudioContentDescription* audio_desc) {
2943 // Codec may already be populated with (only) optional parameters
2944 // (from an fmtp).
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +00002945 cricket::AudioCodec codec =
2946 GetCodecWithPayloadType(audio_desc->codecs(), payload_type);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002947 codec.name = name;
2948 codec.clockrate = clockrate;
2949 codec.bitrate = bitrate;
2950 codec.channels = channels;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002951 AddOrReplaceCodec<AudioContentDescription, cricket::AudioCodec>(audio_desc,
2952 codec);
2953}
2954
2955// Updates or creates a new codec entry in the video description according to
deadbeef67cf2c12016-04-13 10:07:16 -07002956// |name|, |width|, |height|, and |framerate|.
2957void UpdateCodec(int payload_type,
2958 const std::string& name,
2959 int width,
2960 int height,
2961 int framerate,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002962 VideoContentDescription* video_desc) {
2963 // Codec may already be populated with (only) optional parameters
2964 // (from an fmtp).
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +00002965 cricket::VideoCodec codec =
2966 GetCodecWithPayloadType(video_desc->codecs(), payload_type);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002967 codec.name = name;
2968 codec.width = width;
2969 codec.height = height;
2970 codec.framerate = framerate;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002971 AddOrReplaceCodec<VideoContentDescription, cricket::VideoCodec>(video_desc,
2972 codec);
2973}
2974
2975bool ParseRtpmapAttribute(const std::string& line,
2976 const MediaType media_type,
deadbeef67cf2c12016-04-13 10:07:16 -07002977 const std::vector<int>& payload_types,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002978 MediaContentDescription* media_desc,
2979 SdpParseError* error) {
2980 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00002981 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002982 kSdpDelimiterSpace, &fields);
2983 // RFC 4566
2984 // a=rtpmap:<payload type> <encoding name>/<clock rate>[/<encodingparameters>]
2985 const size_t expected_min_fields = 2;
2986 if (fields.size() < expected_min_fields) {
2987 return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
2988 }
2989 std::string payload_type_value;
2990 if (!GetValue(fields[0], kAttributeRtpmap, &payload_type_value, error)) {
2991 return false;
2992 }
wu@webrtc.org5e760e72014-04-02 23:19:09 +00002993 int payload_type = 0;
pkasting@chromium.orge9facf82015-02-17 20:36:28 +00002994 if (!GetPayloadTypeFromString(line, payload_type_value, &payload_type,
2995 error)) {
wu@webrtc.org5e760e72014-04-02 23:19:09 +00002996 return false;
2997 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002998
deadbeef67cf2c12016-04-13 10:07:16 -07002999 if (std::find(payload_types.begin(), payload_types.end(), payload_type) ==
3000 payload_types.end()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003001 LOG(LS_WARNING) << "Ignore rtpmap line that did not appear in the "
3002 << "<fmt> of the m-line: " << line;
3003 return true;
3004 }
jbauch083b73f2015-07-16 02:46:32 -07003005 const std::string& encoder = fields[1];
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003006 std::vector<std::string> codec_params;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00003007 rtc::split(encoder, '/', &codec_params);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003008 // <encoding name>/<clock rate>[/<encodingparameters>]
3009 // 2 mandatory fields
3010 if (codec_params.size() < 2 || codec_params.size() > 3) {
3011 return ParseFailed(line,
3012 "Expected format \"<encoding name>/<clock rate>"
3013 "[/<encodingparameters>]\".",
3014 error);
3015 }
jbauch083b73f2015-07-16 02:46:32 -07003016 const std::string& encoding_name = codec_params[0];
wu@webrtc.org5e760e72014-04-02 23:19:09 +00003017 int clock_rate = 0;
3018 if (!GetValueFromString(line, codec_params[1], &clock_rate, error)) {
3019 return false;
3020 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003021 if (media_type == cricket::MEDIA_TYPE_VIDEO) {
3022 VideoContentDescription* video_desc =
3023 static_cast<VideoContentDescription*>(media_desc);
3024 // TODO: We will send resolution in SDP. For now use
3025 // JsepSessionDescription::kMaxVideoCodecWidth and kMaxVideoCodecHeight.
3026 UpdateCodec(payload_type, encoding_name,
3027 JsepSessionDescription::kMaxVideoCodecWidth,
3028 JsepSessionDescription::kMaxVideoCodecHeight,
3029 JsepSessionDescription::kDefaultVideoCodecFramerate,
deadbeef67cf2c12016-04-13 10:07:16 -07003030 video_desc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003031 } else if (media_type == cricket::MEDIA_TYPE_AUDIO) {
3032 // RFC 4566
3033 // For audio streams, <encoding parameters> indicates the number
3034 // of audio channels. This parameter is OPTIONAL and may be
3035 // omitted if the number of channels is one, provided that no
3036 // additional parameters are needed.
Peter Kasting69558702016-01-12 16:26:35 -08003037 size_t channels = 1;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003038 if (codec_params.size() == 3) {
wu@webrtc.org5e760e72014-04-02 23:19:09 +00003039 if (!GetValueFromString(line, codec_params[2], &channels, error)) {
3040 return false;
3041 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003042 }
3043 int bitrate = 0;
3044 // The default behavior for ISAC (bitrate == 0) in webrtcvoiceengine.cc
3045 // (specifically FindWebRtcCodec) is bandwidth-adaptive variable bitrate.
3046 // The bandwidth adaptation doesn't always work well, so this code
3047 // sets a fixed target bitrate instead.
3048 if (_stricmp(encoding_name.c_str(), kIsacCodecName) == 0) {
3049 if (clock_rate <= 16000) {
3050 bitrate = kIsacWbDefaultRate;
3051 } else {
3052 bitrate = kIsacSwbDefaultRate;
3053 }
3054 }
3055 AudioContentDescription* audio_desc =
3056 static_cast<AudioContentDescription*>(media_desc);
3057 UpdateCodec(payload_type, encoding_name, clock_rate, bitrate, channels,
deadbeef67cf2c12016-04-13 10:07:16 -07003058 audio_desc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003059 } else if (media_type == cricket::MEDIA_TYPE_DATA) {
3060 DataContentDescription* data_desc =
3061 static_cast<DataContentDescription*>(media_desc);
deadbeef67cf2c12016-04-13 10:07:16 -07003062 data_desc->AddCodec(cricket::DataCodec(payload_type, encoding_name));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003063 }
3064 return true;
3065}
3066
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003067bool ParseFmtpParam(const std::string& line, std::string* parameter,
3068 std::string* value, SdpParseError* error) {
Donald Curtis0e07f922015-05-15 09:21:23 -07003069 if (!rtc::tokenize_first(line, kSdpDelimiterEqual, parameter, value)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003070 ParseFailed(line, "Unable to parse fmtp parameter. \'=\' missing.", error);
3071 return false;
3072 }
3073 // a=fmtp:<payload_type> <param1>=<value1>; <param2>=<value2>; ...
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003074 return true;
3075}
3076
3077bool ParseFmtpAttributes(const std::string& line, const MediaType media_type,
3078 MediaContentDescription* media_desc,
3079 SdpParseError* error) {
3080 if (media_type != cricket::MEDIA_TYPE_AUDIO &&
3081 media_type != cricket::MEDIA_TYPE_VIDEO) {
3082 return true;
3083 }
Donald Curtis0e07f922015-05-15 09:21:23 -07003084
3085 std::string line_payload;
3086 std::string line_params;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003087
3088 // RFC 5576
3089 // a=fmtp:<format> <format specific parameters>
3090 // At least two fields, whereas the second one is any of the optional
3091 // parameters.
Donald Curtis144d0182015-05-15 13:14:24 -07003092 if (!rtc::tokenize_first(line.substr(kLinePrefixLength), kSdpDelimiterSpace,
3093 &line_payload, &line_params)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003094 ParseFailedExpectMinFieldNum(line, 2, error);
3095 return false;
3096 }
3097
Donald Curtis0e07f922015-05-15 09:21:23 -07003098 // Parse out the payload information.
pkasting@chromium.orgd3245462015-02-23 21:28:22 +00003099 std::string payload_type_str;
Donald Curtis0e07f922015-05-15 09:21:23 -07003100 if (!GetValue(line_payload, kAttributeFmtp, &payload_type_str, error)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003101 return false;
3102 }
3103
Donald Curtis0e07f922015-05-15 09:21:23 -07003104 int payload_type = 0;
Donald Curtis144d0182015-05-15 13:14:24 -07003105 if (!GetPayloadTypeFromString(line_payload, payload_type_str, &payload_type,
3106 error)) {
Donald Curtis0e07f922015-05-15 09:21:23 -07003107 return false;
3108 }
3109
3110 // Parse out format specific parameters.
3111 std::vector<std::string> fields;
3112 rtc::split(line_params, kSdpDelimiterSemicolon, &fields);
3113
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003114 cricket::CodecParameterMap codec_params;
Donald Curtis144d0182015-05-15 13:14:24 -07003115 for (auto& iter : fields) {
Donald Curtis0e07f922015-05-15 09:21:23 -07003116 if (iter.find(kSdpDelimiterEqual) == std::string::npos) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003117 // Only fmtps with equals are currently supported. Other fmtp types
3118 // should be ignored. Unknown fmtps do not constitute an error.
3119 continue;
3120 }
Donald Curtis0e07f922015-05-15 09:21:23 -07003121
3122 std::string name;
3123 std::string value;
3124 if (!ParseFmtpParam(rtc::string_trim(iter), &name, &value, error)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003125 return false;
3126 }
3127 codec_params[name] = value;
3128 }
3129
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003130 if (media_type == cricket::MEDIA_TYPE_AUDIO) {
3131 UpdateCodec<AudioContentDescription, cricket::AudioCodec>(
pkasting@chromium.orgd3245462015-02-23 21:28:22 +00003132 media_desc, payload_type, codec_params);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003133 } else if (media_type == cricket::MEDIA_TYPE_VIDEO) {
3134 UpdateCodec<VideoContentDescription, cricket::VideoCodec>(
pkasting@chromium.orgd3245462015-02-23 21:28:22 +00003135 media_desc, payload_type, codec_params);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003136 }
3137 return true;
3138}
3139
3140bool ParseRtcpFbAttribute(const std::string& line, const MediaType media_type,
3141 MediaContentDescription* media_desc,
3142 SdpParseError* error) {
3143 if (media_type != cricket::MEDIA_TYPE_AUDIO &&
3144 media_type != cricket::MEDIA_TYPE_VIDEO) {
3145 return true;
3146 }
3147 std::vector<std::string> rtcp_fb_fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00003148 rtc::split(line.c_str(), kSdpDelimiterSpace, &rtcp_fb_fields);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003149 if (rtcp_fb_fields.size() < 2) {
3150 return ParseFailedGetValue(line, kAttributeRtcpFb, error);
3151 }
3152 std::string payload_type_string;
3153 if (!GetValue(rtcp_fb_fields[0], kAttributeRtcpFb, &payload_type_string,
3154 error)) {
3155 return false;
3156 }
wu@webrtc.org5e760e72014-04-02 23:19:09 +00003157 int payload_type = kWildcardPayloadType;
3158 if (payload_type_string != "*") {
pkasting@chromium.orge9facf82015-02-17 20:36:28 +00003159 if (!GetPayloadTypeFromString(line, payload_type_string, &payload_type,
3160 error)) {
wu@webrtc.org5e760e72014-04-02 23:19:09 +00003161 return false;
3162 }
3163 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003164 std::string id = rtcp_fb_fields[1];
3165 std::string param = "";
3166 for (std::vector<std::string>::iterator iter = rtcp_fb_fields.begin() + 2;
3167 iter != rtcp_fb_fields.end(); ++iter) {
3168 param.append(*iter);
3169 }
3170 const cricket::FeedbackParam feedback_param(id, param);
3171
3172 if (media_type == cricket::MEDIA_TYPE_AUDIO) {
pkasting@chromium.orgd3245462015-02-23 21:28:22 +00003173 UpdateCodec<AudioContentDescription, cricket::AudioCodec>(
3174 media_desc, payload_type, feedback_param);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003175 } else if (media_type == cricket::MEDIA_TYPE_VIDEO) {
pkasting@chromium.orgd3245462015-02-23 21:28:22 +00003176 UpdateCodec<VideoContentDescription, cricket::VideoCodec>(
3177 media_desc, payload_type, feedback_param);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003178 }
3179 return true;
3180}
3181
3182} // namespace webrtc