blob: 37d765404ea67ef524d0971df2a65c3ad251e7f6 [file] [log] [blame]
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001/*
kjellanderb24317b2016-02-10 07:54:43 -08002 * Copyright 2011 The WebRTC project authors. All Rights Reserved.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003 *
kjellanderb24317b2016-02-10 07:54:43 -08004 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00009 */
10
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020011#include "pc/webrtcsdp.h"
henrike@webrtc.org28e20752013-07-10 00:45:36 +000012
kjellandera96e2d72016-02-04 23:52:28 -080013#include <ctype.h>
henrike@webrtc.org28e20752013-07-10 00:45:36 +000014#include <limits.h>
15#include <stdio.h>
kwibergd1fe2812016-04-27 06:47:29 -070016
henrike@webrtc.org28e20752013-07-10 00:45:36 +000017#include <algorithm>
Steve Anton36b29d12017-10-30 09:57:42 -070018#include <map>
kwibergd1fe2812016-04-27 06:47:29 -070019#include <memory>
Steve Anton36b29d12017-10-30 09:57:42 -070020#include <set>
henrike@webrtc.org28e20752013-07-10 00:45:36 +000021#include <string>
deadbeef67cf2c12016-04-13 10:07:16 -070022#include <unordered_map>
Steve Anton4905edb2018-10-15 19:27:44 -070023#include <utility>
henrike@webrtc.org28e20752013-07-10 00:45:36 +000024#include <vector>
25
Niels Möller039743e2018-10-23 10:07:25 +020026#include "absl/strings/match.h"
Patrik Höglunde2d6a062017-10-05 14:53:33 +020027#include "api/candidate.h"
Patrik Höglund7aee3d52017-11-15 13:15:17 +010028#include "api/cryptoparams.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020029#include "api/jsepicecandidate.h"
30#include "api/jsepsessiondescription.h"
Niels Möller039743e2018-10-23 10:07:25 +020031#include "api/mediatypes.h"
isheriff6f8d6862016-05-26 11:24:55 -070032// for RtpExtension
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020033#include "api/rtpparameters.h"
34#include "media/base/codec.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020035#include "media/base/mediaconstants.h"
36#include "media/base/rtputils.h"
37#include "media/sctp/sctptransportinternal.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020038#include "p2p/base/p2pconstants.h"
39#include "p2p/base/port.h"
40#include "pc/mediasession.h"
Amit Hilbucha2012042018-12-03 11:35:05 -080041#include "pc/sdpserializer.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020042#include "rtc_base/arraysize.h"
43#include "rtc_base/checks.h"
44#include "rtc_base/logging.h"
45#include "rtc_base/messagedigest.h"
Jonas Olssonec9e4922018-09-05 09:53:49 +020046#include "rtc_base/strings/string_builder.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020047#include "rtc_base/stringutils.h"
henrike@webrtc.org28e20752013-07-10 00:45:36 +000048
49using cricket::AudioContentDescription;
50using cricket::Candidate;
51using cricket::Candidates;
henrike@webrtc.org28e20752013-07-10 00:45:36 +000052using cricket::ContentInfo;
53using cricket::CryptoParams;
54using cricket::DataContentDescription;
55using cricket::ICE_CANDIDATE_COMPONENT_RTP;
56using cricket::ICE_CANDIDATE_COMPONENT_RTCP;
57using cricket::kCodecParamMaxBitrate;
58using cricket::kCodecParamMaxPTime;
59using cricket::kCodecParamMaxQuantization;
60using cricket::kCodecParamMinBitrate;
61using cricket::kCodecParamMinPTime;
62using cricket::kCodecParamPTime;
63using cricket::kCodecParamSPropStereo;
buildbot@webrtc.orged97bb02014-05-07 11:15:20 +000064using cricket::kCodecParamStartBitrate;
henrike@webrtc.org28e20752013-07-10 00:45:36 +000065using cricket::kCodecParamStereo;
66using cricket::kCodecParamUseInbandFec;
Minyue Li7100dcd2015-03-27 05:05:59 +010067using cricket::kCodecParamUseDtx;
henrike@webrtc.org28e20752013-07-10 00:45:36 +000068using cricket::kCodecParamSctpProtocol;
69using cricket::kCodecParamSctpStreams;
henrike@webrtc.org1e09a712013-07-26 19:17:59 +000070using cricket::kCodecParamMaxAverageBitrate;
buildbot@webrtc.org5d639b32014-09-10 07:57:12 +000071using cricket::kCodecParamMaxPlaybackRate;
stefan@webrtc.org85d27942014-06-09 12:51:39 +000072using cricket::kCodecParamAssociatedPayloadType;
henrike@webrtc.org28e20752013-07-10 00:45:36 +000073using cricket::MediaContentDescription;
74using cricket::MediaType;
isheriff6f8d6862016-05-26 11:24:55 -070075using cricket::RtpHeaderExtensions;
Steve Anton5adfafd2017-12-20 16:34:00 -080076using cricket::MediaProtocolType;
Amit Hilbucha2012042018-12-03 11:35:05 -080077using cricket::SimulcastDescription;
henrike@webrtc.org28e20752013-07-10 00:45:36 +000078using cricket::SsrcGroup;
79using cricket::StreamParams;
80using cricket::StreamParamsVec;
81using cricket::TransportDescription;
82using cricket::TransportInfo;
83using cricket::VideoContentDescription;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +000084using rtc::SocketAddress;
henrike@webrtc.org28e20752013-07-10 00:45:36 +000085
henrike@webrtc.org28e20752013-07-10 00:45:36 +000086namespace cricket {
87class SessionDescription;
88}
89
deadbeef90f1e1e2017-02-10 12:35:05 -080090// TODO(deadbeef): Switch to using anonymous namespace rather than declaring
91// everything "static".
henrike@webrtc.org28e20752013-07-10 00:45:36 +000092namespace webrtc {
93
94// Line type
95// RFC 4566
96// An SDP session description consists of a number of lines of text of
97// the form:
98// <type>=<value>
99// where <type> MUST be exactly one case-significant character.
deadbeef9d3584c2016-02-16 17:54:10 -0800100static const int kLinePrefixLength = 2; // Length of <type>=
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000101static const char kLineTypeVersion = 'v';
102static const char kLineTypeOrigin = 'o';
103static const char kLineTypeSessionName = 's';
104static const char kLineTypeSessionInfo = 'i';
105static const char kLineTypeSessionUri = 'u';
106static const char kLineTypeSessionEmail = 'e';
107static const char kLineTypeSessionPhone = 'p';
108static const char kLineTypeSessionBandwidth = 'b';
109static const char kLineTypeTiming = 't';
110static const char kLineTypeRepeatTimes = 'r';
111static const char kLineTypeTimeZone = 'z';
112static const char kLineTypeEncryptionKey = 'k';
113static const char kLineTypeMedia = 'm';
114static const char kLineTypeConnection = 'c';
115static const char kLineTypeAttributes = 'a';
116
117// Attributes
118static const char kAttributeGroup[] = "group";
119static const char kAttributeMid[] = "mid";
deadbeef9d3584c2016-02-16 17:54:10 -0800120static const char kAttributeMsid[] = "msid";
deadbeef25ed4352016-12-12 18:37:36 -0800121static const char kAttributeBundleOnly[] = "bundle-only";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000122static const char kAttributeRtcpMux[] = "rtcp-mux";
deadbeef13871492015-12-09 12:37:51 -0800123static const char kAttributeRtcpReducedSize[] = "rtcp-rsize";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000124static const char kAttributeSsrc[] = "ssrc";
125static const char kSsrcAttributeCname[] = "cname";
Johannes Kron0854eb62018-10-10 22:33:20 +0200126static const char kAttributeExtmapAllowMixed[] = "extmap-allow-mixed";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000127static const char kAttributeExtmap[] = "extmap";
128// draft-alvestrand-mmusic-msid-01
129// a=msid-semantic: WMS
Seth Hampson5b4f0752018-04-02 16:31:36 -0700130// This is a legacy field supported only for Plan B semantics.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000131static const char kAttributeMsidSemantics[] = "msid-semantic";
132static const char kMediaStreamSemantic[] = "WMS";
133static const char kSsrcAttributeMsid[] = "msid";
134static const char kDefaultMsid[] = "default";
Seth Hampson5b4f0752018-04-02 16:31:36 -0700135static const char kNoStreamMsid[] = "-";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000136static const char kSsrcAttributeMslabel[] = "mslabel";
137static const char kSSrcAttributeLabel[] = "label";
138static const char kAttributeSsrcGroup[] = "ssrc-group";
139static const char kAttributeCrypto[] = "crypto";
140static const char kAttributeCandidate[] = "candidate";
141static const char kAttributeCandidateTyp[] = "typ";
142static const char kAttributeCandidateRaddr[] = "raddr";
143static const char kAttributeCandidateRport[] = "rport";
honghaiza54a0802015-12-16 18:37:23 -0800144static const char kAttributeCandidateUfrag[] = "ufrag";
145static const char kAttributeCandidatePwd[] = "pwd";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000146static const char kAttributeCandidateGeneration[] = "generation";
honghaiza0c44ea2016-03-23 16:07:48 -0700147static const char kAttributeCandidateNetworkId[] = "network-id";
honghaize1a0c942016-02-16 14:54:56 -0800148static const char kAttributeCandidateNetworkCost[] = "network-cost";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000149static const char kAttributeFingerprint[] = "fingerprint";
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +0000150static const char kAttributeSetup[] = "setup";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000151static const char kAttributeFmtp[] = "fmtp";
152static const char kAttributeRtpmap[] = "rtpmap";
wu@webrtc.org78187522013-10-07 23:32:02 +0000153static const char kAttributeSctpmap[] = "sctpmap";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000154static const char kAttributeRtcp[] = "rtcp";
155static const char kAttributeIceUfrag[] = "ice-ufrag";
156static const char kAttributeIcePwd[] = "ice-pwd";
157static const char kAttributeIceLite[] = "ice-lite";
158static const char kAttributeIceOption[] = "ice-options";
159static const char kAttributeSendOnly[] = "sendonly";
160static const char kAttributeRecvOnly[] = "recvonly";
161static const char kAttributeRtcpFb[] = "rtcp-fb";
162static const char kAttributeSendRecv[] = "sendrecv";
163static const char kAttributeInactive[] = "inactive";
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +0000164// draft-ietf-mmusic-sctp-sdp-07
165// a=sctp-port
166static const char kAttributeSctpPort[] = "sctp-port";
Amit Hilbucha2012042018-12-03 11:35:05 -0800167// draft-ietf-mmusic-sdp-simulcast-13
168// a=simulcast
169static const char kAttributeSimulcast[] = "simulcast";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000170
171// Experimental flags
172static const char kAttributeXGoogleFlag[] = "x-google-flag";
173static const char kValueConference[] = "conference";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000174
175// Candidate
176static const char kCandidateHost[] = "host";
177static const char kCandidateSrflx[] = "srflx";
Honghai Zhang7fb69db2016-03-14 11:59:18 -0700178static const char kCandidatePrflx[] = "prflx";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000179static const char kCandidateRelay[] = "relay";
mallinath@webrtc.org2d60c5e2014-08-08 22:29:20 +0000180static const char kTcpCandidateType[] = "tcptype";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000181
Jonas Olssonec9e4922018-09-05 09:53:49 +0200182// rtc::StringBuilder doesn't have a << overload for chars, while rtc::split and
183// rtc::tokenize_first both take a char delimiter. To handle both cases these
184// constants come in pairs of a chars and length-one strings.
185static const char kSdpDelimiterEqual[] = "=";
186static const char kSdpDelimiterEqualChar = '=';
187static const char kSdpDelimiterSpace[] = " ";
188static const char kSdpDelimiterSpaceChar = ' ';
189static const char kSdpDelimiterColon[] = ":";
190static const char kSdpDelimiterColonChar = ':';
191static const char kSdpDelimiterSemicolon[] = ";";
192static const char kSdpDelimiterSemicolonChar = ';';
193static const char kSdpDelimiterSlashChar = '/';
194static const char kNewLine[] = "\n";
195static const char kNewLineChar = '\n';
196static const char kReturnChar = '\r';
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000197static const char kLineBreak[] = "\r\n";
198
Steve Anton36b29d12017-10-30 09:57:42 -0700199// TODO(deadbeef): Generate the Session and Time description
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000200// instead of hardcoding.
201static const char kSessionVersion[] = "v=0";
202// RFC 4566
203static const char kSessionOriginUsername[] = "-";
204static const char kSessionOriginSessionId[] = "0";
205static const char kSessionOriginSessionVersion[] = "0";
206static const char kSessionOriginNettype[] = "IN";
207static const char kSessionOriginAddrtype[] = "IP4";
208static const char kSessionOriginAddress[] = "127.0.0.1";
209static const char kSessionName[] = "s=-";
210static const char kTimeDescription[] = "t=0 0";
211static const char kAttrGroup[] = "a=group:BUNDLE";
212static const char kConnectionNettype[] = "IN";
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000213static const char kConnectionIpv4Addrtype[] = "IP4";
214static const char kConnectionIpv6Addrtype[] = "IP6";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000215static const char kMediaTypeVideo[] = "video";
216static const char kMediaTypeAudio[] = "audio";
217static const char kMediaTypeData[] = "application";
218static const char kMediaPortRejected[] = "0";
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000219// draft-ietf-mmusic-trickle-ice-01
220// When no candidates have been gathered, set the connection
221// address to IP6 ::.
perkj@webrtc.orgd105cc82014-11-07 11:22:06 +0000222// TODO(perkj): FF can not parse IP6 ::. See http://crbug/430333
223// Use IPV4 per default.
224static const char kDummyAddress[] = "0.0.0.0";
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000225static const char kDummyPort[] = "9";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000226// RFC 3556
227static const char kApplicationSpecificMaximum[] = "AS";
228
wu@webrtc.org78187522013-10-07 23:32:02 +0000229static const char kDefaultSctpmapProtocol[] = "webrtc-datachannel";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000230
pkasting@chromium.orgd3245462015-02-23 21:28:22 +0000231// RTP payload type is in the 0-127 range. Use -1 to indicate "all" payload
232// types.
233const int kWildcardPayloadType = -1;
234
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000235struct SsrcInfo {
Peter Boström0c4e06b2015-10-07 12:23:21 +0200236 uint32_t ssrc_id;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000237 std::string cname;
deadbeef9d3584c2016-02-16 17:54:10 -0800238 std::string stream_id;
239 std::string track_id;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000240
241 // For backward compatibility.
242 // TODO(ronghuawu): Remove below 2 fields once all the clients support msid.
243 std::string label;
244 std::string mslabel;
245};
246typedef std::vector<SsrcInfo> SsrcInfoVec;
247typedef std::vector<SsrcGroup> SsrcGroupVec;
248
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000249template <class T>
250static void AddFmtpLine(const T& codec, std::string* message);
251static void BuildMediaDescription(const ContentInfo* content_info,
252 const TransportInfo* transport_info,
Patrik Höglundb6b29e02018-06-21 16:58:01 +0200253 const cricket::MediaType media_type,
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000254 const std::vector<Candidate>& candidates,
Steve Antone831b8c2018-02-01 12:22:16 -0800255 int msid_signaling,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000256 std::string* message);
zstein4b2e0822017-02-17 19:48:38 -0800257static void BuildSctpContentAttributes(std::string* message,
258 int sctp_port,
259 bool use_sctpmap);
deadbeef9d3584c2016-02-16 17:54:10 -0800260static void BuildRtpContentAttributes(const MediaContentDescription* media_desc,
Patrik Höglundb6b29e02018-06-21 16:58:01 +0200261 const cricket::MediaType media_type,
Steve Antone831b8c2018-02-01 12:22:16 -0800262 int msid_signaling,
deadbeef9d3584c2016-02-16 17:54:10 -0800263 std::string* message);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000264static void BuildRtpMap(const MediaContentDescription* media_desc,
Patrik Höglundb6b29e02018-06-21 16:58:01 +0200265 const cricket::MediaType media_type,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000266 std::string* message);
267static void BuildCandidate(const std::vector<Candidate>& candidates,
honghaiza54a0802015-12-16 18:37:23 -0800268 bool include_ufrag,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000269 std::string* message);
270static void BuildIceOptions(const std::vector<std::string>& transport_options,
271 std::string* message);
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +0000272static bool IsRtp(const std::string& protocol);
273static bool IsDtlsSctp(const std::string& protocol);
zhihuang38989e52017-03-21 11:04:53 -0700274static bool ParseSessionDescription(const std::string& message,
275 size_t* pos,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000276 std::string* session_id,
277 std::string* session_version,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000278 TransportDescription* session_td,
279 RtpHeaderExtensions* session_extmaps,
zhihuang38989e52017-03-21 11:04:53 -0700280 rtc::SocketAddress* connection_addr,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000281 cricket::SessionDescription* desc,
282 SdpParseError* error);
283static bool ParseGroupAttribute(const std::string& line,
284 cricket::SessionDescription* desc,
285 SdpParseError* error);
286static bool ParseMediaDescription(
287 const std::string& message,
288 const TransportDescription& session_td,
289 const RtpHeaderExtensions& session_extmaps,
zhihuang38989e52017-03-21 11:04:53 -0700290 size_t* pos,
291 const rtc::SocketAddress& session_connection_addr,
292 cricket::SessionDescription* desc,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000293 std::vector<JsepIceCandidate*>* candidates,
294 SdpParseError* error);
295static bool ParseContent(const std::string& message,
Patrik Höglundb6b29e02018-06-21 16:58:01 +0200296 const cricket::MediaType media_type,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000297 int mline_index,
298 const std::string& protocol,
deadbeef67cf2c12016-04-13 10:07:16 -0700299 const std::vector<int>& payload_types,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000300 size_t* pos,
301 std::string* content_name,
deadbeef25ed4352016-12-12 18:37:36 -0800302 bool* bundle_only,
Steve Antone831b8c2018-02-01 12:22:16 -0800303 int* msid_signaling,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000304 MediaContentDescription* media_desc,
305 TransportDescription* transport,
306 std::vector<JsepIceCandidate*>* candidates,
307 SdpParseError* error);
308static bool ParseSsrcAttribute(const std::string& line,
309 SsrcInfoVec* ssrc_infos,
Steve Antone831b8c2018-02-01 12:22:16 -0800310 int* msid_signaling,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000311 SdpParseError* error);
312static bool ParseSsrcGroupAttribute(const std::string& line,
313 SsrcGroupVec* ssrc_groups,
314 SdpParseError* error);
315static bool ParseCryptoAttribute(const std::string& line,
316 MediaContentDescription* media_desc,
317 SdpParseError* error);
318static bool ParseRtpmapAttribute(const std::string& line,
Patrik Höglundb6b29e02018-06-21 16:58:01 +0200319 const cricket::MediaType media_type,
deadbeef67cf2c12016-04-13 10:07:16 -0700320 const std::vector<int>& payload_types,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000321 MediaContentDescription* media_desc,
322 SdpParseError* error);
323static bool ParseFmtpAttributes(const std::string& line,
Patrik Höglundb6b29e02018-06-21 16:58:01 +0200324 const cricket::MediaType media_type,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000325 MediaContentDescription* media_desc,
326 SdpParseError* error);
Yves Gerey665174f2018-06-19 15:03:05 +0200327static bool ParseFmtpParam(const std::string& line,
328 std::string* parameter,
329 std::string* value,
330 SdpParseError* error);
331static bool ParseCandidate(const std::string& message,
332 Candidate* candidate,
333 SdpParseError* error,
334 bool is_raw);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000335static bool ParseRtcpFbAttribute(const std::string& line,
Patrik Höglundb6b29e02018-06-21 16:58:01 +0200336 const cricket::MediaType media_type,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000337 MediaContentDescription* media_desc,
338 SdpParseError* error);
339static bool ParseIceOptions(const std::string& line,
340 std::vector<std::string>* transport_options,
341 SdpParseError* error);
342static bool ParseExtmap(const std::string& line,
isheriff6f8d6862016-05-26 11:24:55 -0700343 RtpExtension* extmap,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000344 SdpParseError* error);
Steve Anton4905edb2018-10-15 19:27:44 -0700345static bool ParseFingerprintAttribute(
346 const std::string& line,
347 std::unique_ptr<rtc::SSLFingerprint>* fingerprint,
348 SdpParseError* error);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +0000349static bool ParseDtlsSetup(const std::string& line,
350 cricket::ConnectionRole* role,
351 SdpParseError* error);
deadbeef9d3584c2016-02-16 17:54:10 -0800352static bool ParseMsidAttribute(const std::string& line,
Seth Hampson5b4f0752018-04-02 16:31:36 -0700353 std::vector<std::string>* stream_ids,
deadbeef9d3584c2016-02-16 17:54:10 -0800354 std::string* track_id,
355 SdpParseError* error);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000356
357// Helper functions
358
359// Below ParseFailed*** functions output the line that caused the parsing
360// failure and the detailed reason (|description|) of the failure to |error|.
361// The functions always return false so that they can be used directly in the
362// following way when error happens:
363// "return ParseFailed***(...);"
364
365// The line starting at |line_start| of |message| is the failing line.
366// The reason for the failure should be provided in the |description|.
367// An example of a description could be "unknown character".
368static bool ParseFailed(const std::string& message,
369 size_t line_start,
370 const std::string& description,
371 SdpParseError* error) {
372 // Get the first line of |message| from |line_start|.
sergeyu@chromium.org5bc25c42013-12-05 00:24:06 +0000373 std::string first_line;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000374 size_t line_end = message.find(kNewLine, line_start);
375 if (line_end != std::string::npos) {
Jonas Olssonec9e4922018-09-05 09:53:49 +0200376 if (line_end > 0 && (message.at(line_end - 1) == kReturnChar)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000377 --line_end;
378 }
379 first_line = message.substr(line_start, (line_end - line_start));
sergeyu@chromium.org5bc25c42013-12-05 00:24:06 +0000380 } else {
381 first_line = message.substr(line_start);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000382 }
383
384 if (error) {
385 error->line = first_line;
386 error->description = description;
387 }
Mirko Bonadei675513b2017-11-09 11:09:25 +0100388 RTC_LOG(LS_ERROR) << "Failed to parse: \"" << first_line
389 << "\". Reason: " << description;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000390 return false;
391}
392
393// |line| is the failing line. The reason for the failure should be
394// provided in the |description|.
395static bool ParseFailed(const std::string& line,
396 const std::string& description,
397 SdpParseError* error) {
398 return ParseFailed(line, 0, description, error);
399}
400
401// Parses failure where the failing SDP line isn't know or there are multiple
402// failing lines.
Yves Gerey665174f2018-06-19 15:03:05 +0200403static bool ParseFailed(const std::string& description, SdpParseError* error) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000404 return ParseFailed("", description, error);
405}
406
407// |line| is the failing line. The failure is due to the fact that |line|
408// doesn't have |expected_fields| fields.
409static bool ParseFailedExpectFieldNum(const std::string& line,
410 int expected_fields,
411 SdpParseError* error) {
Jonas Olssonec9e4922018-09-05 09:53:49 +0200412 rtc::StringBuilder description;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000413 description << "Expects " << expected_fields << " fields.";
414 return ParseFailed(line, description.str(), error);
415}
416
417// |line| is the failing line. The failure is due to the fact that |line| has
418// less than |expected_min_fields| fields.
419static bool ParseFailedExpectMinFieldNum(const std::string& line,
420 int expected_min_fields,
421 SdpParseError* error) {
Jonas Olssonec9e4922018-09-05 09:53:49 +0200422 rtc::StringBuilder description;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000423 description << "Expects at least " << expected_min_fields << " fields.";
424 return ParseFailed(line, description.str(), error);
425}
426
427// |line| is the failing line. The failure is due to the fact that it failed to
428// get the value of |attribute|.
429static bool ParseFailedGetValue(const std::string& line,
430 const std::string& attribute,
431 SdpParseError* error) {
Jonas Olssonec9e4922018-09-05 09:53:49 +0200432 rtc::StringBuilder description;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000433 description << "Failed to get the value of attribute: " << attribute;
434 return ParseFailed(line, description.str(), error);
435}
436
437// The line starting at |line_start| of |message| is the failing line. The
438// failure is due to the line type (e.g. the "m" part of the "m-line")
439// not matching what is expected. The expected line type should be
440// provided as |line_type|.
441static bool ParseFailedExpectLine(const std::string& message,
442 size_t line_start,
443 const char line_type,
444 const std::string& line_value,
445 SdpParseError* error) {
Jonas Olssonec9e4922018-09-05 09:53:49 +0200446 rtc::StringBuilder description;
447 description << "Expect line: " << std::string(1, line_type) << "="
448 << line_value;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000449 return ParseFailed(message, line_start, description.str(), error);
450}
451
452static bool AddLine(const std::string& line, std::string* message) {
453 if (!message)
454 return false;
455
456 message->append(line);
457 message->append(kLineBreak);
458 return true;
459}
460
461static bool GetLine(const std::string& message,
462 size_t* pos,
463 std::string* line) {
464 size_t line_begin = *pos;
465 size_t line_end = message.find(kNewLine, line_begin);
466 if (line_end == std::string::npos) {
467 return false;
468 }
469 // Update the new start position
470 *pos = line_end + 1;
Jonas Olssonec9e4922018-09-05 09:53:49 +0200471 if (line_end > 0 && (message.at(line_end - 1) == kReturnChar)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000472 --line_end;
473 }
474 *line = message.substr(line_begin, (line_end - line_begin));
475 const char* cline = line->c_str();
476 // RFC 4566
477 // An SDP session description consists of a number of lines of text of
478 // the form:
479 // <type>=<value>
480 // where <type> MUST be exactly one case-significant character and
481 // <value> is structured text whose format depends on <type>.
482 // Whitespace MUST NOT be used on either side of the "=" sign.
Taylor Brandstetter93a7b242018-04-16 10:45:24 -0700483 //
484 // However, an exception to the whitespace rule is made for "s=", since
485 // RFC4566 also says:
486 //
487 // If a session has no meaningful name, the value "s= " SHOULD be used
488 // (i.e., a single space as the session name).
489 if (line->length() < 3 || !islower(cline[0]) ||
Jonas Olssonec9e4922018-09-05 09:53:49 +0200490 cline[1] != kSdpDelimiterEqualChar ||
491 (cline[0] != kLineTypeSessionName &&
492 cline[2] == kSdpDelimiterSpaceChar)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000493 *pos = line_begin;
494 return false;
495 }
496 return true;
497}
498
499// Init |os| to "|type|=|value|".
500static void InitLine(const char type,
501 const std::string& value,
Jonas Olssonec9e4922018-09-05 09:53:49 +0200502 rtc::StringBuilder* os) {
503 os->Clear();
504 *os << std::string(1, type) << kSdpDelimiterEqual << value;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000505}
506
507// Init |os| to "a=|attribute|".
Jonas Olssonec9e4922018-09-05 09:53:49 +0200508static void InitAttrLine(const std::string& attribute, rtc::StringBuilder* os) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000509 InitLine(kLineTypeAttributes, attribute, os);
510}
511
512// Writes a SDP attribute line based on |attribute| and |value| to |message|.
Yves Gerey665174f2018-06-19 15:03:05 +0200513static void AddAttributeLine(const std::string& attribute,
514 int value,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000515 std::string* message) {
Jonas Olssonec9e4922018-09-05 09:53:49 +0200516 rtc::StringBuilder os;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000517 InitAttrLine(attribute, &os);
518 os << kSdpDelimiterColon << value;
519 AddLine(os.str(), message);
520}
521
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000522static bool IsLineType(const std::string& message,
523 const char type,
524 size_t line_start) {
525 if (message.size() < line_start + kLinePrefixLength) {
526 return false;
527 }
528 const char* cmessage = message.c_str();
529 return (cmessage[line_start] == type &&
Jonas Olssonec9e4922018-09-05 09:53:49 +0200530 cmessage[line_start + 1] == kSdpDelimiterEqualChar);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000531}
532
Yves Gerey665174f2018-06-19 15:03:05 +0200533static bool IsLineType(const std::string& line, const char type) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000534 return IsLineType(line, type, 0);
535}
536
Yves Gerey665174f2018-06-19 15:03:05 +0200537static bool GetLineWithType(const std::string& message,
538 size_t* pos,
539 std::string* line,
540 const char type) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000541 if (!IsLineType(message, type, *pos)) {
542 return false;
543 }
544
545 if (!GetLine(message, pos, line))
546 return false;
547
548 return true;
549}
550
551static bool HasAttribute(const std::string& line,
552 const std::string& attribute) {
Johannes Kron211856b2018-09-06 12:12:28 +0200553 if (line.compare(kLinePrefixLength, attribute.size(), attribute) == 0) {
554 // Make sure that the match is not only a partial match. If length of
555 // strings doesn't match, the next character of the line must be ':' or ' '.
556 // This function is also used for media descriptions (e.g., "m=audio 9..."),
557 // hence the need to also allow space in the end.
558 RTC_CHECK_LE(kLinePrefixLength + attribute.size(), line.size());
559 if ((kLinePrefixLength + attribute.size()) == line.size() ||
560 line[kLinePrefixLength + attribute.size()] == kSdpDelimiterColonChar ||
561 line[kLinePrefixLength + attribute.size()] == kSdpDelimiterSpaceChar) {
562 return true;
563 }
564 }
565 return false;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000566}
567
Peter Boström0c4e06b2015-10-07 12:23:21 +0200568static bool AddSsrcLine(uint32_t ssrc_id,
569 const std::string& attribute,
570 const std::string& value,
571 std::string* message) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000572 // RFC 5576
573 // a=ssrc:<ssrc-id> <attribute>:<value>
Jonas Olssonec9e4922018-09-05 09:53:49 +0200574 rtc::StringBuilder os;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000575 InitAttrLine(kAttributeSsrc, &os);
Yves Gerey665174f2018-06-19 15:03:05 +0200576 os << kSdpDelimiterColon << ssrc_id << kSdpDelimiterSpace << attribute
577 << kSdpDelimiterColon << value;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000578 return AddLine(os.str(), message);
579}
580
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000581// Get value only from <attribute>:<value>.
Yves Gerey665174f2018-06-19 15:03:05 +0200582static bool GetValue(const std::string& message,
583 const std::string& attribute,
584 std::string* value,
585 SdpParseError* error) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000586 std::string leftpart;
Jonas Olssonec9e4922018-09-05 09:53:49 +0200587 if (!rtc::tokenize_first(message, kSdpDelimiterColonChar, &leftpart, value)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000588 return ParseFailedGetValue(message, attribute, error);
589 }
590 // The left part should end with the expected attribute.
591 if (leftpart.length() < attribute.length() ||
592 leftpart.compare(leftpart.length() - attribute.length(),
593 attribute.length(), attribute) != 0) {
594 return ParseFailedGetValue(message, attribute, error);
595 }
596 return true;
597}
598
599static bool CaseInsensitiveFind(std::string str1, std::string str2) {
Yves Gerey665174f2018-06-19 15:03:05 +0200600 std::transform(str1.begin(), str1.end(), str1.begin(), ::tolower);
601 std::transform(str2.begin(), str2.end(), str2.begin(), ::tolower);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000602 return str1.find(str2) != std::string::npos;
603}
604
wu@webrtc.org5e760e72014-04-02 23:19:09 +0000605template <class T>
606static bool GetValueFromString(const std::string& line,
607 const std::string& s,
608 T* t,
609 SdpParseError* error) {
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000610 if (!rtc::FromString(s, t)) {
Jonas Olssonec9e4922018-09-05 09:53:49 +0200611 rtc::StringBuilder description;
wu@webrtc.org5e760e72014-04-02 23:19:09 +0000612 description << "Invalid value: " << s << ".";
613 return ParseFailed(line, description.str(), error);
614 }
615 return true;
616}
617
pkasting@chromium.orge9facf82015-02-17 20:36:28 +0000618static bool GetPayloadTypeFromString(const std::string& line,
619 const std::string& s,
620 int* payload_type,
621 SdpParseError* error) {
622 return GetValueFromString(line, s, payload_type, error) &&
Yves Gerey665174f2018-06-19 15:03:05 +0200623 cricket::IsValidRtpPayloadType(*payload_type);
pkasting@chromium.orge9facf82015-02-17 20:36:28 +0000624}
625
Seth Hampson5897a6e2018-04-03 11:16:33 -0700626// Creates a StreamParams track in the case when no SSRC lines are signaled.
627// This is a track that does not contain SSRCs and only contains
628// stream_ids/track_id if it's signaled with a=msid lines.
629void CreateTrackWithNoSsrcs(const std::vector<std::string>& msid_stream_ids,
630 const std::string& msid_track_id,
631 StreamParamsVec* tracks) {
632 StreamParams track;
633 if (msid_track_id.empty() || msid_stream_ids.empty()) {
634 // We only create an unsignaled track if a=msid lines were signaled.
635 return;
636 }
637 track.set_stream_ids(msid_stream_ids);
638 track.id = msid_track_id;
639 tracks->push_back(track);
640}
641
642// Creates the StreamParams tracks, for the case when SSRC lines are signaled.
643// |msid_stream_ids| and |msid_track_id| represent the stream/track ID from the
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800644// "a=msid" attribute, if it exists. They are empty if the attribute does not
Seth Hampson5897a6e2018-04-03 11:16:33 -0700645// exist. We prioritize getting stream_ids/track_ids signaled in a=msid lines.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000646void CreateTracksFromSsrcInfos(const SsrcInfoVec& ssrc_infos,
Seth Hampson5b4f0752018-04-02 16:31:36 -0700647 const std::vector<std::string>& msid_stream_ids,
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800648 const std::string& msid_track_id,
Seth Hampson5b4f0752018-04-02 16:31:36 -0700649 StreamParamsVec* tracks,
650 int msid_signaling) {
nisseede5da42017-01-12 05:15:36 -0800651 RTC_DCHECK(tracks != NULL);
Steve Anton4daf66e2018-09-07 14:55:53 -0700652 for (const SsrcInfo& ssrc_info : ssrc_infos) {
653 if (ssrc_info.cname.empty()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000654 continue;
655 }
656
Seth Hampson5b4f0752018-04-02 16:31:36 -0700657 std::vector<std::string> stream_ids;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000658 std::string track_id;
Seth Hampson5b4f0752018-04-02 16:31:36 -0700659 if (msid_signaling & cricket::kMsidSignalingMediaSection) {
660 // This is the case with Unified Plan SDP msid signaling.
661 stream_ids = msid_stream_ids;
Tomas Gunnarsson191bf5c2018-03-30 10:44:43 +0000662 track_id = msid_track_id;
Seth Hampson5b4f0752018-04-02 16:31:36 -0700663 } else if (msid_signaling & cricket::kMsidSignalingSsrcAttribute) {
664 // This is the case with Plan B SDP msid signaling.
Steve Anton4daf66e2018-09-07 14:55:53 -0700665 stream_ids.push_back(ssrc_info.stream_id);
666 track_id = ssrc_info.track_id;
667 } else if (!ssrc_info.mslabel.empty()) {
Seth Hampson5b4f0752018-04-02 16:31:36 -0700668 // Since there's no a=msid or a=ssrc msid signaling, this is a sdp from
669 // an older version of client that doesn't support msid.
670 // In that case, we use the mslabel and label to construct the track.
Steve Anton4daf66e2018-09-07 14:55:53 -0700671 stream_ids.push_back(ssrc_info.mslabel);
672 track_id = ssrc_info.label;
Seth Hampson5b4f0752018-04-02 16:31:36 -0700673 } else {
674 // Since no media streams isn't supported with older SDP signaling, we
675 // use a default a stream id.
676 stream_ids.push_back(kDefaultMsid);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000677 }
Seth Hampson5b4f0752018-04-02 16:31:36 -0700678 // If a track ID wasn't populated from the SSRC attributes OR the
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800679 // msid attribute, use default/random values.
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800680 if (track_id.empty()) {
681 // TODO(ronghuawu): What should we do if the track id doesn't appear?
682 // Create random string (which will be used as track label later)?
683 track_id = rtc::CreateRandomString(8);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000684 }
685
Steve Anton4daf66e2018-09-07 14:55:53 -0700686 auto track_it = std::find_if(
687 tracks->begin(), tracks->end(),
688 [track_id](const StreamParams& track) { return track.id == track_id; });
689 if (track_it == tracks->end()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000690 // If we don't find an existing track, create a new one.
691 tracks->push_back(StreamParams());
Steve Anton4daf66e2018-09-07 14:55:53 -0700692 track_it = tracks->end() - 1;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000693 }
Steve Anton4daf66e2018-09-07 14:55:53 -0700694 StreamParams& track = *track_it;
695 track.add_ssrc(ssrc_info.ssrc_id);
696 track.cname = ssrc_info.cname;
697 track.set_stream_ids(stream_ids);
698 track.id = track_id;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000699 }
700}
701
Seth Hampson845e8782018-03-02 11:34:10 -0800702void GetMediaStreamIds(const ContentInfo* content,
703 std::set<std::string>* labels) {
Steve Antonb1c1de12017-12-21 15:14:30 -0800704 for (const StreamParams& stream_params :
705 content->media_description()->streams()) {
Seth Hampson845e8782018-03-02 11:34:10 -0800706 for (const std::string& stream_id : stream_params.stream_ids()) {
707 labels->insert(stream_id);
Steve Anton5a26a3a2018-02-28 11:38:47 -0800708 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000709 }
710}
711
712// RFC 5245
713// It is RECOMMENDED that default candidates be chosen based on the
714// likelihood of those candidates to work with the peer that is being
715// contacted. It is RECOMMENDED that relayed > reflexive > host.
716static const int kPreferenceUnknown = 0;
717static const int kPreferenceHost = 1;
718static const int kPreferenceReflexive = 2;
719static const int kPreferenceRelayed = 3;
720
721static int GetCandidatePreferenceFromType(const std::string& type) {
722 int preference = kPreferenceUnknown;
723 if (type == cricket::LOCAL_PORT_TYPE) {
724 preference = kPreferenceHost;
725 } else if (type == cricket::STUN_PORT_TYPE) {
726 preference = kPreferenceReflexive;
727 } else if (type == cricket::RELAY_PORT_TYPE) {
728 preference = kPreferenceRelayed;
729 } else {
nissec80e7412017-01-11 05:56:46 -0800730 RTC_NOTREACHED();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000731 }
732 return preference;
733}
734
guoweis@webrtc.org57ac2c82015-02-06 00:45:13 +0000735// Get ip and port of the default destination from the |candidates| with the
736// given value of |component_id|. The default candidate should be the one most
737// likely to work, typically IPv4 relay.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000738// RFC 5245
739// The value of |component_id| currently supported are 1 (RTP) and 2 (RTCP).
Steve Anton36b29d12017-10-30 09:57:42 -0700740// TODO(deadbeef): Decide the default destination in webrtcsession and
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000741// pass it down via SessionDescription.
Yves Gerey665174f2018-06-19 15:03:05 +0200742static void GetDefaultDestination(const std::vector<Candidate>& candidates,
743 int component_id,
744 std::string* port,
745 std::string* ip,
746 std::string* addr_type) {
perkj@webrtc.orgd105cc82014-11-07 11:22:06 +0000747 *addr_type = kConnectionIpv4Addrtype;
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000748 *port = kDummyPort;
749 *ip = kDummyAddress;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000750 int current_preference = kPreferenceUnknown;
guoweis@webrtc.org57ac2c82015-02-06 00:45:13 +0000751 int current_family = AF_UNSPEC;
Steve Anton4daf66e2018-09-07 14:55:53 -0700752 for (const Candidate& candidate : candidates) {
753 if (candidate.component() != component_id) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000754 continue;
755 }
guoweis@webrtc.org57ac2c82015-02-06 00:45:13 +0000756 // Default destination should be UDP only.
Steve Anton4daf66e2018-09-07 14:55:53 -0700757 if (candidate.protocol() != cricket::UDP_PROTOCOL_NAME) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000758 continue;
759 }
Steve Anton4daf66e2018-09-07 14:55:53 -0700760 const int preference = GetCandidatePreferenceFromType(candidate.type());
761 const int family = candidate.address().ipaddr().family();
guoweis@webrtc.org57ac2c82015-02-06 00:45:13 +0000762 // See if this candidate is more preferable then the current one if it's the
763 // same family. Or if the current family is IPv4 already so we could safely
764 // ignore all IPv6 ones. WebRTC bug 4269.
765 // http://code.google.com/p/webrtc/issues/detail?id=4269
766 if ((preference <= current_preference && current_family == family) ||
767 (current_family == AF_INET && family == AF_INET6)) {
768 continue;
769 }
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000770 if (family == AF_INET) {
771 addr_type->assign(kConnectionIpv4Addrtype);
772 } else if (family == AF_INET6) {
773 addr_type->assign(kConnectionIpv6Addrtype);
774 }
guoweis@webrtc.org57ac2c82015-02-06 00:45:13 +0000775 current_preference = preference;
776 current_family = family;
Steve Anton4daf66e2018-09-07 14:55:53 -0700777 *port = candidate.address().PortAsString();
778 *ip = candidate.address().ipaddr().ToString();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000779 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000780}
781
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000782// Gets "a=rtcp" line if found default RTCP candidate from |candidates|.
783static std::string GetRtcpLine(const std::vector<Candidate>& candidates) {
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000784 std::string rtcp_line, rtcp_port, rtcp_ip, addr_type;
Yves Gerey665174f2018-06-19 15:03:05 +0200785 GetDefaultDestination(candidates, ICE_CANDIDATE_COMPONENT_RTCP, &rtcp_port,
786 &rtcp_ip, &addr_type);
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000787 // Found default RTCP candidate.
788 // RFC 5245
789 // If the agent is utilizing RTCP, it MUST encode the RTCP candidate
790 // using the a=rtcp attribute as defined in RFC 3605.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000791
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000792 // RFC 3605
793 // rtcp-attribute = "a=rtcp:" port [nettype space addrtype space
794 // connection-address] CRLF
Jonas Olssonec9e4922018-09-05 09:53:49 +0200795 rtc::StringBuilder os;
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000796 InitAttrLine(kAttributeRtcp, &os);
Yves Gerey665174f2018-06-19 15:03:05 +0200797 os << kSdpDelimiterColon << rtcp_port << " " << kConnectionNettype << " "
798 << addr_type << " " << rtcp_ip;
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000799 rtcp_line = os.str();
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000800 return rtcp_line;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000801}
802
803// Get candidates according to the mline index from SessionDescriptionInterface.
804static void GetCandidatesByMindex(const SessionDescriptionInterface& desci,
805 int mline_index,
806 std::vector<Candidate>* candidates) {
807 if (!candidates) {
808 return;
809 }
810 const IceCandidateCollection* cc = desci.candidates(mline_index);
811 for (size_t i = 0; i < cc->count(); ++i) {
812 const IceCandidateInterface* candidate = cc->at(i);
813 candidates->push_back(candidate->candidate());
814 }
815}
816
deadbeef90f1e1e2017-02-10 12:35:05 -0800817static bool IsValidPort(int port) {
818 return port >= 0 && port <= 65535;
819}
820
Steve Antone831b8c2018-02-01 12:22:16 -0800821std::string SdpSerialize(const JsepSessionDescription& jdesc) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000822 const cricket::SessionDescription* desc = jdesc.description();
823 if (!desc) {
824 return "";
825 }
826
827 std::string message;
828
829 // Session Description.
830 AddLine(kSessionVersion, &message);
831 // Session Origin
832 // RFC 4566
833 // o=<username> <sess-id> <sess-version> <nettype> <addrtype>
834 // <unicast-address>
Jonas Olssonec9e4922018-09-05 09:53:49 +0200835 rtc::StringBuilder os;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000836 InitLine(kLineTypeOrigin, kSessionOriginUsername, &os);
Yves Gerey665174f2018-06-19 15:03:05 +0200837 const std::string& session_id =
838 jdesc.session_id().empty() ? kSessionOriginSessionId : jdesc.session_id();
839 const std::string& session_version = jdesc.session_version().empty()
840 ? kSessionOriginSessionVersion
841 : jdesc.session_version();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000842 os << " " << session_id << " " << session_version << " "
843 << kSessionOriginNettype << " " << kSessionOriginAddrtype << " "
844 << kSessionOriginAddress;
845 AddLine(os.str(), &message);
846 AddLine(kSessionName, &message);
847
848 // Time Description.
849 AddLine(kTimeDescription, &message);
850
851 // Group
852 if (desc->HasGroup(cricket::GROUP_TYPE_BUNDLE)) {
853 std::string group_line = kAttrGroup;
854 const cricket::ContentGroup* group =
855 desc->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
nisseede5da42017-01-12 05:15:36 -0800856 RTC_DCHECK(group != NULL);
Steve Anton4daf66e2018-09-07 14:55:53 -0700857 for (const std::string& content_name : group->content_names()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000858 group_line.append(" ");
Steve Anton4daf66e2018-09-07 14:55:53 -0700859 group_line.append(content_name);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000860 }
861 AddLine(group_line, &message);
862 }
863
Johannes Kron0854eb62018-10-10 22:33:20 +0200864 // Mixed one- and two-byte header extension.
Johannes Kron9581bc42018-10-23 10:17:39 +0200865 if (desc->extmap_allow_mixed()) {
Johannes Kron0854eb62018-10-10 22:33:20 +0200866 InitAttrLine(kAttributeExtmapAllowMixed, &os);
867 AddLine(os.str(), &message);
868 }
869
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000870 // MediaStream semantics
871 InitAttrLine(kAttributeMsidSemantics, &os);
872 os << kSdpDelimiterColon << " " << kMediaStreamSemantic;
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000873
Seth Hampson845e8782018-03-02 11:34:10 -0800874 std::set<std::string> media_stream_ids;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000875 const ContentInfo* audio_content = GetFirstAudioContent(desc);
876 if (audio_content)
Seth Hampson845e8782018-03-02 11:34:10 -0800877 GetMediaStreamIds(audio_content, &media_stream_ids);
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000878
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000879 const ContentInfo* video_content = GetFirstVideoContent(desc);
880 if (video_content)
Seth Hampson845e8782018-03-02 11:34:10 -0800881 GetMediaStreamIds(video_content, &media_stream_ids);
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000882
Steve Anton4daf66e2018-09-07 14:55:53 -0700883 for (const std::string& id : media_stream_ids) {
884 os << " " << id;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000885 }
886 AddLine(os.str(), &message);
887
Taylor Brandstetter2f65ec52018-05-24 11:37:28 -0700888 // a=ice-lite
889 //
890 // TODO(deadbeef): It's weird that we need to iterate TransportInfos for
891 // this, when it's a session-level attribute. It really should be moved to a
892 // session-level structure like SessionDescription.
893 for (const cricket::TransportInfo& transport : desc->transport_infos()) {
894 if (transport.description.ice_mode == cricket::ICEMODE_LITE) {
895 InitAttrLine(kAttributeIceLite, &os);
896 AddLine(os.str(), &message);
897 break;
898 }
899 }
900
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000901 // Preserve the order of the media contents.
902 int mline_index = -1;
Steve Anton4daf66e2018-09-07 14:55:53 -0700903 for (const ContentInfo& content : desc->contents()) {
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000904 std::vector<Candidate> candidates;
905 GetCandidatesByMindex(jdesc, ++mline_index, &candidates);
Steve Anton4daf66e2018-09-07 14:55:53 -0700906 BuildMediaDescription(&content, desc->GetTransportInfoByName(content.name),
907 content.media_description()->type(), candidates,
908 desc->msid_signaling(), &message);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000909 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000910 return message;
911}
912
913// Serializes the passed in IceCandidateInterface to a SDP string.
914// candidate - The candidate to be serialized.
Honghai Zhang7fb69db2016-03-14 11:59:18 -0700915std::string SdpSerializeCandidate(const IceCandidateInterface& candidate) {
916 return SdpSerializeCandidate(candidate.candidate());
917}
918
919// Serializes a cricket Candidate.
920std::string SdpSerializeCandidate(const cricket::Candidate& candidate) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000921 std::string message;
Honghai Zhang7fb69db2016-03-14 11:59:18 -0700922 std::vector<cricket::Candidate> candidates(1, candidate);
honghaiza54a0802015-12-16 18:37:23 -0800923 BuildCandidate(candidates, true, &message);
wu@webrtc.orgec9f5fb2014-06-24 17:05:10 +0000924 // From WebRTC draft section 4.8.1.1 candidate-attribute will be
925 // just candidate:<candidate> not a=candidate:<blah>CRLF
nisseede5da42017-01-12 05:15:36 -0800926 RTC_DCHECK(message.find("a=") == 0);
wu@webrtc.orgec9f5fb2014-06-24 17:05:10 +0000927 message.erase(0, 2);
nisseede5da42017-01-12 05:15:36 -0800928 RTC_DCHECK(message.find(kLineBreak) == message.size() - 2);
wu@webrtc.orgec9f5fb2014-06-24 17:05:10 +0000929 message.resize(message.size() - 2);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000930 return message;
931}
932
933bool SdpDeserialize(const std::string& message,
934 JsepSessionDescription* jdesc,
935 SdpParseError* error) {
936 std::string session_id;
937 std::string session_version;
Peter Thatcher7cbd1882015-09-17 18:54:52 -0700938 TransportDescription session_td("", "");
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000939 RtpHeaderExtensions session_extmaps;
zhihuang38989e52017-03-21 11:04:53 -0700940 rtc::SocketAddress session_connection_addr;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000941 cricket::SessionDescription* desc = new cricket::SessionDescription();
942 std::vector<JsepIceCandidate*> candidates;
943 size_t current_pos = 0;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000944
945 // Session Description
946 if (!ParseSessionDescription(message, &current_pos, &session_id,
deadbeefc80741f2015-10-22 13:14:45 -0700947 &session_version, &session_td, &session_extmaps,
zhihuang38989e52017-03-21 11:04:53 -0700948 &session_connection_addr, desc, error)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000949 delete desc;
950 return false;
951 }
952
953 // Media Description
deadbeefc80741f2015-10-22 13:14:45 -0700954 if (!ParseMediaDescription(message, session_td, session_extmaps, &current_pos,
zhihuang38989e52017-03-21 11:04:53 -0700955 session_connection_addr, desc, &candidates,
956 error)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000957 delete desc;
Steve Anton4daf66e2018-09-07 14:55:53 -0700958 for (JsepIceCandidate* candidate : candidates) {
959 delete candidate;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000960 }
961 return false;
962 }
963
964 jdesc->Initialize(desc, session_id, session_version);
965
Steve Anton4daf66e2018-09-07 14:55:53 -0700966 for (JsepIceCandidate* candidate : candidates) {
967 jdesc->AddCandidate(candidate);
968 delete candidate;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000969 }
970 return true;
971}
972
973bool SdpDeserializeCandidate(const std::string& message,
974 JsepIceCandidate* jcandidate,
975 SdpParseError* error) {
nisseede5da42017-01-12 05:15:36 -0800976 RTC_DCHECK(jcandidate != NULL);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000977 Candidate candidate;
978 if (!ParseCandidate(message, &candidate, error, true)) {
979 return false;
980 }
981 jcandidate->SetCandidate(candidate);
982 return true;
983}
984
Honghai Zhang7fb69db2016-03-14 11:59:18 -0700985bool SdpDeserializeCandidate(const std::string& transport_name,
986 const std::string& message,
987 cricket::Candidate* candidate,
988 SdpParseError* error) {
nisseede5da42017-01-12 05:15:36 -0800989 RTC_DCHECK(candidate != nullptr);
Honghai Zhang7fb69db2016-03-14 11:59:18 -0700990 if (!ParseCandidate(message, candidate, error, true)) {
991 return false;
992 }
993 candidate->set_transport_name(transport_name);
994 return true;
995}
996
Yves Gerey665174f2018-06-19 15:03:05 +0200997bool ParseCandidate(const std::string& message,
998 Candidate* candidate,
999 SdpParseError* error,
1000 bool is_raw) {
nisseede5da42017-01-12 05:15:36 -08001001 RTC_DCHECK(candidate != NULL);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001002
1003 // Get the first line from |message|.
jiayl@webrtc.org7ec3f9f2014-08-08 23:09:15 +00001004 std::string first_line = message;
1005 size_t pos = 0;
1006 GetLine(message, &pos, &first_line);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001007
jiayl@webrtc.org7ec3f9f2014-08-08 23:09:15 +00001008 // Makes sure |message| contains only one line.
1009 if (message.size() > first_line.size()) {
1010 std::string left, right;
Jonas Olssonec9e4922018-09-05 09:53:49 +02001011 if (rtc::tokenize_first(message, kNewLineChar, &left, &right) &&
Donald Curtis0e07f922015-05-15 09:21:23 -07001012 !right.empty()) {
jiayl@webrtc.org7ec3f9f2014-08-08 23:09:15 +00001013 return ParseFailed(message, 0, "Expect one line only", error);
1014 }
1015 }
1016
1017 // From WebRTC draft section 4.8.1.1 candidate-attribute should be
1018 // candidate:<candidate> when trickled, but we still support
1019 // a=candidate:<blah>CRLF for backward compatibility and for parsing a line
1020 // from the SDP.
1021 if (IsLineType(first_line, kLineTypeAttributes)) {
1022 first_line = first_line.substr(kLinePrefixLength);
1023 }
1024
1025 std::string attribute_candidate;
1026 std::string candidate_value;
1027
1028 // |first_line| must be in the form of "candidate:<value>".
Jonas Olssonec9e4922018-09-05 09:53:49 +02001029 if (!rtc::tokenize_first(first_line, kSdpDelimiterColonChar,
1030 &attribute_candidate, &candidate_value) ||
jiayl@webrtc.org7ec3f9f2014-08-08 23:09:15 +00001031 attribute_candidate != kAttributeCandidate) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001032 if (is_raw) {
Jonas Olssonec9e4922018-09-05 09:53:49 +02001033 rtc::StringBuilder description;
Yves Gerey665174f2018-06-19 15:03:05 +02001034 description << "Expect line: " << kAttributeCandidate << ":"
1035 << "<candidate-str>";
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001036 return ParseFailed(first_line, 0, description.str(), error);
1037 } else {
1038 return ParseFailedExpectLine(first_line, 0, kLineTypeAttributes,
1039 kAttributeCandidate, error);
1040 }
1041 }
1042
1043 std::vector<std::string> fields;
Jonas Olssonec9e4922018-09-05 09:53:49 +02001044 rtc::split(candidate_value, kSdpDelimiterSpaceChar, &fields);
jiayl@webrtc.org7ec3f9f2014-08-08 23:09:15 +00001045
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001046 // RFC 5245
1047 // a=candidate:<foundation> <component-id> <transport> <priority>
1048 // <connection-address> <port> typ <candidate-types>
1049 // [raddr <connection-address>] [rport <port>]
1050 // *(SP extension-att-name SP extension-att-value)
1051 const size_t expected_min_fields = 8;
1052 if (fields.size() < expected_min_fields ||
1053 (fields[6] != kAttributeCandidateTyp)) {
1054 return ParseFailedExpectMinFieldNum(first_line, expected_min_fields, error);
1055 }
jbauch083b73f2015-07-16 02:46:32 -07001056 const std::string& foundation = fields[0];
jiayl@webrtc.org7ec3f9f2014-08-08 23:09:15 +00001057
wu@webrtc.org5e760e72014-04-02 23:19:09 +00001058 int component_id = 0;
1059 if (!GetValueFromString(first_line, fields[1], &component_id, error)) {
1060 return false;
1061 }
jbauch083b73f2015-07-16 02:46:32 -07001062 const std::string& transport = fields[2];
Peter Boström0c4e06b2015-10-07 12:23:21 +02001063 uint32_t priority = 0;
wu@webrtc.org5e760e72014-04-02 23:19:09 +00001064 if (!GetValueFromString(first_line, fields[3], &priority, error)) {
1065 return false;
1066 }
jbauch083b73f2015-07-16 02:46:32 -07001067 const std::string& connection_address = fields[4];
wu@webrtc.org5e760e72014-04-02 23:19:09 +00001068 int port = 0;
1069 if (!GetValueFromString(first_line, fields[5], &port, error)) {
1070 return false;
1071 }
deadbeef90f1e1e2017-02-10 12:35:05 -08001072 if (!IsValidPort(port)) {
1073 return ParseFailed(first_line, "Invalid port number.", error);
1074 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001075 SocketAddress address(connection_address, port);
1076
1077 cricket::ProtocolType protocol;
hnslb68cc752016-12-13 10:33:41 -08001078 if (!StringToProto(transport.c_str(), &protocol)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001079 return ParseFailed(first_line, "Unsupported transport type.", error);
1080 }
hnslb68cc752016-12-13 10:33:41 -08001081 switch (protocol) {
1082 case cricket::PROTO_UDP:
1083 case cricket::PROTO_TCP:
1084 case cricket::PROTO_SSLTCP:
1085 // Supported protocol.
1086 break;
1087 default:
1088 return ParseFailed(first_line, "Unsupported transport type.", error);
1089 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001090
1091 std::string candidate_type;
jbauch083b73f2015-07-16 02:46:32 -07001092 const std::string& type = fields[7];
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001093 if (type == kCandidateHost) {
1094 candidate_type = cricket::LOCAL_PORT_TYPE;
1095 } else if (type == kCandidateSrflx) {
1096 candidate_type = cricket::STUN_PORT_TYPE;
1097 } else if (type == kCandidateRelay) {
1098 candidate_type = cricket::RELAY_PORT_TYPE;
Honghai Zhang7fb69db2016-03-14 11:59:18 -07001099 } else if (type == kCandidatePrflx) {
1100 candidate_type = cricket::PRFLX_PORT_TYPE;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001101 } else {
1102 return ParseFailed(first_line, "Unsupported candidate type.", error);
1103 }
1104
1105 size_t current_position = expected_min_fields;
1106 SocketAddress related_address;
1107 // The 2 optional fields for related address
1108 // [raddr <connection-address>] [rport <port>]
1109 if (fields.size() >= (current_position + 2) &&
1110 fields[current_position] == kAttributeCandidateRaddr) {
1111 related_address.SetIP(fields[++current_position]);
1112 ++current_position;
1113 }
1114 if (fields.size() >= (current_position + 2) &&
1115 fields[current_position] == kAttributeCandidateRport) {
wu@webrtc.org5e760e72014-04-02 23:19:09 +00001116 int port = 0;
Yves Gerey665174f2018-06-19 15:03:05 +02001117 if (!GetValueFromString(first_line, fields[++current_position], &port,
1118 error)) {
wu@webrtc.org5e760e72014-04-02 23:19:09 +00001119 return false;
1120 }
deadbeef90f1e1e2017-02-10 12:35:05 -08001121 if (!IsValidPort(port)) {
1122 return ParseFailed(first_line, "Invalid port number.", error);
1123 }
wu@webrtc.org5e760e72014-04-02 23:19:09 +00001124 related_address.SetPort(port);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001125 ++current_position;
1126 }
1127
mallinath@webrtc.org2d60c5e2014-08-08 22:29:20 +00001128 // If this is a TCP candidate, it has additional extension as defined in
1129 // RFC 6544.
1130 std::string tcptype;
1131 if (fields.size() >= (current_position + 2) &&
1132 fields[current_position] == kTcpCandidateType) {
1133 tcptype = fields[++current_position];
1134 ++current_position;
1135
1136 if (tcptype != cricket::TCPTYPE_ACTIVE_STR &&
1137 tcptype != cricket::TCPTYPE_PASSIVE_STR &&
1138 tcptype != cricket::TCPTYPE_SIMOPEN_STR) {
1139 return ParseFailed(first_line, "Invalid TCP candidate type.", error);
1140 }
1141
1142 if (protocol != cricket::PROTO_TCP) {
1143 return ParseFailed(first_line, "Invalid non-TCP candidate", error);
1144 }
1145 }
1146
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001147 // Extension
honghaiza54a0802015-12-16 18:37:23 -08001148 // Though non-standard, we support the ICE ufrag and pwd being signaled on
1149 // the candidate to avoid issues with confusing which generation a candidate
1150 // belongs to when trickling multiple generations at the same time.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001151 std::string username;
1152 std::string password;
Peter Boström0c4e06b2015-10-07 12:23:21 +02001153 uint32_t generation = 0;
honghaiza0c44ea2016-03-23 16:07:48 -07001154 uint16_t network_id = 0;
1155 uint16_t network_cost = 0;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001156 for (size_t i = current_position; i + 1 < fields.size(); ++i) {
1157 // RFC 5245
1158 // *(SP extension-att-name SP extension-att-value)
1159 if (fields[i] == kAttributeCandidateGeneration) {
wu@webrtc.org5e760e72014-04-02 23:19:09 +00001160 if (!GetValueFromString(first_line, fields[++i], &generation, error)) {
1161 return false;
1162 }
honghaiza54a0802015-12-16 18:37:23 -08001163 } else if (fields[i] == kAttributeCandidateUfrag) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001164 username = fields[++i];
honghaiza54a0802015-12-16 18:37:23 -08001165 } else if (fields[i] == kAttributeCandidatePwd) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001166 password = fields[++i];
honghaiza0c44ea2016-03-23 16:07:48 -07001167 } else if (fields[i] == kAttributeCandidateNetworkId) {
1168 if (!GetValueFromString(first_line, fields[++i], &network_id, error)) {
1169 return false;
1170 }
honghaize1a0c942016-02-16 14:54:56 -08001171 } else if (fields[i] == kAttributeCandidateNetworkCost) {
1172 if (!GetValueFromString(first_line, fields[++i], &network_cost, error)) {
1173 return false;
1174 }
Honghai Zhang351d77b2016-05-20 15:08:29 -07001175 network_cost = std::min(network_cost, rtc::kNetworkCostMax);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001176 } else {
1177 // Skip the unknown extension.
1178 ++i;
1179 }
1180 }
1181
guoweis@webrtc.org61c12472015-01-15 06:53:07 +00001182 *candidate = Candidate(component_id, cricket::ProtoToString(protocol),
guoweis@webrtc.org950c5182014-12-16 23:01:31 +00001183 address, priority, username, password, candidate_type,
honghaiza0c44ea2016-03-23 16:07:48 -07001184 generation, foundation, network_id, network_cost);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001185 candidate->set_related_address(related_address);
mallinath@webrtc.org2d60c5e2014-08-08 22:29:20 +00001186 candidate->set_tcptype(tcptype);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001187 return true;
1188}
1189
1190bool ParseIceOptions(const std::string& line,
1191 std::vector<std::string>* transport_options,
1192 SdpParseError* error) {
1193 std::string ice_options;
1194 if (!GetValue(line, kAttributeIceOption, &ice_options, error)) {
1195 return false;
1196 }
1197 std::vector<std::string> fields;
Jonas Olssonec9e4922018-09-05 09:53:49 +02001198 rtc::split(ice_options, kSdpDelimiterSpaceChar, &fields);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001199 for (size_t i = 0; i < fields.size(); ++i) {
1200 transport_options->push_back(fields[i]);
1201 }
1202 return true;
1203}
1204
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00001205bool ParseSctpPort(const std::string& line,
1206 int* sctp_port,
1207 SdpParseError* error) {
1208 // draft-ietf-mmusic-sctp-sdp-07
1209 // a=sctp-port
1210 std::vector<std::string> fields;
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00001211 const size_t expected_min_fields = 2;
Jonas Olssonec9e4922018-09-05 09:53:49 +02001212 rtc::split(line.substr(kLinePrefixLength), kSdpDelimiterColonChar, &fields);
lally69f57602015-10-08 10:15:04 -07001213 if (fields.size() < expected_min_fields) {
1214 fields.resize(0);
Jonas Olssonec9e4922018-09-05 09:53:49 +02001215 rtc::split(line.substr(kLinePrefixLength), kSdpDelimiterSpaceChar, &fields);
lally69f57602015-10-08 10:15:04 -07001216 }
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00001217 if (fields.size() < expected_min_fields) {
1218 return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
1219 }
1220 if (!rtc::FromString(fields[1], sctp_port)) {
lally69f57602015-10-08 10:15:04 -07001221 return ParseFailed(line, "Invalid sctp port value.", error);
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00001222 }
1223 return true;
1224}
1225
isheriff6f8d6862016-05-26 11:24:55 -07001226bool ParseExtmap(const std::string& line,
1227 RtpExtension* extmap,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001228 SdpParseError* error) {
1229 // RFC 5285
1230 // a=extmap:<value>["/"<direction>] <URI> <extensionattributes>
1231 std::vector<std::string> fields;
Jonas Olssonec9e4922018-09-05 09:53:49 +02001232 rtc::split(line.substr(kLinePrefixLength), kSdpDelimiterSpaceChar, &fields);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001233 const size_t expected_min_fields = 2;
1234 if (fields.size() < expected_min_fields) {
1235 return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
1236 }
1237 std::string uri = fields[1];
1238
1239 std::string value_direction;
1240 if (!GetValue(fields[0], kAttributeExtmap, &value_direction, error)) {
1241 return false;
1242 }
1243 std::vector<std::string> sub_fields;
Jonas Olssonec9e4922018-09-05 09:53:49 +02001244 rtc::split(value_direction, kSdpDelimiterSlashChar, &sub_fields);
wu@webrtc.org5e760e72014-04-02 23:19:09 +00001245 int value = 0;
1246 if (!GetValueFromString(line, sub_fields[0], &value, error)) {
1247 return false;
1248 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001249
jbauch5869f502017-06-29 12:31:36 -07001250 bool encrypted = false;
1251 if (uri == RtpExtension::kEncryptHeaderExtensionsUri) {
1252 // RFC 6904
Steve Anton36b29d12017-10-30 09:57:42 -07001253 // a=extmap:<value["/"<direction>] urn:ietf:params:rtp-hdrext:encrypt <URI>
1254 // <extensionattributes>
jbauch5869f502017-06-29 12:31:36 -07001255 const size_t expected_min_fields_encrypted = expected_min_fields + 1;
1256 if (fields.size() < expected_min_fields_encrypted) {
1257 return ParseFailedExpectMinFieldNum(line, expected_min_fields_encrypted,
Yves Gerey665174f2018-06-19 15:03:05 +02001258 error);
jbauch5869f502017-06-29 12:31:36 -07001259 }
1260
1261 encrypted = true;
1262 uri = fields[2];
1263 if (uri == RtpExtension::kEncryptHeaderExtensionsUri) {
1264 return ParseFailed(line, "Recursive encrypted header.", error);
1265 }
1266 }
1267
1268 *extmap = RtpExtension(uri, value, encrypted);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001269 return true;
1270}
1271
1272void BuildMediaDescription(const ContentInfo* content_info,
1273 const TransportInfo* transport_info,
Patrik Höglundb6b29e02018-06-21 16:58:01 +02001274 const cricket::MediaType media_type,
wu@webrtc.org4c3e9912014-07-16 21:03:13 +00001275 const std::vector<Candidate>& candidates,
Steve Antone831b8c2018-02-01 12:22:16 -08001276 int msid_signaling,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001277 std::string* message) {
nisseede5da42017-01-12 05:15:36 -08001278 RTC_DCHECK(message != NULL);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001279 if (content_info == NULL || message == NULL) {
1280 return;
1281 }
Jonas Olssonec9e4922018-09-05 09:53:49 +02001282 rtc::StringBuilder os;
Steve Antonb1c1de12017-12-21 15:14:30 -08001283 const MediaContentDescription* media_desc = content_info->media_description();
1284 RTC_DCHECK(media_desc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001285
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +00001286 int sctp_port = cricket::kSctpDefaultPort;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001287
1288 // RFC 4566
1289 // m=<media> <port> <proto> <fmt>
1290 // fmt is a list of payload type numbers that MAY be used in the session.
1291 const char* type = NULL;
1292 if (media_type == cricket::MEDIA_TYPE_AUDIO)
1293 type = kMediaTypeAudio;
1294 else if (media_type == cricket::MEDIA_TYPE_VIDEO)
1295 type = kMediaTypeVideo;
1296 else if (media_type == cricket::MEDIA_TYPE_DATA)
1297 type = kMediaTypeData;
1298 else
nissec80e7412017-01-11 05:56:46 -08001299 RTC_NOTREACHED();
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001300
1301 std::string fmt;
1302 if (media_type == cricket::MEDIA_TYPE_VIDEO) {
Steve Antonb1c1de12017-12-21 15:14:30 -08001303 const VideoContentDescription* video_desc = media_desc->as_video();
Steve Anton4daf66e2018-09-07 14:55:53 -07001304 for (const cricket::VideoCodec& codec : video_desc->codecs()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001305 fmt.append(" ");
Steve Anton4daf66e2018-09-07 14:55:53 -07001306 fmt.append(rtc::ToString(codec.id));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001307 }
1308 } else if (media_type == cricket::MEDIA_TYPE_AUDIO) {
Steve Antonb1c1de12017-12-21 15:14:30 -08001309 const AudioContentDescription* audio_desc = media_desc->as_audio();
Steve Anton4daf66e2018-09-07 14:55:53 -07001310 for (const cricket::AudioCodec& codec : audio_desc->codecs()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001311 fmt.append(" ");
Steve Anton4daf66e2018-09-07 14:55:53 -07001312 fmt.append(rtc::ToString(codec.id));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001313 }
1314 } else if (media_type == cricket::MEDIA_TYPE_DATA) {
Steve Antonb1c1de12017-12-21 15:14:30 -08001315 const DataContentDescription* data_desc = media_desc->as_data();
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00001316 if (IsDtlsSctp(media_desc->protocol())) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001317 fmt.append(" ");
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +00001318
zstein4b2e0822017-02-17 19:48:38 -08001319 if (data_desc->use_sctpmap()) {
Steve Anton4daf66e2018-09-07 14:55:53 -07001320 for (const cricket::DataCodec& codec : data_desc->codecs()) {
Niels Möller039743e2018-10-23 10:07:25 +02001321 if (absl::EqualsIgnoreCase(codec.name,
1322 cricket::kGoogleSctpDataCodecName) &&
Steve Anton4daf66e2018-09-07 14:55:53 -07001323 codec.GetParam(cricket::kCodecParamPort, &sctp_port)) {
zstein4b2e0822017-02-17 19:48:38 -08001324 break;
1325 }
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +00001326 }
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +00001327
Jonas Olsson6b1985d2018-07-05 11:59:48 +02001328 fmt.append(rtc::ToString(sctp_port));
zstein4b2e0822017-02-17 19:48:38 -08001329 } else {
1330 fmt.append(kDefaultSctpmapProtocol);
1331 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001332 } else {
Steve Anton4daf66e2018-09-07 14:55:53 -07001333 for (const cricket::DataCodec& codec : data_desc->codecs()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001334 fmt.append(" ");
Steve Anton4daf66e2018-09-07 14:55:53 -07001335 fmt.append(rtc::ToString(codec.id));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001336 }
1337 }
1338 }
1339 // The fmt must never be empty. If no codecs are found, set the fmt attribute
1340 // to 0.
1341 if (fmt.empty()) {
1342 fmt = " 0";
1343 }
1344
deadbeef25ed4352016-12-12 18:37:36 -08001345 // The port number in the m line will be updated later when associated with
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001346 // the candidates.
deadbeef25ed4352016-12-12 18:37:36 -08001347 //
1348 // A port value of 0 indicates that the m= section is rejected.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001349 // RFC 3264
1350 // To reject an offered stream, the port number in the corresponding stream in
1351 // the answer MUST be set to zero.
deadbeef25ed4352016-12-12 18:37:36 -08001352 //
1353 // However, the BUNDLE draft adds a new meaning to port zero, when used along
1354 // with a=bundle-only.
zhihuang38989e52017-03-21 11:04:53 -07001355 std::string port = kDummyPort;
1356 if (content_info->rejected || content_info->bundle_only) {
1357 port = kMediaPortRejected;
1358 } else if (!media_desc->connection_address().IsNil()) {
1359 port = rtc::ToString(media_desc->connection_address().port());
1360 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001361
Yves Gerey665174f2018-06-19 15:03:05 +02001362 rtc::SSLFingerprint* fp =
1363 (transport_info) ? transport_info->description.identity_fingerprint.get()
1364 : NULL;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001365
wu@webrtc.org4c3e9912014-07-16 21:03:13 +00001366 // Add the m and c lines.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001367 InitLine(kLineTypeMedia, type, &os);
1368 os << " " << port << " " << media_desc->protocol() << fmt;
zhihuang38989e52017-03-21 11:04:53 -07001369 AddLine(os.str(), message);
1370
1371 InitLine(kLineTypeConnection, kConnectionNettype, &os);
1372 if (media_desc->connection_address().IsNil()) {
1373 os << " " << kConnectionIpv4Addrtype << " " << kDummyAddress;
1374 } else if (media_desc->connection_address().family() == AF_INET) {
1375 os << " " << kConnectionIpv4Addrtype << " "
1376 << media_desc->connection_address().ipaddr().ToString();
1377 } else {
1378 os << " " << kConnectionIpv6Addrtype << " "
1379 << media_desc->connection_address().ipaddr().ToString();
1380 }
1381 AddLine(os.str(), message);
wu@webrtc.org4c3e9912014-07-16 21:03:13 +00001382
1383 // RFC 4566
1384 // b=AS:<bandwidth>
Peter Thatcherc0c3a862015-06-24 15:31:25 -07001385 if (media_desc->bandwidth() >= 1000) {
wu@webrtc.org4c3e9912014-07-16 21:03:13 +00001386 InitLine(kLineTypeSessionBandwidth, kApplicationSpecificMaximum, &os);
1387 os << kSdpDelimiterColon << (media_desc->bandwidth() / 1000);
1388 AddLine(os.str(), message);
1389 }
1390
deadbeef25ed4352016-12-12 18:37:36 -08001391 // Add the a=bundle-only line.
1392 if (content_info->bundle_only) {
1393 InitAttrLine(kAttributeBundleOnly, &os);
1394 AddLine(os.str(), message);
1395 }
1396
wu@webrtc.org4c3e9912014-07-16 21:03:13 +00001397 // Add the a=rtcp line.
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00001398 if (IsRtp(media_desc->protocol())) {
wu@webrtc.org4c3e9912014-07-16 21:03:13 +00001399 std::string rtcp_line = GetRtcpLine(candidates);
1400 if (!rtcp_line.empty()) {
1401 AddLine(rtcp_line, message);
1402 }
1403 }
1404
honghaiza54a0802015-12-16 18:37:23 -08001405 // Build the a=candidate lines. We don't include ufrag and pwd in the
1406 // candidates in the SDP to avoid redundancy.
1407 BuildCandidate(candidates, false, message);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001408
1409 // Use the transport_info to build the media level ice-ufrag and ice-pwd.
1410 if (transport_info) {
1411 // RFC 5245
1412 // ice-pwd-att = "ice-pwd" ":" password
1413 // ice-ufrag-att = "ice-ufrag" ":" ufrag
1414 // ice-ufrag
deadbeef3f7219b2015-12-28 15:17:14 -08001415 if (!transport_info->description.ice_ufrag.empty()) {
1416 InitAttrLine(kAttributeIceUfrag, &os);
1417 os << kSdpDelimiterColon << transport_info->description.ice_ufrag;
1418 AddLine(os.str(), message);
1419 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001420 // ice-pwd
deadbeef3f7219b2015-12-28 15:17:14 -08001421 if (!transport_info->description.ice_pwd.empty()) {
1422 InitAttrLine(kAttributeIcePwd, &os);
1423 os << kSdpDelimiterColon << transport_info->description.ice_pwd;
1424 AddLine(os.str(), message);
1425 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001426
1427 // draft-petithuguenin-mmusic-ice-attributes-level-03
1428 BuildIceOptions(transport_info->description.transport_options, message);
1429
1430 // RFC 4572
1431 // fingerprint-attribute =
1432 // "fingerprint" ":" hash-func SP fingerprint
1433 if (fp) {
1434 // Insert the fingerprint attribute.
1435 InitAttrLine(kAttributeFingerprint, &os);
Yves Gerey665174f2018-06-19 15:03:05 +02001436 os << kSdpDelimiterColon << fp->algorithm << kSdpDelimiterSpace
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001437 << fp->GetRfc4572Fingerprint();
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001438 AddLine(os.str(), message);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001439
1440 // Inserting setup attribute.
1441 if (transport_info->description.connection_role !=
Yves Gerey665174f2018-06-19 15:03:05 +02001442 cricket::CONNECTIONROLE_NONE) {
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001443 // Making sure we are not using "passive" mode.
1444 cricket::ConnectionRole role =
1445 transport_info->description.connection_role;
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +00001446 std::string dtls_role_str;
nissec16fa5e2017-02-07 07:18:43 -08001447 const bool success =
1448 cricket::ConnectionRoleToString(role, &dtls_role_str);
1449 RTC_DCHECK(success);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001450 InitAttrLine(kAttributeSetup, &os);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001451 os << kSdpDelimiterColon << dtls_role_str;
1452 AddLine(os.str(), message);
1453 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001454 }
1455 }
1456
1457 // RFC 3388
1458 // mid-attribute = "a=mid:" identification-tag
1459 // identification-tag = token
1460 // Use the content name as the mid identification-tag.
1461 InitAttrLine(kAttributeMid, &os);
1462 os << kSdpDelimiterColon << content_info->name;
1463 AddLine(os.str(), message);
1464
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00001465 if (IsDtlsSctp(media_desc->protocol())) {
Steve Antonb1c1de12017-12-21 15:14:30 -08001466 const DataContentDescription* data_desc = media_desc->as_data();
zstein4b2e0822017-02-17 19:48:38 -08001467 bool use_sctpmap = data_desc->use_sctpmap();
1468 BuildSctpContentAttributes(message, sctp_port, use_sctpmap);
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00001469 } else if (IsRtp(media_desc->protocol())) {
Steve Antone831b8c2018-02-01 12:22:16 -08001470 BuildRtpContentAttributes(media_desc, media_type, msid_signaling, message);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001471 }
1472}
1473
zstein4b2e0822017-02-17 19:48:38 -08001474void BuildSctpContentAttributes(std::string* message,
1475 int sctp_port,
1476 bool use_sctpmap) {
Jonas Olssonec9e4922018-09-05 09:53:49 +02001477 rtc::StringBuilder os;
zstein4b2e0822017-02-17 19:48:38 -08001478 if (use_sctpmap) {
1479 // draft-ietf-mmusic-sctp-sdp-04
1480 // a=sctpmap:sctpmap-number protocol [streams]
1481 InitAttrLine(kAttributeSctpmap, &os);
1482 os << kSdpDelimiterColon << sctp_port << kSdpDelimiterSpace
1483 << kDefaultSctpmapProtocol << kSdpDelimiterSpace
1484 << cricket::kMaxSctpStreams;
1485 } else {
1486 // draft-ietf-mmusic-sctp-sdp-23
1487 // a=sctp-port:<port>
1488 InitAttrLine(kAttributeSctpPort, &os);
1489 os << kSdpDelimiterColon << sctp_port;
1490 // TODO(zstein): emit max-message-size here
1491 }
wu@webrtc.org78187522013-10-07 23:32:02 +00001492 AddLine(os.str(), message);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001493}
1494
deadbeef9d3584c2016-02-16 17:54:10 -08001495void BuildRtpContentAttributes(const MediaContentDescription* media_desc,
Patrik Höglundb6b29e02018-06-21 16:58:01 +02001496 const cricket::MediaType media_type,
Steve Antone831b8c2018-02-01 12:22:16 -08001497 int msid_signaling,
deadbeef9d3584c2016-02-16 17:54:10 -08001498 std::string* message) {
Jonas Olssonec9e4922018-09-05 09:53:49 +02001499 rtc::StringBuilder os;
Johannes Kron0854eb62018-10-10 22:33:20 +02001500 // RFC 8285
1501 // a=extmap-allow-mixed
1502 // The attribute MUST be either on session level or media level. We support
1503 // responding on both levels, however, we don't respond on media level if it's
1504 // set on session level.
Johannes Kron9581bc42018-10-23 10:17:39 +02001505 if (media_desc->extmap_allow_mixed_enum() ==
Johannes Kron0854eb62018-10-10 22:33:20 +02001506 MediaContentDescription::kMedia) {
1507 InitAttrLine(kAttributeExtmapAllowMixed, &os);
1508 AddLine(os.str(), message);
1509 }
1510 // RFC 8285
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001511 // a=extmap:<value>["/"<direction>] <URI> <extensionattributes>
1512 // The definitions MUST be either all session level or all media level. This
1513 // implementation uses all media level.
1514 for (size_t i = 0; i < media_desc->rtp_header_extensions().size(); ++i) {
jbauch5869f502017-06-29 12:31:36 -07001515 const RtpExtension& extension = media_desc->rtp_header_extensions()[i];
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001516 InitAttrLine(kAttributeExtmap, &os);
jbauch5869f502017-06-29 12:31:36 -07001517 os << kSdpDelimiterColon << extension.id;
1518 if (extension.encrypt) {
1519 os << kSdpDelimiterSpace << RtpExtension::kEncryptHeaderExtensionsUri;
1520 }
1521 os << kSdpDelimiterSpace << extension.uri;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001522 AddLine(os.str(), message);
1523 }
1524
1525 // RFC 3264
1526 // a=sendrecv || a=sendonly || a=sendrecv || a=inactive
deadbeefc80741f2015-10-22 13:14:45 -07001527 switch (media_desc->direction()) {
Steve Anton4e70a722017-11-28 14:57:10 -08001528 case RtpTransceiverDirection::kInactive:
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001529 InitAttrLine(kAttributeInactive, &os);
1530 break;
Steve Anton4e70a722017-11-28 14:57:10 -08001531 case RtpTransceiverDirection::kSendOnly:
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001532 InitAttrLine(kAttributeSendOnly, &os);
1533 break;
Steve Anton4e70a722017-11-28 14:57:10 -08001534 case RtpTransceiverDirection::kRecvOnly:
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001535 InitAttrLine(kAttributeRecvOnly, &os);
1536 break;
Steve Anton4e70a722017-11-28 14:57:10 -08001537 case RtpTransceiverDirection::kSendRecv:
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001538 default:
1539 InitAttrLine(kAttributeSendRecv, &os);
1540 break;
1541 }
1542 AddLine(os.str(), message);
1543
Seth Hampson5b4f0752018-04-02 16:31:36 -07001544 // Specified in https://datatracker.ietf.org/doc/draft-ietf-mmusic-msid/16/
1545 // a=msid:<msid-id> <msid-appdata>
1546 // The msid-id is a 1*64 token char representing the media stream id, and the
1547 // msid-appdata is a 1*64 token char representing the track id. There is a
1548 // line for every media stream, with a special msid-id value of "-"
1549 // representing no streams. The value of "msid-appdata" MUST be identical for
1550 // all lines.
Steve Antone831b8c2018-02-01 12:22:16 -08001551 if (msid_signaling & cricket::kMsidSignalingMediaSection) {
1552 const StreamParamsVec& streams = media_desc->streams();
1553 if (streams.size() == 1u) {
1554 const StreamParams& track = streams[0];
Seth Hampson5b4f0752018-04-02 16:31:36 -07001555 std::vector<std::string> stream_ids = track.stream_ids();
1556 if (stream_ids.empty()) {
1557 stream_ids.push_back(kNoStreamMsid);
1558 }
1559 for (const std::string& stream_id : stream_ids) {
1560 InitAttrLine(kAttributeMsid, &os);
1561 os << kSdpDelimiterColon << stream_id << kSdpDelimiterSpace << track.id;
1562 AddLine(os.str(), message);
1563 }
Steve Antone831b8c2018-02-01 12:22:16 -08001564 } else if (streams.size() > 1u) {
1565 RTC_LOG(LS_WARNING)
1566 << "Trying to serialize Unified Plan SDP with more than "
Jonas Olsson45cc8902018-02-13 10:37:07 +01001567 "one track in a media section. Omitting 'a=msid'.";
deadbeef9d3584c2016-02-16 17:54:10 -08001568 }
1569 }
1570
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001571 // RFC 5761
1572 // a=rtcp-mux
1573 if (media_desc->rtcp_mux()) {
1574 InitAttrLine(kAttributeRtcpMux, &os);
1575 AddLine(os.str(), message);
1576 }
1577
deadbeef13871492015-12-09 12:37:51 -08001578 // RFC 5506
1579 // a=rtcp-rsize
1580 if (media_desc->rtcp_reduced_size()) {
1581 InitAttrLine(kAttributeRtcpReducedSize, &os);
1582 AddLine(os.str(), message);
1583 }
1584
deadbeefd45aea82017-09-16 01:24:29 -07001585 if (media_desc->conference_mode()) {
1586 InitAttrLine(kAttributeXGoogleFlag, &os);
1587 os << kSdpDelimiterColon << kValueConference;
1588 AddLine(os.str(), message);
1589 }
1590
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001591 // RFC 4568
1592 // a=crypto:<tag> <crypto-suite> <key-params> [<session-params>]
Steve Anton4daf66e2018-09-07 14:55:53 -07001593 for (const CryptoParams& crypto_params : media_desc->cryptos()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001594 InitAttrLine(kAttributeCrypto, &os);
Steve Anton4daf66e2018-09-07 14:55:53 -07001595 os << kSdpDelimiterColon << crypto_params.tag << " "
1596 << crypto_params.cipher_suite << " " << crypto_params.key_params;
1597 if (!crypto_params.session_params.empty()) {
1598 os << " " << crypto_params.session_params;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001599 }
1600 AddLine(os.str(), message);
1601 }
1602
1603 // RFC 4566
1604 // a=rtpmap:<payload type> <encoding name>/<clock rate>
1605 // [/<encodingparameters>]
1606 BuildRtpMap(media_desc, media_type, message);
1607
Steve Anton4daf66e2018-09-07 14:55:53 -07001608 for (const StreamParams& track : media_desc->streams()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001609 // Build the ssrc-group lines.
Steve Anton4daf66e2018-09-07 14:55:53 -07001610 for (const SsrcGroup& ssrc_group : track.ssrc_groups) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001611 // RFC 5576
1612 // a=ssrc-group:<semantics> <ssrc-id> ...
Steve Anton4daf66e2018-09-07 14:55:53 -07001613 if (ssrc_group.ssrcs.empty()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001614 continue;
1615 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001616 InitAttrLine(kAttributeSsrcGroup, &os);
Steve Anton4daf66e2018-09-07 14:55:53 -07001617 os << kSdpDelimiterColon << ssrc_group.semantics;
1618 for (uint32_t ssrc : ssrc_group.ssrcs) {
1619 os << kSdpDelimiterSpace << rtc::ToString(ssrc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001620 }
1621 AddLine(os.str(), message);
1622 }
1623 // Build the ssrc lines for each ssrc.
Steve Anton4daf66e2018-09-07 14:55:53 -07001624 for (uint32_t ssrc : track.ssrcs) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001625 // RFC 5576
1626 // a=ssrc:<ssrc-id> cname:<value>
Steve Anton4daf66e2018-09-07 14:55:53 -07001627 AddSsrcLine(ssrc, kSsrcAttributeCname, track.cname, message);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001628
Steve Antone831b8c2018-02-01 12:22:16 -08001629 if (msid_signaling & cricket::kMsidSignalingSsrcAttribute) {
1630 // draft-alvestrand-mmusic-msid-00
1631 // a=ssrc:<ssrc-id> msid:identifier [appdata]
1632 // The appdata consists of the "id" attribute of a MediaStreamTrack,
1633 // which corresponds to the "id" attribute of StreamParams.
Seth Hampson5b4f0752018-04-02 16:31:36 -07001634 // Since a=ssrc msid signaling is used in Plan B SDP semantics, and
1635 // multiple stream ids are not supported for Plan B, we are only adding
1636 // a line for the first media stream id here.
Seth Hampson7fa6ee62018-10-17 10:25:28 -07001637 const std::string& track_stream_id = track.first_stream_id();
1638 // We use a special msid-id value of "-" to represent no streams,
1639 // for Unified Plan compatibility. Plan B will always have a
1640 // track_stream_id.
1641 const std::string& stream_id =
1642 track_stream_id.empty() ? kNoStreamMsid : track_stream_id;
Steve Antone831b8c2018-02-01 12:22:16 -08001643 InitAttrLine(kAttributeSsrc, &os);
1644 os << kSdpDelimiterColon << ssrc << kSdpDelimiterSpace
1645 << kSsrcAttributeMsid << kSdpDelimiterColon << stream_id
Steve Anton4daf66e2018-09-07 14:55:53 -07001646 << kSdpDelimiterSpace << track.id;
Steve Antone831b8c2018-02-01 12:22:16 -08001647 AddLine(os.str(), message);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001648
Steve Antone831b8c2018-02-01 12:22:16 -08001649 // TODO(ronghuawu): Remove below code which is for backward
1650 // compatibility.
1651 // draft-alvestrand-rtcweb-mid-01
1652 // a=ssrc:<ssrc-id> mslabel:<value>
1653 // The label isn't yet defined.
1654 // a=ssrc:<ssrc-id> label:<value>
Seth Hampson5b4f0752018-04-02 16:31:36 -07001655 AddSsrcLine(ssrc, kSsrcAttributeMslabel, stream_id, message);
Steve Anton4daf66e2018-09-07 14:55:53 -07001656 AddSsrcLine(ssrc, kSSrcAttributeLabel, track.id, message);
Steve Antone831b8c2018-02-01 12:22:16 -08001657 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001658 }
1659 }
Amit Hilbucha2012042018-12-03 11:35:05 -08001660
1661 // Simulcast (a=simulcast)
1662 // https://tools.ietf.org/html/draft-ietf-mmusic-sdp-simulcast-13#section-5.1
1663 if (media_desc->as_video() && media_desc->as_video()->HasSimulcast()) {
1664 const auto& simulcast = media_desc->as_video()->simulcast_description();
1665 InitAttrLine(kAttributeSimulcast, &os);
1666 SdpSerializer serializer;
1667 os << kSdpDelimiterColon
1668 << serializer.SerializeSimulcastDescription(simulcast);
1669 AddLine(os.str(), message);
1670 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001671}
1672
Jonas Olssonec9e4922018-09-05 09:53:49 +02001673void WriteFmtpHeader(int payload_type, rtc::StringBuilder* os) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001674 // fmtp header: a=fmtp:|payload_type| <parameters>
1675 // Add a=fmtp
1676 InitAttrLine(kAttributeFmtp, os);
1677 // Add :|payload_type|
1678 *os << kSdpDelimiterColon << payload_type;
1679}
1680
Jonas Olssonec9e4922018-09-05 09:53:49 +02001681void WriteRtcpFbHeader(int payload_type, rtc::StringBuilder* os) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001682 // rtcp-fb header: a=rtcp-fb:|payload_type|
1683 // <parameters>/<ccm <ccm_parameters>>
1684 // Add a=rtcp-fb
1685 InitAttrLine(kAttributeRtcpFb, os);
1686 // Add :
1687 *os << kSdpDelimiterColon;
1688 if (payload_type == kWildcardPayloadType) {
1689 *os << "*";
1690 } else {
1691 *os << payload_type;
1692 }
1693}
1694
1695void WriteFmtpParameter(const std::string& parameter_name,
1696 const std::string& parameter_value,
Jonas Olssonec9e4922018-09-05 09:53:49 +02001697 rtc::StringBuilder* os) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001698 // fmtp parameters: |parameter_name|=|parameter_value|
1699 *os << parameter_name << kSdpDelimiterEqual << parameter_value;
1700}
1701
1702void WriteFmtpParameters(const cricket::CodecParameterMap& parameters,
Jonas Olssonec9e4922018-09-05 09:53:49 +02001703 rtc::StringBuilder* os) {
Steve Anton4daf66e2018-09-07 14:55:53 -07001704 bool first = true;
1705 for (const auto& entry : parameters) {
1706 const std::string& key = entry.first;
1707 const std::string& value = entry.second;
hta62a216e2016-04-15 11:02:14 -07001708 // Parameters are a semicolon-separated list, no spaces.
1709 // The list is separated from the header by a space.
Steve Anton4daf66e2018-09-07 14:55:53 -07001710 if (first) {
hta62a216e2016-04-15 11:02:14 -07001711 *os << kSdpDelimiterSpace;
Steve Anton4daf66e2018-09-07 14:55:53 -07001712 first = false;
hta62a216e2016-04-15 11:02:14 -07001713 } else {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001714 *os << kSdpDelimiterSemicolon;
1715 }
Steve Anton4daf66e2018-09-07 14:55:53 -07001716 WriteFmtpParameter(key, value, os);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001717 }
1718}
1719
1720bool IsFmtpParam(const std::string& name) {
ossuaa4b0772017-01-30 07:41:18 -08001721 // RFC 4855, section 3 specifies the mapping of media format parameters to SDP
1722 // parameters. Only ptime, maxptime, channels and rate are placed outside of
1723 // the fmtp line. In WebRTC, channels and rate are already handled separately
1724 // and thus not included in the CodecParameterMap.
1725 return name != kCodecParamPTime && name != kCodecParamMaxPTime;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001726}
1727
1728// Retreives fmtp parameters from |params|, which may contain other parameters
1729// as well, and puts them in |fmtp_parameters|.
1730void GetFmtpParams(const cricket::CodecParameterMap& params,
1731 cricket::CodecParameterMap* fmtp_parameters) {
Steve Anton4daf66e2018-09-07 14:55:53 -07001732 for (const auto& entry : params) {
1733 const std::string& key = entry.first;
1734 const std::string& value = entry.second;
1735 if (IsFmtpParam(key)) {
1736 (*fmtp_parameters)[key] = value;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001737 }
1738 }
1739}
1740
1741template <class T>
1742void AddFmtpLine(const T& codec, std::string* message) {
1743 cricket::CodecParameterMap fmtp_parameters;
1744 GetFmtpParams(codec.params, &fmtp_parameters);
1745 if (fmtp_parameters.empty()) {
1746 // No need to add an fmtp if it will have no (optional) parameters.
1747 return;
1748 }
Jonas Olssonec9e4922018-09-05 09:53:49 +02001749 rtc::StringBuilder os;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001750 WriteFmtpHeader(codec.id, &os);
1751 WriteFmtpParameters(fmtp_parameters, &os);
1752 AddLine(os.str(), message);
1753 return;
1754}
1755
1756template <class T>
1757void AddRtcpFbLines(const T& codec, std::string* message) {
Steve Anton4daf66e2018-09-07 14:55:53 -07001758 for (const cricket::FeedbackParam& param : codec.feedback_params.params()) {
Jonas Olssonec9e4922018-09-05 09:53:49 +02001759 rtc::StringBuilder os;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001760 WriteRtcpFbHeader(codec.id, &os);
Steve Anton4daf66e2018-09-07 14:55:53 -07001761 os << " " << param.id();
1762 if (!param.param().empty()) {
1763 os << " " << param.param();
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001764 }
1765 AddLine(os.str(), message);
1766 }
1767}
1768
Yves Gerey665174f2018-06-19 15:03:05 +02001769bool AddSctpDataCodec(DataContentDescription* media_desc, int sctp_port) {
solenberg9fa49752016-10-08 13:02:44 -07001770 for (const auto& codec : media_desc->codecs()) {
Niels Möller039743e2018-10-23 10:07:25 +02001771 if (absl::EqualsIgnoreCase(codec.name, cricket::kGoogleSctpDataCodecName)) {
Yves Gerey665174f2018-06-19 15:03:05 +02001772 return ParseFailed("", "Can't have multiple sctp port attributes.", NULL);
solenberg9fa49752016-10-08 13:02:44 -07001773 }
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00001774 }
1775 // Add the SCTP Port number as a pseudo-codec "port" parameter
solenberg9fa49752016-10-08 13:02:44 -07001776 cricket::DataCodec codec_port(cricket::kGoogleSctpDataCodecPlType,
deadbeef67cf2c12016-04-13 10:07:16 -07001777 cricket::kGoogleSctpDataCodecName);
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00001778 codec_port.SetParam(cricket::kCodecParamPort, sctp_port);
Mirko Bonadei675513b2017-11-09 11:09:25 +01001779 RTC_LOG(INFO) << "AddSctpDataCodec: Got SCTP Port Number " << sctp_port;
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00001780 media_desc->AddCodec(codec_port);
1781 return true;
1782}
1783
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001784bool GetMinValue(const std::vector<int>& values, int* value) {
1785 if (values.empty()) {
1786 return false;
1787 }
1788 std::vector<int>::const_iterator found =
1789 std::min_element(values.begin(), values.end());
1790 *value = *found;
1791 return true;
1792}
1793
1794bool GetParameter(const std::string& name,
Yves Gerey665174f2018-06-19 15:03:05 +02001795 const cricket::CodecParameterMap& params,
1796 int* value) {
1797 std::map<std::string, std::string>::const_iterator found = params.find(name);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001798 if (found == params.end()) {
1799 return false;
1800 }
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001801 if (!rtc::FromString(found->second, value)) {
wu@webrtc.org5e760e72014-04-02 23:19:09 +00001802 return false;
1803 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001804 return true;
1805}
1806
1807void BuildRtpMap(const MediaContentDescription* media_desc,
Patrik Höglundb6b29e02018-06-21 16:58:01 +02001808 const cricket::MediaType media_type,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001809 std::string* message) {
nisseede5da42017-01-12 05:15:36 -08001810 RTC_DCHECK(message != NULL);
1811 RTC_DCHECK(media_desc != NULL);
Jonas Olssonec9e4922018-09-05 09:53:49 +02001812 rtc::StringBuilder os;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001813 if (media_type == cricket::MEDIA_TYPE_VIDEO) {
Steve Anton4daf66e2018-09-07 14:55:53 -07001814 for (const cricket::VideoCodec& codec : media_desc->as_video()->codecs()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001815 // RFC 4566
1816 // a=rtpmap:<payload type> <encoding name>/<clock rate>
1817 // [/<encodingparameters>]
Steve Anton4daf66e2018-09-07 14:55:53 -07001818 if (codec.id != kWildcardPayloadType) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001819 InitAttrLine(kAttributeRtpmap, &os);
Steve Anton4daf66e2018-09-07 14:55:53 -07001820 os << kSdpDelimiterColon << codec.id << " " << codec.name << "/"
deadbeefe814a0d2017-02-25 18:15:09 -08001821 << cricket::kVideoCodecClockrate;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001822 AddLine(os.str(), message);
1823 }
Steve Anton4daf66e2018-09-07 14:55:53 -07001824 AddRtcpFbLines(codec, message);
1825 AddFmtpLine(codec, message);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001826 }
1827 } else if (media_type == cricket::MEDIA_TYPE_AUDIO) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001828 std::vector<int> ptimes;
1829 std::vector<int> maxptimes;
1830 int max_minptime = 0;
Steve Anton4daf66e2018-09-07 14:55:53 -07001831 for (const cricket::AudioCodec& codec : media_desc->as_audio()->codecs()) {
1832 RTC_DCHECK(!codec.name.empty());
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001833 // RFC 4566
1834 // a=rtpmap:<payload type> <encoding name>/<clock rate>
1835 // [/<encodingparameters>]
1836 InitAttrLine(kAttributeRtpmap, &os);
Steve Anton4daf66e2018-09-07 14:55:53 -07001837 os << kSdpDelimiterColon << codec.id << " ";
1838 os << codec.name << "/" << codec.clockrate;
1839 if (codec.channels != 1) {
1840 os << "/" << codec.channels;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001841 }
1842 AddLine(os.str(), message);
Steve Anton4daf66e2018-09-07 14:55:53 -07001843 AddRtcpFbLines(codec, message);
1844 AddFmtpLine(codec, message);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001845 int minptime = 0;
Steve Anton4daf66e2018-09-07 14:55:53 -07001846 if (GetParameter(kCodecParamMinPTime, codec.params, &minptime)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001847 max_minptime = std::max(minptime, max_minptime);
1848 }
1849 int ptime;
Steve Anton4daf66e2018-09-07 14:55:53 -07001850 if (GetParameter(kCodecParamPTime, codec.params, &ptime)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001851 ptimes.push_back(ptime);
1852 }
1853 int maxptime;
Steve Anton4daf66e2018-09-07 14:55:53 -07001854 if (GetParameter(kCodecParamMaxPTime, codec.params, &maxptime)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001855 maxptimes.push_back(maxptime);
1856 }
1857 }
1858 // Populate the maxptime attribute with the smallest maxptime of all codecs
1859 // under the same m-line.
1860 int min_maxptime = INT_MAX;
1861 if (GetMinValue(maxptimes, &min_maxptime)) {
1862 AddAttributeLine(kCodecParamMaxPTime, min_maxptime, message);
1863 }
nisseede5da42017-01-12 05:15:36 -08001864 RTC_DCHECK(min_maxptime > max_minptime);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001865 // Populate the ptime attribute with the smallest ptime or the largest
1866 // minptime, whichever is the largest, for all codecs under the same m-line.
1867 int ptime = INT_MAX;
1868 if (GetMinValue(ptimes, &ptime)) {
1869 ptime = std::min(ptime, min_maxptime);
1870 ptime = std::max(ptime, max_minptime);
1871 AddAttributeLine(kCodecParamPTime, ptime, message);
1872 }
1873 } else if (media_type == cricket::MEDIA_TYPE_DATA) {
Steve Anton4daf66e2018-09-07 14:55:53 -07001874 for (const cricket::DataCodec& codec : media_desc->as_data()->codecs()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001875 // RFC 4566
1876 // a=rtpmap:<payload type> <encoding name>/<clock rate>
1877 // [/<encodingparameters>]
1878 InitAttrLine(kAttributeRtpmap, &os);
Steve Anton4daf66e2018-09-07 14:55:53 -07001879 os << kSdpDelimiterColon << codec.id << " " << codec.name << "/"
1880 << codec.clockrate;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001881 AddLine(os.str(), message);
1882 }
1883 }
1884}
1885
1886void BuildCandidate(const std::vector<Candidate>& candidates,
honghaiza54a0802015-12-16 18:37:23 -08001887 bool include_ufrag,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001888 std::string* message) {
Jonas Olssonec9e4922018-09-05 09:53:49 +02001889 rtc::StringBuilder os;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001890
Steve Anton4daf66e2018-09-07 14:55:53 -07001891 for (const Candidate& candidate : candidates) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001892 // RFC 5245
1893 // a=candidate:<foundation> <component-id> <transport> <priority>
1894 // <connection-address> <port> typ <candidate-types>
1895 // [raddr <connection-address>] [rport <port>]
1896 // *(SP extension-att-name SP extension-att-value)
1897 std::string type;
1898 // Map the cricket candidate type to "host" / "srflx" / "prflx" / "relay"
Steve Anton4daf66e2018-09-07 14:55:53 -07001899 if (candidate.type() == cricket::LOCAL_PORT_TYPE) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001900 type = kCandidateHost;
Steve Anton4daf66e2018-09-07 14:55:53 -07001901 } else if (candidate.type() == cricket::STUN_PORT_TYPE) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001902 type = kCandidateSrflx;
Steve Anton4daf66e2018-09-07 14:55:53 -07001903 } else if (candidate.type() == cricket::RELAY_PORT_TYPE) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001904 type = kCandidateRelay;
Steve Anton4daf66e2018-09-07 14:55:53 -07001905 } else if (candidate.type() == cricket::PRFLX_PORT_TYPE) {
Honghai Zhang7fb69db2016-03-14 11:59:18 -07001906 type = kCandidatePrflx;
1907 // Peer reflexive candidate may be signaled for being removed.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001908 } else {
nissec80e7412017-01-11 05:56:46 -08001909 RTC_NOTREACHED();
Peter Thatcher019087f2015-04-28 09:06:26 -07001910 // Never write out candidates if we don't know the type.
1911 continue;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001912 }
1913
1914 InitAttrLine(kAttributeCandidate, &os);
Steve Anton4daf66e2018-09-07 14:55:53 -07001915 os << kSdpDelimiterColon << candidate.foundation() << " "
1916 << candidate.component() << " " << candidate.protocol() << " "
1917 << candidate.priority() << " "
1918 << (candidate.address().ipaddr().IsNil()
1919 ? candidate.address().hostname()
1920 : candidate.address().ipaddr().ToString())
1921 << " " << candidate.address().PortAsString() << " "
1922 << kAttributeCandidateTyp << " " << type << " ";
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001923
1924 // Related address
Steve Anton4daf66e2018-09-07 14:55:53 -07001925 if (!candidate.related_address().IsNil()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001926 os << kAttributeCandidateRaddr << " "
Steve Anton4daf66e2018-09-07 14:55:53 -07001927 << candidate.related_address().ipaddr().ToString() << " "
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001928 << kAttributeCandidateRport << " "
Steve Anton4daf66e2018-09-07 14:55:53 -07001929 << candidate.related_address().PortAsString() << " ";
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001930 }
1931
Steve Anton4daf66e2018-09-07 14:55:53 -07001932 if (candidate.protocol() == cricket::TCP_PROTOCOL_NAME) {
1933 os << kTcpCandidateType << " " << candidate.tcptype() << " ";
mallinath@webrtc.org2d60c5e2014-08-08 22:29:20 +00001934 }
1935
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001936 // Extensions
Steve Anton4daf66e2018-09-07 14:55:53 -07001937 os << kAttributeCandidateGeneration << " " << candidate.generation();
1938 if (include_ufrag && !candidate.username().empty()) {
1939 os << " " << kAttributeCandidateUfrag << " " << candidate.username();
honghaiza54a0802015-12-16 18:37:23 -08001940 }
Steve Anton4daf66e2018-09-07 14:55:53 -07001941 if (candidate.network_id() > 0) {
1942 os << " " << kAttributeCandidateNetworkId << " "
1943 << candidate.network_id();
honghaiza0c44ea2016-03-23 16:07:48 -07001944 }
Steve Anton4daf66e2018-09-07 14:55:53 -07001945 if (candidate.network_cost() > 0) {
1946 os << " " << kAttributeCandidateNetworkCost << " "
1947 << candidate.network_cost();
honghaize1a0c942016-02-16 14:54:56 -08001948 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001949
1950 AddLine(os.str(), message);
1951 }
1952}
1953
1954void BuildIceOptions(const std::vector<std::string>& transport_options,
1955 std::string* message) {
1956 if (!transport_options.empty()) {
Jonas Olssonec9e4922018-09-05 09:53:49 +02001957 rtc::StringBuilder os;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001958 InitAttrLine(kAttributeIceOption, &os);
1959 os << kSdpDelimiterColon << transport_options[0];
1960 for (size_t i = 1; i < transport_options.size(); ++i) {
1961 os << kSdpDelimiterSpace << transport_options[i];
1962 }
1963 AddLine(os.str(), message);
1964 }
1965}
1966
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00001967bool IsRtp(const std::string& protocol) {
1968 return protocol.empty() ||
Yves Gerey665174f2018-06-19 15:03:05 +02001969 (protocol.find(cricket::kMediaProtocolRtpPrefix) != std::string::npos);
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00001970}
1971
1972bool IsDtlsSctp(const std::string& protocol) {
1973 // This intentionally excludes "SCTP" and "SCTP/DTLS".
lally@webrtc.orga7470932015-02-24 20:19:21 +00001974 return protocol.find(cricket::kMediaProtocolDtlsSctp) != std::string::npos;
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00001975}
1976
zhihuang38989e52017-03-21 11:04:53 -07001977bool ParseConnectionData(const std::string& line,
1978 rtc::SocketAddress* addr,
1979 SdpParseError* error) {
1980 // Parse the line from left to right.
1981 std::string token;
1982 std::string rightpart;
1983 // RFC 4566
1984 // c=<nettype> <addrtype> <connection-address>
1985 // Skip the "c="
Jonas Olssonec9e4922018-09-05 09:53:49 +02001986 if (!rtc::tokenize_first(line, kSdpDelimiterEqualChar, &token, &rightpart)) {
zhihuang38989e52017-03-21 11:04:53 -07001987 return ParseFailed(line, "Failed to parse the network type.", error);
1988 }
1989
1990 // Extract and verify the <nettype>
Jonas Olssonec9e4922018-09-05 09:53:49 +02001991 if (!rtc::tokenize_first(rightpart, kSdpDelimiterSpaceChar, &token,
1992 &rightpart) ||
zhihuang38989e52017-03-21 11:04:53 -07001993 token != kConnectionNettype) {
1994 return ParseFailed(line,
1995 "Failed to parse the connection data. The network type "
1996 "is not currently supported.",
1997 error);
1998 }
1999
2000 // Extract the "<addrtype>" and "<connection-address>".
Jonas Olssonec9e4922018-09-05 09:53:49 +02002001 if (!rtc::tokenize_first(rightpart, kSdpDelimiterSpaceChar, &token,
2002 &rightpart)) {
zhihuang38989e52017-03-21 11:04:53 -07002003 return ParseFailed(line, "Failed to parse the address type.", error);
2004 }
2005
2006 // The rightpart part should be the IP address without the slash which is used
2007 // for multicast.
2008 if (rightpart.find('/') != std::string::npos) {
2009 return ParseFailed(line,
2010 "Failed to parse the connection data. Multicast is not "
2011 "currently supported.",
2012 error);
2013 }
2014 addr->SetIP(rightpart);
2015
2016 // Verify that the addrtype matches the type of the parsed address.
2017 if ((addr->family() == AF_INET && token != "IP4") ||
2018 (addr->family() == AF_INET6 && token != "IP6")) {
2019 addr->Clear();
2020 return ParseFailed(
2021 line,
2022 "Failed to parse the connection data. The address type is mismatching.",
2023 error);
2024 }
2025 return true;
2026}
2027
2028bool ParseSessionDescription(const std::string& message,
2029 size_t* pos,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002030 std::string* session_id,
2031 std::string* session_version,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002032 TransportDescription* session_td,
2033 RtpHeaderExtensions* session_extmaps,
zhihuang38989e52017-03-21 11:04:53 -07002034 rtc::SocketAddress* connection_addr,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002035 cricket::SessionDescription* desc,
2036 SdpParseError* error) {
2037 std::string line;
2038
deadbeefc80741f2015-10-22 13:14:45 -07002039 desc->set_msid_supported(false);
Johannes Kron9581bc42018-10-23 10:17:39 +02002040 desc->set_extmap_allow_mixed(false);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002041 // RFC 4566
2042 // v= (protocol version)
2043 if (!GetLineWithType(message, pos, &line, kLineTypeVersion)) {
Yves Gerey665174f2018-06-19 15:03:05 +02002044 return ParseFailedExpectLine(message, *pos, kLineTypeVersion, std::string(),
2045 error);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002046 }
2047 // RFC 4566
2048 // o=<username> <sess-id> <sess-version> <nettype> <addrtype>
2049 // <unicast-address>
2050 if (!GetLineWithType(message, pos, &line, kLineTypeOrigin)) {
Yves Gerey665174f2018-06-19 15:03:05 +02002051 return ParseFailedExpectLine(message, *pos, kLineTypeOrigin, std::string(),
2052 error);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002053 }
2054 std::vector<std::string> fields;
Jonas Olssonec9e4922018-09-05 09:53:49 +02002055 rtc::split(line.substr(kLinePrefixLength), kSdpDelimiterSpaceChar, &fields);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002056 const size_t expected_fields = 6;
2057 if (fields.size() != expected_fields) {
2058 return ParseFailedExpectFieldNum(line, expected_fields, error);
2059 }
2060 *session_id = fields[1];
2061 *session_version = fields[2];
2062
2063 // RFC 4566
2064 // s= (session name)
2065 if (!GetLineWithType(message, pos, &line, kLineTypeSessionName)) {
2066 return ParseFailedExpectLine(message, *pos, kLineTypeSessionName,
2067 std::string(), error);
2068 }
2069
Danil Chapovalov66cadcc2018-06-19 16:47:43 +02002070 // absl::optional lines
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002071 // Those are the optional lines, so shouldn't return false if not present.
2072 // RFC 4566
2073 // i=* (session information)
2074 GetLineWithType(message, pos, &line, kLineTypeSessionInfo);
2075
2076 // RFC 4566
2077 // u=* (URI of description)
2078 GetLineWithType(message, pos, &line, kLineTypeSessionUri);
2079
2080 // RFC 4566
2081 // e=* (email address)
2082 GetLineWithType(message, pos, &line, kLineTypeSessionEmail);
2083
2084 // RFC 4566
2085 // p=* (phone number)
2086 GetLineWithType(message, pos, &line, kLineTypeSessionPhone);
2087
2088 // RFC 4566
2089 // c=* (connection information -- not required if included in
2090 // all media)
zhihuang38989e52017-03-21 11:04:53 -07002091 if (GetLineWithType(message, pos, &line, kLineTypeConnection)) {
2092 if (!ParseConnectionData(line, connection_addr, error)) {
2093 return false;
2094 }
2095 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002096
2097 // RFC 4566
2098 // b=* (zero or more bandwidth information lines)
2099 while (GetLineWithType(message, pos, &line, kLineTypeSessionBandwidth)) {
2100 // By pass zero or more b lines.
2101 }
2102
2103 // RFC 4566
2104 // One or more time descriptions ("t=" and "r=" lines; see below)
2105 // t= (time the session is active)
2106 // r=* (zero or more repeat times)
2107 // Ensure there's at least one time description
2108 if (!GetLineWithType(message, pos, &line, kLineTypeTiming)) {
2109 return ParseFailedExpectLine(message, *pos, kLineTypeTiming, std::string(),
2110 error);
2111 }
2112
2113 while (GetLineWithType(message, pos, &line, kLineTypeRepeatTimes)) {
2114 // By pass zero or more r lines.
2115 }
2116
2117 // Go through the rest of the time descriptions
2118 while (GetLineWithType(message, pos, &line, kLineTypeTiming)) {
2119 while (GetLineWithType(message, pos, &line, kLineTypeRepeatTimes)) {
2120 // By pass zero or more r lines.
2121 }
2122 }
2123
2124 // RFC 4566
2125 // z=* (time zone adjustments)
2126 GetLineWithType(message, pos, &line, kLineTypeTimeZone);
2127
2128 // RFC 4566
2129 // k=* (encryption key)
2130 GetLineWithType(message, pos, &line, kLineTypeEncryptionKey);
2131
2132 // RFC 4566
2133 // a=* (zero or more session attribute lines)
2134 while (GetLineWithType(message, pos, &line, kLineTypeAttributes)) {
2135 if (HasAttribute(line, kAttributeGroup)) {
2136 if (!ParseGroupAttribute(line, desc, error)) {
2137 return false;
2138 }
2139 } else if (HasAttribute(line, kAttributeIceUfrag)) {
Yves Gerey665174f2018-06-19 15:03:05 +02002140 if (!GetValue(line, kAttributeIceUfrag, &(session_td->ice_ufrag),
2141 error)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002142 return false;
2143 }
2144 } else if (HasAttribute(line, kAttributeIcePwd)) {
2145 if (!GetValue(line, kAttributeIcePwd, &(session_td->ice_pwd), error)) {
2146 return false;
2147 }
2148 } else if (HasAttribute(line, kAttributeIceLite)) {
2149 session_td->ice_mode = cricket::ICEMODE_LITE;
2150 } else if (HasAttribute(line, kAttributeIceOption)) {
2151 if (!ParseIceOptions(line, &(session_td->transport_options), error)) {
2152 return false;
2153 }
2154 } else if (HasAttribute(line, kAttributeFingerprint)) {
2155 if (session_td->identity_fingerprint.get()) {
2156 return ParseFailed(
2157 line,
2158 "Can't have multiple fingerprint attributes at the same level.",
2159 error);
2160 }
Steve Anton4905edb2018-10-15 19:27:44 -07002161 std::unique_ptr<rtc::SSLFingerprint> fingerprint;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002162 if (!ParseFingerprintAttribute(line, &fingerprint, error)) {
2163 return false;
2164 }
Steve Anton4905edb2018-10-15 19:27:44 -07002165 session_td->identity_fingerprint = std::move(fingerprint);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00002166 } else if (HasAttribute(line, kAttributeSetup)) {
2167 if (!ParseDtlsSetup(line, &(session_td->connection_role), error)) {
2168 return false;
2169 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002170 } else if (HasAttribute(line, kAttributeMsidSemantics)) {
2171 std::string semantics;
2172 if (!GetValue(line, kAttributeMsidSemantics, &semantics, error)) {
2173 return false;
2174 }
deadbeefc80741f2015-10-22 13:14:45 -07002175 desc->set_msid_supported(
2176 CaseInsensitiveFind(semantics, kMediaStreamSemantic));
Johannes Kron0854eb62018-10-10 22:33:20 +02002177 } else if (HasAttribute(line, kAttributeExtmapAllowMixed)) {
Johannes Kron9581bc42018-10-23 10:17:39 +02002178 desc->set_extmap_allow_mixed(true);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002179 } else if (HasAttribute(line, kAttributeExtmap)) {
isheriff6f8d6862016-05-26 11:24:55 -07002180 RtpExtension extmap;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002181 if (!ParseExtmap(line, &extmap, error)) {
2182 return false;
2183 }
2184 session_extmaps->push_back(extmap);
2185 }
2186 }
2187
2188 return true;
2189}
2190
2191bool ParseGroupAttribute(const std::string& line,
2192 cricket::SessionDescription* desc,
2193 SdpParseError* error) {
nisseede5da42017-01-12 05:15:36 -08002194 RTC_DCHECK(desc != NULL);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002195
2196 // RFC 5888 and draft-holmberg-mmusic-sdp-bundle-negotiation-00
2197 // a=group:BUNDLE video voice
2198 std::vector<std::string> fields;
Jonas Olssonec9e4922018-09-05 09:53:49 +02002199 rtc::split(line.substr(kLinePrefixLength), kSdpDelimiterSpaceChar, &fields);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002200 std::string semantics;
2201 if (!GetValue(fields[0], kAttributeGroup, &semantics, error)) {
2202 return false;
2203 }
2204 cricket::ContentGroup group(semantics);
2205 for (size_t i = 1; i < fields.size(); ++i) {
2206 group.AddContentName(fields[i]);
2207 }
2208 desc->AddGroup(group);
2209 return true;
2210}
2211
Steve Anton4905edb2018-10-15 19:27:44 -07002212static bool ParseFingerprintAttribute(
2213 const std::string& line,
2214 std::unique_ptr<rtc::SSLFingerprint>* fingerprint,
2215 SdpParseError* error) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002216 if (!IsLineType(line, kLineTypeAttributes) ||
2217 !HasAttribute(line, kAttributeFingerprint)) {
2218 return ParseFailedExpectLine(line, 0, kLineTypeAttributes,
2219 kAttributeFingerprint, error);
2220 }
2221
2222 std::vector<std::string> fields;
Jonas Olssonec9e4922018-09-05 09:53:49 +02002223 rtc::split(line.substr(kLinePrefixLength), kSdpDelimiterSpaceChar, &fields);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002224 const size_t expected_fields = 2;
2225 if (fields.size() != expected_fields) {
2226 return ParseFailedExpectFieldNum(line, expected_fields, error);
2227 }
2228
2229 // The first field here is "fingerprint:<hash>.
2230 std::string algorithm;
2231 if (!GetValue(fields[0], kAttributeFingerprint, &algorithm, error)) {
2232 return false;
2233 }
2234
2235 // Downcase the algorithm. Note that we don't need to downcase the
2236 // fingerprint because hex_decode can handle upper-case.
2237 std::transform(algorithm.begin(), algorithm.end(), algorithm.begin(),
2238 ::tolower);
2239
2240 // The second field is the digest value. De-hexify it.
Steve Anton4905edb2018-10-15 19:27:44 -07002241 *fingerprint =
2242 rtc::SSLFingerprint::CreateUniqueFromRfc4572(algorithm, fields[1]);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002243 if (!*fingerprint) {
Yves Gerey665174f2018-06-19 15:03:05 +02002244 return ParseFailed(line, "Failed to create fingerprint from the digest.",
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002245 error);
2246 }
2247
2248 return true;
2249}
2250
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00002251static bool ParseDtlsSetup(const std::string& line,
2252 cricket::ConnectionRole* role,
2253 SdpParseError* error) {
2254 // setup-attr = "a=setup:" role
2255 // role = "active" / "passive" / "actpass" / "holdconn"
2256 std::vector<std::string> fields;
Jonas Olssonec9e4922018-09-05 09:53:49 +02002257 rtc::split(line.substr(kLinePrefixLength), kSdpDelimiterColonChar, &fields);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00002258 const size_t expected_fields = 2;
2259 if (fields.size() != expected_fields) {
2260 return ParseFailedExpectFieldNum(line, expected_fields, error);
2261 }
2262 std::string role_str = fields[1];
2263 if (!cricket::StringToConnectionRole(role_str, role)) {
2264 return ParseFailed(line, "Invalid attribute value.", error);
2265 }
2266 return true;
2267}
2268
deadbeef9d3584c2016-02-16 17:54:10 -08002269static bool ParseMsidAttribute(const std::string& line,
Seth Hampson5b4f0752018-04-02 16:31:36 -07002270 std::vector<std::string>* stream_ids,
deadbeef9d3584c2016-02-16 17:54:10 -08002271 std::string* track_id,
2272 SdpParseError* error) {
Seth Hampson5b4f0752018-04-02 16:31:36 -07002273 // https://datatracker.ietf.org/doc/draft-ietf-mmusic-msid/16/
deadbeef9d3584c2016-02-16 17:54:10 -08002274 // a=msid:<stream id> <track id>
2275 // msid-value = msid-id [ SP msid-appdata ]
2276 // msid-id = 1*64token-char ; see RFC 4566
2277 // msid-appdata = 1*64token-char ; see RFC 4566
2278 std::string field1;
Seth Hampson5b4f0752018-04-02 16:31:36 -07002279 std::string new_stream_id;
2280 std::string new_track_id;
Jonas Olssonec9e4922018-09-05 09:53:49 +02002281 if (!rtc::tokenize_first(line.substr(kLinePrefixLength),
2282 kSdpDelimiterSpaceChar, &field1, &new_track_id)) {
deadbeef9d3584c2016-02-16 17:54:10 -08002283 const size_t expected_fields = 2;
2284 return ParseFailedExpectFieldNum(line, expected_fields, error);
2285 }
2286
Seth Hampson5b4f0752018-04-02 16:31:36 -07002287 if (new_track_id.empty()) {
deadbeefa4549d62017-02-10 17:26:22 -08002288 return ParseFailed(line, "Missing track ID in msid attribute.", error);
2289 }
Seth Hampson5b4f0752018-04-02 16:31:36 -07002290 // All track ids should be the same within an m section in a Unified Plan SDP.
2291 if (!track_id->empty() && new_track_id.compare(*track_id) != 0) {
2292 return ParseFailed(
2293 line, "Two different track IDs in msid attribute in one m= section",
2294 error);
2295 }
2296 *track_id = new_track_id;
deadbeefa4549d62017-02-10 17:26:22 -08002297
deadbeef9d3584c2016-02-16 17:54:10 -08002298 // msid:<msid-id>
Seth Hampson5b4f0752018-04-02 16:31:36 -07002299 if (!GetValue(field1, kAttributeMsid, &new_stream_id, error)) {
deadbeef9d3584c2016-02-16 17:54:10 -08002300 return false;
2301 }
Seth Hampson5b4f0752018-04-02 16:31:36 -07002302 if (new_stream_id.empty()) {
deadbeefa4549d62017-02-10 17:26:22 -08002303 return ParseFailed(line, "Missing stream ID in msid attribute.", error);
2304 }
Seth Hampson5b4f0752018-04-02 16:31:36 -07002305 // The special value "-" indicates "no MediaStream".
2306 if (new_stream_id.compare(kNoStreamMsid) != 0) {
2307 stream_ids->push_back(new_stream_id);
2308 }
deadbeef9d3584c2016-02-16 17:54:10 -08002309 return true;
2310}
2311
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002312// RFC 3551
2313// PT encoding media type clock rate channels
2314// name (Hz)
2315// 0 PCMU A 8,000 1
2316// 1 reserved A
2317// 2 reserved A
2318// 3 GSM A 8,000 1
2319// 4 G723 A 8,000 1
2320// 5 DVI4 A 8,000 1
2321// 6 DVI4 A 16,000 1
2322// 7 LPC A 8,000 1
2323// 8 PCMA A 8,000 1
2324// 9 G722 A 8,000 1
2325// 10 L16 A 44,100 2
2326// 11 L16 A 44,100 1
2327// 12 QCELP A 8,000 1
2328// 13 CN A 8,000 1
2329// 14 MPA A 90,000 (see text)
2330// 15 G728 A 8,000 1
2331// 16 DVI4 A 11,025 1
2332// 17 DVI4 A 22,050 1
2333// 18 G729 A 8,000 1
2334struct StaticPayloadAudioCodec {
2335 const char* name;
2336 int clockrate;
Peter Kasting69558702016-01-12 16:26:35 -08002337 size_t channels;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002338};
2339static const StaticPayloadAudioCodec kStaticPayloadAudioCodecs[] = {
Yves Gerey665174f2018-06-19 15:03:05 +02002340 {"PCMU", 8000, 1}, {"reserved", 0, 0}, {"reserved", 0, 0},
2341 {"GSM", 8000, 1}, {"G723", 8000, 1}, {"DVI4", 8000, 1},
2342 {"DVI4", 16000, 1}, {"LPC", 8000, 1}, {"PCMA", 8000, 1},
2343 {"G722", 8000, 1}, {"L16", 44100, 2}, {"L16", 44100, 1},
2344 {"QCELP", 8000, 1}, {"CN", 8000, 1}, {"MPA", 90000, 1},
2345 {"G728", 8000, 1}, {"DVI4", 11025, 1}, {"DVI4", 22050, 1},
2346 {"G729", 8000, 1},
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002347};
2348
Yves Gerey665174f2018-06-19 15:03:05 +02002349void MaybeCreateStaticPayloadAudioCodecs(const std::vector<int>& fmts,
2350 AudioContentDescription* media_desc) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002351 if (!media_desc) {
2352 return;
2353 }
deadbeef67cf2c12016-04-13 10:07:16 -07002354 RTC_DCHECK(media_desc->codecs().empty());
solenberg9fa49752016-10-08 13:02:44 -07002355 for (int payload_type : fmts) {
Yves Gerey665174f2018-06-19 15:03:05 +02002356 if (!media_desc->HasCodec(payload_type) && payload_type >= 0 &&
kjellander3e33bfe2016-06-20 07:04:09 -07002357 static_cast<uint32_t>(payload_type) <
2358 arraysize(kStaticPayloadAudioCodecs)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002359 std::string encoding_name = kStaticPayloadAudioCodecs[payload_type].name;
2360 int clock_rate = kStaticPayloadAudioCodecs[payload_type].clockrate;
Peter Kasting69558702016-01-12 16:26:35 -08002361 size_t channels = kStaticPayloadAudioCodecs[payload_type].channels;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002362 media_desc->AddCodec(cricket::AudioCodec(payload_type, encoding_name,
deadbeef67cf2c12016-04-13 10:07:16 -07002363 clock_rate, 0, channels));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002364 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002365 }
2366}
2367
2368template <class C>
2369static C* ParseContentDescription(const std::string& message,
Patrik Höglundb6b29e02018-06-21 16:58:01 +02002370 const cricket::MediaType media_type,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002371 int mline_index,
2372 const std::string& protocol,
deadbeef67cf2c12016-04-13 10:07:16 -07002373 const std::vector<int>& payload_types,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002374 size_t* pos,
2375 std::string* content_name,
deadbeef25ed4352016-12-12 18:37:36 -08002376 bool* bundle_only,
Steve Antone831b8c2018-02-01 12:22:16 -08002377 int* msid_signaling,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002378 TransportDescription* transport,
2379 std::vector<JsepIceCandidate*>* candidates,
2380 webrtc::SdpParseError* error) {
2381 C* media_desc = new C();
2382 switch (media_type) {
2383 case cricket::MEDIA_TYPE_AUDIO:
2384 *content_name = cricket::CN_AUDIO;
2385 break;
2386 case cricket::MEDIA_TYPE_VIDEO:
2387 *content_name = cricket::CN_VIDEO;
2388 break;
2389 case cricket::MEDIA_TYPE_DATA:
2390 *content_name = cricket::CN_DATA;
2391 break;
2392 default:
nissec80e7412017-01-11 05:56:46 -08002393 RTC_NOTREACHED();
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002394 break;
2395 }
deadbeef67cf2c12016-04-13 10:07:16 -07002396 if (!ParseContent(message, media_type, mline_index, protocol, payload_types,
Steve Antone831b8c2018-02-01 12:22:16 -08002397 pos, content_name, bundle_only, msid_signaling, media_desc,
2398 transport, candidates, error)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002399 delete media_desc;
zhihuang38989e52017-03-21 11:04:53 -07002400 return nullptr;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002401 }
2402 // Sort the codecs according to the m-line fmt list.
deadbeef67cf2c12016-04-13 10:07:16 -07002403 std::unordered_map<int, int> payload_type_preferences;
2404 // "size + 1" so that the lowest preference payload type has a preference of
2405 // 1, which is greater than the default (0) for payload types not in the fmt
2406 // list.
2407 int preference = static_cast<int>(payload_types.size() + 1);
2408 for (int pt : payload_types) {
2409 payload_type_preferences[pt] = preference--;
2410 }
2411 std::vector<typename C::CodecType> codecs = media_desc->codecs();
Yves Gerey665174f2018-06-19 15:03:05 +02002412 std::sort(codecs.begin(), codecs.end(),
2413 [&payload_type_preferences](const typename C::CodecType& a,
2414 const typename C::CodecType& b) {
2415 return payload_type_preferences[a.id] >
2416 payload_type_preferences[b.id];
2417 });
deadbeef67cf2c12016-04-13 10:07:16 -07002418 media_desc->set_codecs(codecs);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002419 return media_desc;
2420}
2421
2422bool ParseMediaDescription(const std::string& message,
2423 const TransportDescription& session_td,
2424 const RtpHeaderExtensions& session_extmaps,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002425 size_t* pos,
zhihuang38989e52017-03-21 11:04:53 -07002426 const rtc::SocketAddress& session_connection_addr,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002427 cricket::SessionDescription* desc,
2428 std::vector<JsepIceCandidate*>* candidates,
2429 SdpParseError* error) {
nisseede5da42017-01-12 05:15:36 -08002430 RTC_DCHECK(desc != NULL);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002431 std::string line;
2432 int mline_index = -1;
Steve Antone831b8c2018-02-01 12:22:16 -08002433 int msid_signaling = 0;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002434
2435 // Zero or more media descriptions
2436 // RFC 4566
2437 // m=<media> <port> <proto> <fmt>
2438 while (GetLineWithType(message, pos, &line, kLineTypeMedia)) {
2439 ++mline_index;
2440
2441 std::vector<std::string> fields;
Jonas Olssonec9e4922018-09-05 09:53:49 +02002442 rtc::split(line.substr(kLinePrefixLength), kSdpDelimiterSpaceChar, &fields);
zstein4b2e0822017-02-17 19:48:38 -08002443
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002444 const size_t expected_min_fields = 4;
2445 if (fields.size() < expected_min_fields) {
2446 return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
2447 }
deadbeef25ed4352016-12-12 18:37:36 -08002448 bool port_rejected = false;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002449 // RFC 3264
2450 // To reject an offered stream, the port number in the corresponding stream
2451 // in the answer MUST be set to zero.
2452 if (fields[1] == kMediaPortRejected) {
deadbeef25ed4352016-12-12 18:37:36 -08002453 port_rejected = true;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002454 }
2455
zhihuang38989e52017-03-21 11:04:53 -07002456 int port = 0;
2457 if (!rtc::FromString<int>(fields[1], &port) || !IsValidPort(port)) {
2458 return ParseFailed(line, "The port number is invalid", error);
2459 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002460 std::string protocol = fields[2];
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002461
2462 // <fmt>
deadbeef67cf2c12016-04-13 10:07:16 -07002463 std::vector<int> payload_types;
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00002464 if (IsRtp(protocol)) {
Yves Gerey665174f2018-06-19 15:03:05 +02002465 for (size_t j = 3; j < fields.size(); ++j) {
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00002466 // TODO(wu): Remove when below bug is fixed.
2467 // https://bugzilla.mozilla.org/show_bug.cgi?id=996329
pbosbb36fdf2015-07-09 07:48:14 -07002468 if (fields[j].empty() && j == fields.size() - 1) {
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00002469 continue;
2470 }
wu@webrtc.org36eda7c2014-04-15 20:37:30 +00002471
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00002472 int pl = 0;
pkasting@chromium.orge9facf82015-02-17 20:36:28 +00002473 if (!GetPayloadTypeFromString(line, fields[j], &pl, error)) {
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00002474 return false;
2475 }
deadbeef67cf2c12016-04-13 10:07:16 -07002476 payload_types.push_back(pl);
wu@webrtc.org5e760e72014-04-02 23:19:09 +00002477 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002478 }
2479
2480 // Make a temporary TransportDescription based on |session_td|.
2481 // Some of this gets overwritten by ParseContent.
deadbeef46eed762016-01-28 13:24:37 -08002482 TransportDescription transport(
2483 session_td.transport_options, session_td.ice_ufrag, session_td.ice_pwd,
2484 session_td.ice_mode, session_td.connection_role,
2485 session_td.identity_fingerprint.get());
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002486
kwibergd1fe2812016-04-27 06:47:29 -07002487 std::unique_ptr<MediaContentDescription> content;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002488 std::string content_name;
deadbeef25ed4352016-12-12 18:37:36 -08002489 bool bundle_only = false;
Steve Antone831b8c2018-02-01 12:22:16 -08002490 int section_msid_signaling = 0;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002491 if (HasAttribute(line, kMediaTypeVideo)) {
2492 content.reset(ParseContentDescription<VideoContentDescription>(
deadbeef67cf2c12016-04-13 10:07:16 -07002493 message, cricket::MEDIA_TYPE_VIDEO, mline_index, protocol,
Steve Antone831b8c2018-02-01 12:22:16 -08002494 payload_types, pos, &content_name, &bundle_only,
2495 &section_msid_signaling, &transport, candidates, error));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002496 } else if (HasAttribute(line, kMediaTypeAudio)) {
2497 content.reset(ParseContentDescription<AudioContentDescription>(
deadbeef67cf2c12016-04-13 10:07:16 -07002498 message, cricket::MEDIA_TYPE_AUDIO, mline_index, protocol,
Steve Antone831b8c2018-02-01 12:22:16 -08002499 payload_types, pos, &content_name, &bundle_only,
2500 &section_msid_signaling, &transport, candidates, error));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002501 } else if (HasAttribute(line, kMediaTypeData)) {
jiayl@webrtc.org0e527722014-09-08 21:43:43 +00002502 DataContentDescription* data_desc =
wu@webrtc.org78187522013-10-07 23:32:02 +00002503 ParseContentDescription<DataContentDescription>(
deadbeef67cf2c12016-04-13 10:07:16 -07002504 message, cricket::MEDIA_TYPE_DATA, mline_index, protocol,
Steve Antone831b8c2018-02-01 12:22:16 -08002505 payload_types, pos, &content_name, &bundle_only,
2506 &section_msid_signaling, &transport, candidates, error);
jiayl@webrtc.org0e527722014-09-08 21:43:43 +00002507 content.reset(data_desc);
wu@webrtc.org78187522013-10-07 23:32:02 +00002508
zstein4b2e0822017-02-17 19:48:38 -08002509 if (data_desc && IsDtlsSctp(protocol)) {
2510 int p;
2511 if (rtc::FromString(fields[3], &p)) {
2512 if (!AddSctpDataCodec(data_desc, p)) {
2513 return false;
2514 }
2515 } else if (fields[3] == kDefaultSctpmapProtocol) {
2516 data_desc->set_use_sctpmap(false);
2517 }
wu@webrtc.org78187522013-10-07 23:32:02 +00002518 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002519 } else {
Mirko Bonadei675513b2017-11-09 11:09:25 +01002520 RTC_LOG(LS_WARNING) << "Unsupported media type: " << line;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002521 continue;
2522 }
2523 if (!content.get()) {
2524 // ParseContentDescription returns NULL if failed.
2525 return false;
2526 }
2527
Steve Antone831b8c2018-02-01 12:22:16 -08002528 msid_signaling |= section_msid_signaling;
2529
deadbeef25ed4352016-12-12 18:37:36 -08002530 bool content_rejected = false;
deadbeef12771a12017-01-03 13:53:47 -08002531 // A port of 0 is not interpreted as a rejected m= section when it's
2532 // used along with a=bundle-only.
deadbeef25ed4352016-12-12 18:37:36 -08002533 if (bundle_only) {
deadbeef25ed4352016-12-12 18:37:36 -08002534 if (!port_rejected) {
deadbeef12771a12017-01-03 13:53:47 -08002535 // Usage of bundle-only with a nonzero port is unspecified. So just
2536 // ignore bundle-only if we see this.
2537 bundle_only = false;
Mirko Bonadei675513b2017-11-09 11:09:25 +01002538 RTC_LOG(LS_WARNING)
deadbeef12771a12017-01-03 13:53:47 -08002539 << "a=bundle-only attribute observed with a nonzero "
Jonas Olsson45cc8902018-02-13 10:37:07 +01002540 "port; this usage is unspecified so the attribute is being "
2541 "ignored.";
deadbeef25ed4352016-12-12 18:37:36 -08002542 }
2543 } else {
2544 // If not using bundle-only, interpret port 0 in the normal way; the m=
2545 // section is being rejected.
2546 content_rejected = port_rejected;
2547 }
2548
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00002549 if (IsRtp(protocol)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002550 // Set the extmap.
2551 if (!session_extmaps.empty() &&
2552 !content->rtp_header_extensions().empty()) {
2553 return ParseFailed("",
2554 "The a=extmap MUST be either all session level or "
2555 "all media level.",
2556 error);
2557 }
2558 for (size_t i = 0; i < session_extmaps.size(); ++i) {
2559 content->AddRtpHeaderExtension(session_extmaps[i]);
2560 }
2561 }
2562 content->set_protocol(protocol);
zhihuang38989e52017-03-21 11:04:53 -07002563
2564 // Use the session level connection address if the media level addresses are
2565 // not specified.
2566 rtc::SocketAddress address;
2567 address = content->connection_address().IsNil()
2568 ? session_connection_addr
2569 : content->connection_address();
2570 address.SetPort(port);
2571 content->set_connection_address(address);
2572
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002573 desc->AddContent(content_name,
Steve Anton5adfafd2017-12-20 16:34:00 -08002574 IsDtlsSctp(protocol) ? MediaProtocolType::kSctp
2575 : MediaProtocolType::kRtp,
deadbeef25ed4352016-12-12 18:37:36 -08002576 content_rejected, bundle_only, content.release());
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002577 // Create TransportInfo with the media level "ice-pwd" and "ice-ufrag".
2578 TransportInfo transport_info(content_name, transport);
2579
2580 if (!desc->AddTransportInfo(transport_info)) {
Jonas Olssonec9e4922018-09-05 09:53:49 +02002581 rtc::StringBuilder description;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002582 description << "Failed to AddTransportInfo with content name: "
2583 << content_name;
2584 return ParseFailed("", description.str(), error);
2585 }
2586 }
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00002587
Steve Antone831b8c2018-02-01 12:22:16 -08002588 desc->set_msid_signaling(msid_signaling);
2589
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00002590 size_t end_of_message = message.size();
2591 if (mline_index == -1 && *pos != end_of_message) {
2592 ParseFailed(message, *pos, "Expects m line.", error);
2593 return false;
2594 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002595 return true;
2596}
2597
2598bool VerifyCodec(const cricket::Codec& codec) {
2599 // Codec has not been populated correctly unless the name has been set. This
2600 // can happen if an SDP has an fmtp or rtcp-fb with a payload type but doesn't
2601 // have a corresponding "rtpmap" line.
htab39db842016-12-08 01:50:48 -08002602 return !codec.name.empty();
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002603}
2604
2605bool VerifyAudioCodecs(const AudioContentDescription* audio_desc) {
2606 const std::vector<cricket::AudioCodec>& codecs = audio_desc->codecs();
Steve Anton4daf66e2018-09-07 14:55:53 -07002607 return std::all_of(codecs.begin(), codecs.end(), &VerifyCodec);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002608}
2609
2610bool VerifyVideoCodecs(const VideoContentDescription* video_desc) {
2611 const std::vector<cricket::VideoCodec>& codecs = video_desc->codecs();
Steve Anton4daf66e2018-09-07 14:55:53 -07002612 return std::all_of(codecs.begin(), codecs.end(), &VerifyCodec);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002613}
2614
2615void AddParameters(const cricket::CodecParameterMap& parameters,
2616 cricket::Codec* codec) {
Steve Anton4daf66e2018-09-07 14:55:53 -07002617 for (const auto& entry : parameters) {
2618 const std::string& key = entry.first;
2619 const std::string& value = entry.second;
2620 codec->SetParam(key, value);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002621 }
2622}
2623
2624void AddFeedbackParameter(const cricket::FeedbackParam& feedback_param,
2625 cricket::Codec* codec) {
2626 codec->AddFeedbackParam(feedback_param);
2627}
2628
2629void AddFeedbackParameters(const cricket::FeedbackParams& feedback_params,
2630 cricket::Codec* codec) {
Steve Anton4daf66e2018-09-07 14:55:53 -07002631 for (const cricket::FeedbackParam& param : feedback_params.params()) {
2632 codec->AddFeedbackParam(param);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002633 }
2634}
2635
2636// Gets the current codec setting associated with |payload_type|. If there
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +00002637// is no Codec associated with that payload type it returns an empty codec
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002638// with that payload type.
2639template <class T>
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +00002640T GetCodecWithPayloadType(const std::vector<T>& codecs, int payload_type) {
magjedb05fa242016-11-11 04:00:16 -08002641 const T* codec = FindCodecById(codecs, payload_type);
2642 if (codec)
2643 return *codec;
2644 // Return empty codec with |payload_type|.
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +00002645 T ret_val;
magjedb05fa242016-11-11 04:00:16 -08002646 ret_val.id = payload_type;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002647 return ret_val;
2648}
2649
2650// Updates or creates a new codec entry in the audio description.
2651template <class T, class U>
2652void AddOrReplaceCodec(MediaContentDescription* content_desc, const U& codec) {
2653 T* desc = static_cast<T*>(content_desc);
2654 std::vector<U> codecs = desc->codecs();
2655 bool found = false;
Steve Anton4daf66e2018-09-07 14:55:53 -07002656 for (U& existing_codec : codecs) {
2657 if (codec.id == existing_codec.id) {
2658 // Overwrite existing codec with the new codec.
2659 existing_codec = codec;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002660 found = true;
2661 break;
2662 }
2663 }
2664 if (!found) {
2665 desc->AddCodec(codec);
2666 return;
2667 }
2668 desc->set_codecs(codecs);
2669}
2670
2671// Adds or updates existing codec corresponding to |payload_type| according
2672// to |parameters|.
2673template <class T, class U>
Yves Gerey665174f2018-06-19 15:03:05 +02002674void UpdateCodec(MediaContentDescription* content_desc,
2675 int payload_type,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002676 const cricket::CodecParameterMap& parameters) {
2677 // Codec might already have been populated (from rtpmap).
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +00002678 U new_codec = GetCodecWithPayloadType(static_cast<T*>(content_desc)->codecs(),
2679 payload_type);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002680 AddParameters(parameters, &new_codec);
2681 AddOrReplaceCodec<T, U>(content_desc, new_codec);
2682}
2683
2684// Adds or updates existing codec corresponding to |payload_type| according
2685// to |feedback_param|.
2686template <class T, class U>
Yves Gerey665174f2018-06-19 15:03:05 +02002687void UpdateCodec(MediaContentDescription* content_desc,
2688 int payload_type,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002689 const cricket::FeedbackParam& feedback_param) {
2690 // Codec might already have been populated (from rtpmap).
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +00002691 U new_codec = GetCodecWithPayloadType(static_cast<T*>(content_desc)->codecs(),
2692 payload_type);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002693 AddFeedbackParameter(feedback_param, &new_codec);
2694 AddOrReplaceCodec<T, U>(content_desc, new_codec);
2695}
2696
jlmiller@webrtc.orga744a282015-02-18 21:37:46 +00002697template <class T>
2698bool PopWildcardCodec(std::vector<T>* codecs, T* wildcard_codec) {
2699 for (auto iter = codecs->begin(); iter != codecs->end(); ++iter) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002700 if (iter->id == kWildcardPayloadType) {
2701 *wildcard_codec = *iter;
2702 codecs->erase(iter);
2703 return true;
2704 }
2705 }
2706 return false;
2707}
2708
Yves Gerey665174f2018-06-19 15:03:05 +02002709template <class T>
jlmiller@webrtc.orga744a282015-02-18 21:37:46 +00002710void UpdateFromWildcardCodecs(cricket::MediaContentDescriptionImpl<T>* desc) {
2711 auto codecs = desc->codecs();
2712 T wildcard_codec;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002713 if (!PopWildcardCodec(&codecs, &wildcard_codec)) {
2714 return;
2715 }
jlmiller@webrtc.orga744a282015-02-18 21:37:46 +00002716 for (auto& codec : codecs) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002717 AddFeedbackParameters(wildcard_codec.feedback_params, &codec);
2718 }
jlmiller@webrtc.orga744a282015-02-18 21:37:46 +00002719 desc->set_codecs(codecs);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002720}
2721
Yves Gerey665174f2018-06-19 15:03:05 +02002722void AddAudioAttribute(const std::string& name,
2723 const std::string& value,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002724 AudioContentDescription* audio_desc) {
2725 if (value.empty()) {
2726 return;
2727 }
2728 std::vector<cricket::AudioCodec> codecs = audio_desc->codecs();
Steve Anton4daf66e2018-09-07 14:55:53 -07002729 for (cricket::AudioCodec& codec : codecs) {
2730 codec.params[name] = value;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002731 }
2732 audio_desc->set_codecs(codecs);
2733}
2734
2735bool ParseContent(const std::string& message,
Patrik Höglundb6b29e02018-06-21 16:58:01 +02002736 const cricket::MediaType media_type,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002737 int mline_index,
2738 const std::string& protocol,
deadbeef67cf2c12016-04-13 10:07:16 -07002739 const std::vector<int>& payload_types,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002740 size_t* pos,
2741 std::string* content_name,
deadbeef25ed4352016-12-12 18:37:36 -08002742 bool* bundle_only,
Steve Antone831b8c2018-02-01 12:22:16 -08002743 int* msid_signaling,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002744 MediaContentDescription* media_desc,
2745 TransportDescription* transport,
2746 std::vector<JsepIceCandidate*>* candidates,
2747 SdpParseError* error) {
nisseede5da42017-01-12 05:15:36 -08002748 RTC_DCHECK(media_desc != NULL);
2749 RTC_DCHECK(content_name != NULL);
2750 RTC_DCHECK(transport != NULL);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002751
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +00002752 if (media_type == cricket::MEDIA_TYPE_AUDIO) {
Steve Antonb1c1de12017-12-21 15:14:30 -08002753 MaybeCreateStaticPayloadAudioCodecs(payload_types, media_desc->as_audio());
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +00002754 }
2755
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002756 // The media level "ice-ufrag" and "ice-pwd".
2757 // The candidates before update the media level "ice-pwd" and "ice-ufrag".
2758 Candidates candidates_orig;
2759 std::string line;
2760 std::string mline_id;
2761 // Tracks created out of the ssrc attributes.
2762 StreamParamsVec tracks;
2763 SsrcInfoVec ssrc_infos;
2764 SsrcGroupVec ssrc_groups;
2765 std::string maxptime_as_string;
2766 std::string ptime_as_string;
Seth Hampson5b4f0752018-04-02 16:31:36 -07002767 std::vector<std::string> stream_ids;
deadbeef9d3584c2016-02-16 17:54:10 -08002768 std::string track_id;
Amit Hilbucha2012042018-12-03 11:35:05 -08002769 SimulcastDescription simulcast;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002770
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002771 // Loop until the next m line
2772 while (!IsLineType(message, kLineTypeMedia, *pos)) {
2773 if (!GetLine(message, pos, &line)) {
2774 if (*pos >= message.size()) {
2775 break; // Done parsing
2776 } else {
sergeyu@chromium.org5bc25c42013-12-05 00:24:06 +00002777 return ParseFailed(message, *pos, "Invalid SDP line.", error);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002778 }
2779 }
2780
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002781 // RFC 4566
2782 // b=* (zero or more bandwidth information lines)
2783 if (IsLineType(line, kLineTypeSessionBandwidth)) {
2784 std::string bandwidth;
2785 if (HasAttribute(line, kApplicationSpecificMaximum)) {
2786 if (!GetValue(line, kApplicationSpecificMaximum, &bandwidth, error)) {
2787 return false;
2788 } else {
wu@webrtc.org5e760e72014-04-02 23:19:09 +00002789 int b = 0;
2790 if (!GetValueFromString(line, bandwidth, &b, error)) {
2791 return false;
2792 }
deadbeef3e8016e2017-08-03 17:49:30 -07002793 // TODO(deadbeef): Historically, applications may be setting a value
2794 // of -1 to mean "unset any previously set bandwidth limit", even
2795 // though ommitting the "b=AS" entirely will do just that. Once we've
2796 // transitioned applications to doing the right thing, it would be
2797 // better to treat this as a hard error instead of just ignoring it.
2798 if (b == -1) {
Mirko Bonadei675513b2017-11-09 11:09:25 +01002799 RTC_LOG(LS_WARNING)
2800 << "Ignoring \"b=AS:-1\"; will be treated as \"no "
2801 "bandwidth limit\".";
deadbeef3e8016e2017-08-03 17:49:30 -07002802 continue;
2803 }
deadbeefbc88c6b2017-08-02 11:26:34 -07002804 if (b < 0) {
2805 return ParseFailed(line, "b=AS value can't be negative.", error);
2806 }
Peter Thatcherc0c3a862015-06-24 15:31:25 -07002807 // We should never use more than the default bandwidth for RTP-based
2808 // data channels. Don't allow SDP to set the bandwidth, because
2809 // that would give JS the opportunity to "break the Internet".
2810 // See: https://code.google.com/p/chromium/issues/detail?id=280726
2811 if (media_type == cricket::MEDIA_TYPE_DATA && IsRtp(protocol) &&
2812 b > cricket::kDataMaxBandwidth / 1000) {
Jonas Olssonec9e4922018-09-05 09:53:49 +02002813 rtc::StringBuilder description;
Peter Thatcherc0c3a862015-06-24 15:31:25 -07002814 description << "RTP-based data channels may not send more than "
2815 << cricket::kDataMaxBandwidth / 1000 << "kbps.";
2816 return ParseFailed(line, description.str(), error);
2817 }
deadbeefb2362572016-12-13 16:37:06 -08002818 // Prevent integer overflow.
2819 b = std::min(b, INT_MAX / 1000);
wu@webrtc.org5e760e72014-04-02 23:19:09 +00002820 media_desc->set_bandwidth(b * 1000);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002821 }
2822 }
2823 continue;
2824 }
2825
zhihuang38989e52017-03-21 11:04:53 -07002826 // Parse the media level connection data.
2827 if (IsLineType(line, kLineTypeConnection)) {
2828 rtc::SocketAddress addr;
2829 if (!ParseConnectionData(line, &addr, error)) {
2830 return false;
2831 }
2832 media_desc->set_connection_address(addr);
2833 continue;
2834 }
2835
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002836 if (!IsLineType(line, kLineTypeAttributes)) {
Steve Anton36b29d12017-10-30 09:57:42 -07002837 // TODO(deadbeef): Handle other lines if needed.
Mirko Bonadei675513b2017-11-09 11:09:25 +01002838 RTC_LOG(LS_INFO) << "Ignored line: " << line;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002839 continue;
2840 }
2841
2842 // Handle attributes common to SCTP and RTP.
2843 if (HasAttribute(line, kAttributeMid)) {
2844 // RFC 3388
2845 // mid-attribute = "a=mid:" identification-tag
2846 // identification-tag = token
2847 // Use the mid identification-tag as the content name.
2848 if (!GetValue(line, kAttributeMid, &mline_id, error)) {
2849 return false;
2850 }
2851 *content_name = mline_id;
deadbeef25ed4352016-12-12 18:37:36 -08002852 } else if (HasAttribute(line, kAttributeBundleOnly)) {
2853 *bundle_only = true;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002854 } else if (HasAttribute(line, kAttributeCandidate)) {
2855 Candidate candidate;
2856 if (!ParseCandidate(line, &candidate, error, false)) {
2857 return false;
2858 }
deadbeef7bcdb692017-01-20 12:43:58 -08002859 // ParseCandidate will parse non-standard ufrag and password attributes,
2860 // since it's used for candidate trickling, but we only want to process
2861 // the "a=ice-ufrag"/"a=ice-pwd" values in a session description, so
2862 // strip them off at this point.
2863 candidate.set_username(std::string());
2864 candidate.set_password(std::string());
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002865 candidates_orig.push_back(candidate);
2866 } else if (HasAttribute(line, kAttributeIceUfrag)) {
2867 if (!GetValue(line, kAttributeIceUfrag, &transport->ice_ufrag, error)) {
2868 return false;
2869 }
2870 } else if (HasAttribute(line, kAttributeIcePwd)) {
2871 if (!GetValue(line, kAttributeIcePwd, &transport->ice_pwd, error)) {
2872 return false;
2873 }
2874 } else if (HasAttribute(line, kAttributeIceOption)) {
2875 if (!ParseIceOptions(line, &transport->transport_options, error)) {
2876 return false;
2877 }
2878 } else if (HasAttribute(line, kAttributeFmtp)) {
2879 if (!ParseFmtpAttributes(line, media_type, media_desc, error)) {
2880 return false;
2881 }
2882 } else if (HasAttribute(line, kAttributeFingerprint)) {
Steve Anton4905edb2018-10-15 19:27:44 -07002883 std::unique_ptr<rtc::SSLFingerprint> fingerprint;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002884 if (!ParseFingerprintAttribute(line, &fingerprint, error)) {
2885 return false;
2886 }
Steve Anton4905edb2018-10-15 19:27:44 -07002887 transport->identity_fingerprint = std::move(fingerprint);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00002888 } else if (HasAttribute(line, kAttributeSetup)) {
2889 if (!ParseDtlsSetup(line, &(transport->connection_role), error)) {
2890 return false;
2891 }
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00002892 } else if (IsDtlsSctp(protocol) && HasAttribute(line, kAttributeSctpPort)) {
deadbeef7e146cb2016-09-28 10:04:34 -07002893 if (media_type != cricket::MEDIA_TYPE_DATA) {
2894 return ParseFailed(
2895 line, "sctp-port attribute found in non-data media description.",
2896 error);
2897 }
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00002898 int sctp_port;
2899 if (!ParseSctpPort(line, &sctp_port, error)) {
2900 return false;
2901 }
Steve Antonb1c1de12017-12-21 15:14:30 -08002902 if (!AddSctpDataCodec(media_desc->as_data(), sctp_port)) {
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00002903 return false;
2904 }
pthatcher@webrtc.org3341b402015-02-13 21:14:22 +00002905 } else if (IsRtp(protocol)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002906 //
2907 // RTP specific attrubtes
2908 //
2909 if (HasAttribute(line, kAttributeRtcpMux)) {
2910 media_desc->set_rtcp_mux(true);
deadbeef13871492015-12-09 12:37:51 -08002911 } else if (HasAttribute(line, kAttributeRtcpReducedSize)) {
2912 media_desc->set_rtcp_reduced_size(true);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002913 } else if (HasAttribute(line, kAttributeSsrcGroup)) {
2914 if (!ParseSsrcGroupAttribute(line, &ssrc_groups, error)) {
2915 return false;
2916 }
2917 } else if (HasAttribute(line, kAttributeSsrc)) {
Steve Antone831b8c2018-02-01 12:22:16 -08002918 if (!ParseSsrcAttribute(line, &ssrc_infos, msid_signaling, error)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002919 return false;
2920 }
2921 } else if (HasAttribute(line, kAttributeCrypto)) {
2922 if (!ParseCryptoAttribute(line, media_desc, error)) {
2923 return false;
2924 }
2925 } else if (HasAttribute(line, kAttributeRtpmap)) {
deadbeef67cf2c12016-04-13 10:07:16 -07002926 if (!ParseRtpmapAttribute(line, media_type, payload_types, media_desc,
2927 error)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002928 return false;
2929 }
2930 } else if (HasAttribute(line, kCodecParamMaxPTime)) {
2931 if (!GetValue(line, kCodecParamMaxPTime, &maxptime_as_string, error)) {
2932 return false;
2933 }
2934 } else if (HasAttribute(line, kAttributeRtcpFb)) {
2935 if (!ParseRtcpFbAttribute(line, media_type, media_desc, error)) {
2936 return false;
2937 }
2938 } else if (HasAttribute(line, kCodecParamPTime)) {
2939 if (!GetValue(line, kCodecParamPTime, &ptime_as_string, error)) {
2940 return false;
2941 }
2942 } else if (HasAttribute(line, kAttributeSendOnly)) {
Steve Anton4e70a722017-11-28 14:57:10 -08002943 media_desc->set_direction(RtpTransceiverDirection::kSendOnly);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002944 } else if (HasAttribute(line, kAttributeRecvOnly)) {
Steve Anton4e70a722017-11-28 14:57:10 -08002945 media_desc->set_direction(RtpTransceiverDirection::kRecvOnly);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002946 } else if (HasAttribute(line, kAttributeInactive)) {
Steve Anton4e70a722017-11-28 14:57:10 -08002947 media_desc->set_direction(RtpTransceiverDirection::kInactive);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002948 } else if (HasAttribute(line, kAttributeSendRecv)) {
Steve Anton4e70a722017-11-28 14:57:10 -08002949 media_desc->set_direction(RtpTransceiverDirection::kSendRecv);
Johannes Kron0854eb62018-10-10 22:33:20 +02002950 } else if (HasAttribute(line, kAttributeExtmapAllowMixed)) {
Johannes Kron9581bc42018-10-23 10:17:39 +02002951 media_desc->set_extmap_allow_mixed_enum(
Johannes Kron0854eb62018-10-10 22:33:20 +02002952 MediaContentDescription::kMedia);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002953 } else if (HasAttribute(line, kAttributeExtmap)) {
isheriff6f8d6862016-05-26 11:24:55 -07002954 RtpExtension extmap;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002955 if (!ParseExtmap(line, &extmap, error)) {
2956 return false;
2957 }
2958 media_desc->AddRtpHeaderExtension(extmap);
2959 } else if (HasAttribute(line, kAttributeXGoogleFlag)) {
2960 // Experimental attribute. Conference mode activates more aggressive
2961 // AEC and NS settings.
Steve Anton36b29d12017-10-30 09:57:42 -07002962 // TODO(deadbeef): expose API to set these directly.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002963 std::string flag_value;
2964 if (!GetValue(line, kAttributeXGoogleFlag, &flag_value, error)) {
2965 return false;
2966 }
2967 if (flag_value.compare(kValueConference) == 0)
2968 media_desc->set_conference_mode(true);
deadbeef9d3584c2016-02-16 17:54:10 -08002969 } else if (HasAttribute(line, kAttributeMsid)) {
Seth Hampson5b4f0752018-04-02 16:31:36 -07002970 if (!ParseMsidAttribute(line, &stream_ids, &track_id, error)) {
deadbeef9d3584c2016-02-16 17:54:10 -08002971 return false;
2972 }
Steve Antone831b8c2018-02-01 12:22:16 -08002973 *msid_signaling |= cricket::kMsidSignalingMediaSection;
Amit Hilbucha2012042018-12-03 11:35:05 -08002974 } else if (HasAttribute(line, kAttributeSimulcast)) {
2975 const size_t kSimulcastPrefixLength =
2976 kLinePrefixLength + arraysize(kAttributeSimulcast);
2977 if (line.size() <= kSimulcastPrefixLength) {
2978 return ParseFailed(line, "Simulcast attribute is empty.", error);
2979 }
2980
2981 if (!simulcast.empty()) {
2982 return ParseFailed(line, "Multiple Simulcast attributes specified.",
2983 error);
2984 }
2985
2986 SdpSerializer deserializer;
2987 RTCErrorOr<SimulcastDescription> error_or_simulcast =
2988 deserializer.DeserializeSimulcastDescription(
2989 line.substr(kSimulcastPrefixLength));
2990 if (!error_or_simulcast.ok()) {
2991 return ParseFailed(line,
2992 std::string("Malformed simulcast line: ") +
2993 error_or_simulcast.error().message(),
2994 error);
2995 }
2996
2997 simulcast = error_or_simulcast.value();
2998 } else {
2999 // Unrecognized attribute in RTP protocol.
3000 RTC_LOG(LS_INFO) << "Ignored line: " << line;
3001 continue;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003002 }
3003 } else {
3004 // Only parse lines that we are interested of.
Mirko Bonadei675513b2017-11-09 11:09:25 +01003005 RTC_LOG(LS_INFO) << "Ignored line: " << line;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003006 continue;
3007 }
3008 }
3009
3010 // Create tracks from the |ssrc_infos|.
Taylor Brandstetter5de6b752016-03-09 17:02:30 -08003011 // If the stream_id/track_id for all SSRCS are identical, one StreamParams
3012 // will be created in CreateTracksFromSsrcInfos, containing all the SSRCs from
3013 // the m= section.
Seth Hampson5897a6e2018-04-03 11:16:33 -07003014 if (!ssrc_infos.empty()) {
3015 CreateTracksFromSsrcInfos(ssrc_infos, stream_ids, track_id, &tracks,
3016 *msid_signaling);
3017 } else if (media_type != cricket::MEDIA_TYPE_DATA &&
3018 (*msid_signaling & cricket::kMsidSignalingMediaSection)) {
3019 // If the stream_ids/track_id was signaled but SSRCs were unsignaled we
3020 // still create a track. This isn't done for data media types because
3021 // StreamParams aren't used for SCTP streams, and RTP data channels don't
3022 // support unsignaled SSRCs.
3023 CreateTrackWithNoSsrcs(stream_ids, track_id, &tracks);
3024 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003025
3026 // Add the ssrc group to the track.
Steve Anton4daf66e2018-09-07 14:55:53 -07003027 for (const SsrcGroup& ssrc_group : ssrc_groups) {
3028 if (ssrc_group.ssrcs.empty()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003029 continue;
3030 }
Steve Anton4daf66e2018-09-07 14:55:53 -07003031 uint32_t ssrc = ssrc_group.ssrcs.front();
3032 for (StreamParams& track : tracks) {
3033 if (track.has_ssrc(ssrc)) {
3034 track.ssrc_groups.push_back(ssrc_group);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003035 }
3036 }
3037 }
3038
3039 // Add the new tracks to the |media_desc|.
deadbeef9d3584c2016-02-16 17:54:10 -08003040 for (StreamParams& track : tracks) {
3041 media_desc->AddStream(track);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003042 }
3043
3044 if (media_type == cricket::MEDIA_TYPE_AUDIO) {
Steve Antonb1c1de12017-12-21 15:14:30 -08003045 AudioContentDescription* audio_desc = media_desc->as_audio();
jlmiller@webrtc.orga744a282015-02-18 21:37:46 +00003046 UpdateFromWildcardCodecs(audio_desc);
3047
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003048 // Verify audio codec ensures that no audio codec has been populated with
3049 // only fmtp.
3050 if (!VerifyAudioCodecs(audio_desc)) {
3051 return ParseFailed("Failed to parse audio codecs correctly.", error);
3052 }
3053 AddAudioAttribute(kCodecParamMaxPTime, maxptime_as_string, audio_desc);
3054 AddAudioAttribute(kCodecParamPTime, ptime_as_string, audio_desc);
3055 }
3056
3057 if (media_type == cricket::MEDIA_TYPE_VIDEO) {
Steve Antonb1c1de12017-12-21 15:14:30 -08003058 VideoContentDescription* video_desc = media_desc->as_video();
jlmiller@webrtc.orga744a282015-02-18 21:37:46 +00003059 UpdateFromWildcardCodecs(video_desc);
3060 // Verify video codec ensures that no video codec has been populated with
3061 // only rtcp-fb.
3062 if (!VerifyVideoCodecs(video_desc)) {
3063 return ParseFailed("Failed to parse video codecs correctly.", error);
3064 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003065 }
3066
3067 // RFC 5245
3068 // Update the candidates with the media level "ice-pwd" and "ice-ufrag".
Steve Anton4daf66e2018-09-07 14:55:53 -07003069 for (Candidate& candidate : candidates_orig) {
3070 RTC_DCHECK(candidate.username().empty() ||
3071 candidate.username() == transport->ice_ufrag);
3072 candidate.set_username(transport->ice_ufrag);
3073 RTC_DCHECK(candidate.password().empty());
3074 candidate.set_password(transport->ice_pwd);
3075 candidates->push_back(
3076 new JsepIceCandidate(mline_id, mline_index, candidate));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003077 }
Amit Hilbucha2012042018-12-03 11:35:05 -08003078
3079 if (!simulcast.empty()) {
3080 // TODO(amithi, bugs.webrtc.org/10073):
3081 // Verify that the rids in simulcast match rids in sdp.
3082 media_desc->set_simulcast_description(simulcast);
3083 }
3084
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003085 return true;
3086}
3087
Steve Antone831b8c2018-02-01 12:22:16 -08003088bool ParseSsrcAttribute(const std::string& line,
3089 SsrcInfoVec* ssrc_infos,
3090 int* msid_signaling,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003091 SdpParseError* error) {
nisseede5da42017-01-12 05:15:36 -08003092 RTC_DCHECK(ssrc_infos != NULL);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003093 // RFC 5576
3094 // a=ssrc:<ssrc-id> <attribute>
3095 // a=ssrc:<ssrc-id> <attribute>:<value>
3096 std::string field1, field2;
Jonas Olssonec9e4922018-09-05 09:53:49 +02003097 if (!rtc::tokenize_first(line.substr(kLinePrefixLength),
3098 kSdpDelimiterSpaceChar, &field1, &field2)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003099 const size_t expected_fields = 2;
3100 return ParseFailedExpectFieldNum(line, expected_fields, error);
3101 }
3102
3103 // ssrc:<ssrc-id>
3104 std::string ssrc_id_s;
3105 if (!GetValue(field1, kAttributeSsrc, &ssrc_id_s, error)) {
3106 return false;
3107 }
Peter Boström0c4e06b2015-10-07 12:23:21 +02003108 uint32_t ssrc_id = 0;
wu@webrtc.org5e760e72014-04-02 23:19:09 +00003109 if (!GetValueFromString(line, ssrc_id_s, &ssrc_id, error)) {
3110 return false;
3111 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003112
3113 std::string attribute;
3114 std::string value;
Jonas Olssonec9e4922018-09-05 09:53:49 +02003115 if (!rtc::tokenize_first(field2, kSdpDelimiterColonChar, &attribute,
3116 &value)) {
3117 rtc::StringBuilder description;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003118 description << "Failed to get the ssrc attribute value from " << field2
3119 << ". Expected format <attribute>:<value>.";
3120 return ParseFailed(line, description.str(), error);
3121 }
3122
3123 // Check if there's already an item for this |ssrc_id|. Create a new one if
3124 // there isn't.
Steve Anton4daf66e2018-09-07 14:55:53 -07003125 auto ssrc_info_it = std::find_if(ssrc_infos->begin(), ssrc_infos->end(),
3126 [ssrc_id](const SsrcInfo& ssrc_info) {
3127 return ssrc_info.ssrc_id == ssrc_id;
3128 });
3129 if (ssrc_info_it == ssrc_infos->end()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003130 SsrcInfo info;
3131 info.ssrc_id = ssrc_id;
3132 ssrc_infos->push_back(info);
Steve Anton4daf66e2018-09-07 14:55:53 -07003133 ssrc_info_it = ssrc_infos->end() - 1;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003134 }
Steve Anton4daf66e2018-09-07 14:55:53 -07003135 SsrcInfo& ssrc_info = *ssrc_info_it;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003136
3137 // Store the info to the |ssrc_info|.
3138 if (attribute == kSsrcAttributeCname) {
3139 // RFC 5576
3140 // cname:<value>
Steve Anton4daf66e2018-09-07 14:55:53 -07003141 ssrc_info.cname = value;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003142 } else if (attribute == kSsrcAttributeMsid) {
3143 // draft-alvestrand-mmusic-msid-00
Seth Hampson5b4f0752018-04-02 16:31:36 -07003144 // msid:identifier [appdata]
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003145 std::vector<std::string> fields;
Jonas Olssonec9e4922018-09-05 09:53:49 +02003146 rtc::split(value, kSdpDelimiterSpaceChar, &fields);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003147 if (fields.size() < 1 || fields.size() > 2) {
Yves Gerey665174f2018-06-19 15:03:05 +02003148 return ParseFailed(
3149 line, "Expected format \"msid:<identifier>[ <appdata>]\".", error);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003150 }
Steve Anton4daf66e2018-09-07 14:55:53 -07003151 ssrc_info.stream_id = fields[0];
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003152 if (fields.size() == 2) {
Steve Anton4daf66e2018-09-07 14:55:53 -07003153 ssrc_info.track_id = fields[1];
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003154 }
Steve Antone831b8c2018-02-01 12:22:16 -08003155 *msid_signaling |= cricket::kMsidSignalingSsrcAttribute;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003156 } else if (attribute == kSsrcAttributeMslabel) {
3157 // draft-alvestrand-rtcweb-mid-01
3158 // mslabel:<value>
Steve Anton4daf66e2018-09-07 14:55:53 -07003159 ssrc_info.mslabel = value;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003160 } else if (attribute == kSSrcAttributeLabel) {
3161 // The label isn't defined.
3162 // label:<value>
Steve Anton4daf66e2018-09-07 14:55:53 -07003163 ssrc_info.label = value;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003164 }
3165 return true;
3166}
3167
3168bool ParseSsrcGroupAttribute(const std::string& line,
3169 SsrcGroupVec* ssrc_groups,
3170 SdpParseError* error) {
nisseede5da42017-01-12 05:15:36 -08003171 RTC_DCHECK(ssrc_groups != NULL);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003172 // RFC 5576
3173 // a=ssrc-group:<semantics> <ssrc-id> ...
3174 std::vector<std::string> fields;
Jonas Olssonec9e4922018-09-05 09:53:49 +02003175 rtc::split(line.substr(kLinePrefixLength), kSdpDelimiterSpaceChar, &fields);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003176 const size_t expected_min_fields = 2;
3177 if (fields.size() < expected_min_fields) {
3178 return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
3179 }
3180 std::string semantics;
3181 if (!GetValue(fields[0], kAttributeSsrcGroup, &semantics, error)) {
3182 return false;
3183 }
Peter Boström0c4e06b2015-10-07 12:23:21 +02003184 std::vector<uint32_t> ssrcs;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003185 for (size_t i = 1; i < fields.size(); ++i) {
Peter Boström0c4e06b2015-10-07 12:23:21 +02003186 uint32_t ssrc = 0;
wu@webrtc.org5e760e72014-04-02 23:19:09 +00003187 if (!GetValueFromString(line, fields[i], &ssrc, error)) {
3188 return false;
3189 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003190 ssrcs.push_back(ssrc);
3191 }
3192 ssrc_groups->push_back(SsrcGroup(semantics, ssrcs));
3193 return true;
3194}
3195
3196bool ParseCryptoAttribute(const std::string& line,
3197 MediaContentDescription* media_desc,
3198 SdpParseError* error) {
3199 std::vector<std::string> fields;
Jonas Olssonec9e4922018-09-05 09:53:49 +02003200 rtc::split(line.substr(kLinePrefixLength), kSdpDelimiterSpaceChar, &fields);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003201 // RFC 4568
3202 // a=crypto:<tag> <crypto-suite> <key-params> [<session-params>]
3203 const size_t expected_min_fields = 3;
3204 if (fields.size() < expected_min_fields) {
3205 return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
3206 }
3207 std::string tag_value;
3208 if (!GetValue(fields[0], kAttributeCrypto, &tag_value, error)) {
3209 return false;
3210 }
wu@webrtc.org5e760e72014-04-02 23:19:09 +00003211 int tag = 0;
3212 if (!GetValueFromString(line, tag_value, &tag, error)) {
3213 return false;
3214 }
jbauch083b73f2015-07-16 02:46:32 -07003215 const std::string& crypto_suite = fields[1];
3216 const std::string& key_params = fields[2];
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003217 std::string session_params;
3218 if (fields.size() > 3) {
3219 session_params = fields[3];
3220 }
Yves Gerey665174f2018-06-19 15:03:05 +02003221 media_desc->AddCrypto(
3222 CryptoParams(tag, crypto_suite, key_params, session_params));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003223 return true;
3224}
3225
3226// Updates or creates a new codec entry in the audio description with according
deadbeef67cf2c12016-04-13 10:07:16 -07003227// to |name|, |clockrate|, |bitrate|, and |channels|.
3228void UpdateCodec(int payload_type,
3229 const std::string& name,
3230 int clockrate,
3231 int bitrate,
3232 size_t channels,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003233 AudioContentDescription* audio_desc) {
3234 // Codec may already be populated with (only) optional parameters
3235 // (from an fmtp).
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +00003236 cricket::AudioCodec codec =
3237 GetCodecWithPayloadType(audio_desc->codecs(), payload_type);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003238 codec.name = name;
3239 codec.clockrate = clockrate;
3240 codec.bitrate = bitrate;
3241 codec.channels = channels;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003242 AddOrReplaceCodec<AudioContentDescription, cricket::AudioCodec>(audio_desc,
3243 codec);
3244}
3245
3246// Updates or creates a new codec entry in the video description according to
deadbeef67cf2c12016-04-13 10:07:16 -07003247// |name|, |width|, |height|, and |framerate|.
3248void UpdateCodec(int payload_type,
3249 const std::string& name,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003250 VideoContentDescription* video_desc) {
3251 // Codec may already be populated with (only) optional parameters
3252 // (from an fmtp).
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +00003253 cricket::VideoCodec codec =
3254 GetCodecWithPayloadType(video_desc->codecs(), payload_type);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003255 codec.name = name;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003256 AddOrReplaceCodec<VideoContentDescription, cricket::VideoCodec>(video_desc,
3257 codec);
3258}
3259
3260bool ParseRtpmapAttribute(const std::string& line,
Patrik Höglundb6b29e02018-06-21 16:58:01 +02003261 const cricket::MediaType media_type,
deadbeef67cf2c12016-04-13 10:07:16 -07003262 const std::vector<int>& payload_types,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003263 MediaContentDescription* media_desc,
3264 SdpParseError* error) {
3265 std::vector<std::string> fields;
Jonas Olssonec9e4922018-09-05 09:53:49 +02003266 rtc::split(line.substr(kLinePrefixLength), kSdpDelimiterSpaceChar, &fields);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003267 // RFC 4566
3268 // a=rtpmap:<payload type> <encoding name>/<clock rate>[/<encodingparameters>]
3269 const size_t expected_min_fields = 2;
3270 if (fields.size() < expected_min_fields) {
3271 return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
3272 }
3273 std::string payload_type_value;
3274 if (!GetValue(fields[0], kAttributeRtpmap, &payload_type_value, error)) {
3275 return false;
3276 }
wu@webrtc.org5e760e72014-04-02 23:19:09 +00003277 int payload_type = 0;
pkasting@chromium.orge9facf82015-02-17 20:36:28 +00003278 if (!GetPayloadTypeFromString(line, payload_type_value, &payload_type,
3279 error)) {
wu@webrtc.org5e760e72014-04-02 23:19:09 +00003280 return false;
3281 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003282
deadbeef67cf2c12016-04-13 10:07:16 -07003283 if (std::find(payload_types.begin(), payload_types.end(), payload_type) ==
3284 payload_types.end()) {
Mirko Bonadei675513b2017-11-09 11:09:25 +01003285 RTC_LOG(LS_WARNING) << "Ignore rtpmap line that did not appear in the "
Jonas Olsson45cc8902018-02-13 10:37:07 +01003286 "<fmt> of the m-line: "
3287 << line;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003288 return true;
3289 }
jbauch083b73f2015-07-16 02:46:32 -07003290 const std::string& encoder = fields[1];
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003291 std::vector<std::string> codec_params;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00003292 rtc::split(encoder, '/', &codec_params);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003293 // <encoding name>/<clock rate>[/<encodingparameters>]
3294 // 2 mandatory fields
3295 if (codec_params.size() < 2 || codec_params.size() > 3) {
3296 return ParseFailed(line,
3297 "Expected format \"<encoding name>/<clock rate>"
3298 "[/<encodingparameters>]\".",
3299 error);
3300 }
jbauch083b73f2015-07-16 02:46:32 -07003301 const std::string& encoding_name = codec_params[0];
wu@webrtc.org5e760e72014-04-02 23:19:09 +00003302 int clock_rate = 0;
3303 if (!GetValueFromString(line, codec_params[1], &clock_rate, error)) {
3304 return false;
3305 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003306 if (media_type == cricket::MEDIA_TYPE_VIDEO) {
Steve Antonb1c1de12017-12-21 15:14:30 -08003307 VideoContentDescription* video_desc = media_desc->as_video();
Yves Gerey665174f2018-06-19 15:03:05 +02003308 UpdateCodec(payload_type, encoding_name, video_desc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003309 } else if (media_type == cricket::MEDIA_TYPE_AUDIO) {
3310 // RFC 4566
3311 // For audio streams, <encoding parameters> indicates the number
3312 // of audio channels. This parameter is OPTIONAL and may be
3313 // omitted if the number of channels is one, provided that no
3314 // additional parameters are needed.
Peter Kasting69558702016-01-12 16:26:35 -08003315 size_t channels = 1;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003316 if (codec_params.size() == 3) {
wu@webrtc.org5e760e72014-04-02 23:19:09 +00003317 if (!GetValueFromString(line, codec_params[2], &channels, error)) {
3318 return false;
3319 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003320 }
Steve Antonb1c1de12017-12-21 15:14:30 -08003321 AudioContentDescription* audio_desc = media_desc->as_audio();
ossue1405ad2017-01-23 08:55:48 -08003322 UpdateCodec(payload_type, encoding_name, clock_rate, 0, channels,
deadbeef67cf2c12016-04-13 10:07:16 -07003323 audio_desc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003324 } else if (media_type == cricket::MEDIA_TYPE_DATA) {
Steve Antonb1c1de12017-12-21 15:14:30 -08003325 DataContentDescription* data_desc = media_desc->as_data();
deadbeef67cf2c12016-04-13 10:07:16 -07003326 data_desc->AddCodec(cricket::DataCodec(payload_type, encoding_name));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003327 }
3328 return true;
3329}
3330
Yves Gerey665174f2018-06-19 15:03:05 +02003331bool ParseFmtpParam(const std::string& line,
3332 std::string* parameter,
3333 std::string* value,
3334 SdpParseError* error) {
Jonas Olssonec9e4922018-09-05 09:53:49 +02003335 if (!rtc::tokenize_first(line, kSdpDelimiterEqualChar, parameter, value)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003336 ParseFailed(line, "Unable to parse fmtp parameter. \'=\' missing.", error);
3337 return false;
3338 }
3339 // a=fmtp:<payload_type> <param1>=<value1>; <param2>=<value2>; ...
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003340 return true;
3341}
3342
Yves Gerey665174f2018-06-19 15:03:05 +02003343bool ParseFmtpAttributes(const std::string& line,
Patrik Höglundb6b29e02018-06-21 16:58:01 +02003344 const cricket::MediaType media_type,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003345 MediaContentDescription* media_desc,
3346 SdpParseError* error) {
3347 if (media_type != cricket::MEDIA_TYPE_AUDIO &&
3348 media_type != cricket::MEDIA_TYPE_VIDEO) {
3349 return true;
3350 }
Donald Curtis0e07f922015-05-15 09:21:23 -07003351
3352 std::string line_payload;
3353 std::string line_params;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003354
3355 // RFC 5576
3356 // a=fmtp:<format> <format specific parameters>
3357 // At least two fields, whereas the second one is any of the optional
3358 // parameters.
Jonas Olssonec9e4922018-09-05 09:53:49 +02003359 if (!rtc::tokenize_first(line.substr(kLinePrefixLength),
3360 kSdpDelimiterSpaceChar, &line_payload,
3361 &line_params)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003362 ParseFailedExpectMinFieldNum(line, 2, error);
3363 return false;
3364 }
3365
Donald Curtis0e07f922015-05-15 09:21:23 -07003366 // Parse out the payload information.
pkasting@chromium.orgd3245462015-02-23 21:28:22 +00003367 std::string payload_type_str;
Donald Curtis0e07f922015-05-15 09:21:23 -07003368 if (!GetValue(line_payload, kAttributeFmtp, &payload_type_str, error)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003369 return false;
3370 }
3371
Donald Curtis0e07f922015-05-15 09:21:23 -07003372 int payload_type = 0;
Donald Curtis144d0182015-05-15 13:14:24 -07003373 if (!GetPayloadTypeFromString(line_payload, payload_type_str, &payload_type,
3374 error)) {
Donald Curtis0e07f922015-05-15 09:21:23 -07003375 return false;
3376 }
3377
3378 // Parse out format specific parameters.
3379 std::vector<std::string> fields;
Jonas Olssonec9e4922018-09-05 09:53:49 +02003380 rtc::split(line_params, kSdpDelimiterSemicolonChar, &fields);
Donald Curtis0e07f922015-05-15 09:21:23 -07003381
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003382 cricket::CodecParameterMap codec_params;
Donald Curtis144d0182015-05-15 13:14:24 -07003383 for (auto& iter : fields) {
Donald Curtis0e07f922015-05-15 09:21:23 -07003384 if (iter.find(kSdpDelimiterEqual) == std::string::npos) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003385 // Only fmtps with equals are currently supported. Other fmtp types
3386 // should be ignored. Unknown fmtps do not constitute an error.
3387 continue;
3388 }
Donald Curtis0e07f922015-05-15 09:21:23 -07003389
3390 std::string name;
3391 std::string value;
3392 if (!ParseFmtpParam(rtc::string_trim(iter), &name, &value, error)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003393 return false;
3394 }
3395 codec_params[name] = value;
3396 }
3397
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003398 if (media_type == cricket::MEDIA_TYPE_AUDIO) {
3399 UpdateCodec<AudioContentDescription, cricket::AudioCodec>(
pkasting@chromium.orgd3245462015-02-23 21:28:22 +00003400 media_desc, payload_type, codec_params);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003401 } else if (media_type == cricket::MEDIA_TYPE_VIDEO) {
3402 UpdateCodec<VideoContentDescription, cricket::VideoCodec>(
pkasting@chromium.orgd3245462015-02-23 21:28:22 +00003403 media_desc, payload_type, codec_params);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003404 }
3405 return true;
3406}
3407
Yves Gerey665174f2018-06-19 15:03:05 +02003408bool ParseRtcpFbAttribute(const std::string& line,
Patrik Höglundb6b29e02018-06-21 16:58:01 +02003409 const cricket::MediaType media_type,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003410 MediaContentDescription* media_desc,
3411 SdpParseError* error) {
3412 if (media_type != cricket::MEDIA_TYPE_AUDIO &&
3413 media_type != cricket::MEDIA_TYPE_VIDEO) {
3414 return true;
3415 }
3416 std::vector<std::string> rtcp_fb_fields;
Jonas Olssonec9e4922018-09-05 09:53:49 +02003417 rtc::split(line.c_str(), kSdpDelimiterSpaceChar, &rtcp_fb_fields);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003418 if (rtcp_fb_fields.size() < 2) {
3419 return ParseFailedGetValue(line, kAttributeRtcpFb, error);
3420 }
3421 std::string payload_type_string;
3422 if (!GetValue(rtcp_fb_fields[0], kAttributeRtcpFb, &payload_type_string,
3423 error)) {
3424 return false;
3425 }
wu@webrtc.org5e760e72014-04-02 23:19:09 +00003426 int payload_type = kWildcardPayloadType;
3427 if (payload_type_string != "*") {
pkasting@chromium.orge9facf82015-02-17 20:36:28 +00003428 if (!GetPayloadTypeFromString(line, payload_type_string, &payload_type,
3429 error)) {
wu@webrtc.org5e760e72014-04-02 23:19:09 +00003430 return false;
3431 }
3432 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003433 std::string id = rtcp_fb_fields[1];
3434 std::string param = "";
3435 for (std::vector<std::string>::iterator iter = rtcp_fb_fields.begin() + 2;
3436 iter != rtcp_fb_fields.end(); ++iter) {
3437 param.append(*iter);
3438 }
3439 const cricket::FeedbackParam feedback_param(id, param);
3440
3441 if (media_type == cricket::MEDIA_TYPE_AUDIO) {
pkasting@chromium.orgd3245462015-02-23 21:28:22 +00003442 UpdateCodec<AudioContentDescription, cricket::AudioCodec>(
3443 media_desc, payload_type, feedback_param);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003444 } else if (media_type == cricket::MEDIA_TYPE_VIDEO) {
pkasting@chromium.orgd3245462015-02-23 21:28:22 +00003445 UpdateCodec<VideoContentDescription, cricket::VideoCodec>(
3446 media_desc, payload_type, feedback_param);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003447 }
3448 return true;
3449}
3450
3451} // namespace webrtc