blob: ced85cb323d7cf643427257c19d9b874a8c6f3c4 [file] [log] [blame]
henrike@webrtc.org28e20752013-07-10 00:45:361/*
kjellanderb24317b2016-02-10 15:54:432 * Copyright 2011 The WebRTC project authors. All Rights Reserved.
henrike@webrtc.org28e20752013-07-10 00:45:363 *
kjellanderb24317b2016-02-10 15:54:434 * 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:369 */
10
ossu7bb87ee2017-01-23 12:56:2511#include "webrtc/pc/webrtcsdp.h"
henrike@webrtc.org28e20752013-07-10 00:45:3612
kjellandera96e2d72016-02-05 07:52:2813#include <ctype.h>
henrike@webrtc.org28e20752013-07-10 00:45:3614#include <limits.h>
15#include <stdio.h>
kwibergd1fe2812016-04-27 13:47:2916
henrike@webrtc.org28e20752013-07-10 00:45:3617#include <algorithm>
kwibergd1fe2812016-04-27 13:47:2918#include <memory>
henrike@webrtc.org28e20752013-07-10 00:45:3619#include <string>
deadbeef67cf2c12016-04-13 17:07:1620#include <unordered_map>
henrike@webrtc.org28e20752013-07-10 00:45:3621#include <vector>
22
Henrik Kjellander15583c12016-02-10 09:53:1223#include "webrtc/api/jsepicecandidate.h"
24#include "webrtc/api/jsepsessiondescription.h"
tfarina5237aaf2015-11-11 07:44:3025#include "webrtc/base/arraysize.h"
nissec80e7412017-01-11 13:56:4626#include "webrtc/base/checks.h"
buildbot@webrtc.orga09a9992014-08-13 17:26:0827#include "webrtc/base/common.h"
28#include "webrtc/base/logging.h"
29#include "webrtc/base/messagedigest.h"
30#include "webrtc/base/stringutils.h"
isheriff6f8d6862016-05-26 18:24:5531// for RtpExtension
32#include "webrtc/config.h"
kjellandera96e2d72016-02-05 07:52:2833#include "webrtc/media/base/codec.h"
kjellandera96e2d72016-02-05 07:52:2834#include "webrtc/media/base/cryptoparams.h"
kjellanderf4752772016-03-02 13:42:3035#include "webrtc/media/base/mediaconstants.h"
kjellandera96e2d72016-02-05 07:52:2836#include "webrtc/media/base/rtputils.h"
deadbeef953c2ce2017-01-09 22:53:4137#include "webrtc/media/sctp/sctptransportinternal.h"
kjellandera96e2d72016-02-05 07:52:2838#include "webrtc/p2p/base/candidate.h"
kjellanderf4752772016-03-02 13:42:3039#include "webrtc/p2p/base/p2pconstants.h"
kjellandera96e2d72016-02-05 07:52:2840#include "webrtc/p2p/base/port.h"
kjellander@webrtc.org9b8df252016-02-12 05:47:5941#include "webrtc/pc/mediasession.h"
henrike@webrtc.org28e20752013-07-10 00:45:3642
43using cricket::AudioContentDescription;
44using cricket::Candidate;
45using cricket::Candidates;
46using cricket::ContentDescription;
47using cricket::ContentInfo;
48using cricket::CryptoParams;
49using cricket::DataContentDescription;
50using cricket::ICE_CANDIDATE_COMPONENT_RTP;
51using cricket::ICE_CANDIDATE_COMPONENT_RTCP;
52using cricket::kCodecParamMaxBitrate;
53using cricket::kCodecParamMaxPTime;
54using cricket::kCodecParamMaxQuantization;
55using cricket::kCodecParamMinBitrate;
56using cricket::kCodecParamMinPTime;
57using cricket::kCodecParamPTime;
58using cricket::kCodecParamSPropStereo;
buildbot@webrtc.orged97bb02014-05-07 11:15:2059using cricket::kCodecParamStartBitrate;
henrike@webrtc.org28e20752013-07-10 00:45:3660using cricket::kCodecParamStereo;
61using cricket::kCodecParamUseInbandFec;
Minyue Li7100dcd2015-03-27 04:05:5962using cricket::kCodecParamUseDtx;
henrike@webrtc.org28e20752013-07-10 00:45:3663using cricket::kCodecParamSctpProtocol;
64using cricket::kCodecParamSctpStreams;
henrike@webrtc.org1e09a712013-07-26 19:17:5965using cricket::kCodecParamMaxAverageBitrate;
buildbot@webrtc.org5d639b32014-09-10 07:57:1266using cricket::kCodecParamMaxPlaybackRate;
stefan@webrtc.org85d27942014-06-09 12:51:3967using cricket::kCodecParamAssociatedPayloadType;
henrike@webrtc.org28e20752013-07-10 00:45:3668using cricket::MediaContentDescription;
69using cricket::MediaType;
isheriff6f8d6862016-05-26 18:24:5570using cricket::RtpHeaderExtensions;
henrike@webrtc.org28e20752013-07-10 00:45:3671using cricket::SsrcGroup;
72using cricket::StreamParams;
73using cricket::StreamParamsVec;
74using cricket::TransportDescription;
75using cricket::TransportInfo;
76using cricket::VideoContentDescription;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:5277using rtc::SocketAddress;
henrike@webrtc.org28e20752013-07-10 00:45:3678
henrike@webrtc.org28e20752013-07-10 00:45:3679namespace cricket {
80class SessionDescription;
81}
82
83namespace webrtc {
84
85// Line type
86// RFC 4566
87// An SDP session description consists of a number of lines of text of
88// the form:
89// <type>=<value>
90// where <type> MUST be exactly one case-significant character.
deadbeef9d3584c2016-02-17 01:54:1091static const int kLinePrefixLength = 2; // Length of <type>=
henrike@webrtc.org28e20752013-07-10 00:45:3692static const char kLineTypeVersion = 'v';
93static const char kLineTypeOrigin = 'o';
94static const char kLineTypeSessionName = 's';
95static const char kLineTypeSessionInfo = 'i';
96static const char kLineTypeSessionUri = 'u';
97static const char kLineTypeSessionEmail = 'e';
98static const char kLineTypeSessionPhone = 'p';
99static const char kLineTypeSessionBandwidth = 'b';
100static const char kLineTypeTiming = 't';
101static const char kLineTypeRepeatTimes = 'r';
102static const char kLineTypeTimeZone = 'z';
103static const char kLineTypeEncryptionKey = 'k';
104static const char kLineTypeMedia = 'm';
105static const char kLineTypeConnection = 'c';
106static const char kLineTypeAttributes = 'a';
107
108// Attributes
109static const char kAttributeGroup[] = "group";
110static const char kAttributeMid[] = "mid";
deadbeef9d3584c2016-02-17 01:54:10111static const char kAttributeMsid[] = "msid";
deadbeef25ed4352016-12-13 02:37:36112static const char kAttributeBundleOnly[] = "bundle-only";
henrike@webrtc.org28e20752013-07-10 00:45:36113static const char kAttributeRtcpMux[] = "rtcp-mux";
deadbeef13871492015-12-09 20:37:51114static const char kAttributeRtcpReducedSize[] = "rtcp-rsize";
henrike@webrtc.org28e20752013-07-10 00:45:36115static const char kAttributeSsrc[] = "ssrc";
116static const char kSsrcAttributeCname[] = "cname";
117static const char kAttributeExtmap[] = "extmap";
118// draft-alvestrand-mmusic-msid-01
119// a=msid-semantic: WMS
120static const char kAttributeMsidSemantics[] = "msid-semantic";
121static const char kMediaStreamSemantic[] = "WMS";
122static const char kSsrcAttributeMsid[] = "msid";
123static const char kDefaultMsid[] = "default";
henrike@webrtc.org28e20752013-07-10 00:45:36124static const char kSsrcAttributeMslabel[] = "mslabel";
125static const char kSSrcAttributeLabel[] = "label";
126static const char kAttributeSsrcGroup[] = "ssrc-group";
127static const char kAttributeCrypto[] = "crypto";
128static const char kAttributeCandidate[] = "candidate";
129static const char kAttributeCandidateTyp[] = "typ";
130static const char kAttributeCandidateRaddr[] = "raddr";
131static const char kAttributeCandidateRport[] = "rport";
honghaiza54a0802015-12-17 02:37:23132static const char kAttributeCandidateUfrag[] = "ufrag";
133static const char kAttributeCandidatePwd[] = "pwd";
henrike@webrtc.org28e20752013-07-10 00:45:36134static const char kAttributeCandidateGeneration[] = "generation";
honghaiza0c44ea2016-03-23 23:07:48135static const char kAttributeCandidateNetworkId[] = "network-id";
honghaize1a0c942016-02-16 22:54:56136static const char kAttributeCandidateNetworkCost[] = "network-cost";
henrike@webrtc.org28e20752013-07-10 00:45:36137static const char kAttributeFingerprint[] = "fingerprint";
sergeyu@chromium.org0be6aa02013-08-23 23:21:25138static const char kAttributeSetup[] = "setup";
henrike@webrtc.org28e20752013-07-10 00:45:36139static const char kAttributeFmtp[] = "fmtp";
140static const char kAttributeRtpmap[] = "rtpmap";
wu@webrtc.org78187522013-10-07 23:32:02141static const char kAttributeSctpmap[] = "sctpmap";
henrike@webrtc.org28e20752013-07-10 00:45:36142static const char kAttributeRtcp[] = "rtcp";
143static const char kAttributeIceUfrag[] = "ice-ufrag";
144static const char kAttributeIcePwd[] = "ice-pwd";
145static const char kAttributeIceLite[] = "ice-lite";
146static const char kAttributeIceOption[] = "ice-options";
147static const char kAttributeSendOnly[] = "sendonly";
148static const char kAttributeRecvOnly[] = "recvonly";
149static const char kAttributeRtcpFb[] = "rtcp-fb";
150static const char kAttributeSendRecv[] = "sendrecv";
151static const char kAttributeInactive[] = "inactive";
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56152// draft-ietf-mmusic-sctp-sdp-07
153// a=sctp-port
154static const char kAttributeSctpPort[] = "sctp-port";
henrike@webrtc.org28e20752013-07-10 00:45:36155
156// Experimental flags
157static const char kAttributeXGoogleFlag[] = "x-google-flag";
158static const char kValueConference[] = "conference";
henrike@webrtc.org28e20752013-07-10 00:45:36159
160// Candidate
161static const char kCandidateHost[] = "host";
162static const char kCandidateSrflx[] = "srflx";
Honghai Zhang7fb69db2016-03-14 18:59:18163static const char kCandidatePrflx[] = "prflx";
henrike@webrtc.org28e20752013-07-10 00:45:36164static const char kCandidateRelay[] = "relay";
mallinath@webrtc.org2d60c5e2014-08-08 22:29:20165static const char kTcpCandidateType[] = "tcptype";
henrike@webrtc.org28e20752013-07-10 00:45:36166
167static const char kSdpDelimiterEqual = '=';
168static const char kSdpDelimiterSpace = ' ';
169static const char kSdpDelimiterColon = ':';
170static const char kSdpDelimiterSemicolon = ';';
171static const char kSdpDelimiterSlash = '/';
172static const char kNewLine = '\n';
173static const char kReturn = '\r';
174static const char kLineBreak[] = "\r\n";
175
176// TODO: Generate the Session and Time description
177// instead of hardcoding.
178static const char kSessionVersion[] = "v=0";
179// RFC 4566
180static const char kSessionOriginUsername[] = "-";
181static const char kSessionOriginSessionId[] = "0";
182static const char kSessionOriginSessionVersion[] = "0";
183static const char kSessionOriginNettype[] = "IN";
184static const char kSessionOriginAddrtype[] = "IP4";
185static const char kSessionOriginAddress[] = "127.0.0.1";
186static const char kSessionName[] = "s=-";
187static const char kTimeDescription[] = "t=0 0";
188static const char kAttrGroup[] = "a=group:BUNDLE";
189static const char kConnectionNettype[] = "IN";
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22190static const char kConnectionIpv4Addrtype[] = "IP4";
191static const char kConnectionIpv6Addrtype[] = "IP6";
henrike@webrtc.org28e20752013-07-10 00:45:36192static const char kMediaTypeVideo[] = "video";
193static const char kMediaTypeAudio[] = "audio";
194static const char kMediaTypeData[] = "application";
195static const char kMediaPortRejected[] = "0";
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22196// draft-ietf-mmusic-trickle-ice-01
197// When no candidates have been gathered, set the connection
198// address to IP6 ::.
perkj@webrtc.orgd105cc82014-11-07 11:22:06199// TODO(perkj): FF can not parse IP6 ::. See http://crbug/430333
200// Use IPV4 per default.
201static const char kDummyAddress[] = "0.0.0.0";
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22202static const char kDummyPort[] = "9";
henrike@webrtc.org28e20752013-07-10 00:45:36203// RFC 3556
204static const char kApplicationSpecificMaximum[] = "AS";
205
206static const int kDefaultVideoClockrate = 90000;
207
wu@webrtc.org78187522013-10-07 23:32:02208static const char kDefaultSctpmapProtocol[] = "webrtc-datachannel";
henrike@webrtc.org28e20752013-07-10 00:45:36209
pkasting@chromium.orgd3245462015-02-23 21:28:22210// RTP payload type is in the 0-127 range. Use -1 to indicate "all" payload
211// types.
212const int kWildcardPayloadType = -1;
213
henrike@webrtc.org28e20752013-07-10 00:45:36214struct SsrcInfo {
Peter Boström0c4e06b2015-10-07 10:23:21215 uint32_t ssrc_id;
henrike@webrtc.org28e20752013-07-10 00:45:36216 std::string cname;
deadbeef9d3584c2016-02-17 01:54:10217 std::string stream_id;
218 std::string track_id;
henrike@webrtc.org28e20752013-07-10 00:45:36219
220 // For backward compatibility.
221 // TODO(ronghuawu): Remove below 2 fields once all the clients support msid.
222 std::string label;
223 std::string mslabel;
224};
225typedef std::vector<SsrcInfo> SsrcInfoVec;
226typedef std::vector<SsrcGroup> SsrcGroupVec;
227
henrike@webrtc.org28e20752013-07-10 00:45:36228template <class T>
229static void AddFmtpLine(const T& codec, std::string* message);
230static void BuildMediaDescription(const ContentInfo* content_info,
231 const TransportInfo* transport_info,
232 const MediaType media_type,
wu@webrtc.org4c3e9912014-07-16 21:03:13233 const std::vector<Candidate>& candidates,
deadbeef9d3584c2016-02-17 01:54:10234 bool unified_plan_sdp,
henrike@webrtc.org28e20752013-07-10 00:45:36235 std::string* message);
jiayl@webrtc.org9c16c392014-05-01 18:30:30236static void BuildSctpContentAttributes(std::string* message, int sctp_port);
deadbeef9d3584c2016-02-17 01:54:10237static void BuildRtpContentAttributes(const MediaContentDescription* media_desc,
238 const MediaType media_type,
239 bool unified_plan_sdp,
240 std::string* message);
henrike@webrtc.org28e20752013-07-10 00:45:36241static void BuildRtpMap(const MediaContentDescription* media_desc,
242 const MediaType media_type,
243 std::string* message);
244static void BuildCandidate(const std::vector<Candidate>& candidates,
honghaiza54a0802015-12-17 02:37:23245 bool include_ufrag,
henrike@webrtc.org28e20752013-07-10 00:45:36246 std::string* message);
247static void BuildIceOptions(const std::vector<std::string>& transport_options,
248 std::string* message);
pthatcher@webrtc.org3341b402015-02-13 21:14:22249static bool IsRtp(const std::string& protocol);
250static bool IsDtlsSctp(const std::string& protocol);
henrike@webrtc.org28e20752013-07-10 00:45:36251static bool ParseSessionDescription(const std::string& message, size_t* pos,
252 std::string* session_id,
253 std::string* session_version,
henrike@webrtc.org28e20752013-07-10 00:45:36254 TransportDescription* session_td,
255 RtpHeaderExtensions* session_extmaps,
256 cricket::SessionDescription* desc,
257 SdpParseError* error);
258static bool ParseGroupAttribute(const std::string& line,
259 cricket::SessionDescription* desc,
260 SdpParseError* error);
261static bool ParseMediaDescription(
262 const std::string& message,
263 const TransportDescription& session_td,
264 const RtpHeaderExtensions& session_extmaps,
henrike@webrtc.org28e20752013-07-10 00:45:36265 size_t* pos, cricket::SessionDescription* desc,
266 std::vector<JsepIceCandidate*>* candidates,
267 SdpParseError* error);
268static bool ParseContent(const std::string& message,
269 const MediaType media_type,
270 int mline_index,
271 const std::string& protocol,
deadbeef67cf2c12016-04-13 17:07:16272 const std::vector<int>& payload_types,
henrike@webrtc.org28e20752013-07-10 00:45:36273 size_t* pos,
274 std::string* content_name,
deadbeef25ed4352016-12-13 02:37:36275 bool* bundle_only,
henrike@webrtc.org28e20752013-07-10 00:45:36276 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 17:07:16291 const std::vector<int>& payload_types,
henrike@webrtc.org28e20752013-07-10 00:45:36292 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,
isheriff6f8d6862016-05-26 18:24:55310 RtpExtension* extmap,
henrike@webrtc.org28e20752013-07-10 00:45:36311 SdpParseError* error);
312static bool ParseFingerprintAttribute(const std::string& line,
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52313 rtc::SSLFingerprint** fingerprint,
henrike@webrtc.org28e20752013-07-10 00:45:36314 SdpParseError* error);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25315static bool ParseDtlsSetup(const std::string& line,
316 cricket::ConnectionRole* role,
317 SdpParseError* error);
deadbeef9d3584c2016-02-17 01:54:10318static 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:36322
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:06339 std::string first_line;
henrike@webrtc.org28e20752013-07-10 00:45:36340 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:06346 } else {
347 first_line = message.substr(line_start);
henrike@webrtc.org28e20752013-07-10 00:45:36348 }
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:51449 if (line->length() < 3 ||
450 !islower(cline[0]) ||
henrike@webrtc.org28e20752013-07-10 00:45:36451 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:36481static 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 10:23:21514static 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:36518 // 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:36527// 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 16:21:23531 if (!rtc::tokenize_first(message, kSdpDelimiterColon, &leftpart, value)) {
henrike@webrtc.org28e20752013-07-10 00:45:36532 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:09551template <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:52556 if (!rtc::FromString(s, t)) {
wu@webrtc.org5e760e72014-04-02 23:19:09557 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:28564static 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-10 01:02:30572// |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:36575void CreateTracksFromSsrcInfos(const SsrcInfoVec& ssrc_infos,
Taylor Brandstetter5de6b752016-03-10 01:02:30576 const std::string& msid_stream_id,
577 const std::string& msid_track_id,
henrike@webrtc.org28e20752013-07-10 00:45:36578 StreamParamsVec* tracks) {
nisseede5da42017-01-12 13:15:36579 RTC_DCHECK(tracks != NULL);
580 RTC_DCHECK(msid_stream_id.empty() == msid_track_id.empty());
henrike@webrtc.org28e20752013-07-10 00:45:36581 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-10 01:02:30587 std::string stream_id;
henrike@webrtc.org28e20752013-07-10 00:45:36588 std::string track_id;
Taylor Brandstetter5de6b752016-03-10 01:02:30589 if (ssrc_info->stream_id.empty() && !ssrc_info->mslabel.empty()) {
henrike@webrtc.org28e20752013-07-10 00:45:36590 // 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-10 01:02:30593 stream_id = ssrc_info->mslabel;
henrike@webrtc.org28e20752013-07-10 00:45:36594 track_id = ssrc_info->label;
Taylor Brandstetter5de6b752016-03-10 01:02:30595 } 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:36600 } else {
Taylor Brandstetter5de6b752016-03-10 01:02:30601 stream_id = ssrc_info->stream_id;
deadbeef9d3584c2016-02-17 01:54:10602 track_id = ssrc_info->track_id;
henrike@webrtc.org28e20752013-07-10 00:45:36603 }
Taylor Brandstetter5de6b752016-03-10 01:02:30604 // 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:36613 }
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-10 01:02:30628 track->sync_label = stream_id;
henrike@webrtc.org28e20752013-07-10 00:45:36629 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:49636 static_cast<const MediaContentDescription*>(
henrike@webrtc.org28e20752013-07-10 00:45:36637 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 {
nissec80e7412017-01-11 13:56:46663 RTC_NOTREACHED();
henrike@webrtc.org28e20752013-07-10 00:45:36664 }
665 return preference;
666}
667
guoweis@webrtc.org57ac2c82015-02-06 00:45:13668// 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:36671// 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:22675static 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:06679 *addr_type = kConnectionIpv4Addrtype;
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22680 *port = kDummyPort;
681 *ip = kDummyAddress;
henrike@webrtc.org28e20752013-07-10 00:45:36682 int current_preference = kPreferenceUnknown;
guoweis@webrtc.org57ac2c82015-02-06 00:45:13683 int current_family = AF_UNSPEC;
henrike@webrtc.org28e20752013-07-10 00:45:36684 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:13689 // Default destination should be UDP only.
690 if (it->protocol() != cricket::UDP_PROTOCOL_NAME) {
henrike@webrtc.org28e20752013-07-10 00:45:36691 continue;
692 }
guoweis@webrtc.org57ac2c82015-02-06 00:45:13693 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:22703 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:13708 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:36712 }
henrike@webrtc.org28e20752013-07-10 00:45:36713}
714
wu@webrtc.org4c3e9912014-07-16 21:03:13715// Update |mline|'s default destination and append a c line after it.
henrike@webrtc.org28e20752013-07-10 00:45:36716static void UpdateMediaDefaultDestination(
wu@webrtc.org4c3e9912014-07-16 21:03:13717 const std::vector<Candidate>& candidates,
jbauch083b73f2015-07-16 09:46:32718 const std::string& mline,
wu@webrtc.org4c3e9912014-07-16 21:03:13719 std::string* message) {
720 std::string new_lines;
721 AddLine(mline, &new_lines);
henrike@webrtc.org28e20752013-07-10 00:45:36722 // RFC 4566
723 // m=<media> <port> <proto> <fmt> ...
724 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52725 rtc::split(mline, kSdpDelimiterSpace, &fields);
henrike@webrtc.org28e20752013-07-10 00:45:36726 if (fields.size() < 3) {
727 return;
728 }
729
henrike@webrtc.org28e20752013-07-10 00:45:36730 std::ostringstream os;
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22731 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:36746 }
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22747 // 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:13753 message->append(new_lines);
754}
henrike@webrtc.org28e20752013-07-10 00:45:36755
wu@webrtc.org4c3e9912014-07-16 21:03:13756// 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:22758 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:36765
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22766 // 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:13777 return rtcp_line;
henrike@webrtc.org28e20752013-07-10 00:45:36778}
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-17 01:54:10794std::string SdpSerialize(const JsepSessionDescription& jdesc,
795 bool unified_plan_sdp) {
henrike@webrtc.org28e20752013-07-10 00:45:36796 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 09:46:32811 const std::string& session_id = jdesc.session_id().empty() ?
henrike@webrtc.org28e20752013-07-10 00:45:36812 kSessionOriginSessionId : jdesc.session_id();
jbauch083b73f2015-07-16 09:46:32813 const std::string& session_version = jdesc.session_version().empty() ?
henrike@webrtc.org28e20752013-07-10 00:45:36814 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);
nisseede5da42017-01-12 13:15:36829 RTC_DCHECK(group != NULL);
henrike@webrtc.org28e20752013-07-10 00:45:36830 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:13842
henrike@webrtc.org28e20752013-07-10 00:45:36843 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:13847
henrike@webrtc.org28e20752013-07-10 00:45:36848 const ContentInfo* video_content = GetFirstVideoContent(desc);
849 if (video_content)
850 GetMediaStreamLabels(video_content, &media_stream_labels);
wu@webrtc.org4c3e9912014-07-16 21:03:13851
henrike@webrtc.org28e20752013-07-10 00:45:36852 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:13858 // 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-17 01:54:10866 BuildMediaDescription(&*it, desc->GetTransportInfoByName(it->name),
867 mdesc->type(), candidates, unified_plan_sdp,
wu@webrtc.org4c3e9912014-07-16 21:03:13868 &message);
henrike@webrtc.org28e20752013-07-10 00:45:36869 }
henrike@webrtc.org28e20752013-07-10 00:45:36870 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 18:59:18875std::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:36881 std::string message;
Honghai Zhang7fb69db2016-03-14 18:59:18882 std::vector<cricket::Candidate> candidates(1, candidate);
honghaiza54a0802015-12-17 02:37:23883 BuildCandidate(candidates, true, &message);
wu@webrtc.orgec9f5fb2014-06-24 17:05:10884 // From WebRTC draft section 4.8.1.1 candidate-attribute will be
885 // just candidate:<candidate> not a=candidate:<blah>CRLF
nisseede5da42017-01-12 13:15:36886 RTC_DCHECK(message.find("a=") == 0);
wu@webrtc.orgec9f5fb2014-06-24 17:05:10887 message.erase(0, 2);
nisseede5da42017-01-12 13:15:36888 RTC_DCHECK(message.find(kLineBreak) == message.size() - 2);
wu@webrtc.orgec9f5fb2014-06-24 17:05:10889 message.resize(message.size() - 2);
henrike@webrtc.org28e20752013-07-10 00:45:36890 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-18 01:54:52898 TransportDescription session_td("", "");
henrike@webrtc.org28e20752013-07-10 00:45:36899 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:36903
904 // Session Description
905 if (!ParseSessionDescription(message, &current_pos, &session_id,
deadbeefc80741f2015-10-22 20:14:45906 &session_version, &session_td, &session_extmaps,
907 desc, error)) {
henrike@webrtc.org28e20752013-07-10 00:45:36908 delete desc;
909 return false;
910 }
911
912 // Media Description
deadbeefc80741f2015-10-22 20:14:45913 if (!ParseMediaDescription(message, session_td, session_extmaps, &current_pos,
914 desc, &candidates, error)) {
henrike@webrtc.org28e20752013-07-10 00:45:36915 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) {
nisseede5da42017-01-12 13:15:36936 RTC_DCHECK(jcandidate != NULL);
henrike@webrtc.org28e20752013-07-10 00:45:36937 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 18:59:18945bool SdpDeserializeCandidate(const std::string& transport_name,
946 const std::string& message,
947 cricket::Candidate* candidate,
948 SdpParseError* error) {
nisseede5da42017-01-12 13:15:36949 RTC_DCHECK(candidate != nullptr);
Honghai Zhang7fb69db2016-03-14 18:59:18950 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:36957bool ParseCandidate(const std::string& message, Candidate* candidate,
958 SdpParseError* error, bool is_raw) {
nisseede5da42017-01-12 13:15:36959 RTC_DCHECK(candidate != NULL);
henrike@webrtc.org28e20752013-07-10 00:45:36960
961 // Get the first line from |message|.
jiayl@webrtc.org7ec3f9f2014-08-08 23:09:15962 std::string first_line = message;
963 size_t pos = 0;
964 GetLine(message, &pos, &first_line);
henrike@webrtc.org28e20752013-07-10 00:45:36965
jiayl@webrtc.org7ec3f9f2014-08-08 23:09:15966 // Makes sure |message| contains only one line.
967 if (message.size() > first_line.size()) {
968 std::string left, right;
Donald Curtis0e07f922015-05-15 16:21:23969 if (rtc::tokenize_first(message, kNewLine, &left, &right) &&
970 !right.empty()) {
jiayl@webrtc.org7ec3f9f2014-08-08 23:09:15971 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 20:14:24987 if (!rtc::tokenize_first(first_line, kSdpDelimiterColon, &attribute_candidate,
988 &candidate_value) ||
jiayl@webrtc.org7ec3f9f2014-08-08 23:09:15989 attribute_candidate != kAttributeCandidate) {
henrike@webrtc.org28e20752013-07-10 00:45:36990 if (is_raw) {
991 std::ostringstream description;
jiayl@webrtc.org7ec3f9f2014-08-08 23:09:15992 description << "Expect line: " << kAttributeCandidate
henrike@webrtc.org28e20752013-07-10 00:45:36993 << ":" << "<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:151002 rtc::split(candidate_value, kSdpDelimiterSpace, &fields);
1003
henrike@webrtc.org28e20752013-07-10 00:45:361004 // 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 09:46:321014 const std::string& foundation = fields[0];
jiayl@webrtc.org7ec3f9f2014-08-08 23:09:151015
wu@webrtc.org5e760e72014-04-02 23:19:091016 int component_id = 0;
1017 if (!GetValueFromString(first_line, fields[1], &component_id, error)) {
1018 return false;
1019 }
jbauch083b73f2015-07-16 09:46:321020 const std::string& transport = fields[2];
Peter Boström0c4e06b2015-10-07 10:23:211021 uint32_t priority = 0;
wu@webrtc.org5e760e72014-04-02 23:19:091022 if (!GetValueFromString(first_line, fields[3], &priority, error)) {
1023 return false;
1024 }
jbauch083b73f2015-07-16 09:46:321025 const std::string& connection_address = fields[4];
wu@webrtc.org5e760e72014-04-02 23:19:091026 int port = 0;
1027 if (!GetValueFromString(first_line, fields[5], &port, error)) {
1028 return false;
1029 }
henrike@webrtc.org28e20752013-07-10 00:45:361030 SocketAddress address(connection_address, port);
1031
1032 cricket::ProtocolType protocol;
hnslb68cc752016-12-13 18:33:411033 if (!StringToProto(transport.c_str(), &protocol)) {
henrike@webrtc.org28e20752013-07-10 00:45:361034 return ParseFailed(first_line, "Unsupported transport type.", error);
1035 }
hnslb68cc752016-12-13 18:33:411036 switch (protocol) {
1037 case cricket::PROTO_UDP:
1038 case cricket::PROTO_TCP:
1039 case cricket::PROTO_SSLTCP:
1040 // Supported protocol.
1041 break;
1042 default:
1043 return ParseFailed(first_line, "Unsupported transport type.", error);
1044 }
henrike@webrtc.org28e20752013-07-10 00:45:361045
1046 std::string candidate_type;
jbauch083b73f2015-07-16 09:46:321047 const std::string& type = fields[7];
henrike@webrtc.org28e20752013-07-10 00:45:361048 if (type == kCandidateHost) {
1049 candidate_type = cricket::LOCAL_PORT_TYPE;
1050 } else if (type == kCandidateSrflx) {
1051 candidate_type = cricket::STUN_PORT_TYPE;
1052 } else if (type == kCandidateRelay) {
1053 candidate_type = cricket::RELAY_PORT_TYPE;
Honghai Zhang7fb69db2016-03-14 18:59:181054 } else if (type == kCandidatePrflx) {
1055 candidate_type = cricket::PRFLX_PORT_TYPE;
henrike@webrtc.org28e20752013-07-10 00:45:361056 } else {
1057 return ParseFailed(first_line, "Unsupported candidate type.", error);
1058 }
1059
1060 size_t current_position = expected_min_fields;
1061 SocketAddress related_address;
1062 // The 2 optional fields for related address
1063 // [raddr <connection-address>] [rport <port>]
1064 if (fields.size() >= (current_position + 2) &&
1065 fields[current_position] == kAttributeCandidateRaddr) {
1066 related_address.SetIP(fields[++current_position]);
1067 ++current_position;
1068 }
1069 if (fields.size() >= (current_position + 2) &&
1070 fields[current_position] == kAttributeCandidateRport) {
wu@webrtc.org5e760e72014-04-02 23:19:091071 int port = 0;
1072 if (!GetValueFromString(
1073 first_line, fields[++current_position], &port, error)) {
1074 return false;
1075 }
1076 related_address.SetPort(port);
henrike@webrtc.org28e20752013-07-10 00:45:361077 ++current_position;
1078 }
1079
mallinath@webrtc.org2d60c5e2014-08-08 22:29:201080 // If this is a TCP candidate, it has additional extension as defined in
1081 // RFC 6544.
1082 std::string tcptype;
1083 if (fields.size() >= (current_position + 2) &&
1084 fields[current_position] == kTcpCandidateType) {
1085 tcptype = fields[++current_position];
1086 ++current_position;
1087
1088 if (tcptype != cricket::TCPTYPE_ACTIVE_STR &&
1089 tcptype != cricket::TCPTYPE_PASSIVE_STR &&
1090 tcptype != cricket::TCPTYPE_SIMOPEN_STR) {
1091 return ParseFailed(first_line, "Invalid TCP candidate type.", error);
1092 }
1093
1094 if (protocol != cricket::PROTO_TCP) {
1095 return ParseFailed(first_line, "Invalid non-TCP candidate", error);
1096 }
1097 }
1098
henrike@webrtc.org28e20752013-07-10 00:45:361099 // Extension
honghaiza54a0802015-12-17 02:37:231100 // Though non-standard, we support the ICE ufrag and pwd being signaled on
1101 // the candidate to avoid issues with confusing which generation a candidate
1102 // belongs to when trickling multiple generations at the same time.
henrike@webrtc.org28e20752013-07-10 00:45:361103 std::string username;
1104 std::string password;
Peter Boström0c4e06b2015-10-07 10:23:211105 uint32_t generation = 0;
honghaiza0c44ea2016-03-23 23:07:481106 uint16_t network_id = 0;
1107 uint16_t network_cost = 0;
henrike@webrtc.org28e20752013-07-10 00:45:361108 for (size_t i = current_position; i + 1 < fields.size(); ++i) {
1109 // RFC 5245
1110 // *(SP extension-att-name SP extension-att-value)
1111 if (fields[i] == kAttributeCandidateGeneration) {
wu@webrtc.org5e760e72014-04-02 23:19:091112 if (!GetValueFromString(first_line, fields[++i], &generation, error)) {
1113 return false;
1114 }
honghaiza54a0802015-12-17 02:37:231115 } else if (fields[i] == kAttributeCandidateUfrag) {
henrike@webrtc.org28e20752013-07-10 00:45:361116 username = fields[++i];
honghaiza54a0802015-12-17 02:37:231117 } else if (fields[i] == kAttributeCandidatePwd) {
henrike@webrtc.org28e20752013-07-10 00:45:361118 password = fields[++i];
honghaiza0c44ea2016-03-23 23:07:481119 } else if (fields[i] == kAttributeCandidateNetworkId) {
1120 if (!GetValueFromString(first_line, fields[++i], &network_id, error)) {
1121 return false;
1122 }
honghaize1a0c942016-02-16 22:54:561123 } else if (fields[i] == kAttributeCandidateNetworkCost) {
1124 if (!GetValueFromString(first_line, fields[++i], &network_cost, error)) {
1125 return false;
1126 }
Honghai Zhang351d77b2016-05-20 22:08:291127 network_cost = std::min(network_cost, rtc::kNetworkCostMax);
henrike@webrtc.org28e20752013-07-10 00:45:361128 } else {
1129 // Skip the unknown extension.
1130 ++i;
1131 }
1132 }
1133
guoweis@webrtc.org61c12472015-01-15 06:53:071134 *candidate = Candidate(component_id, cricket::ProtoToString(protocol),
guoweis@webrtc.org950c5182014-12-16 23:01:311135 address, priority, username, password, candidate_type,
honghaiza0c44ea2016-03-23 23:07:481136 generation, foundation, network_id, network_cost);
henrike@webrtc.org28e20752013-07-10 00:45:361137 candidate->set_related_address(related_address);
mallinath@webrtc.org2d60c5e2014-08-08 22:29:201138 candidate->set_tcptype(tcptype);
henrike@webrtc.org28e20752013-07-10 00:45:361139 return true;
1140}
1141
1142bool ParseIceOptions(const std::string& line,
1143 std::vector<std::string>* transport_options,
1144 SdpParseError* error) {
1145 std::string ice_options;
1146 if (!GetValue(line, kAttributeIceOption, &ice_options, error)) {
1147 return false;
1148 }
1149 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:521150 rtc::split(ice_options, kSdpDelimiterSpace, &fields);
henrike@webrtc.org28e20752013-07-10 00:45:361151 for (size_t i = 0; i < fields.size(); ++i) {
1152 transport_options->push_back(fields[i]);
1153 }
1154 return true;
1155}
1156
jiayl@webrtc.orgddb85ab2014-09-05 16:31:561157bool ParseSctpPort(const std::string& line,
1158 int* sctp_port,
1159 SdpParseError* error) {
1160 // draft-ietf-mmusic-sctp-sdp-07
1161 // a=sctp-port
1162 std::vector<std::string> fields;
jiayl@webrtc.orgddb85ab2014-09-05 16:31:561163 const size_t expected_min_fields = 2;
lally69f57602015-10-08 17:15:041164 rtc::split(line.substr(kLinePrefixLength), kSdpDelimiterColon, &fields);
1165 if (fields.size() < expected_min_fields) {
1166 fields.resize(0);
1167 rtc::split(line.substr(kLinePrefixLength), kSdpDelimiterSpace, &fields);
1168 }
jiayl@webrtc.orgddb85ab2014-09-05 16:31:561169 if (fields.size() < expected_min_fields) {
1170 return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
1171 }
1172 if (!rtc::FromString(fields[1], sctp_port)) {
lally69f57602015-10-08 17:15:041173 return ParseFailed(line, "Invalid sctp port value.", error);
jiayl@webrtc.orgddb85ab2014-09-05 16:31:561174 }
1175 return true;
1176}
1177
isheriff6f8d6862016-05-26 18:24:551178bool ParseExtmap(const std::string& line,
1179 RtpExtension* extmap,
henrike@webrtc.org28e20752013-07-10 00:45:361180 SdpParseError* error) {
1181 // RFC 5285
1182 // a=extmap:<value>["/"<direction>] <URI> <extensionattributes>
1183 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:521184 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:361185 kSdpDelimiterSpace, &fields);
1186 const size_t expected_min_fields = 2;
1187 if (fields.size() < expected_min_fields) {
1188 return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
1189 }
1190 std::string uri = fields[1];
1191
1192 std::string value_direction;
1193 if (!GetValue(fields[0], kAttributeExtmap, &value_direction, error)) {
1194 return false;
1195 }
1196 std::vector<std::string> sub_fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:521197 rtc::split(value_direction, kSdpDelimiterSlash, &sub_fields);
wu@webrtc.org5e760e72014-04-02 23:19:091198 int value = 0;
1199 if (!GetValueFromString(line, sub_fields[0], &value, error)) {
1200 return false;
1201 }
henrike@webrtc.org28e20752013-07-10 00:45:361202
isheriff6f8d6862016-05-26 18:24:551203 *extmap = RtpExtension(uri, value);
henrike@webrtc.org28e20752013-07-10 00:45:361204 return true;
1205}
1206
1207void BuildMediaDescription(const ContentInfo* content_info,
1208 const TransportInfo* transport_info,
1209 const MediaType media_type,
wu@webrtc.org4c3e9912014-07-16 21:03:131210 const std::vector<Candidate>& candidates,
deadbeef9d3584c2016-02-17 01:54:101211 bool unified_plan_sdp,
henrike@webrtc.org28e20752013-07-10 00:45:361212 std::string* message) {
nisseede5da42017-01-12 13:15:361213 RTC_DCHECK(message != NULL);
henrike@webrtc.org28e20752013-07-10 00:45:361214 if (content_info == NULL || message == NULL) {
1215 return;
1216 }
1217 // TODO: Rethink if we should use sprintfn instead of stringstream.
1218 // According to the style guide, streams should only be used for logging.
1219 // http://google-styleguide.googlecode.com/svn/
1220 // trunk/cppguide.xml?showone=Streams#Streams
1221 std::ostringstream os;
1222 const MediaContentDescription* media_desc =
henrike@webrtc.org28654cb2013-07-22 21:07:491223 static_cast<const MediaContentDescription*>(
henrike@webrtc.org28e20752013-07-10 00:45:361224 content_info->description);
nisseede5da42017-01-12 13:15:361225 RTC_DCHECK(media_desc != NULL);
henrike@webrtc.org28e20752013-07-10 00:45:361226
jiayl@webrtc.org9c16c392014-05-01 18:30:301227 int sctp_port = cricket::kSctpDefaultPort;
henrike@webrtc.org28e20752013-07-10 00:45:361228
1229 // RFC 4566
1230 // m=<media> <port> <proto> <fmt>
1231 // fmt is a list of payload type numbers that MAY be used in the session.
1232 const char* type = NULL;
1233 if (media_type == cricket::MEDIA_TYPE_AUDIO)
1234 type = kMediaTypeAudio;
1235 else if (media_type == cricket::MEDIA_TYPE_VIDEO)
1236 type = kMediaTypeVideo;
1237 else if (media_type == cricket::MEDIA_TYPE_DATA)
1238 type = kMediaTypeData;
1239 else
nissec80e7412017-01-11 13:56:461240 RTC_NOTREACHED();
henrike@webrtc.org28e20752013-07-10 00:45:361241
1242 std::string fmt;
1243 if (media_type == cricket::MEDIA_TYPE_VIDEO) {
1244 const VideoContentDescription* video_desc =
1245 static_cast<const VideoContentDescription*>(media_desc);
1246 for (std::vector<cricket::VideoCodec>::const_iterator it =
1247 video_desc->codecs().begin();
1248 it != video_desc->codecs().end(); ++it) {
1249 fmt.append(" ");
buildbot@webrtc.orgd4e598d2014-07-29 17:36:521250 fmt.append(rtc::ToString<int>(it->id));
henrike@webrtc.org28e20752013-07-10 00:45:361251 }
1252 } else if (media_type == cricket::MEDIA_TYPE_AUDIO) {
1253 const AudioContentDescription* audio_desc =
1254 static_cast<const AudioContentDescription*>(media_desc);
1255 for (std::vector<cricket::AudioCodec>::const_iterator it =
1256 audio_desc->codecs().begin();
1257 it != audio_desc->codecs().end(); ++it) {
1258 fmt.append(" ");
buildbot@webrtc.orgd4e598d2014-07-29 17:36:521259 fmt.append(rtc::ToString<int>(it->id));
henrike@webrtc.org28e20752013-07-10 00:45:361260 }
1261 } else if (media_type == cricket::MEDIA_TYPE_DATA) {
jiayl@webrtc.org9c16c392014-05-01 18:30:301262 const DataContentDescription* data_desc =
1263 static_cast<const DataContentDescription*>(media_desc);
pthatcher@webrtc.org3341b402015-02-13 21:14:221264 if (IsDtlsSctp(media_desc->protocol())) {
henrike@webrtc.org28e20752013-07-10 00:45:361265 fmt.append(" ");
jiayl@webrtc.org9c16c392014-05-01 18:30:301266
1267 for (std::vector<cricket::DataCodec>::const_iterator it =
1268 data_desc->codecs().begin();
1269 it != data_desc->codecs().end(); ++it) {
solenberg9fa49752016-10-08 20:02:441270 if (cricket::CodecNamesEq(it->name, cricket::kGoogleSctpDataCodecName)
1271 && it->GetParam(cricket::kCodecParamPort, &sctp_port)) {
jiayl@webrtc.org9c16c392014-05-01 18:30:301272 break;
1273 }
1274 }
1275
buildbot@webrtc.orgd4e598d2014-07-29 17:36:521276 fmt.append(rtc::ToString<int>(sctp_port));
henrike@webrtc.org28e20752013-07-10 00:45:361277 } else {
henrike@webrtc.org28e20752013-07-10 00:45:361278 for (std::vector<cricket::DataCodec>::const_iterator it =
1279 data_desc->codecs().begin();
1280 it != data_desc->codecs().end(); ++it) {
1281 fmt.append(" ");
buildbot@webrtc.orgd4e598d2014-07-29 17:36:521282 fmt.append(rtc::ToString<int>(it->id));
henrike@webrtc.org28e20752013-07-10 00:45:361283 }
1284 }
1285 }
1286 // The fmt must never be empty. If no codecs are found, set the fmt attribute
1287 // to 0.
1288 if (fmt.empty()) {
1289 fmt = " 0";
1290 }
1291
deadbeef25ed4352016-12-13 02:37:361292 // The port number in the m line will be updated later when associated with
henrike@webrtc.org28e20752013-07-10 00:45:361293 // the candidates.
deadbeef25ed4352016-12-13 02:37:361294 //
1295 // A port value of 0 indicates that the m= section is rejected.
henrike@webrtc.org28e20752013-07-10 00:45:361296 // RFC 3264
1297 // To reject an offered stream, the port number in the corresponding stream in
1298 // the answer MUST be set to zero.
deadbeef25ed4352016-12-13 02:37:361299 //
1300 // However, the BUNDLE draft adds a new meaning to port zero, when used along
1301 // with a=bundle-only.
1302 const std::string& port =
1303 (content_info->rejected || content_info->bundle_only) ? kMediaPortRejected
1304 : kDummyPort;
henrike@webrtc.org28e20752013-07-10 00:45:361305
buildbot@webrtc.orgd4e598d2014-07-29 17:36:521306 rtc::SSLFingerprint* fp = (transport_info) ?
henrike@webrtc.org28e20752013-07-10 00:45:361307 transport_info->description.identity_fingerprint.get() : NULL;
1308
wu@webrtc.org4c3e9912014-07-16 21:03:131309 // Add the m and c lines.
henrike@webrtc.org28e20752013-07-10 00:45:361310 InitLine(kLineTypeMedia, type, &os);
1311 os << " " << port << " " << media_desc->protocol() << fmt;
wu@webrtc.org4c3e9912014-07-16 21:03:131312 std::string mline = os.str();
1313 UpdateMediaDefaultDestination(candidates, mline, message);
1314
1315 // RFC 4566
1316 // b=AS:<bandwidth>
Peter Thatcherc0c3a862015-06-24 22:31:251317 if (media_desc->bandwidth() >= 1000) {
wu@webrtc.org4c3e9912014-07-16 21:03:131318 InitLine(kLineTypeSessionBandwidth, kApplicationSpecificMaximum, &os);
1319 os << kSdpDelimiterColon << (media_desc->bandwidth() / 1000);
1320 AddLine(os.str(), message);
1321 }
1322
deadbeef25ed4352016-12-13 02:37:361323 // Add the a=bundle-only line.
1324 if (content_info->bundle_only) {
1325 InitAttrLine(kAttributeBundleOnly, &os);
1326 AddLine(os.str(), message);
1327 }
1328
wu@webrtc.org4c3e9912014-07-16 21:03:131329 // Add the a=rtcp line.
pthatcher@webrtc.org3341b402015-02-13 21:14:221330 if (IsRtp(media_desc->protocol())) {
wu@webrtc.org4c3e9912014-07-16 21:03:131331 std::string rtcp_line = GetRtcpLine(candidates);
1332 if (!rtcp_line.empty()) {
1333 AddLine(rtcp_line, message);
1334 }
1335 }
1336
honghaiza54a0802015-12-17 02:37:231337 // Build the a=candidate lines. We don't include ufrag and pwd in the
1338 // candidates in the SDP to avoid redundancy.
1339 BuildCandidate(candidates, false, message);
henrike@webrtc.org28e20752013-07-10 00:45:361340
1341 // Use the transport_info to build the media level ice-ufrag and ice-pwd.
1342 if (transport_info) {
1343 // RFC 5245
1344 // ice-pwd-att = "ice-pwd" ":" password
1345 // ice-ufrag-att = "ice-ufrag" ":" ufrag
1346 // ice-ufrag
deadbeef3f7219b2015-12-28 23:17:141347 if (!transport_info->description.ice_ufrag.empty()) {
1348 InitAttrLine(kAttributeIceUfrag, &os);
1349 os << kSdpDelimiterColon << transport_info->description.ice_ufrag;
1350 AddLine(os.str(), message);
1351 }
henrike@webrtc.org28e20752013-07-10 00:45:361352 // ice-pwd
deadbeef3f7219b2015-12-28 23:17:141353 if (!transport_info->description.ice_pwd.empty()) {
1354 InitAttrLine(kAttributeIcePwd, &os);
1355 os << kSdpDelimiterColon << transport_info->description.ice_pwd;
1356 AddLine(os.str(), message);
1357 }
henrike@webrtc.org28e20752013-07-10 00:45:361358
1359 // draft-petithuguenin-mmusic-ice-attributes-level-03
1360 BuildIceOptions(transport_info->description.transport_options, message);
1361
1362 // RFC 4572
1363 // fingerprint-attribute =
1364 // "fingerprint" ":" hash-func SP fingerprint
1365 if (fp) {
1366 // Insert the fingerprint attribute.
1367 InitAttrLine(kAttributeFingerprint, &os);
1368 os << kSdpDelimiterColon
1369 << fp->algorithm << kSdpDelimiterSpace
1370 << fp->GetRfc4572Fingerprint();
henrike@webrtc.org28e20752013-07-10 00:45:361371 AddLine(os.str(), message);
sergeyu@chromium.org0be6aa02013-08-23 23:21:251372
1373 // Inserting setup attribute.
1374 if (transport_info->description.connection_role !=
1375 cricket::CONNECTIONROLE_NONE) {
1376 // Making sure we are not using "passive" mode.
1377 cricket::ConnectionRole role =
1378 transport_info->description.connection_role;
mallinath@webrtc.org19f27e62013-10-13 17:18:271379 std::string dtls_role_str;
1380 VERIFY(cricket::ConnectionRoleToString(role, &dtls_role_str));
sergeyu@chromium.org0be6aa02013-08-23 23:21:251381 InitAttrLine(kAttributeSetup, &os);
sergeyu@chromium.org0be6aa02013-08-23 23:21:251382 os << kSdpDelimiterColon << dtls_role_str;
1383 AddLine(os.str(), message);
1384 }
henrike@webrtc.org28e20752013-07-10 00:45:361385 }
1386 }
1387
1388 // RFC 3388
1389 // mid-attribute = "a=mid:" identification-tag
1390 // identification-tag = token
1391 // Use the content name as the mid identification-tag.
1392 InitAttrLine(kAttributeMid, &os);
1393 os << kSdpDelimiterColon << content_info->name;
1394 AddLine(os.str(), message);
1395
pthatcher@webrtc.org3341b402015-02-13 21:14:221396 if (IsDtlsSctp(media_desc->protocol())) {
jiayl@webrtc.org9c16c392014-05-01 18:30:301397 BuildSctpContentAttributes(message, sctp_port);
pthatcher@webrtc.org3341b402015-02-13 21:14:221398 } else if (IsRtp(media_desc->protocol())) {
deadbeef9d3584c2016-02-17 01:54:101399 BuildRtpContentAttributes(media_desc, media_type, unified_plan_sdp,
1400 message);
henrike@webrtc.org28e20752013-07-10 00:45:361401 }
1402}
1403
jiayl@webrtc.org9c16c392014-05-01 18:30:301404void BuildSctpContentAttributes(std::string* message, int sctp_port) {
wu@webrtc.org78187522013-10-07 23:32:021405 // draft-ietf-mmusic-sctp-sdp-04
1406 // a=sctpmap:sctpmap-number protocol [streams]
lally69f57602015-10-08 17:15:041407 // TODO(lally): switch this over to mmusic-sctp-sdp-12 (or later), with
1408 // 'a=sctp-port:'
wu@webrtc.org78187522013-10-07 23:32:021409 std::ostringstream os;
1410 InitAttrLine(kAttributeSctpmap, &os);
jiayl@webrtc.org9c16c392014-05-01 18:30:301411 os << kSdpDelimiterColon << sctp_port << kSdpDelimiterSpace
wu@webrtc.org78187522013-10-07 23:32:021412 << kDefaultSctpmapProtocol << kSdpDelimiterSpace
Taylor Brandstetter1d7a6372016-08-24 20:15:271413 << cricket::kMaxSctpStreams;
wu@webrtc.org78187522013-10-07 23:32:021414 AddLine(os.str(), message);
henrike@webrtc.org28e20752013-07-10 00:45:361415}
1416
deadbeef9d3584c2016-02-17 01:54:101417// If unified_plan_sdp is true, will use "a=msid".
1418void BuildRtpContentAttributes(const MediaContentDescription* media_desc,
1419 const MediaType media_type,
1420 bool unified_plan_sdp,
1421 std::string* message) {
henrike@webrtc.org28e20752013-07-10 00:45:361422 std::ostringstream os;
1423 // RFC 5285
1424 // a=extmap:<value>["/"<direction>] <URI> <extensionattributes>
1425 // The definitions MUST be either all session level or all media level. This
1426 // implementation uses all media level.
1427 for (size_t i = 0; i < media_desc->rtp_header_extensions().size(); ++i) {
1428 InitAttrLine(kAttributeExtmap, &os);
1429 os << kSdpDelimiterColon << media_desc->rtp_header_extensions()[i].id
1430 << kSdpDelimiterSpace << media_desc->rtp_header_extensions()[i].uri;
1431 AddLine(os.str(), message);
1432 }
1433
1434 // RFC 3264
1435 // a=sendrecv || a=sendonly || a=sendrecv || a=inactive
deadbeefc80741f2015-10-22 20:14:451436 switch (media_desc->direction()) {
henrike@webrtc.org28e20752013-07-10 00:45:361437 case cricket::MD_INACTIVE:
1438 InitAttrLine(kAttributeInactive, &os);
1439 break;
1440 case cricket::MD_SENDONLY:
1441 InitAttrLine(kAttributeSendOnly, &os);
1442 break;
1443 case cricket::MD_RECVONLY:
1444 InitAttrLine(kAttributeRecvOnly, &os);
1445 break;
1446 case cricket::MD_SENDRECV:
1447 default:
1448 InitAttrLine(kAttributeSendRecv, &os);
1449 break;
1450 }
1451 AddLine(os.str(), message);
1452
deadbeef9d3584c2016-02-17 01:54:101453 // draft-ietf-mmusic-msid-11
1454 // a=msid:<stream id> <track id>
1455 if (unified_plan_sdp && !media_desc->streams().empty()) {
1456 if (media_desc->streams().size() > 1u) {
1457 LOG(LS_WARNING) << "Trying to serialize unified plan SDP with more than "
1458 << "one track in a media section. Omitting 'a=msid'.";
1459 } else {
1460 auto track = media_desc->streams().begin();
1461 const std::string& stream_id = track->sync_label;
deadbeef9d3584c2016-02-17 01:54:101462 InitAttrLine(kAttributeMsid, &os);
1463 os << kSdpDelimiterColon << stream_id << kSdpDelimiterSpace << track->id;
1464 AddLine(os.str(), message);
1465 }
1466 }
1467
henrike@webrtc.org28e20752013-07-10 00:45:361468 // RFC 5761
1469 // a=rtcp-mux
1470 if (media_desc->rtcp_mux()) {
1471 InitAttrLine(kAttributeRtcpMux, &os);
1472 AddLine(os.str(), message);
1473 }
1474
deadbeef13871492015-12-09 20:37:511475 // RFC 5506
1476 // a=rtcp-rsize
1477 if (media_desc->rtcp_reduced_size()) {
1478 InitAttrLine(kAttributeRtcpReducedSize, &os);
1479 AddLine(os.str(), message);
1480 }
1481
henrike@webrtc.org28e20752013-07-10 00:45:361482 // RFC 4568
1483 // a=crypto:<tag> <crypto-suite> <key-params> [<session-params>]
1484 for (std::vector<CryptoParams>::const_iterator it =
1485 media_desc->cryptos().begin();
1486 it != media_desc->cryptos().end(); ++it) {
1487 InitAttrLine(kAttributeCrypto, &os);
1488 os << kSdpDelimiterColon << it->tag << " " << it->cipher_suite << " "
1489 << it->key_params;
1490 if (!it->session_params.empty()) {
1491 os << " " << it->session_params;
1492 }
1493 AddLine(os.str(), message);
1494 }
1495
1496 // RFC 4566
1497 // a=rtpmap:<payload type> <encoding name>/<clock rate>
1498 // [/<encodingparameters>]
1499 BuildRtpMap(media_desc, media_type, message);
1500
henrike@webrtc.org28e20752013-07-10 00:45:361501 for (StreamParamsVec::const_iterator track = media_desc->streams().begin();
1502 track != media_desc->streams().end(); ++track) {
1503 // Require that the track belongs to a media stream,
1504 // ie the sync_label is set. This extra check is necessary since the
1505 // MediaContentDescription always contains a streamparam with an ssrc even
1506 // if no track or media stream have been created.
1507 if (track->sync_label.empty()) continue;
1508
1509 // Build the ssrc-group lines.
1510 for (size_t i = 0; i < track->ssrc_groups.size(); ++i) {
1511 // RFC 5576
1512 // a=ssrc-group:<semantics> <ssrc-id> ...
1513 if (track->ssrc_groups[i].ssrcs.empty()) {
1514 continue;
1515 }
henrike@webrtc.org28e20752013-07-10 00:45:361516 InitAttrLine(kAttributeSsrcGroup, &os);
1517 os << kSdpDelimiterColon << track->ssrc_groups[i].semantics;
Peter Boström0c4e06b2015-10-07 10:23:211518 std::vector<uint32_t>::const_iterator ssrc =
henrike@webrtc.org28e20752013-07-10 00:45:361519 track->ssrc_groups[i].ssrcs.begin();
1520 for (; ssrc != track->ssrc_groups[i].ssrcs.end(); ++ssrc) {
Peter Boström0c4e06b2015-10-07 10:23:211521 os << kSdpDelimiterSpace << rtc::ToString<uint32_t>(*ssrc);
henrike@webrtc.org28e20752013-07-10 00:45:361522 }
1523 AddLine(os.str(), message);
1524 }
1525 // Build the ssrc lines for each ssrc.
1526 for (size_t i = 0; i < track->ssrcs.size(); ++i) {
Peter Boström0c4e06b2015-10-07 10:23:211527 uint32_t ssrc = track->ssrcs[i];
henrike@webrtc.org28e20752013-07-10 00:45:361528 // RFC 5576
1529 // a=ssrc:<ssrc-id> cname:<value>
1530 AddSsrcLine(ssrc, kSsrcAttributeCname,
1531 track->cname, message);
1532
1533 // draft-alvestrand-mmusic-msid-00
1534 // a=ssrc:<ssrc-id> msid:identifier [appdata]
deadbeef9d3584c2016-02-17 01:54:101535 // The appdata consists of the "id" attribute of a MediaStreamTrack,
1536 // which corresponds to the "id" attribute of StreamParams.
1537 const std::string& stream_id = track->sync_label;
henrike@webrtc.org28e20752013-07-10 00:45:361538 InitAttrLine(kAttributeSsrc, &os);
1539 os << kSdpDelimiterColon << ssrc << kSdpDelimiterSpace
deadbeef9d3584c2016-02-17 01:54:101540 << kSsrcAttributeMsid << kSdpDelimiterColon << stream_id
1541 << kSdpDelimiterSpace << track->id;
henrike@webrtc.org28e20752013-07-10 00:45:361542 AddLine(os.str(), message);
1543
deadbeef9d3584c2016-02-17 01:54:101544 // TODO(ronghuawu): Remove below code which is for backward
1545 // compatibility.
henrike@webrtc.org28e20752013-07-10 00:45:361546 // draft-alvestrand-rtcweb-mid-01
1547 // a=ssrc:<ssrc-id> mslabel:<value>
1548 // The label isn't yet defined.
1549 // a=ssrc:<ssrc-id> label:<value>
1550 AddSsrcLine(ssrc, kSsrcAttributeMslabel, track->sync_label, message);
1551 AddSsrcLine(ssrc, kSSrcAttributeLabel, track->id, message);
1552 }
1553 }
1554}
1555
1556void WriteFmtpHeader(int payload_type, std::ostringstream* os) {
1557 // fmtp header: a=fmtp:|payload_type| <parameters>
1558 // Add a=fmtp
1559 InitAttrLine(kAttributeFmtp, os);
1560 // Add :|payload_type|
1561 *os << kSdpDelimiterColon << payload_type;
1562}
1563
1564void WriteRtcpFbHeader(int payload_type, std::ostringstream* os) {
1565 // rtcp-fb header: a=rtcp-fb:|payload_type|
1566 // <parameters>/<ccm <ccm_parameters>>
1567 // Add a=rtcp-fb
1568 InitAttrLine(kAttributeRtcpFb, os);
1569 // Add :
1570 *os << kSdpDelimiterColon;
1571 if (payload_type == kWildcardPayloadType) {
1572 *os << "*";
1573 } else {
1574 *os << payload_type;
1575 }
1576}
1577
1578void WriteFmtpParameter(const std::string& parameter_name,
1579 const std::string& parameter_value,
1580 std::ostringstream* os) {
1581 // fmtp parameters: |parameter_name|=|parameter_value|
1582 *os << parameter_name << kSdpDelimiterEqual << parameter_value;
1583}
1584
1585void WriteFmtpParameters(const cricket::CodecParameterMap& parameters,
1586 std::ostringstream* os) {
1587 for (cricket::CodecParameterMap::const_iterator fmtp = parameters.begin();
1588 fmtp != parameters.end(); ++fmtp) {
hta62a216e2016-04-15 18:02:141589 // Parameters are a semicolon-separated list, no spaces.
1590 // The list is separated from the header by a space.
1591 if (fmtp == parameters.begin()) {
1592 *os << kSdpDelimiterSpace;
1593 } else {
henrike@webrtc.org28e20752013-07-10 00:45:361594 *os << kSdpDelimiterSemicolon;
1595 }
henrike@webrtc.org28e20752013-07-10 00:45:361596 WriteFmtpParameter(fmtp->first, fmtp->second, os);
1597 }
1598}
1599
1600bool IsFmtpParam(const std::string& name) {
1601 const char* kFmtpParams[] = {
htaa6b99442016-04-12 17:29:171602 // TODO(hta): Split FMTP parameters apart from parameters in general.
1603 // FMTP parameters are codec specific, not generic.
1604 kCodecParamMinPTime,
1605 kCodecParamSPropStereo,
1606 kCodecParamStereo,
1607 kCodecParamUseInbandFec,
1608 kCodecParamUseDtx,
1609 kCodecParamStartBitrate,
1610 kCodecParamMaxBitrate,
1611 kCodecParamMinBitrate,
1612 kCodecParamMaxQuantization,
1613 kCodecParamSctpProtocol,
1614 kCodecParamSctpStreams,
1615 kCodecParamMaxAverageBitrate,
1616 kCodecParamMaxPlaybackRate,
1617 kCodecParamAssociatedPayloadType,
1618 cricket::kH264FmtpPacketizationMode,
1619 cricket::kH264FmtpLevelAsymmetryAllowed,
brandtr87d7d772016-11-07 11:03:411620 cricket::kH264FmtpProfileLevelId,
1621 cricket::kFlexfecFmtpRepairWindow};
tfarina5237aaf2015-11-11 07:44:301622 for (size_t i = 0; i < arraysize(kFmtpParams); ++i) {
htaa6b99442016-04-12 17:29:171623 if (name.compare(kFmtpParams[i]) == 0) {
henrike@webrtc.org28e20752013-07-10 00:45:361624 return true;
1625 }
1626 }
1627 return false;
1628}
1629
1630// Retreives fmtp parameters from |params|, which may contain other parameters
1631// as well, and puts them in |fmtp_parameters|.
1632void GetFmtpParams(const cricket::CodecParameterMap& params,
1633 cricket::CodecParameterMap* fmtp_parameters) {
1634 for (cricket::CodecParameterMap::const_iterator iter = params.begin();
1635 iter != params.end(); ++iter) {
1636 if (IsFmtpParam(iter->first)) {
1637 (*fmtp_parameters)[iter->first] = iter->second;
1638 }
1639 }
1640}
1641
1642template <class T>
1643void AddFmtpLine(const T& codec, std::string* message) {
1644 cricket::CodecParameterMap fmtp_parameters;
1645 GetFmtpParams(codec.params, &fmtp_parameters);
1646 if (fmtp_parameters.empty()) {
1647 // No need to add an fmtp if it will have no (optional) parameters.
1648 return;
1649 }
1650 std::ostringstream os;
1651 WriteFmtpHeader(codec.id, &os);
1652 WriteFmtpParameters(fmtp_parameters, &os);
1653 AddLine(os.str(), message);
1654 return;
1655}
1656
1657template <class T>
1658void AddRtcpFbLines(const T& codec, std::string* message) {
1659 for (std::vector<cricket::FeedbackParam>::const_iterator iter =
1660 codec.feedback_params.params().begin();
1661 iter != codec.feedback_params.params().end(); ++iter) {
1662 std::ostringstream os;
1663 WriteRtcpFbHeader(codec.id, &os);
1664 os << " " << iter->id();
1665 if (!iter->param().empty()) {
1666 os << " " << iter->param();
1667 }
1668 AddLine(os.str(), message);
1669 }
1670}
1671
jiayl@webrtc.orgddb85ab2014-09-05 16:31:561672bool AddSctpDataCodec(DataContentDescription* media_desc,
1673 int sctp_port) {
solenberg9fa49752016-10-08 20:02:441674 for (const auto& codec : media_desc->codecs()) {
1675 if (cricket::CodecNamesEq(codec.name, cricket::kGoogleSctpDataCodecName)) {
1676 return ParseFailed("",
1677 "Can't have multiple sctp port attributes.",
1678 NULL);
1679 }
jiayl@webrtc.orgddb85ab2014-09-05 16:31:561680 }
1681 // Add the SCTP Port number as a pseudo-codec "port" parameter
solenberg9fa49752016-10-08 20:02:441682 cricket::DataCodec codec_port(cricket::kGoogleSctpDataCodecPlType,
deadbeef67cf2c12016-04-13 17:07:161683 cricket::kGoogleSctpDataCodecName);
jiayl@webrtc.orgddb85ab2014-09-05 16:31:561684 codec_port.SetParam(cricket::kCodecParamPort, sctp_port);
1685 LOG(INFO) << "AddSctpDataCodec: Got SCTP Port Number "
1686 << sctp_port;
1687 media_desc->AddCodec(codec_port);
1688 return true;
1689}
1690
henrike@webrtc.org28e20752013-07-10 00:45:361691bool GetMinValue(const std::vector<int>& values, int* value) {
1692 if (values.empty()) {
1693 return false;
1694 }
1695 std::vector<int>::const_iterator found =
1696 std::min_element(values.begin(), values.end());
1697 *value = *found;
1698 return true;
1699}
1700
1701bool GetParameter(const std::string& name,
1702 const cricket::CodecParameterMap& params, int* value) {
1703 std::map<std::string, std::string>::const_iterator found =
1704 params.find(name);
1705 if (found == params.end()) {
1706 return false;
1707 }
buildbot@webrtc.orgd4e598d2014-07-29 17:36:521708 if (!rtc::FromString(found->second, value)) {
wu@webrtc.org5e760e72014-04-02 23:19:091709 return false;
1710 }
henrike@webrtc.org28e20752013-07-10 00:45:361711 return true;
1712}
1713
1714void BuildRtpMap(const MediaContentDescription* media_desc,
1715 const MediaType media_type,
1716 std::string* message) {
nisseede5da42017-01-12 13:15:361717 RTC_DCHECK(message != NULL);
1718 RTC_DCHECK(media_desc != NULL);
henrike@webrtc.org28e20752013-07-10 00:45:361719 std::ostringstream os;
1720 if (media_type == cricket::MEDIA_TYPE_VIDEO) {
1721 const VideoContentDescription* video_desc =
1722 static_cast<const VideoContentDescription*>(media_desc);
1723 for (std::vector<cricket::VideoCodec>::const_iterator it =
1724 video_desc->codecs().begin();
1725 it != video_desc->codecs().end(); ++it) {
1726 // RFC 4566
1727 // a=rtpmap:<payload type> <encoding name>/<clock rate>
1728 // [/<encodingparameters>]
1729 if (it->id != kWildcardPayloadType) {
1730 InitAttrLine(kAttributeRtpmap, &os);
1731 os << kSdpDelimiterColon << it->id << " " << it->name
1732 << "/" << kDefaultVideoClockrate;
1733 AddLine(os.str(), message);
1734 }
1735 AddRtcpFbLines(*it, message);
1736 AddFmtpLine(*it, message);
1737 }
1738 } else if (media_type == cricket::MEDIA_TYPE_AUDIO) {
1739 const AudioContentDescription* audio_desc =
1740 static_cast<const AudioContentDescription*>(media_desc);
1741 std::vector<int> ptimes;
1742 std::vector<int> maxptimes;
1743 int max_minptime = 0;
1744 for (std::vector<cricket::AudioCodec>::const_iterator it =
1745 audio_desc->codecs().begin();
1746 it != audio_desc->codecs().end(); ++it) {
nisseede5da42017-01-12 13:15:361747 RTC_DCHECK(!it->name.empty());
henrike@webrtc.org28e20752013-07-10 00:45:361748 // RFC 4566
1749 // a=rtpmap:<payload type> <encoding name>/<clock rate>
1750 // [/<encodingparameters>]
1751 InitAttrLine(kAttributeRtpmap, &os);
1752 os << kSdpDelimiterColon << it->id << " ";
1753 os << it->name << "/" << it->clockrate;
1754 if (it->channels != 1) {
1755 os << "/" << it->channels;
1756 }
1757 AddLine(os.str(), message);
1758 AddRtcpFbLines(*it, message);
1759 AddFmtpLine(*it, message);
1760 int minptime = 0;
1761 if (GetParameter(kCodecParamMinPTime, it->params, &minptime)) {
1762 max_minptime = std::max(minptime, max_minptime);
1763 }
1764 int ptime;
1765 if (GetParameter(kCodecParamPTime, it->params, &ptime)) {
1766 ptimes.push_back(ptime);
1767 }
1768 int maxptime;
1769 if (GetParameter(kCodecParamMaxPTime, it->params, &maxptime)) {
1770 maxptimes.push_back(maxptime);
1771 }
1772 }
1773 // Populate the maxptime attribute with the smallest maxptime of all codecs
1774 // under the same m-line.
1775 int min_maxptime = INT_MAX;
1776 if (GetMinValue(maxptimes, &min_maxptime)) {
1777 AddAttributeLine(kCodecParamMaxPTime, min_maxptime, message);
1778 }
nisseede5da42017-01-12 13:15:361779 RTC_DCHECK(min_maxptime > max_minptime);
henrike@webrtc.org28e20752013-07-10 00:45:361780 // Populate the ptime attribute with the smallest ptime or the largest
1781 // minptime, whichever is the largest, for all codecs under the same m-line.
1782 int ptime = INT_MAX;
1783 if (GetMinValue(ptimes, &ptime)) {
1784 ptime = std::min(ptime, min_maxptime);
1785 ptime = std::max(ptime, max_minptime);
1786 AddAttributeLine(kCodecParamPTime, ptime, message);
1787 }
1788 } else if (media_type == cricket::MEDIA_TYPE_DATA) {
1789 const DataContentDescription* data_desc =
1790 static_cast<const DataContentDescription*>(media_desc);
1791 for (std::vector<cricket::DataCodec>::const_iterator it =
1792 data_desc->codecs().begin();
1793 it != data_desc->codecs().end(); ++it) {
1794 // RFC 4566
1795 // a=rtpmap:<payload type> <encoding name>/<clock rate>
1796 // [/<encodingparameters>]
1797 InitAttrLine(kAttributeRtpmap, &os);
1798 os << kSdpDelimiterColon << it->id << " "
1799 << it->name << "/" << it->clockrate;
1800 AddLine(os.str(), message);
1801 }
1802 }
1803}
1804
1805void BuildCandidate(const std::vector<Candidate>& candidates,
honghaiza54a0802015-12-17 02:37:231806 bool include_ufrag,
henrike@webrtc.org28e20752013-07-10 00:45:361807 std::string* message) {
1808 std::ostringstream os;
1809
1810 for (std::vector<Candidate>::const_iterator it = candidates.begin();
1811 it != candidates.end(); ++it) {
1812 // RFC 5245
1813 // a=candidate:<foundation> <component-id> <transport> <priority>
1814 // <connection-address> <port> typ <candidate-types>
1815 // [raddr <connection-address>] [rport <port>]
1816 // *(SP extension-att-name SP extension-att-value)
1817 std::string type;
1818 // Map the cricket candidate type to "host" / "srflx" / "prflx" / "relay"
1819 if (it->type() == cricket::LOCAL_PORT_TYPE) {
1820 type = kCandidateHost;
1821 } else if (it->type() == cricket::STUN_PORT_TYPE) {
1822 type = kCandidateSrflx;
1823 } else if (it->type() == cricket::RELAY_PORT_TYPE) {
1824 type = kCandidateRelay;
Honghai Zhang7fb69db2016-03-14 18:59:181825 } else if (it->type() == cricket::PRFLX_PORT_TYPE) {
1826 type = kCandidatePrflx;
1827 // Peer reflexive candidate may be signaled for being removed.
henrike@webrtc.org28e20752013-07-10 00:45:361828 } else {
nissec80e7412017-01-11 13:56:461829 RTC_NOTREACHED();
Peter Thatcher019087f2015-04-28 16:06:261830 // Never write out candidates if we don't know the type.
1831 continue;
henrike@webrtc.org28e20752013-07-10 00:45:361832 }
1833
1834 InitAttrLine(kAttributeCandidate, &os);
1835 os << kSdpDelimiterColon
mallinath@webrtc.org2d60c5e2014-08-08 22:29:201836 << it->foundation() << " "
1837 << it->component() << " "
1838 << it->protocol() << " "
1839 << it->priority() << " "
henrike@webrtc.org28e20752013-07-10 00:45:361840 << it->address().ipaddr().ToString() << " "
1841 << it->address().PortAsString() << " "
mallinath@webrtc.org2d60c5e2014-08-08 22:29:201842 << kAttributeCandidateTyp << " "
1843 << type << " ";
henrike@webrtc.org28e20752013-07-10 00:45:361844
1845 // Related address
1846 if (!it->related_address().IsNil()) {
1847 os << kAttributeCandidateRaddr << " "
1848 << it->related_address().ipaddr().ToString() << " "
1849 << kAttributeCandidateRport << " "
1850 << it->related_address().PortAsString() << " ";
1851 }
1852
mallinath@webrtc.org2d60c5e2014-08-08 22:29:201853 if (it->protocol() == cricket::TCP_PROTOCOL_NAME) {
mallinath@webrtc.orge999bd02014-08-13 06:05:551854 os << kTcpCandidateType << " " << it->tcptype() << " ";
mallinath@webrtc.org2d60c5e2014-08-08 22:29:201855 }
1856
henrike@webrtc.org28e20752013-07-10 00:45:361857 // Extensions
1858 os << kAttributeCandidateGeneration << " " << it->generation();
honghaiza54a0802015-12-17 02:37:231859 if (include_ufrag && !it->username().empty()) {
1860 os << " " << kAttributeCandidateUfrag << " " << it->username();
1861 }
honghaiza0c44ea2016-03-23 23:07:481862 if (it->network_id() > 0) {
1863 os << " " << kAttributeCandidateNetworkId << " " << it->network_id();
1864 }
honghaize1a0c942016-02-16 22:54:561865 if (it->network_cost() > 0) {
1866 os << " " << kAttributeCandidateNetworkCost << " " << it->network_cost();
1867 }
henrike@webrtc.org28e20752013-07-10 00:45:361868
1869 AddLine(os.str(), message);
1870 }
1871}
1872
1873void BuildIceOptions(const std::vector<std::string>& transport_options,
1874 std::string* message) {
1875 if (!transport_options.empty()) {
1876 std::ostringstream os;
1877 InitAttrLine(kAttributeIceOption, &os);
1878 os << kSdpDelimiterColon << transport_options[0];
1879 for (size_t i = 1; i < transport_options.size(); ++i) {
1880 os << kSdpDelimiterSpace << transport_options[i];
1881 }
1882 AddLine(os.str(), message);
1883 }
1884}
1885
pthatcher@webrtc.org3341b402015-02-13 21:14:221886bool IsRtp(const std::string& protocol) {
1887 return protocol.empty() ||
1888 (protocol.find(cricket::kMediaProtocolRtpPrefix) != std::string::npos);
1889}
1890
1891bool IsDtlsSctp(const std::string& protocol) {
1892 // This intentionally excludes "SCTP" and "SCTP/DTLS".
lally@webrtc.orga7470932015-02-24 20:19:211893 return protocol.find(cricket::kMediaProtocolDtlsSctp) != std::string::npos;
pthatcher@webrtc.org3341b402015-02-13 21:14:221894}
1895
henrike@webrtc.org28e20752013-07-10 00:45:361896bool ParseSessionDescription(const std::string& message, size_t* pos,
1897 std::string* session_id,
1898 std::string* session_version,
henrike@webrtc.org28e20752013-07-10 00:45:361899 TransportDescription* session_td,
1900 RtpHeaderExtensions* session_extmaps,
1901 cricket::SessionDescription* desc,
1902 SdpParseError* error) {
1903 std::string line;
1904
deadbeefc80741f2015-10-22 20:14:451905 desc->set_msid_supported(false);
1906
henrike@webrtc.org28e20752013-07-10 00:45:361907 // RFC 4566
1908 // v= (protocol version)
1909 if (!GetLineWithType(message, pos, &line, kLineTypeVersion)) {
1910 return ParseFailedExpectLine(message, *pos, kLineTypeVersion,
1911 std::string(), error);
1912 }
1913 // RFC 4566
1914 // o=<username> <sess-id> <sess-version> <nettype> <addrtype>
1915 // <unicast-address>
1916 if (!GetLineWithType(message, pos, &line, kLineTypeOrigin)) {
1917 return ParseFailedExpectLine(message, *pos, kLineTypeOrigin,
1918 std::string(), error);
1919 }
1920 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:521921 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:361922 kSdpDelimiterSpace, &fields);
1923 const size_t expected_fields = 6;
1924 if (fields.size() != expected_fields) {
1925 return ParseFailedExpectFieldNum(line, expected_fields, error);
1926 }
1927 *session_id = fields[1];
1928 *session_version = fields[2];
1929
1930 // RFC 4566
1931 // s= (session name)
1932 if (!GetLineWithType(message, pos, &line, kLineTypeSessionName)) {
1933 return ParseFailedExpectLine(message, *pos, kLineTypeSessionName,
1934 std::string(), error);
1935 }
1936
1937 // Optional lines
1938 // Those are the optional lines, so shouldn't return false if not present.
1939 // RFC 4566
1940 // i=* (session information)
1941 GetLineWithType(message, pos, &line, kLineTypeSessionInfo);
1942
1943 // RFC 4566
1944 // u=* (URI of description)
1945 GetLineWithType(message, pos, &line, kLineTypeSessionUri);
1946
1947 // RFC 4566
1948 // e=* (email address)
1949 GetLineWithType(message, pos, &line, kLineTypeSessionEmail);
1950
1951 // RFC 4566
1952 // p=* (phone number)
1953 GetLineWithType(message, pos, &line, kLineTypeSessionPhone);
1954
1955 // RFC 4566
1956 // c=* (connection information -- not required if included in
1957 // all media)
1958 GetLineWithType(message, pos, &line, kLineTypeConnection);
1959
1960 // RFC 4566
1961 // b=* (zero or more bandwidth information lines)
1962 while (GetLineWithType(message, pos, &line, kLineTypeSessionBandwidth)) {
1963 // By pass zero or more b lines.
1964 }
1965
1966 // RFC 4566
1967 // One or more time descriptions ("t=" and "r=" lines; see below)
1968 // t= (time the session is active)
1969 // r=* (zero or more repeat times)
1970 // Ensure there's at least one time description
1971 if (!GetLineWithType(message, pos, &line, kLineTypeTiming)) {
1972 return ParseFailedExpectLine(message, *pos, kLineTypeTiming, std::string(),
1973 error);
1974 }
1975
1976 while (GetLineWithType(message, pos, &line, kLineTypeRepeatTimes)) {
1977 // By pass zero or more r lines.
1978 }
1979
1980 // Go through the rest of the time descriptions
1981 while (GetLineWithType(message, pos, &line, kLineTypeTiming)) {
1982 while (GetLineWithType(message, pos, &line, kLineTypeRepeatTimes)) {
1983 // By pass zero or more r lines.
1984 }
1985 }
1986
1987 // RFC 4566
1988 // z=* (time zone adjustments)
1989 GetLineWithType(message, pos, &line, kLineTypeTimeZone);
1990
1991 // RFC 4566
1992 // k=* (encryption key)
1993 GetLineWithType(message, pos, &line, kLineTypeEncryptionKey);
1994
1995 // RFC 4566
1996 // a=* (zero or more session attribute lines)
1997 while (GetLineWithType(message, pos, &line, kLineTypeAttributes)) {
1998 if (HasAttribute(line, kAttributeGroup)) {
1999 if (!ParseGroupAttribute(line, desc, error)) {
2000 return false;
2001 }
2002 } else if (HasAttribute(line, kAttributeIceUfrag)) {
2003 if (!GetValue(line, kAttributeIceUfrag,
2004 &(session_td->ice_ufrag), error)) {
2005 return false;
2006 }
2007 } else if (HasAttribute(line, kAttributeIcePwd)) {
2008 if (!GetValue(line, kAttributeIcePwd, &(session_td->ice_pwd), error)) {
2009 return false;
2010 }
2011 } else if (HasAttribute(line, kAttributeIceLite)) {
2012 session_td->ice_mode = cricket::ICEMODE_LITE;
2013 } else if (HasAttribute(line, kAttributeIceOption)) {
2014 if (!ParseIceOptions(line, &(session_td->transport_options), error)) {
2015 return false;
2016 }
2017 } else if (HasAttribute(line, kAttributeFingerprint)) {
2018 if (session_td->identity_fingerprint.get()) {
2019 return ParseFailed(
2020 line,
2021 "Can't have multiple fingerprint attributes at the same level.",
2022 error);
2023 }
buildbot@webrtc.orgd4e598d2014-07-29 17:36:522024 rtc::SSLFingerprint* fingerprint = NULL;
henrike@webrtc.org28e20752013-07-10 00:45:362025 if (!ParseFingerprintAttribute(line, &fingerprint, error)) {
2026 return false;
2027 }
2028 session_td->identity_fingerprint.reset(fingerprint);
sergeyu@chromium.org0be6aa02013-08-23 23:21:252029 } else if (HasAttribute(line, kAttributeSetup)) {
2030 if (!ParseDtlsSetup(line, &(session_td->connection_role), error)) {
2031 return false;
2032 }
henrike@webrtc.org28e20752013-07-10 00:45:362033 } else if (HasAttribute(line, kAttributeMsidSemantics)) {
2034 std::string semantics;
2035 if (!GetValue(line, kAttributeMsidSemantics, &semantics, error)) {
2036 return false;
2037 }
deadbeefc80741f2015-10-22 20:14:452038 desc->set_msid_supported(
2039 CaseInsensitiveFind(semantics, kMediaStreamSemantic));
henrike@webrtc.org28e20752013-07-10 00:45:362040 } else if (HasAttribute(line, kAttributeExtmap)) {
isheriff6f8d6862016-05-26 18:24:552041 RtpExtension extmap;
henrike@webrtc.org28e20752013-07-10 00:45:362042 if (!ParseExtmap(line, &extmap, error)) {
2043 return false;
2044 }
2045 session_extmaps->push_back(extmap);
2046 }
2047 }
2048
2049 return true;
2050}
2051
2052bool ParseGroupAttribute(const std::string& line,
2053 cricket::SessionDescription* desc,
2054 SdpParseError* error) {
nisseede5da42017-01-12 13:15:362055 RTC_DCHECK(desc != NULL);
henrike@webrtc.org28e20752013-07-10 00:45:362056
2057 // RFC 5888 and draft-holmberg-mmusic-sdp-bundle-negotiation-00
2058 // a=group:BUNDLE video voice
2059 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:522060 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:362061 kSdpDelimiterSpace, &fields);
2062 std::string semantics;
2063 if (!GetValue(fields[0], kAttributeGroup, &semantics, error)) {
2064 return false;
2065 }
2066 cricket::ContentGroup group(semantics);
2067 for (size_t i = 1; i < fields.size(); ++i) {
2068 group.AddContentName(fields[i]);
2069 }
2070 desc->AddGroup(group);
2071 return true;
2072}
2073
2074static bool ParseFingerprintAttribute(const std::string& line,
buildbot@webrtc.orgd4e598d2014-07-29 17:36:522075 rtc::SSLFingerprint** fingerprint,
henrike@webrtc.org28e20752013-07-10 00:45:362076 SdpParseError* error) {
2077 if (!IsLineType(line, kLineTypeAttributes) ||
2078 !HasAttribute(line, kAttributeFingerprint)) {
2079 return ParseFailedExpectLine(line, 0, kLineTypeAttributes,
2080 kAttributeFingerprint, error);
2081 }
2082
2083 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:522084 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:362085 kSdpDelimiterSpace, &fields);
2086 const size_t expected_fields = 2;
2087 if (fields.size() != expected_fields) {
2088 return ParseFailedExpectFieldNum(line, expected_fields, error);
2089 }
2090
2091 // The first field here is "fingerprint:<hash>.
2092 std::string algorithm;
2093 if (!GetValue(fields[0], kAttributeFingerprint, &algorithm, error)) {
2094 return false;
2095 }
2096
2097 // Downcase the algorithm. Note that we don't need to downcase the
2098 // fingerprint because hex_decode can handle upper-case.
2099 std::transform(algorithm.begin(), algorithm.end(), algorithm.begin(),
2100 ::tolower);
2101
2102 // The second field is the digest value. De-hexify it.
buildbot@webrtc.orgd4e598d2014-07-29 17:36:522103 *fingerprint = rtc::SSLFingerprint::CreateFromRfc4572(
henrike@webrtc.org28e20752013-07-10 00:45:362104 algorithm, fields[1]);
2105 if (!*fingerprint) {
2106 return ParseFailed(line,
2107 "Failed to create fingerprint from the digest.",
2108 error);
2109 }
2110
2111 return true;
2112}
2113
sergeyu@chromium.org0be6aa02013-08-23 23:21:252114static bool ParseDtlsSetup(const std::string& line,
2115 cricket::ConnectionRole* role,
2116 SdpParseError* error) {
2117 // setup-attr = "a=setup:" role
2118 // role = "active" / "passive" / "actpass" / "holdconn"
2119 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:522120 rtc::split(line.substr(kLinePrefixLength), kSdpDelimiterColon, &fields);
sergeyu@chromium.org0be6aa02013-08-23 23:21:252121 const size_t expected_fields = 2;
2122 if (fields.size() != expected_fields) {
2123 return ParseFailedExpectFieldNum(line, expected_fields, error);
2124 }
2125 std::string role_str = fields[1];
2126 if (!cricket::StringToConnectionRole(role_str, role)) {
2127 return ParseFailed(line, "Invalid attribute value.", error);
2128 }
2129 return true;
2130}
2131
deadbeef9d3584c2016-02-17 01:54:102132static bool ParseMsidAttribute(const std::string& line,
2133 std::string* stream_id,
2134 std::string* track_id,
2135 SdpParseError* error) {
2136 // draft-ietf-mmusic-msid-11
2137 // a=msid:<stream id> <track id>
2138 // msid-value = msid-id [ SP msid-appdata ]
2139 // msid-id = 1*64token-char ; see RFC 4566
2140 // msid-appdata = 1*64token-char ; see RFC 4566
2141 std::string field1;
2142 if (!rtc::tokenize_first(line.substr(kLinePrefixLength), kSdpDelimiterSpace,
2143 &field1, track_id)) {
2144 const size_t expected_fields = 2;
2145 return ParseFailedExpectFieldNum(line, expected_fields, error);
2146 }
2147
2148 // msid:<msid-id>
2149 if (!GetValue(field1, kAttributeMsid, stream_id, error)) {
2150 return false;
2151 }
2152 return true;
2153}
2154
henrike@webrtc.org28e20752013-07-10 00:45:362155// RFC 3551
2156// PT encoding media type clock rate channels
2157// name (Hz)
2158// 0 PCMU A 8,000 1
2159// 1 reserved A
2160// 2 reserved A
2161// 3 GSM A 8,000 1
2162// 4 G723 A 8,000 1
2163// 5 DVI4 A 8,000 1
2164// 6 DVI4 A 16,000 1
2165// 7 LPC A 8,000 1
2166// 8 PCMA A 8,000 1
2167// 9 G722 A 8,000 1
2168// 10 L16 A 44,100 2
2169// 11 L16 A 44,100 1
2170// 12 QCELP A 8,000 1
2171// 13 CN A 8,000 1
2172// 14 MPA A 90,000 (see text)
2173// 15 G728 A 8,000 1
2174// 16 DVI4 A 11,025 1
2175// 17 DVI4 A 22,050 1
2176// 18 G729 A 8,000 1
2177struct StaticPayloadAudioCodec {
2178 const char* name;
2179 int clockrate;
Peter Kasting69558702016-01-13 00:26:352180 size_t channels;
henrike@webrtc.org28e20752013-07-10 00:45:362181};
2182static const StaticPayloadAudioCodec kStaticPayloadAudioCodecs[] = {
2183 { "PCMU", 8000, 1 },
2184 { "reserved", 0, 0 },
2185 { "reserved", 0, 0 },
2186 { "GSM", 8000, 1 },
2187 { "G723", 8000, 1 },
2188 { "DVI4", 8000, 1 },
2189 { "DVI4", 16000, 1 },
2190 { "LPC", 8000, 1 },
2191 { "PCMA", 8000, 1 },
2192 { "G722", 8000, 1 },
2193 { "L16", 44100, 2 },
2194 { "L16", 44100, 1 },
2195 { "QCELP", 8000, 1 },
2196 { "CN", 8000, 1 },
2197 { "MPA", 90000, 1 },
2198 { "G728", 8000, 1 },
2199 { "DVI4", 11025, 1 },
2200 { "DVI4", 22050, 1 },
2201 { "G729", 8000, 1 },
2202};
2203
2204void MaybeCreateStaticPayloadAudioCodecs(
2205 const std::vector<int>& fmts, AudioContentDescription* media_desc) {
2206 if (!media_desc) {
2207 return;
2208 }
deadbeef67cf2c12016-04-13 17:07:162209 RTC_DCHECK(media_desc->codecs().empty());
solenberg9fa49752016-10-08 20:02:442210 for (int payload_type : fmts) {
henrike@webrtc.org28e20752013-07-10 00:45:362211 if (!media_desc->HasCodec(payload_type) &&
2212 payload_type >= 0 &&
kjellander3e33bfe2016-06-20 14:04:092213 static_cast<uint32_t>(payload_type) <
2214 arraysize(kStaticPayloadAudioCodecs)) {
henrike@webrtc.org28e20752013-07-10 00:45:362215 std::string encoding_name = kStaticPayloadAudioCodecs[payload_type].name;
2216 int clock_rate = kStaticPayloadAudioCodecs[payload_type].clockrate;
Peter Kasting69558702016-01-13 00:26:352217 size_t channels = kStaticPayloadAudioCodecs[payload_type].channels;
henrike@webrtc.org28e20752013-07-10 00:45:362218 media_desc->AddCodec(cricket::AudioCodec(payload_type, encoding_name,
deadbeef67cf2c12016-04-13 17:07:162219 clock_rate, 0, channels));
henrike@webrtc.org28e20752013-07-10 00:45:362220 }
henrike@webrtc.org28e20752013-07-10 00:45:362221 }
2222}
2223
2224template <class C>
2225static C* ParseContentDescription(const std::string& message,
2226 const MediaType media_type,
2227 int mline_index,
2228 const std::string& protocol,
deadbeef67cf2c12016-04-13 17:07:162229 const std::vector<int>& payload_types,
henrike@webrtc.org28e20752013-07-10 00:45:362230 size_t* pos,
2231 std::string* content_name,
deadbeef25ed4352016-12-13 02:37:362232 bool* bundle_only,
henrike@webrtc.org28e20752013-07-10 00:45:362233 TransportDescription* transport,
2234 std::vector<JsepIceCandidate*>* candidates,
2235 webrtc::SdpParseError* error) {
2236 C* media_desc = new C();
2237 switch (media_type) {
2238 case cricket::MEDIA_TYPE_AUDIO:
2239 *content_name = cricket::CN_AUDIO;
2240 break;
2241 case cricket::MEDIA_TYPE_VIDEO:
2242 *content_name = cricket::CN_VIDEO;
2243 break;
2244 case cricket::MEDIA_TYPE_DATA:
2245 *content_name = cricket::CN_DATA;
2246 break;
2247 default:
nissec80e7412017-01-11 13:56:462248 RTC_NOTREACHED();
henrike@webrtc.org28e20752013-07-10 00:45:362249 break;
2250 }
deadbeef67cf2c12016-04-13 17:07:162251 if (!ParseContent(message, media_type, mline_index, protocol, payload_types,
deadbeef25ed4352016-12-13 02:37:362252 pos, content_name, bundle_only, media_desc, transport,
2253 candidates, error)) {
henrike@webrtc.org28e20752013-07-10 00:45:362254 delete media_desc;
2255 return NULL;
2256 }
2257 // Sort the codecs according to the m-line fmt list.
deadbeef67cf2c12016-04-13 17:07:162258 std::unordered_map<int, int> payload_type_preferences;
2259 // "size + 1" so that the lowest preference payload type has a preference of
2260 // 1, which is greater than the default (0) for payload types not in the fmt
2261 // list.
2262 int preference = static_cast<int>(payload_types.size() + 1);
2263 for (int pt : payload_types) {
2264 payload_type_preferences[pt] = preference--;
2265 }
2266 std::vector<typename C::CodecType> codecs = media_desc->codecs();
2267 std::sort(codecs.begin(), codecs.end(), [&payload_type_preferences](
2268 const typename C::CodecType& a,
2269 const typename C::CodecType& b) {
2270 return payload_type_preferences[a.id] > payload_type_preferences[b.id];
2271 });
2272 media_desc->set_codecs(codecs);
henrike@webrtc.org28e20752013-07-10 00:45:362273 return media_desc;
2274}
2275
2276bool ParseMediaDescription(const std::string& message,
2277 const TransportDescription& session_td,
2278 const RtpHeaderExtensions& session_extmaps,
henrike@webrtc.org28e20752013-07-10 00:45:362279 size_t* pos,
2280 cricket::SessionDescription* desc,
2281 std::vector<JsepIceCandidate*>* candidates,
2282 SdpParseError* error) {
nisseede5da42017-01-12 13:15:362283 RTC_DCHECK(desc != NULL);
henrike@webrtc.org28e20752013-07-10 00:45:362284 std::string line;
2285 int mline_index = -1;
2286
2287 // Zero or more media descriptions
2288 // RFC 4566
2289 // m=<media> <port> <proto> <fmt>
2290 while (GetLineWithType(message, pos, &line, kLineTypeMedia)) {
2291 ++mline_index;
2292
2293 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:522294 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:362295 kSdpDelimiterSpace, &fields);
2296 const size_t expected_min_fields = 4;
2297 if (fields.size() < expected_min_fields) {
2298 return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
2299 }
deadbeef25ed4352016-12-13 02:37:362300 bool port_rejected = false;
henrike@webrtc.org28e20752013-07-10 00:45:362301 // RFC 3264
2302 // To reject an offered stream, the port number in the corresponding stream
2303 // in the answer MUST be set to zero.
2304 if (fields[1] == kMediaPortRejected) {
deadbeef25ed4352016-12-13 02:37:362305 port_rejected = true;
henrike@webrtc.org28e20752013-07-10 00:45:362306 }
2307
2308 std::string protocol = fields[2];
henrike@webrtc.org28e20752013-07-10 00:45:362309
2310 // <fmt>
deadbeef67cf2c12016-04-13 17:07:162311 std::vector<int> payload_types;
pthatcher@webrtc.org3341b402015-02-13 21:14:222312 if (IsRtp(protocol)) {
jiayl@webrtc.orgddb85ab2014-09-05 16:31:562313 for (size_t j = 3 ; j < fields.size(); ++j) {
2314 // TODO(wu): Remove when below bug is fixed.
2315 // https://bugzilla.mozilla.org/show_bug.cgi?id=996329
pbosbb36fdf2015-07-09 14:48:142316 if (fields[j].empty() && j == fields.size() - 1) {
jiayl@webrtc.orgddb85ab2014-09-05 16:31:562317 continue;
2318 }
wu@webrtc.org36eda7c2014-04-15 20:37:302319
jiayl@webrtc.orgddb85ab2014-09-05 16:31:562320 int pl = 0;
pkasting@chromium.orge9facf82015-02-17 20:36:282321 if (!GetPayloadTypeFromString(line, fields[j], &pl, error)) {
jiayl@webrtc.orgddb85ab2014-09-05 16:31:562322 return false;
2323 }
deadbeef67cf2c12016-04-13 17:07:162324 payload_types.push_back(pl);
wu@webrtc.org5e760e72014-04-02 23:19:092325 }
henrike@webrtc.org28e20752013-07-10 00:45:362326 }
2327
2328 // Make a temporary TransportDescription based on |session_td|.
2329 // Some of this gets overwritten by ParseContent.
deadbeef46eed762016-01-28 21:24:372330 TransportDescription transport(
2331 session_td.transport_options, session_td.ice_ufrag, session_td.ice_pwd,
2332 session_td.ice_mode, session_td.connection_role,
2333 session_td.identity_fingerprint.get());
henrike@webrtc.org28e20752013-07-10 00:45:362334
kwibergd1fe2812016-04-27 13:47:292335 std::unique_ptr<MediaContentDescription> content;
henrike@webrtc.org28e20752013-07-10 00:45:362336 std::string content_name;
deadbeef25ed4352016-12-13 02:37:362337 bool bundle_only = false;
henrike@webrtc.org28e20752013-07-10 00:45:362338 if (HasAttribute(line, kMediaTypeVideo)) {
2339 content.reset(ParseContentDescription<VideoContentDescription>(
deadbeef67cf2c12016-04-13 17:07:162340 message, cricket::MEDIA_TYPE_VIDEO, mline_index, protocol,
deadbeef25ed4352016-12-13 02:37:362341 payload_types, pos, &content_name, &bundle_only, &transport,
2342 candidates, error));
henrike@webrtc.org28e20752013-07-10 00:45:362343 } else if (HasAttribute(line, kMediaTypeAudio)) {
2344 content.reset(ParseContentDescription<AudioContentDescription>(
deadbeef67cf2c12016-04-13 17:07:162345 message, cricket::MEDIA_TYPE_AUDIO, mline_index, protocol,
deadbeef25ed4352016-12-13 02:37:362346 payload_types, pos, &content_name, &bundle_only, &transport,
2347 candidates, error));
henrike@webrtc.org28e20752013-07-10 00:45:362348 } else if (HasAttribute(line, kMediaTypeData)) {
jiayl@webrtc.org0e527722014-09-08 21:43:432349 DataContentDescription* data_desc =
wu@webrtc.org78187522013-10-07 23:32:022350 ParseContentDescription<DataContentDescription>(
deadbeef67cf2c12016-04-13 17:07:162351 message, cricket::MEDIA_TYPE_DATA, mline_index, protocol,
deadbeef25ed4352016-12-13 02:37:362352 payload_types, pos, &content_name, &bundle_only, &transport,
2353 candidates, error);
jiayl@webrtc.org0e527722014-09-08 21:43:432354 content.reset(data_desc);
wu@webrtc.org78187522013-10-07 23:32:022355
jiayl@webrtc.orgddb85ab2014-09-05 16:31:562356 int p;
pthatcher@webrtc.org3341b402015-02-13 21:14:222357 if (data_desc && IsDtlsSctp(protocol) && rtc::FromString(fields[3], &p)) {
jiayl@webrtc.org0e527722014-09-08 21:43:432358 if (!AddSctpDataCodec(data_desc, p))
jiayl@webrtc.orgddb85ab2014-09-05 16:31:562359 return false;
wu@webrtc.org78187522013-10-07 23:32:022360 }
henrike@webrtc.org28e20752013-07-10 00:45:362361 } else {
2362 LOG(LS_WARNING) << "Unsupported media type: " << line;
2363 continue;
2364 }
2365 if (!content.get()) {
2366 // ParseContentDescription returns NULL if failed.
2367 return false;
2368 }
2369
deadbeef25ed4352016-12-13 02:37:362370 bool content_rejected = false;
deadbeef12771a12017-01-03 21:53:472371 // A port of 0 is not interpreted as a rejected m= section when it's
2372 // used along with a=bundle-only.
deadbeef25ed4352016-12-13 02:37:362373 if (bundle_only) {
deadbeef25ed4352016-12-13 02:37:362374 if (!port_rejected) {
deadbeef12771a12017-01-03 21:53:472375 // Usage of bundle-only with a nonzero port is unspecified. So just
2376 // ignore bundle-only if we see this.
2377 bundle_only = false;
2378 LOG(LS_WARNING)
2379 << "a=bundle-only attribute observed with a nonzero "
2380 << "port; this usage is unspecified so the attribute is being "
2381 << "ignored.";
deadbeef25ed4352016-12-13 02:37:362382 }
2383 } else {
2384 // If not using bundle-only, interpret port 0 in the normal way; the m=
2385 // section is being rejected.
2386 content_rejected = port_rejected;
2387 }
2388
pthatcher@webrtc.org3341b402015-02-13 21:14:222389 if (IsRtp(protocol)) {
henrike@webrtc.org28e20752013-07-10 00:45:362390 // Set the extmap.
2391 if (!session_extmaps.empty() &&
2392 !content->rtp_header_extensions().empty()) {
2393 return ParseFailed("",
2394 "The a=extmap MUST be either all session level or "
2395 "all media level.",
2396 error);
2397 }
2398 for (size_t i = 0; i < session_extmaps.size(); ++i) {
2399 content->AddRtpHeaderExtension(session_extmaps[i]);
2400 }
2401 }
2402 content->set_protocol(protocol);
2403 desc->AddContent(content_name,
deadbeef25ed4352016-12-13 02:37:362404 IsDtlsSctp(protocol) ? cricket::NS_JINGLE_DRAFT_SCTP
2405 : cricket::NS_JINGLE_RTP,
2406 content_rejected, bundle_only, content.release());
henrike@webrtc.org28e20752013-07-10 00:45:362407 // Create TransportInfo with the media level "ice-pwd" and "ice-ufrag".
2408 TransportInfo transport_info(content_name, transport);
2409
2410 if (!desc->AddTransportInfo(transport_info)) {
2411 std::ostringstream description;
2412 description << "Failed to AddTransportInfo with content name: "
2413 << content_name;
2414 return ParseFailed("", description.str(), error);
2415 }
2416 }
wu@webrtc.orgcecfd182013-10-30 05:18:122417
2418 size_t end_of_message = message.size();
2419 if (mline_index == -1 && *pos != end_of_message) {
2420 ParseFailed(message, *pos, "Expects m line.", error);
2421 return false;
2422 }
henrike@webrtc.org28e20752013-07-10 00:45:362423 return true;
2424}
2425
2426bool VerifyCodec(const cricket::Codec& codec) {
2427 // Codec has not been populated correctly unless the name has been set. This
2428 // can happen if an SDP has an fmtp or rtcp-fb with a payload type but doesn't
2429 // have a corresponding "rtpmap" line.
htab39db842016-12-08 09:50:482430 return !codec.name.empty();
henrike@webrtc.org28e20752013-07-10 00:45:362431}
2432
2433bool VerifyAudioCodecs(const AudioContentDescription* audio_desc) {
2434 const std::vector<cricket::AudioCodec>& codecs = audio_desc->codecs();
2435 for (std::vector<cricket::AudioCodec>::const_iterator iter = codecs.begin();
2436 iter != codecs.end(); ++iter) {
2437 if (!VerifyCodec(*iter)) {
2438 return false;
2439 }
2440 }
2441 return true;
2442}
2443
2444bool VerifyVideoCodecs(const VideoContentDescription* video_desc) {
2445 const std::vector<cricket::VideoCodec>& codecs = video_desc->codecs();
2446 for (std::vector<cricket::VideoCodec>::const_iterator iter = codecs.begin();
2447 iter != codecs.end(); ++iter) {
2448 if (!VerifyCodec(*iter)) {
2449 return false;
2450 }
2451 }
2452 return true;
2453}
2454
2455void AddParameters(const cricket::CodecParameterMap& parameters,
2456 cricket::Codec* codec) {
2457 for (cricket::CodecParameterMap::const_iterator iter =
2458 parameters.begin(); iter != parameters.end(); ++iter) {
2459 codec->SetParam(iter->first, iter->second);
2460 }
2461}
2462
2463void AddFeedbackParameter(const cricket::FeedbackParam& feedback_param,
2464 cricket::Codec* codec) {
2465 codec->AddFeedbackParam(feedback_param);
2466}
2467
2468void AddFeedbackParameters(const cricket::FeedbackParams& feedback_params,
2469 cricket::Codec* codec) {
2470 for (std::vector<cricket::FeedbackParam>::const_iterator iter =
2471 feedback_params.params().begin();
2472 iter != feedback_params.params().end(); ++iter) {
2473 codec->AddFeedbackParam(*iter);
2474 }
2475}
2476
2477// Gets the current codec setting associated with |payload_type|. If there
changbin.shao@webrtc.org2d25b442015-03-16 04:14:342478// is no Codec associated with that payload type it returns an empty codec
henrike@webrtc.org28e20752013-07-10 00:45:362479// with that payload type.
2480template <class T>
changbin.shao@webrtc.org2d25b442015-03-16 04:14:342481T GetCodecWithPayloadType(const std::vector<T>& codecs, int payload_type) {
magjedb05fa242016-11-11 12:00:162482 const T* codec = FindCodecById(codecs, payload_type);
2483 if (codec)
2484 return *codec;
2485 // Return empty codec with |payload_type|.
changbin.shao@webrtc.org2d25b442015-03-16 04:14:342486 T ret_val;
magjedb05fa242016-11-11 12:00:162487 ret_val.id = payload_type;
henrike@webrtc.org28e20752013-07-10 00:45:362488 return ret_val;
2489}
2490
2491// Updates or creates a new codec entry in the audio description.
2492template <class T, class U>
2493void AddOrReplaceCodec(MediaContentDescription* content_desc, const U& codec) {
2494 T* desc = static_cast<T*>(content_desc);
2495 std::vector<U> codecs = desc->codecs();
2496 bool found = false;
2497
2498 typename std::vector<U>::iterator iter;
2499 for (iter = codecs.begin(); iter != codecs.end(); ++iter) {
2500 if (iter->id == codec.id) {
2501 *iter = codec;
2502 found = true;
2503 break;
2504 }
2505 }
2506 if (!found) {
2507 desc->AddCodec(codec);
2508 return;
2509 }
2510 desc->set_codecs(codecs);
2511}
2512
2513// Adds or updates existing codec corresponding to |payload_type| according
2514// to |parameters|.
2515template <class T, class U>
2516void UpdateCodec(MediaContentDescription* content_desc, int payload_type,
2517 const cricket::CodecParameterMap& parameters) {
2518 // Codec might already have been populated (from rtpmap).
changbin.shao@webrtc.org2d25b442015-03-16 04:14:342519 U new_codec = GetCodecWithPayloadType(static_cast<T*>(content_desc)->codecs(),
2520 payload_type);
henrike@webrtc.org28e20752013-07-10 00:45:362521 AddParameters(parameters, &new_codec);
2522 AddOrReplaceCodec<T, U>(content_desc, new_codec);
2523}
2524
2525// Adds or updates existing codec corresponding to |payload_type| according
2526// to |feedback_param|.
2527template <class T, class U>
2528void UpdateCodec(MediaContentDescription* content_desc, int payload_type,
2529 const cricket::FeedbackParam& feedback_param) {
2530 // Codec might already have been populated (from rtpmap).
changbin.shao@webrtc.org2d25b442015-03-16 04:14:342531 U new_codec = GetCodecWithPayloadType(static_cast<T*>(content_desc)->codecs(),
2532 payload_type);
henrike@webrtc.org28e20752013-07-10 00:45:362533 AddFeedbackParameter(feedback_param, &new_codec);
2534 AddOrReplaceCodec<T, U>(content_desc, new_codec);
2535}
2536
jlmiller@webrtc.orga744a282015-02-18 21:37:462537template <class T>
2538bool PopWildcardCodec(std::vector<T>* codecs, T* wildcard_codec) {
2539 for (auto iter = codecs->begin(); iter != codecs->end(); ++iter) {
henrike@webrtc.org28e20752013-07-10 00:45:362540 if (iter->id == kWildcardPayloadType) {
2541 *wildcard_codec = *iter;
2542 codecs->erase(iter);
2543 return true;
2544 }
2545 }
2546 return false;
2547}
2548
jlmiller@webrtc.orga744a282015-02-18 21:37:462549template<class T>
2550void UpdateFromWildcardCodecs(cricket::MediaContentDescriptionImpl<T>* desc) {
2551 auto codecs = desc->codecs();
2552 T wildcard_codec;
henrike@webrtc.org28e20752013-07-10 00:45:362553 if (!PopWildcardCodec(&codecs, &wildcard_codec)) {
2554 return;
2555 }
jlmiller@webrtc.orga744a282015-02-18 21:37:462556 for (auto& codec : codecs) {
henrike@webrtc.org28e20752013-07-10 00:45:362557 AddFeedbackParameters(wildcard_codec.feedback_params, &codec);
2558 }
jlmiller@webrtc.orga744a282015-02-18 21:37:462559 desc->set_codecs(codecs);
henrike@webrtc.org28e20752013-07-10 00:45:362560}
2561
2562void AddAudioAttribute(const std::string& name, const std::string& value,
2563 AudioContentDescription* audio_desc) {
2564 if (value.empty()) {
2565 return;
2566 }
2567 std::vector<cricket::AudioCodec> codecs = audio_desc->codecs();
2568 for (std::vector<cricket::AudioCodec>::iterator iter = codecs.begin();
2569 iter != codecs.end(); ++iter) {
2570 iter->params[name] = value;
2571 }
2572 audio_desc->set_codecs(codecs);
2573}
2574
2575bool ParseContent(const std::string& message,
2576 const MediaType media_type,
2577 int mline_index,
2578 const std::string& protocol,
deadbeef67cf2c12016-04-13 17:07:162579 const std::vector<int>& payload_types,
henrike@webrtc.org28e20752013-07-10 00:45:362580 size_t* pos,
2581 std::string* content_name,
deadbeef25ed4352016-12-13 02:37:362582 bool* bundle_only,
henrike@webrtc.org28e20752013-07-10 00:45:362583 MediaContentDescription* media_desc,
2584 TransportDescription* transport,
2585 std::vector<JsepIceCandidate*>* candidates,
2586 SdpParseError* error) {
nisseede5da42017-01-12 13:15:362587 RTC_DCHECK(media_desc != NULL);
2588 RTC_DCHECK(content_name != NULL);
2589 RTC_DCHECK(transport != NULL);
henrike@webrtc.org28e20752013-07-10 00:45:362590
henrike@webrtc.org704bf9e2014-02-27 17:52:042591 if (media_type == cricket::MEDIA_TYPE_AUDIO) {
2592 MaybeCreateStaticPayloadAudioCodecs(
deadbeef67cf2c12016-04-13 17:07:162593 payload_types, static_cast<AudioContentDescription*>(media_desc));
henrike@webrtc.org704bf9e2014-02-27 17:52:042594 }
2595
henrike@webrtc.org28e20752013-07-10 00:45:362596 // The media level "ice-ufrag" and "ice-pwd".
2597 // The candidates before update the media level "ice-pwd" and "ice-ufrag".
2598 Candidates candidates_orig;
2599 std::string line;
2600 std::string mline_id;
2601 // Tracks created out of the ssrc attributes.
2602 StreamParamsVec tracks;
2603 SsrcInfoVec ssrc_infos;
2604 SsrcGroupVec ssrc_groups;
2605 std::string maxptime_as_string;
2606 std::string ptime_as_string;
deadbeef9d3584c2016-02-17 01:54:102607 std::string stream_id;
2608 std::string track_id;
henrike@webrtc.org28e20752013-07-10 00:45:362609
henrike@webrtc.org28e20752013-07-10 00:45:362610 // Loop until the next m line
2611 while (!IsLineType(message, kLineTypeMedia, *pos)) {
2612 if (!GetLine(message, pos, &line)) {
2613 if (*pos >= message.size()) {
2614 break; // Done parsing
2615 } else {
sergeyu@chromium.org5bc25c42013-12-05 00:24:062616 return ParseFailed(message, *pos, "Invalid SDP line.", error);
henrike@webrtc.org28e20752013-07-10 00:45:362617 }
2618 }
2619
henrike@webrtc.org28e20752013-07-10 00:45:362620 // RFC 4566
2621 // b=* (zero or more bandwidth information lines)
2622 if (IsLineType(line, kLineTypeSessionBandwidth)) {
2623 std::string bandwidth;
2624 if (HasAttribute(line, kApplicationSpecificMaximum)) {
2625 if (!GetValue(line, kApplicationSpecificMaximum, &bandwidth, error)) {
2626 return false;
2627 } else {
wu@webrtc.org5e760e72014-04-02 23:19:092628 int b = 0;
2629 if (!GetValueFromString(line, bandwidth, &b, error)) {
2630 return false;
2631 }
Peter Thatcherc0c3a862015-06-24 22:31:252632 // We should never use more than the default bandwidth for RTP-based
2633 // data channels. Don't allow SDP to set the bandwidth, because
2634 // that would give JS the opportunity to "break the Internet".
2635 // See: https://code.google.com/p/chromium/issues/detail?id=280726
2636 if (media_type == cricket::MEDIA_TYPE_DATA && IsRtp(protocol) &&
2637 b > cricket::kDataMaxBandwidth / 1000) {
2638 std::ostringstream description;
2639 description << "RTP-based data channels may not send more than "
2640 << cricket::kDataMaxBandwidth / 1000 << "kbps.";
2641 return ParseFailed(line, description.str(), error);
2642 }
deadbeefb2362572016-12-14 00:37:062643 // Prevent integer overflow.
2644 b = std::min(b, INT_MAX / 1000);
wu@webrtc.org5e760e72014-04-02 23:19:092645 media_desc->set_bandwidth(b * 1000);
henrike@webrtc.org28e20752013-07-10 00:45:362646 }
2647 }
2648 continue;
2649 }
2650
2651 if (!IsLineType(line, kLineTypeAttributes)) {
2652 // TODO: Handle other lines if needed.
2653 LOG(LS_INFO) << "Ignored line: " << line;
2654 continue;
2655 }
2656
2657 // Handle attributes common to SCTP and RTP.
2658 if (HasAttribute(line, kAttributeMid)) {
2659 // RFC 3388
2660 // mid-attribute = "a=mid:" identification-tag
2661 // identification-tag = token
2662 // Use the mid identification-tag as the content name.
2663 if (!GetValue(line, kAttributeMid, &mline_id, error)) {
2664 return false;
2665 }
2666 *content_name = mline_id;
deadbeef25ed4352016-12-13 02:37:362667 } else if (HasAttribute(line, kAttributeBundleOnly)) {
2668 *bundle_only = true;
henrike@webrtc.org28e20752013-07-10 00:45:362669 } else if (HasAttribute(line, kAttributeCandidate)) {
2670 Candidate candidate;
2671 if (!ParseCandidate(line, &candidate, error, false)) {
2672 return false;
2673 }
deadbeef7bcdb692017-01-20 20:43:582674 // ParseCandidate will parse non-standard ufrag and password attributes,
2675 // since it's used for candidate trickling, but we only want to process
2676 // the "a=ice-ufrag"/"a=ice-pwd" values in a session description, so
2677 // strip them off at this point.
2678 candidate.set_username(std::string());
2679 candidate.set_password(std::string());
henrike@webrtc.org28e20752013-07-10 00:45:362680 candidates_orig.push_back(candidate);
2681 } else if (HasAttribute(line, kAttributeIceUfrag)) {
2682 if (!GetValue(line, kAttributeIceUfrag, &transport->ice_ufrag, error)) {
2683 return false;
2684 }
2685 } else if (HasAttribute(line, kAttributeIcePwd)) {
2686 if (!GetValue(line, kAttributeIcePwd, &transport->ice_pwd, error)) {
2687 return false;
2688 }
2689 } else if (HasAttribute(line, kAttributeIceOption)) {
2690 if (!ParseIceOptions(line, &transport->transport_options, error)) {
2691 return false;
2692 }
2693 } else if (HasAttribute(line, kAttributeFmtp)) {
2694 if (!ParseFmtpAttributes(line, media_type, media_desc, error)) {
2695 return false;
2696 }
2697 } else if (HasAttribute(line, kAttributeFingerprint)) {
buildbot@webrtc.orgd4e598d2014-07-29 17:36:522698 rtc::SSLFingerprint* fingerprint = NULL;
henrike@webrtc.org28e20752013-07-10 00:45:362699
2700 if (!ParseFingerprintAttribute(line, &fingerprint, error)) {
2701 return false;
2702 }
2703 transport->identity_fingerprint.reset(fingerprint);
sergeyu@chromium.org0be6aa02013-08-23 23:21:252704 } else if (HasAttribute(line, kAttributeSetup)) {
2705 if (!ParseDtlsSetup(line, &(transport->connection_role), error)) {
2706 return false;
2707 }
pthatcher@webrtc.org3341b402015-02-13 21:14:222708 } else if (IsDtlsSctp(protocol) && HasAttribute(line, kAttributeSctpPort)) {
deadbeef7e146cb2016-09-28 17:04:342709 if (media_type != cricket::MEDIA_TYPE_DATA) {
2710 return ParseFailed(
2711 line, "sctp-port attribute found in non-data media description.",
2712 error);
2713 }
jiayl@webrtc.orgddb85ab2014-09-05 16:31:562714 int sctp_port;
2715 if (!ParseSctpPort(line, &sctp_port, error)) {
2716 return false;
2717 }
2718 if (!AddSctpDataCodec(static_cast<DataContentDescription*>(media_desc),
2719 sctp_port)) {
2720 return false;
2721 }
pthatcher@webrtc.org3341b402015-02-13 21:14:222722 } else if (IsRtp(protocol)) {
henrike@webrtc.org28e20752013-07-10 00:45:362723 //
2724 // RTP specific attrubtes
2725 //
2726 if (HasAttribute(line, kAttributeRtcpMux)) {
2727 media_desc->set_rtcp_mux(true);
deadbeef13871492015-12-09 20:37:512728 } else if (HasAttribute(line, kAttributeRtcpReducedSize)) {
2729 media_desc->set_rtcp_reduced_size(true);
henrike@webrtc.org28e20752013-07-10 00:45:362730 } else if (HasAttribute(line, kAttributeSsrcGroup)) {
2731 if (!ParseSsrcGroupAttribute(line, &ssrc_groups, error)) {
2732 return false;
2733 }
2734 } else if (HasAttribute(line, kAttributeSsrc)) {
2735 if (!ParseSsrcAttribute(line, &ssrc_infos, error)) {
2736 return false;
2737 }
2738 } else if (HasAttribute(line, kAttributeCrypto)) {
2739 if (!ParseCryptoAttribute(line, media_desc, error)) {
2740 return false;
2741 }
2742 } else if (HasAttribute(line, kAttributeRtpmap)) {
deadbeef67cf2c12016-04-13 17:07:162743 if (!ParseRtpmapAttribute(line, media_type, payload_types, media_desc,
2744 error)) {
henrike@webrtc.org28e20752013-07-10 00:45:362745 return false;
2746 }
2747 } else if (HasAttribute(line, kCodecParamMaxPTime)) {
2748 if (!GetValue(line, kCodecParamMaxPTime, &maxptime_as_string, error)) {
2749 return false;
2750 }
2751 } else if (HasAttribute(line, kAttributeRtcpFb)) {
2752 if (!ParseRtcpFbAttribute(line, media_type, media_desc, error)) {
2753 return false;
2754 }
2755 } else if (HasAttribute(line, kCodecParamPTime)) {
2756 if (!GetValue(line, kCodecParamPTime, &ptime_as_string, error)) {
2757 return false;
2758 }
2759 } else if (HasAttribute(line, kAttributeSendOnly)) {
2760 media_desc->set_direction(cricket::MD_SENDONLY);
2761 } else if (HasAttribute(line, kAttributeRecvOnly)) {
2762 media_desc->set_direction(cricket::MD_RECVONLY);
2763 } else if (HasAttribute(line, kAttributeInactive)) {
2764 media_desc->set_direction(cricket::MD_INACTIVE);
2765 } else if (HasAttribute(line, kAttributeSendRecv)) {
2766 media_desc->set_direction(cricket::MD_SENDRECV);
2767 } else if (HasAttribute(line, kAttributeExtmap)) {
isheriff6f8d6862016-05-26 18:24:552768 RtpExtension extmap;
henrike@webrtc.org28e20752013-07-10 00:45:362769 if (!ParseExtmap(line, &extmap, error)) {
2770 return false;
2771 }
2772 media_desc->AddRtpHeaderExtension(extmap);
2773 } else if (HasAttribute(line, kAttributeXGoogleFlag)) {
2774 // Experimental attribute. Conference mode activates more aggressive
2775 // AEC and NS settings.
2776 // TODO: expose API to set these directly.
2777 std::string flag_value;
2778 if (!GetValue(line, kAttributeXGoogleFlag, &flag_value, error)) {
2779 return false;
2780 }
2781 if (flag_value.compare(kValueConference) == 0)
2782 media_desc->set_conference_mode(true);
deadbeef9d3584c2016-02-17 01:54:102783 } else if (HasAttribute(line, kAttributeMsid)) {
2784 if (!ParseMsidAttribute(line, &stream_id, &track_id, error)) {
2785 return false;
2786 }
henrike@webrtc.org28e20752013-07-10 00:45:362787 }
2788 } else {
2789 // Only parse lines that we are interested of.
2790 LOG(LS_INFO) << "Ignored line: " << line;
2791 continue;
2792 }
2793 }
2794
2795 // Create tracks from the |ssrc_infos|.
Taylor Brandstetter5de6b752016-03-10 01:02:302796 // If the stream_id/track_id for all SSRCS are identical, one StreamParams
2797 // will be created in CreateTracksFromSsrcInfos, containing all the SSRCs from
2798 // the m= section.
2799 CreateTracksFromSsrcInfos(ssrc_infos, stream_id, track_id, &tracks);
henrike@webrtc.org28e20752013-07-10 00:45:362800
2801 // Add the ssrc group to the track.
2802 for (SsrcGroupVec::iterator ssrc_group = ssrc_groups.begin();
2803 ssrc_group != ssrc_groups.end(); ++ssrc_group) {
2804 if (ssrc_group->ssrcs.empty()) {
2805 continue;
2806 }
Peter Boström0c4e06b2015-10-07 10:23:212807 uint32_t ssrc = ssrc_group->ssrcs.front();
henrike@webrtc.org28e20752013-07-10 00:45:362808 for (StreamParamsVec::iterator track = tracks.begin();
2809 track != tracks.end(); ++track) {
2810 if (track->has_ssrc(ssrc)) {
2811 track->ssrc_groups.push_back(*ssrc_group);
2812 }
2813 }
2814 }
2815
2816 // Add the new tracks to the |media_desc|.
deadbeef9d3584c2016-02-17 01:54:102817 for (StreamParams& track : tracks) {
2818 media_desc->AddStream(track);
henrike@webrtc.org28e20752013-07-10 00:45:362819 }
2820
2821 if (media_type == cricket::MEDIA_TYPE_AUDIO) {
2822 AudioContentDescription* audio_desc =
2823 static_cast<AudioContentDescription*>(media_desc);
jlmiller@webrtc.orga744a282015-02-18 21:37:462824 UpdateFromWildcardCodecs(audio_desc);
2825
henrike@webrtc.org28e20752013-07-10 00:45:362826 // Verify audio codec ensures that no audio codec has been populated with
2827 // only fmtp.
2828 if (!VerifyAudioCodecs(audio_desc)) {
2829 return ParseFailed("Failed to parse audio codecs correctly.", error);
2830 }
2831 AddAudioAttribute(kCodecParamMaxPTime, maxptime_as_string, audio_desc);
2832 AddAudioAttribute(kCodecParamPTime, ptime_as_string, audio_desc);
2833 }
2834
2835 if (media_type == cricket::MEDIA_TYPE_VIDEO) {
jlmiller@webrtc.orga744a282015-02-18 21:37:462836 VideoContentDescription* video_desc =
2837 static_cast<VideoContentDescription*>(media_desc);
2838 UpdateFromWildcardCodecs(video_desc);
2839 // Verify video codec ensures that no video codec has been populated with
2840 // only rtcp-fb.
2841 if (!VerifyVideoCodecs(video_desc)) {
2842 return ParseFailed("Failed to parse video codecs correctly.", error);
2843 }
henrike@webrtc.org28e20752013-07-10 00:45:362844 }
2845
2846 // RFC 5245
2847 // Update the candidates with the media level "ice-pwd" and "ice-ufrag".
2848 for (Candidates::iterator it = candidates_orig.begin();
2849 it != candidates_orig.end(); ++it) {
nisseede5da42017-01-12 13:15:362850 RTC_DCHECK((*it).username().empty() ||
2851 (*it).username() == transport->ice_ufrag);
henrike@webrtc.org28e20752013-07-10 00:45:362852 (*it).set_username(transport->ice_ufrag);
nisseede5da42017-01-12 13:15:362853 RTC_DCHECK((*it).password().empty());
henrike@webrtc.org28e20752013-07-10 00:45:362854 (*it).set_password(transport->ice_pwd);
2855 candidates->push_back(
2856 new JsepIceCandidate(mline_id, mline_index, *it));
2857 }
2858 return true;
2859}
2860
2861bool ParseSsrcAttribute(const std::string& line, SsrcInfoVec* ssrc_infos,
2862 SdpParseError* error) {
nisseede5da42017-01-12 13:15:362863 RTC_DCHECK(ssrc_infos != NULL);
henrike@webrtc.org28e20752013-07-10 00:45:362864 // RFC 5576
2865 // a=ssrc:<ssrc-id> <attribute>
2866 // a=ssrc:<ssrc-id> <attribute>:<value>
2867 std::string field1, field2;
Donald Curtis144d0182015-05-15 20:14:242868 if (!rtc::tokenize_first(line.substr(kLinePrefixLength), kSdpDelimiterSpace,
2869 &field1, &field2)) {
henrike@webrtc.org28e20752013-07-10 00:45:362870 const size_t expected_fields = 2;
2871 return ParseFailedExpectFieldNum(line, expected_fields, error);
2872 }
2873
2874 // ssrc:<ssrc-id>
2875 std::string ssrc_id_s;
2876 if (!GetValue(field1, kAttributeSsrc, &ssrc_id_s, error)) {
2877 return false;
2878 }
Peter Boström0c4e06b2015-10-07 10:23:212879 uint32_t ssrc_id = 0;
wu@webrtc.org5e760e72014-04-02 23:19:092880 if (!GetValueFromString(line, ssrc_id_s, &ssrc_id, error)) {
2881 return false;
2882 }
henrike@webrtc.org28e20752013-07-10 00:45:362883
2884 std::string attribute;
2885 std::string value;
Donald Curtis144d0182015-05-15 20:14:242886 if (!rtc::tokenize_first(field2, kSdpDelimiterColon, &attribute, &value)) {
henrike@webrtc.org28e20752013-07-10 00:45:362887 std::ostringstream description;
2888 description << "Failed to get the ssrc attribute value from " << field2
2889 << ". Expected format <attribute>:<value>.";
2890 return ParseFailed(line, description.str(), error);
2891 }
2892
2893 // Check if there's already an item for this |ssrc_id|. Create a new one if
2894 // there isn't.
2895 SsrcInfoVec::iterator ssrc_info = ssrc_infos->begin();
2896 for (; ssrc_info != ssrc_infos->end(); ++ssrc_info) {
2897 if (ssrc_info->ssrc_id == ssrc_id) {
2898 break;
2899 }
2900 }
2901 if (ssrc_info == ssrc_infos->end()) {
2902 SsrcInfo info;
2903 info.ssrc_id = ssrc_id;
2904 ssrc_infos->push_back(info);
2905 ssrc_info = ssrc_infos->end() - 1;
2906 }
2907
2908 // Store the info to the |ssrc_info|.
2909 if (attribute == kSsrcAttributeCname) {
2910 // RFC 5576
2911 // cname:<value>
2912 ssrc_info->cname = value;
2913 } else if (attribute == kSsrcAttributeMsid) {
2914 // draft-alvestrand-mmusic-msid-00
2915 // "msid:" identifier [ " " appdata ]
2916 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:522917 rtc::split(value, kSdpDelimiterSpace, &fields);
henrike@webrtc.org28e20752013-07-10 00:45:362918 if (fields.size() < 1 || fields.size() > 2) {
2919 return ParseFailed(line,
2920 "Expected format \"msid:<identifier>[ <appdata>]\".",
2921 error);
2922 }
deadbeef9d3584c2016-02-17 01:54:102923 ssrc_info->stream_id = fields[0];
henrike@webrtc.org28e20752013-07-10 00:45:362924 if (fields.size() == 2) {
deadbeef9d3584c2016-02-17 01:54:102925 ssrc_info->track_id = fields[1];
henrike@webrtc.org28e20752013-07-10 00:45:362926 }
2927 } else if (attribute == kSsrcAttributeMslabel) {
2928 // draft-alvestrand-rtcweb-mid-01
2929 // mslabel:<value>
2930 ssrc_info->mslabel = value;
2931 } else if (attribute == kSSrcAttributeLabel) {
2932 // The label isn't defined.
2933 // label:<value>
2934 ssrc_info->label = value;
2935 }
2936 return true;
2937}
2938
2939bool ParseSsrcGroupAttribute(const std::string& line,
2940 SsrcGroupVec* ssrc_groups,
2941 SdpParseError* error) {
nisseede5da42017-01-12 13:15:362942 RTC_DCHECK(ssrc_groups != NULL);
henrike@webrtc.org28e20752013-07-10 00:45:362943 // RFC 5576
2944 // a=ssrc-group:<semantics> <ssrc-id> ...
2945 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:522946 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:362947 kSdpDelimiterSpace, &fields);
2948 const size_t expected_min_fields = 2;
2949 if (fields.size() < expected_min_fields) {
2950 return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
2951 }
2952 std::string semantics;
2953 if (!GetValue(fields[0], kAttributeSsrcGroup, &semantics, error)) {
2954 return false;
2955 }
Peter Boström0c4e06b2015-10-07 10:23:212956 std::vector<uint32_t> ssrcs;
henrike@webrtc.org28e20752013-07-10 00:45:362957 for (size_t i = 1; i < fields.size(); ++i) {
Peter Boström0c4e06b2015-10-07 10:23:212958 uint32_t ssrc = 0;
wu@webrtc.org5e760e72014-04-02 23:19:092959 if (!GetValueFromString(line, fields[i], &ssrc, error)) {
2960 return false;
2961 }
henrike@webrtc.org28e20752013-07-10 00:45:362962 ssrcs.push_back(ssrc);
2963 }
2964 ssrc_groups->push_back(SsrcGroup(semantics, ssrcs));
2965 return true;
2966}
2967
2968bool ParseCryptoAttribute(const std::string& line,
2969 MediaContentDescription* media_desc,
2970 SdpParseError* error) {
2971 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:522972 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:362973 kSdpDelimiterSpace, &fields);
2974 // RFC 4568
2975 // a=crypto:<tag> <crypto-suite> <key-params> [<session-params>]
2976 const size_t expected_min_fields = 3;
2977 if (fields.size() < expected_min_fields) {
2978 return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
2979 }
2980 std::string tag_value;
2981 if (!GetValue(fields[0], kAttributeCrypto, &tag_value, error)) {
2982 return false;
2983 }
wu@webrtc.org5e760e72014-04-02 23:19:092984 int tag = 0;
2985 if (!GetValueFromString(line, tag_value, &tag, error)) {
2986 return false;
2987 }
jbauch083b73f2015-07-16 09:46:322988 const std::string& crypto_suite = fields[1];
2989 const std::string& key_params = fields[2];
henrike@webrtc.org28e20752013-07-10 00:45:362990 std::string session_params;
2991 if (fields.size() > 3) {
2992 session_params = fields[3];
2993 }
2994 media_desc->AddCrypto(CryptoParams(tag, crypto_suite, key_params,
2995 session_params));
2996 return true;
2997}
2998
2999// Updates or creates a new codec entry in the audio description with according
deadbeef67cf2c12016-04-13 17:07:163000// to |name|, |clockrate|, |bitrate|, and |channels|.
3001void UpdateCodec(int payload_type,
3002 const std::string& name,
3003 int clockrate,
3004 int bitrate,
3005 size_t channels,
henrike@webrtc.org28e20752013-07-10 00:45:363006 AudioContentDescription* audio_desc) {
3007 // Codec may already be populated with (only) optional parameters
3008 // (from an fmtp).
changbin.shao@webrtc.org2d25b442015-03-16 04:14:343009 cricket::AudioCodec codec =
3010 GetCodecWithPayloadType(audio_desc->codecs(), payload_type);
henrike@webrtc.org28e20752013-07-10 00:45:363011 codec.name = name;
3012 codec.clockrate = clockrate;
3013 codec.bitrate = bitrate;
3014 codec.channels = channels;
henrike@webrtc.org28e20752013-07-10 00:45:363015 AddOrReplaceCodec<AudioContentDescription, cricket::AudioCodec>(audio_desc,
3016 codec);
3017}
3018
3019// Updates or creates a new codec entry in the video description according to
deadbeef67cf2c12016-04-13 17:07:163020// |name|, |width|, |height|, and |framerate|.
3021void UpdateCodec(int payload_type,
3022 const std::string& name,
henrike@webrtc.org28e20752013-07-10 00:45:363023 VideoContentDescription* video_desc) {
3024 // Codec may already be populated with (only) optional parameters
3025 // (from an fmtp).
changbin.shao@webrtc.org2d25b442015-03-16 04:14:343026 cricket::VideoCodec codec =
3027 GetCodecWithPayloadType(video_desc->codecs(), payload_type);
henrike@webrtc.org28e20752013-07-10 00:45:363028 codec.name = name;
henrike@webrtc.org28e20752013-07-10 00:45:363029 AddOrReplaceCodec<VideoContentDescription, cricket::VideoCodec>(video_desc,
3030 codec);
3031}
3032
3033bool ParseRtpmapAttribute(const std::string& line,
3034 const MediaType media_type,
deadbeef67cf2c12016-04-13 17:07:163035 const std::vector<int>& payload_types,
henrike@webrtc.org28e20752013-07-10 00:45:363036 MediaContentDescription* media_desc,
3037 SdpParseError* error) {
3038 std::vector<std::string> fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:523039 rtc::split(line.substr(kLinePrefixLength),
henrike@webrtc.org28e20752013-07-10 00:45:363040 kSdpDelimiterSpace, &fields);
3041 // RFC 4566
3042 // a=rtpmap:<payload type> <encoding name>/<clock rate>[/<encodingparameters>]
3043 const size_t expected_min_fields = 2;
3044 if (fields.size() < expected_min_fields) {
3045 return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
3046 }
3047 std::string payload_type_value;
3048 if (!GetValue(fields[0], kAttributeRtpmap, &payload_type_value, error)) {
3049 return false;
3050 }
wu@webrtc.org5e760e72014-04-02 23:19:093051 int payload_type = 0;
pkasting@chromium.orge9facf82015-02-17 20:36:283052 if (!GetPayloadTypeFromString(line, payload_type_value, &payload_type,
3053 error)) {
wu@webrtc.org5e760e72014-04-02 23:19:093054 return false;
3055 }
henrike@webrtc.org28e20752013-07-10 00:45:363056
deadbeef67cf2c12016-04-13 17:07:163057 if (std::find(payload_types.begin(), payload_types.end(), payload_type) ==
3058 payload_types.end()) {
henrike@webrtc.org28e20752013-07-10 00:45:363059 LOG(LS_WARNING) << "Ignore rtpmap line that did not appear in the "
3060 << "<fmt> of the m-line: " << line;
3061 return true;
3062 }
jbauch083b73f2015-07-16 09:46:323063 const std::string& encoder = fields[1];
henrike@webrtc.org28e20752013-07-10 00:45:363064 std::vector<std::string> codec_params;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:523065 rtc::split(encoder, '/', &codec_params);
henrike@webrtc.org28e20752013-07-10 00:45:363066 // <encoding name>/<clock rate>[/<encodingparameters>]
3067 // 2 mandatory fields
3068 if (codec_params.size() < 2 || codec_params.size() > 3) {
3069 return ParseFailed(line,
3070 "Expected format \"<encoding name>/<clock rate>"
3071 "[/<encodingparameters>]\".",
3072 error);
3073 }
jbauch083b73f2015-07-16 09:46:323074 const std::string& encoding_name = codec_params[0];
wu@webrtc.org5e760e72014-04-02 23:19:093075 int clock_rate = 0;
3076 if (!GetValueFromString(line, codec_params[1], &clock_rate, error)) {
3077 return false;
3078 }
henrike@webrtc.org28e20752013-07-10 00:45:363079 if (media_type == cricket::MEDIA_TYPE_VIDEO) {
3080 VideoContentDescription* video_desc =
3081 static_cast<VideoContentDescription*>(media_desc);
henrike@webrtc.org28e20752013-07-10 00:45:363082 UpdateCodec(payload_type, encoding_name,
deadbeef67cf2c12016-04-13 17:07:163083 video_desc);
henrike@webrtc.org28e20752013-07-10 00:45:363084 } else if (media_type == cricket::MEDIA_TYPE_AUDIO) {
3085 // RFC 4566
3086 // For audio streams, <encoding parameters> indicates the number
3087 // of audio channels. This parameter is OPTIONAL and may be
3088 // omitted if the number of channels is one, provided that no
3089 // additional parameters are needed.
Peter Kasting69558702016-01-13 00:26:353090 size_t channels = 1;
henrike@webrtc.org28e20752013-07-10 00:45:363091 if (codec_params.size() == 3) {
wu@webrtc.org5e760e72014-04-02 23:19:093092 if (!GetValueFromString(line, codec_params[2], &channels, error)) {
3093 return false;
3094 }
henrike@webrtc.org28e20752013-07-10 00:45:363095 }
henrike@webrtc.org28e20752013-07-10 00:45:363096 AudioContentDescription* audio_desc =
3097 static_cast<AudioContentDescription*>(media_desc);
ossue1405ad2017-01-23 16:55:483098 UpdateCodec(payload_type, encoding_name, clock_rate, 0, channels,
deadbeef67cf2c12016-04-13 17:07:163099 audio_desc);
henrike@webrtc.org28e20752013-07-10 00:45:363100 } else if (media_type == cricket::MEDIA_TYPE_DATA) {
3101 DataContentDescription* data_desc =
3102 static_cast<DataContentDescription*>(media_desc);
deadbeef67cf2c12016-04-13 17:07:163103 data_desc->AddCodec(cricket::DataCodec(payload_type, encoding_name));
henrike@webrtc.org28e20752013-07-10 00:45:363104 }
3105 return true;
3106}
3107
henrike@webrtc.org28e20752013-07-10 00:45:363108bool ParseFmtpParam(const std::string& line, std::string* parameter,
3109 std::string* value, SdpParseError* error) {
Donald Curtis0e07f922015-05-15 16:21:233110 if (!rtc::tokenize_first(line, kSdpDelimiterEqual, parameter, value)) {
henrike@webrtc.org28e20752013-07-10 00:45:363111 ParseFailed(line, "Unable to parse fmtp parameter. \'=\' missing.", error);
3112 return false;
3113 }
3114 // a=fmtp:<payload_type> <param1>=<value1>; <param2>=<value2>; ...
henrike@webrtc.org28e20752013-07-10 00:45:363115 return true;
3116}
3117
3118bool ParseFmtpAttributes(const std::string& line, const MediaType media_type,
3119 MediaContentDescription* media_desc,
3120 SdpParseError* error) {
3121 if (media_type != cricket::MEDIA_TYPE_AUDIO &&
3122 media_type != cricket::MEDIA_TYPE_VIDEO) {
3123 return true;
3124 }
Donald Curtis0e07f922015-05-15 16:21:233125
3126 std::string line_payload;
3127 std::string line_params;
henrike@webrtc.org28e20752013-07-10 00:45:363128
3129 // RFC 5576
3130 // a=fmtp:<format> <format specific parameters>
3131 // At least two fields, whereas the second one is any of the optional
3132 // parameters.
Donald Curtis144d0182015-05-15 20:14:243133 if (!rtc::tokenize_first(line.substr(kLinePrefixLength), kSdpDelimiterSpace,
3134 &line_payload, &line_params)) {
henrike@webrtc.org28e20752013-07-10 00:45:363135 ParseFailedExpectMinFieldNum(line, 2, error);
3136 return false;
3137 }
3138
Donald Curtis0e07f922015-05-15 16:21:233139 // Parse out the payload information.
pkasting@chromium.orgd3245462015-02-23 21:28:223140 std::string payload_type_str;
Donald Curtis0e07f922015-05-15 16:21:233141 if (!GetValue(line_payload, kAttributeFmtp, &payload_type_str, error)) {
henrike@webrtc.org28e20752013-07-10 00:45:363142 return false;
3143 }
3144
Donald Curtis0e07f922015-05-15 16:21:233145 int payload_type = 0;
Donald Curtis144d0182015-05-15 20:14:243146 if (!GetPayloadTypeFromString(line_payload, payload_type_str, &payload_type,
3147 error)) {
Donald Curtis0e07f922015-05-15 16:21:233148 return false;
3149 }
3150
3151 // Parse out format specific parameters.
3152 std::vector<std::string> fields;
3153 rtc::split(line_params, kSdpDelimiterSemicolon, &fields);
3154
henrike@webrtc.org28e20752013-07-10 00:45:363155 cricket::CodecParameterMap codec_params;
Donald Curtis144d0182015-05-15 20:14:243156 for (auto& iter : fields) {
Donald Curtis0e07f922015-05-15 16:21:233157 if (iter.find(kSdpDelimiterEqual) == std::string::npos) {
henrike@webrtc.org28e20752013-07-10 00:45:363158 // Only fmtps with equals are currently supported. Other fmtp types
3159 // should be ignored. Unknown fmtps do not constitute an error.
3160 continue;
3161 }
Donald Curtis0e07f922015-05-15 16:21:233162
3163 std::string name;
3164 std::string value;
3165 if (!ParseFmtpParam(rtc::string_trim(iter), &name, &value, error)) {
henrike@webrtc.org28e20752013-07-10 00:45:363166 return false;
3167 }
3168 codec_params[name] = value;
3169 }
3170
henrike@webrtc.org28e20752013-07-10 00:45:363171 if (media_type == cricket::MEDIA_TYPE_AUDIO) {
3172 UpdateCodec<AudioContentDescription, cricket::AudioCodec>(
pkasting@chromium.orgd3245462015-02-23 21:28:223173 media_desc, payload_type, codec_params);
henrike@webrtc.org28e20752013-07-10 00:45:363174 } else if (media_type == cricket::MEDIA_TYPE_VIDEO) {
3175 UpdateCodec<VideoContentDescription, cricket::VideoCodec>(
pkasting@chromium.orgd3245462015-02-23 21:28:223176 media_desc, payload_type, codec_params);
henrike@webrtc.org28e20752013-07-10 00:45:363177 }
3178 return true;
3179}
3180
3181bool ParseRtcpFbAttribute(const std::string& line, const MediaType media_type,
3182 MediaContentDescription* media_desc,
3183 SdpParseError* error) {
3184 if (media_type != cricket::MEDIA_TYPE_AUDIO &&
3185 media_type != cricket::MEDIA_TYPE_VIDEO) {
3186 return true;
3187 }
3188 std::vector<std::string> rtcp_fb_fields;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:523189 rtc::split(line.c_str(), kSdpDelimiterSpace, &rtcp_fb_fields);
henrike@webrtc.org28e20752013-07-10 00:45:363190 if (rtcp_fb_fields.size() < 2) {
3191 return ParseFailedGetValue(line, kAttributeRtcpFb, error);
3192 }
3193 std::string payload_type_string;
3194 if (!GetValue(rtcp_fb_fields[0], kAttributeRtcpFb, &payload_type_string,
3195 error)) {
3196 return false;
3197 }
wu@webrtc.org5e760e72014-04-02 23:19:093198 int payload_type = kWildcardPayloadType;
3199 if (payload_type_string != "*") {
pkasting@chromium.orge9facf82015-02-17 20:36:283200 if (!GetPayloadTypeFromString(line, payload_type_string, &payload_type,
3201 error)) {
wu@webrtc.org5e760e72014-04-02 23:19:093202 return false;
3203 }
3204 }
henrike@webrtc.org28e20752013-07-10 00:45:363205 std::string id = rtcp_fb_fields[1];
3206 std::string param = "";
3207 for (std::vector<std::string>::iterator iter = rtcp_fb_fields.begin() + 2;
3208 iter != rtcp_fb_fields.end(); ++iter) {
3209 param.append(*iter);
3210 }
3211 const cricket::FeedbackParam feedback_param(id, param);
3212
3213 if (media_type == cricket::MEDIA_TYPE_AUDIO) {
pkasting@chromium.orgd3245462015-02-23 21:28:223214 UpdateCodec<AudioContentDescription, cricket::AudioCodec>(
3215 media_desc, payload_type, feedback_param);
henrike@webrtc.org28e20752013-07-10 00:45:363216 } else if (media_type == cricket::MEDIA_TYPE_VIDEO) {
pkasting@chromium.orgd3245462015-02-23 21:28:223217 UpdateCodec<VideoContentDescription, cricket::VideoCodec>(
3218 media_desc, payload_type, feedback_param);
henrike@webrtc.org28e20752013-07-10 00:45:363219 }
3220 return true;
3221}
3222
3223} // namespace webrtc