blob: 4372b0b74386c029d969e7715729f20408fe7447 [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
Steve Anton10542f22019-01-11 09:11:00 -080011#include "pc/webrtc_sdp.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>
kwibergd1fe2812016-04-27 06:47:29 -070015
henrike@webrtc.org28e20752013-07-10 00:45:36 +000016#include <algorithm>
Harald Alvestrandc24a2182022-02-23 13:44:59 +000017#include <cstddef>
Harald Alvestrand5761e7b2021-01-29 14:45:08 +000018#include <cstdint>
Steve Anton36b29d12017-10-30 09:57:42 -070019#include <map>
kwibergd1fe2812016-04-27 06:47:29 -070020#include <memory>
Steve Anton36b29d12017-10-30 09:57:42 -070021#include <set>
henrike@webrtc.org28e20752013-07-10 00:45:36 +000022#include <string>
deadbeef67cf2c12016-04-13 10:07:16 -070023#include <unordered_map>
Steve Anton4905edb2018-10-15 19:27:44 -070024#include <utility>
henrike@webrtc.org28e20752013-07-10 00:45:36 +000025#include <vector>
26
Steve Anton64b626b2019-01-28 17:25:26 -080027#include "absl/algorithm/container.h"
Niels Möller658b88a2022-03-17 10:47:42 +010028#include "absl/strings/ascii.h"
Patrik Höglunde2d6a062017-10-05 14:53:33 +020029#include "api/candidate.h"
Harald Alvestrand0d018412021-11-04 13:52:31 +000030#include "api/crypto_params.h"
Steve Anton10542f22019-01-11 09:11:00 -080031#include "api/jsep_ice_candidate.h"
32#include "api/jsep_session_description.h"
33#include "api/media_types.h"
isheriff6f8d6862016-05-26 11:24:55 -070034// for RtpExtension
Harald Alvestrandc24a2182022-02-23 13:44:59 +000035#include "absl/strings/string_view.h"
Harald Alvestrand5761e7b2021-01-29 14:45:08 +000036#include "absl/types/optional.h"
37#include "api/rtc_error.h"
Steve Anton10542f22019-01-11 09:11:00 -080038#include "api/rtp_parameters.h"
Harald Alvestrand5761e7b2021-01-29 14:45:08 +000039#include "api/rtp_transceiver_direction.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020040#include "media/base/codec.h"
Steve Anton10542f22019-01-11 09:11:00 -080041#include "media/base/media_constants.h"
Harald Alvestrand5761e7b2021-01-29 14:45:08 +000042#include "media/base/rid_description.h"
Steve Anton10542f22019-01-11 09:11:00 -080043#include "media/base/rtp_utils.h"
Harald Alvestrand5761e7b2021-01-29 14:45:08 +000044#include "media/base/stream_params.h"
Steve Anton10542f22019-01-11 09:11:00 -080045#include "media/sctp/sctp_transport_internal.h"
Harald Alvestrand5761e7b2021-01-29 14:45:08 +000046#include "p2p/base/candidate_pair_interface.h"
47#include "p2p/base/ice_transport_internal.h"
Steve Anton10542f22019-01-11 09:11:00 -080048#include "p2p/base/p2p_constants.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020049#include "p2p/base/port.h"
Harald Alvestrand5761e7b2021-01-29 14:45:08 +000050#include "p2p/base/port_interface.h"
51#include "p2p/base/transport_description.h"
52#include "p2p/base/transport_info.h"
53#include "pc/media_protocol_names.h"
Steve Anton10542f22019-01-11 09:11:00 -080054#include "pc/media_session.h"
55#include "pc/sdp_serializer.h"
Harald Alvestrand5761e7b2021-01-29 14:45:08 +000056#include "pc/session_description.h"
57#include "pc/simulcast_description.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020058#include "rtc_base/arraysize.h"
59#include "rtc_base/checks.h"
Harald Alvestrand5761e7b2021-01-29 14:45:08 +000060#include "rtc_base/helpers.h"
61#include "rtc_base/ip_address.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020062#include "rtc_base/logging.h"
Harald Alvestrand5761e7b2021-01-29 14:45:08 +000063#include "rtc_base/net_helper.h"
64#include "rtc_base/network_constants.h"
65#include "rtc_base/socket_address.h"
66#include "rtc_base/ssl_fingerprint.h"
67#include "rtc_base/string_encode.h"
Niels Möller198cf002019-05-10 12:29:13 +020068#include "rtc_base/string_utils.h"
Jonas Olssonec9e4922018-09-05 09:53:49 +020069#include "rtc_base/strings/string_builder.h"
henrike@webrtc.org28e20752013-07-10 00:45:36 +000070
71using cricket::AudioContentDescription;
72using cricket::Candidate;
73using cricket::Candidates;
henrike@webrtc.org28e20752013-07-10 00:45:36 +000074using cricket::ContentInfo;
Harald Alvestrand0d018412021-11-04 13:52:31 +000075using cricket::CryptoParams;
Harald Alvestrand141c0ad2019-05-05 19:00:00 +000076using cricket::ICE_CANDIDATE_COMPONENT_RTCP;
Harald Alvestrand37f2b432019-05-09 09:19:54 +020077using cricket::ICE_CANDIDATE_COMPONENT_RTP;
Taylor Brandstetteree8c2462020-07-27 15:52:02 -070078using cricket::kApplicationSpecificBandwidth;
henrike@webrtc.org28e20752013-07-10 00:45:36 +000079using cricket::kCodecParamMaxPTime;
henrike@webrtc.org28e20752013-07-10 00:45:36 +000080using cricket::kCodecParamMinPTime;
81using cricket::kCodecParamPTime;
Taylor Brandstetteree8c2462020-07-27 15:52:02 -070082using cricket::kTransportSpecificBandwidth;
henrike@webrtc.org28e20752013-07-10 00:45:36 +000083using cricket::MediaContentDescription;
Harald Alvestrand141c0ad2019-05-05 19:00:00 +000084using cricket::MediaProtocolType;
Harald Alvestrand37f2b432019-05-09 09:19:54 +020085using cricket::MediaType;
Harald Alvestrand141c0ad2019-05-05 19:00:00 +000086using cricket::RidDescription;
Harald Alvestrand37f2b432019-05-09 09:19:54 +020087using cricket::RtpHeaderExtensions;
Harald Alvestrand5fc28b12019-05-13 13:36:16 +020088using cricket::SctpDataContentDescription;
Amit Hilbucha2012042018-12-03 11:35:05 -080089using cricket::SimulcastDescription;
Amit Hilbuchc57d5732018-12-11 15:30:11 -080090using cricket::SimulcastLayer;
91using cricket::SimulcastLayerList;
henrike@webrtc.org28e20752013-07-10 00:45:36 +000092using cricket::SsrcGroup;
93using cricket::StreamParams;
94using cricket::StreamParamsVec;
95using cricket::TransportDescription;
96using cricket::TransportInfo;
Philipp Hancke4e8c1152020-10-13 12:43:15 +020097using cricket::UnsupportedContentDescription;
henrike@webrtc.org28e20752013-07-10 00:45:36 +000098using cricket::VideoContentDescription;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +000099using rtc::SocketAddress;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000100
deadbeef90f1e1e2017-02-10 12:35:05 -0800101// TODO(deadbeef): Switch to using anonymous namespace rather than declaring
102// everything "static".
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000103namespace webrtc {
104
105// Line type
106// RFC 4566
107// An SDP session description consists of a number of lines of text of
108// the form:
109// <type>=<value>
110// where <type> MUST be exactly one case-significant character.
Harald Alvestrand99bcf602021-03-03 07:44:39 +0000111
Philipp Hancke62bb58f2021-11-15 12:33:10 +0100112// Check if passed character is a "token-char" from RFC 4566.
113// https://datatracker.ietf.org/doc/html/rfc4566#section-9
Harald Alvestrand99bcf602021-03-03 07:44:39 +0000114// token-char = %x21 / %x23-27 / %x2A-2B / %x2D-2E / %x30-39
115// / %x41-5A / %x5E-7E
Philipp Hancke62bb58f2021-11-15 12:33:10 +0100116bool IsTokenChar(char ch) {
117 return ch == 0x21 || (ch >= 0x23 && ch <= 0x27) || ch == 0x2a || ch == 0x2b ||
118 ch == 0x2d || ch == 0x2e || (ch >= 0x30 && ch <= 0x39) ||
119 (ch >= 0x41 && ch <= 0x5a) || (ch >= 0x5e && ch <= 0x7e);
120}
deadbeef9d3584c2016-02-16 17:54:10 -0800121static const int kLinePrefixLength = 2; // Length of <type>=
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000122static const char kLineTypeVersion = 'v';
123static const char kLineTypeOrigin = 'o';
124static const char kLineTypeSessionName = 's';
125static const char kLineTypeSessionInfo = 'i';
126static const char kLineTypeSessionUri = 'u';
127static const char kLineTypeSessionEmail = 'e';
128static const char kLineTypeSessionPhone = 'p';
129static const char kLineTypeSessionBandwidth = 'b';
130static const char kLineTypeTiming = 't';
131static const char kLineTypeRepeatTimes = 'r';
132static const char kLineTypeTimeZone = 'z';
133static const char kLineTypeEncryptionKey = 'k';
134static const char kLineTypeMedia = 'm';
135static const char kLineTypeConnection = 'c';
136static const char kLineTypeAttributes = 'a';
137
138// Attributes
139static const char kAttributeGroup[] = "group";
140static const char kAttributeMid[] = "mid";
deadbeef9d3584c2016-02-16 17:54:10 -0800141static const char kAttributeMsid[] = "msid";
deadbeef25ed4352016-12-12 18:37:36 -0800142static const char kAttributeBundleOnly[] = "bundle-only";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000143static const char kAttributeRtcpMux[] = "rtcp-mux";
deadbeef13871492015-12-09 12:37:51 -0800144static const char kAttributeRtcpReducedSize[] = "rtcp-rsize";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000145static const char kAttributeSsrc[] = "ssrc";
146static const char kSsrcAttributeCname[] = "cname";
Johannes Kron0854eb62018-10-10 22:33:20 +0200147static const char kAttributeExtmapAllowMixed[] = "extmap-allow-mixed";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000148static const char kAttributeExtmap[] = "extmap";
149// draft-alvestrand-mmusic-msid-01
150// a=msid-semantic: WMS
Seth Hampson5b4f0752018-04-02 16:31:36 -0700151// This is a legacy field supported only for Plan B semantics.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000152static const char kAttributeMsidSemantics[] = "msid-semantic";
153static const char kMediaStreamSemantic[] = "WMS";
154static const char kSsrcAttributeMsid[] = "msid";
155static const char kDefaultMsid[] = "default";
Seth Hampson5b4f0752018-04-02 16:31:36 -0700156static const char kNoStreamMsid[] = "-";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000157static const char kAttributeSsrcGroup[] = "ssrc-group";
Harald Alvestrand0d018412021-11-04 13:52:31 +0000158static const char kAttributeCrypto[] = "crypto";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000159static const char kAttributeCandidate[] = "candidate";
160static const char kAttributeCandidateTyp[] = "typ";
161static const char kAttributeCandidateRaddr[] = "raddr";
162static const char kAttributeCandidateRport[] = "rport";
honghaiza54a0802015-12-16 18:37:23 -0800163static const char kAttributeCandidateUfrag[] = "ufrag";
164static const char kAttributeCandidatePwd[] = "pwd";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000165static const char kAttributeCandidateGeneration[] = "generation";
honghaiza0c44ea2016-03-23 16:07:48 -0700166static const char kAttributeCandidateNetworkId[] = "network-id";
honghaize1a0c942016-02-16 14:54:56 -0800167static const char kAttributeCandidateNetworkCost[] = "network-cost";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000168static const char kAttributeFingerprint[] = "fingerprint";
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +0000169static const char kAttributeSetup[] = "setup";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000170static const char kAttributeFmtp[] = "fmtp";
171static const char kAttributeRtpmap[] = "rtpmap";
wu@webrtc.org78187522013-10-07 23:32:02 +0000172static const char kAttributeSctpmap[] = "sctpmap";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000173static const char kAttributeRtcp[] = "rtcp";
174static const char kAttributeIceUfrag[] = "ice-ufrag";
175static const char kAttributeIcePwd[] = "ice-pwd";
176static const char kAttributeIceLite[] = "ice-lite";
177static const char kAttributeIceOption[] = "ice-options";
178static const char kAttributeSendOnly[] = "sendonly";
179static const char kAttributeRecvOnly[] = "recvonly";
180static const char kAttributeRtcpFb[] = "rtcp-fb";
181static const char kAttributeSendRecv[] = "sendrecv";
182static const char kAttributeInactive[] = "inactive";
Harald Alvestrand48cce4d2019-04-11 10:41:24 +0200183// draft-ietf-mmusic-sctp-sdp-26
184// a=sctp-port, a=max-message-size
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +0000185static const char kAttributeSctpPort[] = "sctp-port";
Harald Alvestrand48cce4d2019-04-11 10:41:24 +0200186static const char kAttributeMaxMessageSize[] = "max-message-size";
Harald Alvestrandfbb45bd2019-05-15 08:07:47 +0200187static const int kDefaultSctpMaxMessageSize = 65536;
Amit Hilbucha2012042018-12-03 11:35:05 -0800188// draft-ietf-mmusic-sdp-simulcast-13
189// a=simulcast
190static const char kAttributeSimulcast[] = "simulcast";
Amit Hilbuchc57d5732018-12-11 15:30:11 -0800191// draft-ietf-mmusic-rid-15
192// a=rid
193static const char kAttributeRid[] = "rid";
Mirta Dvornicic479a3c02019-06-04 15:38:50 +0200194static const char kAttributePacketization[] = "packetization";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000195
196// Experimental flags
197static const char kAttributeXGoogleFlag[] = "x-google-flag";
198static const char kValueConference[] = "conference";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000199
Sebastian Janssone1795f42019-07-24 11:38:03 +0200200static const char kAttributeRtcpRemoteEstimate[] = "remote-net-estimate";
201
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000202// Candidate
203static const char kCandidateHost[] = "host";
204static const char kCandidateSrflx[] = "srflx";
Honghai Zhang7fb69db2016-03-14 11:59:18 -0700205static const char kCandidatePrflx[] = "prflx";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000206static const char kCandidateRelay[] = "relay";
mallinath@webrtc.org2d60c5e2014-08-08 22:29:20 +0000207static const char kTcpCandidateType[] = "tcptype";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000208
Jonas Olssonec9e4922018-09-05 09:53:49 +0200209// rtc::StringBuilder doesn't have a << overload for chars, while rtc::split and
210// rtc::tokenize_first both take a char delimiter. To handle both cases these
211// constants come in pairs of a chars and length-one strings.
212static const char kSdpDelimiterEqual[] = "=";
213static const char kSdpDelimiterEqualChar = '=';
214static const char kSdpDelimiterSpace[] = " ";
215static const char kSdpDelimiterSpaceChar = ' ';
216static const char kSdpDelimiterColon[] = ":";
217static const char kSdpDelimiterColonChar = ':';
218static const char kSdpDelimiterSemicolon[] = ";";
219static const char kSdpDelimiterSemicolonChar = ';';
220static const char kSdpDelimiterSlashChar = '/';
221static const char kNewLine[] = "\n";
222static const char kNewLineChar = '\n';
223static const char kReturnChar = '\r';
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000224static const char kLineBreak[] = "\r\n";
225
Steve Anton36b29d12017-10-30 09:57:42 -0700226// TODO(deadbeef): Generate the Session and Time description
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000227// instead of hardcoding.
228static const char kSessionVersion[] = "v=0";
229// RFC 4566
230static const char kSessionOriginUsername[] = "-";
231static const char kSessionOriginSessionId[] = "0";
232static const char kSessionOriginSessionVersion[] = "0";
233static const char kSessionOriginNettype[] = "IN";
234static const char kSessionOriginAddrtype[] = "IP4";
235static const char kSessionOriginAddress[] = "127.0.0.1";
236static const char kSessionName[] = "s=-";
237static const char kTimeDescription[] = "t=0 0";
238static const char kAttrGroup[] = "a=group:BUNDLE";
239static const char kConnectionNettype[] = "IN";
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000240static const char kConnectionIpv4Addrtype[] = "IP4";
241static const char kConnectionIpv6Addrtype[] = "IP6";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000242static const char kMediaTypeVideo[] = "video";
243static const char kMediaTypeAudio[] = "audio";
244static const char kMediaTypeData[] = "application";
245static const char kMediaPortRejected[] = "0";
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000246// draft-ietf-mmusic-trickle-ice-01
247// When no candidates have been gathered, set the connection
248// address to IP6 ::.
perkj@webrtc.orgd105cc82014-11-07 11:22:06 +0000249// TODO(perkj): FF can not parse IP6 ::. See http://crbug/430333
250// Use IPV4 per default.
251static const char kDummyAddress[] = "0.0.0.0";
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000252static const char kDummyPort[] = "9";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000253
wu@webrtc.org78187522013-10-07 23:32:02 +0000254static const char kDefaultSctpmapProtocol[] = "webrtc-datachannel";
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000255
pkasting@chromium.orgd3245462015-02-23 21:28:22 +0000256// RTP payload type is in the 0-127 range. Use -1 to indicate "all" payload
257// types.
258const int kWildcardPayloadType = -1;
259
Philipp Hancked58ac5a2021-11-25 08:57:54 +0100260// Maximum number of channels allowed.
261static const size_t kMaxNumberOfChannels = 24;
262
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000263struct SsrcInfo {
Peter Boström0c4e06b2015-10-07 12:23:21 +0200264 uint32_t ssrc_id;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000265 std::string cname;
deadbeef9d3584c2016-02-16 17:54:10 -0800266 std::string stream_id;
267 std::string track_id;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000268};
269typedef std::vector<SsrcInfo> SsrcInfoVec;
270typedef std::vector<SsrcGroup> SsrcGroupVec;
271
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000272template <class T>
273static void AddFmtpLine(const T& codec, std::string* message);
274static void BuildMediaDescription(const ContentInfo* content_info,
275 const TransportInfo* transport_info,
Patrik Höglundb6b29e02018-06-21 16:58:01 +0200276 const cricket::MediaType media_type,
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000277 const std::vector<Candidate>& candidates,
Steve Antone831b8c2018-02-01 12:22:16 -0800278 int msid_signaling,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000279 std::string* message);
deadbeef9d3584c2016-02-16 17:54:10 -0800280static void BuildRtpContentAttributes(const MediaContentDescription* media_desc,
Patrik Höglundb6b29e02018-06-21 16:58:01 +0200281 const cricket::MediaType media_type,
Steve Antone831b8c2018-02-01 12:22:16 -0800282 int msid_signaling,
deadbeef9d3584c2016-02-16 17:54:10 -0800283 std::string* message);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000284static void BuildRtpMap(const MediaContentDescription* media_desc,
Patrik Höglundb6b29e02018-06-21 16:58:01 +0200285 const cricket::MediaType media_type,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000286 std::string* message);
287static void BuildCandidate(const std::vector<Candidate>& candidates,
honghaiza54a0802015-12-16 18:37:23 -0800288 bool include_ufrag,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000289 std::string* message);
290static void BuildIceOptions(const std::vector<std::string>& transport_options,
291 std::string* message);
zhihuang38989e52017-03-21 11:04:53 -0700292static bool ParseSessionDescription(const std::string& message,
293 size_t* pos,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000294 std::string* session_id,
295 std::string* session_version,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000296 TransportDescription* session_td,
297 RtpHeaderExtensions* session_extmaps,
zhihuang38989e52017-03-21 11:04:53 -0700298 rtc::SocketAddress* connection_addr,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000299 cricket::SessionDescription* desc,
300 SdpParseError* error);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000301static bool ParseMediaDescription(
302 const std::string& message,
303 const TransportDescription& session_td,
304 const RtpHeaderExtensions& session_extmaps,
zhihuang38989e52017-03-21 11:04:53 -0700305 size_t* pos,
306 const rtc::SocketAddress& session_connection_addr,
307 cricket::SessionDescription* desc,
Steve Anton5a1de872018-12-18 11:25:55 -0800308 std::vector<std::unique_ptr<JsepIceCandidate>>* candidates,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000309 SdpParseError* error);
Steve Anton5a1de872018-12-18 11:25:55 -0800310static bool ParseContent(
311 const std::string& message,
312 const cricket::MediaType media_type,
313 int mline_index,
314 const std::string& protocol,
315 const std::vector<int>& payload_types,
316 size_t* pos,
317 std::string* content_name,
318 bool* bundle_only,
319 int* msid_signaling,
320 MediaContentDescription* media_desc,
321 TransportDescription* transport,
322 std::vector<std::unique_ptr<JsepIceCandidate>>* candidates,
323 SdpParseError* error);
Philipp Hancke4e8c1152020-10-13 12:43:15 +0200324static bool ParseGroupAttribute(const std::string& line,
325 cricket::SessionDescription* desc,
326 SdpParseError* error);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000327static bool ParseSsrcAttribute(const std::string& line,
328 SsrcInfoVec* ssrc_infos,
Steve Antone831b8c2018-02-01 12:22:16 -0800329 int* msid_signaling,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000330 SdpParseError* error);
331static bool ParseSsrcGroupAttribute(const std::string& line,
332 SsrcGroupVec* ssrc_groups,
333 SdpParseError* error);
Harald Alvestrand0d018412021-11-04 13:52:31 +0000334static bool ParseCryptoAttribute(const std::string& line,
335 MediaContentDescription* media_desc,
336 SdpParseError* error);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000337static bool ParseRtpmapAttribute(const std::string& line,
Patrik Höglundb6b29e02018-06-21 16:58:01 +0200338 const cricket::MediaType media_type,
deadbeef67cf2c12016-04-13 10:07:16 -0700339 const std::vector<int>& payload_types,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000340 MediaContentDescription* media_desc,
341 SdpParseError* error);
342static bool ParseFmtpAttributes(const std::string& line,
Patrik Höglundb6b29e02018-06-21 16:58:01 +0200343 const cricket::MediaType media_type,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000344 MediaContentDescription* media_desc,
345 SdpParseError* error);
Niels Möller658b88a2022-03-17 10:47:42 +0100346static bool ParseFmtpParam(absl::string_view line,
Yves Gerey665174f2018-06-19 15:03:05 +0200347 std::string* parameter,
348 std::string* value,
349 SdpParseError* error);
Mirta Dvornicic479a3c02019-06-04 15:38:50 +0200350static bool ParsePacketizationAttribute(const std::string& line,
351 const cricket::MediaType media_type,
352 MediaContentDescription* media_desc,
353 SdpParseError* error);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000354static bool ParseRtcpFbAttribute(const std::string& line,
Patrik Höglundb6b29e02018-06-21 16:58:01 +0200355 const cricket::MediaType media_type,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000356 MediaContentDescription* media_desc,
357 SdpParseError* error);
358static bool ParseIceOptions(const std::string& line,
359 std::vector<std::string>* transport_options,
360 SdpParseError* error);
361static bool ParseExtmap(const std::string& line,
isheriff6f8d6862016-05-26 11:24:55 -0700362 RtpExtension* extmap,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000363 SdpParseError* error);
Steve Anton4905edb2018-10-15 19:27:44 -0700364static bool ParseFingerprintAttribute(
365 const std::string& line,
366 std::unique_ptr<rtc::SSLFingerprint>* fingerprint,
367 SdpParseError* error);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +0000368static bool ParseDtlsSetup(const std::string& line,
369 cricket::ConnectionRole* role,
370 SdpParseError* error);
deadbeef9d3584c2016-02-16 17:54:10 -0800371static bool ParseMsidAttribute(const std::string& line,
Seth Hampson5b4f0752018-04-02 16:31:36 -0700372 std::vector<std::string>* stream_ids,
deadbeef9d3584c2016-02-16 17:54:10 -0800373 std::string* track_id,
374 SdpParseError* error);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000375
Amit Hilbuchc57d5732018-12-11 15:30:11 -0800376static void RemoveInvalidRidDescriptions(const std::vector<int>& payload_types,
377 std::vector<RidDescription>* rids);
378
379static SimulcastLayerList RemoveRidsFromSimulcastLayerList(
380 const std::set<std::string>& to_remove,
381 const SimulcastLayerList& layers);
382
383static void RemoveInvalidRidsFromSimulcast(
384 const std::vector<RidDescription>& rids,
385 SimulcastDescription* simulcast);
386
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000387// Helper functions
388
389// Below ParseFailed*** functions output the line that caused the parsing
Artem Titov880fa812021-07-30 22:30:23 +0200390// failure and the detailed reason (`description`) of the failure to `error`.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000391// The functions always return false so that they can be used directly in the
392// following way when error happens:
393// "return ParseFailed***(...);"
394
Artem Titov880fa812021-07-30 22:30:23 +0200395// The line starting at `line_start` of `message` is the failing line.
396// The reason for the failure should be provided in the `description`.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000397// An example of a description could be "unknown character".
Niels Möllera820cc22021-08-11 10:07:45 +0200398static bool ParseFailed(absl::string_view message,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000399 size_t line_start,
Niels Möllera820cc22021-08-11 10:07:45 +0200400 std::string description,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000401 SdpParseError* error) {
Artem Titov880fa812021-07-30 22:30:23 +0200402 // Get the first line of `message` from `line_start`.
Niels Möllera820cc22021-08-11 10:07:45 +0200403 absl::string_view first_line;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000404 size_t line_end = message.find(kNewLine, line_start);
405 if (line_end != std::string::npos) {
Jonas Olssonec9e4922018-09-05 09:53:49 +0200406 if (line_end > 0 && (message.at(line_end - 1) == kReturnChar)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000407 --line_end;
408 }
409 first_line = message.substr(line_start, (line_end - line_start));
sergeyu@chromium.org5bc25c42013-12-05 00:24:06 +0000410 } else {
411 first_line = message.substr(line_start);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000412 }
413
Mirko Bonadei675513b2017-11-09 11:09:25 +0100414 RTC_LOG(LS_ERROR) << "Failed to parse: \"" << first_line
415 << "\". Reason: " << description;
Niels Möllera820cc22021-08-11 10:07:45 +0200416 if (error) {
Niels Möllera654e072021-09-30 08:48:05 +0200417 // TODO(bugs.webrtc.org/13220): In C++17, we can use plain assignment, with
418 // a string_view on the right hand side.
419 error->line.assign(first_line.data(), first_line.size());
Niels Möllera820cc22021-08-11 10:07:45 +0200420 error->description = std::move(description);
421 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000422 return false;
423}
424
Artem Titov880fa812021-07-30 22:30:23 +0200425// `line` is the failing line. The reason for the failure should be
426// provided in the `description`.
Niels Möllera820cc22021-08-11 10:07:45 +0200427static bool ParseFailed(absl::string_view line,
428 std::string description,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000429 SdpParseError* error) {
Niels Möllera820cc22021-08-11 10:07:45 +0200430 return ParseFailed(line, 0, std::move(description), error);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000431}
432
433// Parses failure where the failing SDP line isn't know or there are multiple
434// failing lines.
Niels Möllera820cc22021-08-11 10:07:45 +0200435static bool ParseFailed(std::string description, SdpParseError* error) {
436 return ParseFailed("", std::move(description), error);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000437}
438
Artem Titov880fa812021-07-30 22:30:23 +0200439// `line` is the failing line. The failure is due to the fact that `line`
440// doesn't have `expected_fields` fields.
Niels Möllera820cc22021-08-11 10:07:45 +0200441static bool ParseFailedExpectFieldNum(absl::string_view line,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000442 int expected_fields,
443 SdpParseError* error) {
Jonas Olssonec9e4922018-09-05 09:53:49 +0200444 rtc::StringBuilder description;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000445 description << "Expects " << expected_fields << " fields.";
Niels Möllera820cc22021-08-11 10:07:45 +0200446 return ParseFailed(line, description.Release(), error);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000447}
448
Artem Titov880fa812021-07-30 22:30:23 +0200449// `line` is the failing line. The failure is due to the fact that `line` has
450// less than `expected_min_fields` fields.
Niels Möllera820cc22021-08-11 10:07:45 +0200451static bool ParseFailedExpectMinFieldNum(absl::string_view line,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000452 int expected_min_fields,
453 SdpParseError* error) {
Jonas Olssonec9e4922018-09-05 09:53:49 +0200454 rtc::StringBuilder description;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000455 description << "Expects at least " << expected_min_fields << " fields.";
Niels Möllera820cc22021-08-11 10:07:45 +0200456 return ParseFailed(line, description.Release(), error);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000457}
458
Artem Titov880fa812021-07-30 22:30:23 +0200459// `line` is the failing line. The failure is due to the fact that it failed to
460// get the value of `attribute`.
Niels Möllera820cc22021-08-11 10:07:45 +0200461static bool ParseFailedGetValue(absl::string_view line,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000462 const std::string& attribute,
463 SdpParseError* error) {
Jonas Olssonec9e4922018-09-05 09:53:49 +0200464 rtc::StringBuilder description;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000465 description << "Failed to get the value of attribute: " << attribute;
Niels Möllera820cc22021-08-11 10:07:45 +0200466 return ParseFailed(line, description.Release(), error);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000467}
468
Artem Titov880fa812021-07-30 22:30:23 +0200469// The line starting at `line_start` of `message` is the failing line. The
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000470// failure is due to the line type (e.g. the "m" part of the "m-line")
471// not matching what is expected. The expected line type should be
Artem Titov880fa812021-07-30 22:30:23 +0200472// provided as `line_type`.
Niels Möllera820cc22021-08-11 10:07:45 +0200473static bool ParseFailedExpectLine(absl::string_view message,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000474 size_t line_start,
475 const char line_type,
Niels Möllera820cc22021-08-11 10:07:45 +0200476 absl::string_view line_value,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000477 SdpParseError* error) {
Jonas Olssonec9e4922018-09-05 09:53:49 +0200478 rtc::StringBuilder description;
479 description << "Expect line: " << std::string(1, line_type) << "="
480 << line_value;
Niels Möllera820cc22021-08-11 10:07:45 +0200481 return ParseFailed(message, line_start, description.Release(), error);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000482}
483
484static bool AddLine(const std::string& line, std::string* message) {
485 if (!message)
486 return false;
487
488 message->append(line);
489 message->append(kLineBreak);
490 return true;
491}
492
493static bool GetLine(const std::string& message,
494 size_t* pos,
495 std::string* line) {
496 size_t line_begin = *pos;
497 size_t line_end = message.find(kNewLine, line_begin);
498 if (line_end == std::string::npos) {
499 return false;
500 }
501 // Update the new start position
502 *pos = line_end + 1;
Jonas Olssonec9e4922018-09-05 09:53:49 +0200503 if (line_end > 0 && (message.at(line_end - 1) == kReturnChar)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000504 --line_end;
505 }
506 *line = message.substr(line_begin, (line_end - line_begin));
507 const char* cline = line->c_str();
508 // RFC 4566
509 // An SDP session description consists of a number of lines of text of
510 // the form:
511 // <type>=<value>
512 // where <type> MUST be exactly one case-significant character and
513 // <value> is structured text whose format depends on <type>.
514 // Whitespace MUST NOT be used on either side of the "=" sign.
Taylor Brandstetter93a7b242018-04-16 10:45:24 -0700515 //
516 // However, an exception to the whitespace rule is made for "s=", since
517 // RFC4566 also says:
518 //
519 // If a session has no meaningful name, the value "s= " SHOULD be used
520 // (i.e., a single space as the session name).
521 if (line->length() < 3 || !islower(cline[0]) ||
Jonas Olssonec9e4922018-09-05 09:53:49 +0200522 cline[1] != kSdpDelimiterEqualChar ||
523 (cline[0] != kLineTypeSessionName &&
524 cline[2] == kSdpDelimiterSpaceChar)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000525 *pos = line_begin;
526 return false;
527 }
528 return true;
529}
530
Artem Titov880fa812021-07-30 22:30:23 +0200531// Init `os` to "`type`=`value`".
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000532static void InitLine(const char type,
533 const std::string& value,
Jonas Olssonec9e4922018-09-05 09:53:49 +0200534 rtc::StringBuilder* os) {
535 os->Clear();
536 *os << std::string(1, type) << kSdpDelimiterEqual << value;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000537}
538
Artem Titov880fa812021-07-30 22:30:23 +0200539// Init `os` to "a=`attribute`".
Jonas Olssonec9e4922018-09-05 09:53:49 +0200540static void InitAttrLine(const std::string& attribute, rtc::StringBuilder* os) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000541 InitLine(kLineTypeAttributes, attribute, os);
542}
543
Artem Titov880fa812021-07-30 22:30:23 +0200544// Writes a SDP attribute line based on `attribute` and `value` to `message`.
Yves Gerey665174f2018-06-19 15:03:05 +0200545static void AddAttributeLine(const std::string& attribute,
546 int value,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000547 std::string* message) {
Jonas Olssonec9e4922018-09-05 09:53:49 +0200548 rtc::StringBuilder os;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000549 InitAttrLine(attribute, &os);
550 os << kSdpDelimiterColon << value;
551 AddLine(os.str(), message);
552}
553
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000554static bool IsLineType(const std::string& message,
555 const char type,
556 size_t line_start) {
557 if (message.size() < line_start + kLinePrefixLength) {
558 return false;
559 }
560 const char* cmessage = message.c_str();
561 return (cmessage[line_start] == type &&
Jonas Olssonec9e4922018-09-05 09:53:49 +0200562 cmessage[line_start + 1] == kSdpDelimiterEqualChar);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000563}
564
Yves Gerey665174f2018-06-19 15:03:05 +0200565static bool IsLineType(const std::string& line, const char type) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000566 return IsLineType(line, type, 0);
567}
568
Yves Gerey665174f2018-06-19 15:03:05 +0200569static bool GetLineWithType(const std::string& message,
570 size_t* pos,
571 std::string* line,
572 const char type) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000573 if (!IsLineType(message, type, *pos)) {
574 return false;
575 }
576
577 if (!GetLine(message, pos, line))
578 return false;
579
580 return true;
581}
582
583static bool HasAttribute(const std::string& line,
584 const std::string& attribute) {
Johannes Kron211856b2018-09-06 12:12:28 +0200585 if (line.compare(kLinePrefixLength, attribute.size(), attribute) == 0) {
586 // Make sure that the match is not only a partial match. If length of
587 // strings doesn't match, the next character of the line must be ':' or ' '.
588 // This function is also used for media descriptions (e.g., "m=audio 9..."),
589 // hence the need to also allow space in the end.
590 RTC_CHECK_LE(kLinePrefixLength + attribute.size(), line.size());
591 if ((kLinePrefixLength + attribute.size()) == line.size() ||
592 line[kLinePrefixLength + attribute.size()] == kSdpDelimiterColonChar ||
593 line[kLinePrefixLength + attribute.size()] == kSdpDelimiterSpaceChar) {
594 return true;
595 }
596 }
597 return false;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000598}
599
Peter Boström0c4e06b2015-10-07 12:23:21 +0200600static bool AddSsrcLine(uint32_t ssrc_id,
601 const std::string& attribute,
602 const std::string& value,
603 std::string* message) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000604 // RFC 5576
605 // a=ssrc:<ssrc-id> <attribute>:<value>
Jonas Olssonec9e4922018-09-05 09:53:49 +0200606 rtc::StringBuilder os;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000607 InitAttrLine(kAttributeSsrc, &os);
Yves Gerey665174f2018-06-19 15:03:05 +0200608 os << kSdpDelimiterColon << ssrc_id << kSdpDelimiterSpace << attribute
609 << kSdpDelimiterColon << value;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000610 return AddLine(os.str(), message);
611}
612
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000613// Get value only from <attribute>:<value>.
Yves Gerey665174f2018-06-19 15:03:05 +0200614static bool GetValue(const std::string& message,
615 const std::string& attribute,
616 std::string* value,
617 SdpParseError* error) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000618 std::string leftpart;
Jonas Olssonec9e4922018-09-05 09:53:49 +0200619 if (!rtc::tokenize_first(message, kSdpDelimiterColonChar, &leftpart, value)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000620 return ParseFailedGetValue(message, attribute, error);
621 }
622 // The left part should end with the expected attribute.
623 if (leftpart.length() < attribute.length() ||
624 leftpart.compare(leftpart.length() - attribute.length(),
625 attribute.length(), attribute) != 0) {
626 return ParseFailedGetValue(message, attribute, error);
627 }
628 return true;
629}
630
Harald Alvestrand99bcf602021-03-03 07:44:39 +0000631// Get a single [token] from <attribute>:<token>
632static bool GetSingleTokenValue(const std::string& message,
633 const std::string& attribute,
634 std::string* value,
635 SdpParseError* error) {
636 if (!GetValue(message, attribute, value, error)) {
637 return false;
638 }
Philipp Hancke62bb58f2021-11-15 12:33:10 +0100639 if (!absl::c_all_of(absl::string_view(*value), IsTokenChar)) {
Harald Alvestrand99bcf602021-03-03 07:44:39 +0000640 rtc::StringBuilder description;
641 description << "Illegal character found in the value of " << attribute;
Niels Möllera820cc22021-08-11 10:07:45 +0200642 return ParseFailed(message, description.Release(), error);
Harald Alvestrand99bcf602021-03-03 07:44:39 +0000643 }
644 return true;
645}
646
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000647static bool CaseInsensitiveFind(std::string str1, std::string str2) {
Steve Anton64b626b2019-01-28 17:25:26 -0800648 absl::c_transform(str1, str1.begin(), ::tolower);
649 absl::c_transform(str2, str2.begin(), ::tolower);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000650 return str1.find(str2) != std::string::npos;
651}
652
wu@webrtc.org5e760e72014-04-02 23:19:09 +0000653template <class T>
654static bool GetValueFromString(const std::string& line,
655 const std::string& s,
656 T* t,
657 SdpParseError* error) {
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000658 if (!rtc::FromString(s, t)) {
Jonas Olssonec9e4922018-09-05 09:53:49 +0200659 rtc::StringBuilder description;
wu@webrtc.org5e760e72014-04-02 23:19:09 +0000660 description << "Invalid value: " << s << ".";
Niels Möllera820cc22021-08-11 10:07:45 +0200661 return ParseFailed(line, description.Release(), error);
wu@webrtc.org5e760e72014-04-02 23:19:09 +0000662 }
663 return true;
664}
665
pkasting@chromium.orge9facf82015-02-17 20:36:28 +0000666static bool GetPayloadTypeFromString(const std::string& line,
667 const std::string& s,
668 int* payload_type,
669 SdpParseError* error) {
670 return GetValueFromString(line, s, payload_type, error) &&
Yves Gerey665174f2018-06-19 15:03:05 +0200671 cricket::IsValidRtpPayloadType(*payload_type);
pkasting@chromium.orge9facf82015-02-17 20:36:28 +0000672}
673
Seth Hampson5897a6e2018-04-03 11:16:33 -0700674// Creates a StreamParams track in the case when no SSRC lines are signaled.
675// This is a track that does not contain SSRCs and only contains
676// stream_ids/track_id if it's signaled with a=msid lines.
677void CreateTrackWithNoSsrcs(const std::vector<std::string>& msid_stream_ids,
678 const std::string& msid_track_id,
Amit Hilbuchc57d5732018-12-11 15:30:11 -0800679 const std::vector<RidDescription>& rids,
Seth Hampson5897a6e2018-04-03 11:16:33 -0700680 StreamParamsVec* tracks) {
681 StreamParams track;
Amit Hilbuch6df49d82019-04-15 15:47:21 -0700682 if (msid_track_id.empty() && rids.empty()) {
Seth Hampson5897a6e2018-04-03 11:16:33 -0700683 // We only create an unsignaled track if a=msid lines were signaled.
Amit Hilbuch6df49d82019-04-15 15:47:21 -0700684 RTC_LOG(LS_INFO) << "MSID not signaled, skipping creation of StreamParams";
Seth Hampson5897a6e2018-04-03 11:16:33 -0700685 return;
686 }
687 track.set_stream_ids(msid_stream_ids);
688 track.id = msid_track_id;
Amit Hilbuchc57d5732018-12-11 15:30:11 -0800689 track.set_rids(rids);
Seth Hampson5897a6e2018-04-03 11:16:33 -0700690 tracks->push_back(track);
691}
692
693// Creates the StreamParams tracks, for the case when SSRC lines are signaled.
Artem Titov880fa812021-07-30 22:30:23 +0200694// `msid_stream_ids` and `msid_track_id` represent the stream/track ID from the
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800695// "a=msid" attribute, if it exists. They are empty if the attribute does not
Seth Hampson5897a6e2018-04-03 11:16:33 -0700696// exist. We prioritize getting stream_ids/track_ids signaled in a=msid lines.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000697void CreateTracksFromSsrcInfos(const SsrcInfoVec& ssrc_infos,
Seth Hampson5b4f0752018-04-02 16:31:36 -0700698 const std::vector<std::string>& msid_stream_ids,
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800699 const std::string& msid_track_id,
Seth Hampson5b4f0752018-04-02 16:31:36 -0700700 StreamParamsVec* tracks,
701 int msid_signaling) {
nisseede5da42017-01-12 05:15:36 -0800702 RTC_DCHECK(tracks != NULL);
Steve Anton4daf66e2018-09-07 14:55:53 -0700703 for (const SsrcInfo& ssrc_info : ssrc_infos) {
Taylor Brandstetter4256df02020-02-18 14:05:07 -0800704 // According to https://tools.ietf.org/html/rfc5576#section-6.1, the CNAME
705 // attribute is mandatory, but we relax that restriction.
Steve Anton4daf66e2018-09-07 14:55:53 -0700706 if (ssrc_info.cname.empty()) {
Taylor Brandstetter4256df02020-02-18 14:05:07 -0800707 RTC_LOG(LS_WARNING) << "CNAME attribute missing for SSRC "
708 << ssrc_info.ssrc_id;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000709 }
Seth Hampson5b4f0752018-04-02 16:31:36 -0700710 std::vector<std::string> stream_ids;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000711 std::string track_id;
Seth Hampson5b4f0752018-04-02 16:31:36 -0700712 if (msid_signaling & cricket::kMsidSignalingMediaSection) {
713 // This is the case with Unified Plan SDP msid signaling.
714 stream_ids = msid_stream_ids;
Tomas Gunnarsson191bf5c2018-03-30 10:44:43 +0000715 track_id = msid_track_id;
Seth Hampson5b4f0752018-04-02 16:31:36 -0700716 } else if (msid_signaling & cricket::kMsidSignalingSsrcAttribute) {
717 // This is the case with Plan B SDP msid signaling.
Steve Anton4daf66e2018-09-07 14:55:53 -0700718 stream_ids.push_back(ssrc_info.stream_id);
719 track_id = ssrc_info.track_id;
Seth Hampson5b4f0752018-04-02 16:31:36 -0700720 } else {
721 // Since no media streams isn't supported with older SDP signaling, we
722 // use a default a stream id.
723 stream_ids.push_back(kDefaultMsid);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000724 }
Seth Hampson5b4f0752018-04-02 16:31:36 -0700725 // If a track ID wasn't populated from the SSRC attributes OR the
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800726 // msid attribute, use default/random values.
Taylor Brandstetter5de6b752016-03-09 17:02:30 -0800727 if (track_id.empty()) {
728 // TODO(ronghuawu): What should we do if the track id doesn't appear?
729 // Create random string (which will be used as track label later)?
730 track_id = rtc::CreateRandomString(8);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000731 }
732
Steve Anton64b626b2019-01-28 17:25:26 -0800733 auto track_it = absl::c_find_if(
734 *tracks,
Steve Anton4daf66e2018-09-07 14:55:53 -0700735 [track_id](const StreamParams& track) { return track.id == track_id; });
736 if (track_it == tracks->end()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000737 // If we don't find an existing track, create a new one.
738 tracks->push_back(StreamParams());
Steve Anton4daf66e2018-09-07 14:55:53 -0700739 track_it = tracks->end() - 1;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000740 }
Steve Anton4daf66e2018-09-07 14:55:53 -0700741 StreamParams& track = *track_it;
742 track.add_ssrc(ssrc_info.ssrc_id);
743 track.cname = ssrc_info.cname;
744 track.set_stream_ids(stream_ids);
745 track.id = track_id;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000746 }
747}
748
Seth Hampson845e8782018-03-02 11:34:10 -0800749void GetMediaStreamIds(const ContentInfo* content,
750 std::set<std::string>* labels) {
Steve Antonb1c1de12017-12-21 15:14:30 -0800751 for (const StreamParams& stream_params :
752 content->media_description()->streams()) {
Seth Hampson845e8782018-03-02 11:34:10 -0800753 for (const std::string& stream_id : stream_params.stream_ids()) {
754 labels->insert(stream_id);
Steve Anton5a26a3a2018-02-28 11:38:47 -0800755 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000756 }
757}
758
759// RFC 5245
760// It is RECOMMENDED that default candidates be chosen based on the
761// likelihood of those candidates to work with the peer that is being
762// contacted. It is RECOMMENDED that relayed > reflexive > host.
763static const int kPreferenceUnknown = 0;
764static const int kPreferenceHost = 1;
765static const int kPreferenceReflexive = 2;
766static const int kPreferenceRelayed = 3;
767
768static int GetCandidatePreferenceFromType(const std::string& type) {
769 int preference = kPreferenceUnknown;
770 if (type == cricket::LOCAL_PORT_TYPE) {
771 preference = kPreferenceHost;
772 } else if (type == cricket::STUN_PORT_TYPE) {
773 preference = kPreferenceReflexive;
774 } else if (type == cricket::RELAY_PORT_TYPE) {
775 preference = kPreferenceRelayed;
776 } else {
Artem Titovd3251962021-11-15 16:57:07 +0100777 RTC_DCHECK_NOTREACHED();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000778 }
779 return preference;
780}
781
Artem Titov880fa812021-07-30 22:30:23 +0200782// Get ip and port of the default destination from the `candidates` with the
783// given value of `component_id`. The default candidate should be the one most
guoweis@webrtc.org57ac2c82015-02-06 00:45:13 +0000784// likely to work, typically IPv4 relay.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000785// RFC 5245
Artem Titov880fa812021-07-30 22:30:23 +0200786// The value of `component_id` currently supported are 1 (RTP) and 2 (RTCP).
Steve Anton36b29d12017-10-30 09:57:42 -0700787// TODO(deadbeef): Decide the default destination in webrtcsession and
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000788// pass it down via SessionDescription.
Yves Gerey665174f2018-06-19 15:03:05 +0200789static void GetDefaultDestination(const std::vector<Candidate>& candidates,
790 int component_id,
791 std::string* port,
792 std::string* ip,
793 std::string* addr_type) {
perkj@webrtc.orgd105cc82014-11-07 11:22:06 +0000794 *addr_type = kConnectionIpv4Addrtype;
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000795 *port = kDummyPort;
796 *ip = kDummyAddress;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000797 int current_preference = kPreferenceUnknown;
guoweis@webrtc.org57ac2c82015-02-06 00:45:13 +0000798 int current_family = AF_UNSPEC;
Steve Anton4daf66e2018-09-07 14:55:53 -0700799 for (const Candidate& candidate : candidates) {
800 if (candidate.component() != component_id) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000801 continue;
802 }
guoweis@webrtc.org57ac2c82015-02-06 00:45:13 +0000803 // Default destination should be UDP only.
Steve Anton4daf66e2018-09-07 14:55:53 -0700804 if (candidate.protocol() != cricket::UDP_PROTOCOL_NAME) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000805 continue;
806 }
Steve Anton4daf66e2018-09-07 14:55:53 -0700807 const int preference = GetCandidatePreferenceFromType(candidate.type());
808 const int family = candidate.address().ipaddr().family();
guoweis@webrtc.org57ac2c82015-02-06 00:45:13 +0000809 // See if this candidate is more preferable then the current one if it's the
810 // same family. Or if the current family is IPv4 already so we could safely
811 // ignore all IPv6 ones. WebRTC bug 4269.
812 // http://code.google.com/p/webrtc/issues/detail?id=4269
813 if ((preference <= current_preference && current_family == family) ||
814 (current_family == AF_INET && family == AF_INET6)) {
815 continue;
816 }
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000817 if (family == AF_INET) {
818 addr_type->assign(kConnectionIpv4Addrtype);
819 } else if (family == AF_INET6) {
820 addr_type->assign(kConnectionIpv6Addrtype);
821 }
guoweis@webrtc.org57ac2c82015-02-06 00:45:13 +0000822 current_preference = preference;
823 current_family = family;
Steve Anton4daf66e2018-09-07 14:55:53 -0700824 *port = candidate.address().PortAsString();
825 *ip = candidate.address().ipaddr().ToString();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000826 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000827}
828
Artem Titov880fa812021-07-30 22:30:23 +0200829// Gets "a=rtcp" line if found default RTCP candidate from `candidates`.
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000830static std::string GetRtcpLine(const std::vector<Candidate>& candidates) {
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000831 std::string rtcp_line, rtcp_port, rtcp_ip, addr_type;
Yves Gerey665174f2018-06-19 15:03:05 +0200832 GetDefaultDestination(candidates, ICE_CANDIDATE_COMPONENT_RTCP, &rtcp_port,
833 &rtcp_ip, &addr_type);
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000834 // Found default RTCP candidate.
835 // RFC 5245
836 // If the agent is utilizing RTCP, it MUST encode the RTCP candidate
837 // using the a=rtcp attribute as defined in RFC 3605.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000838
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000839 // RFC 3605
840 // rtcp-attribute = "a=rtcp:" port [nettype space addrtype space
841 // connection-address] CRLF
Jonas Olssonec9e4922018-09-05 09:53:49 +0200842 rtc::StringBuilder os;
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000843 InitAttrLine(kAttributeRtcp, &os);
Yves Gerey665174f2018-06-19 15:03:05 +0200844 os << kSdpDelimiterColon << rtcp_port << " " << kConnectionNettype << " "
845 << addr_type << " " << rtcp_ip;
pthatcher@webrtc.orgc9d6d142014-10-23 23:37:22 +0000846 rtcp_line = os.str();
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000847 return rtcp_line;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000848}
849
850// Get candidates according to the mline index from SessionDescriptionInterface.
851static void GetCandidatesByMindex(const SessionDescriptionInterface& desci,
852 int mline_index,
853 std::vector<Candidate>* candidates) {
854 if (!candidates) {
855 return;
856 }
857 const IceCandidateCollection* cc = desci.candidates(mline_index);
858 for (size_t i = 0; i < cc->count(); ++i) {
859 const IceCandidateInterface* candidate = cc->at(i);
860 candidates->push_back(candidate->candidate());
861 }
862}
863
deadbeef90f1e1e2017-02-10 12:35:05 -0800864static bool IsValidPort(int port) {
865 return port >= 0 && port <= 65535;
866}
867
Steve Antone831b8c2018-02-01 12:22:16 -0800868std::string SdpSerialize(const JsepSessionDescription& jdesc) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000869 const cricket::SessionDescription* desc = jdesc.description();
870 if (!desc) {
871 return "";
872 }
873
874 std::string message;
875
876 // Session Description.
877 AddLine(kSessionVersion, &message);
878 // Session Origin
879 // RFC 4566
880 // o=<username> <sess-id> <sess-version> <nettype> <addrtype>
881 // <unicast-address>
Jonas Olssonec9e4922018-09-05 09:53:49 +0200882 rtc::StringBuilder os;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000883 InitLine(kLineTypeOrigin, kSessionOriginUsername, &os);
Yves Gerey665174f2018-06-19 15:03:05 +0200884 const std::string& session_id =
885 jdesc.session_id().empty() ? kSessionOriginSessionId : jdesc.session_id();
886 const std::string& session_version = jdesc.session_version().empty()
887 ? kSessionOriginSessionVersion
888 : jdesc.session_version();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000889 os << " " << session_id << " " << session_version << " "
890 << kSessionOriginNettype << " " << kSessionOriginAddrtype << " "
891 << kSessionOriginAddress;
892 AddLine(os.str(), &message);
893 AddLine(kSessionName, &message);
894
895 // Time Description.
896 AddLine(kTimeDescription, &message);
897
Henrik Boströmf8187e02021-04-26 21:04:26 +0200898 // BUNDLE Groups
899 std::vector<const cricket::ContentGroup*> groups =
900 desc->GetGroupsByName(cricket::GROUP_TYPE_BUNDLE);
901 for (const cricket::ContentGroup* group : groups) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000902 std::string group_line = kAttrGroup;
nisseede5da42017-01-12 05:15:36 -0800903 RTC_DCHECK(group != NULL);
Steve Anton4daf66e2018-09-07 14:55:53 -0700904 for (const std::string& content_name : group->content_names()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000905 group_line.append(" ");
Steve Anton4daf66e2018-09-07 14:55:53 -0700906 group_line.append(content_name);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000907 }
908 AddLine(group_line, &message);
909 }
910
Johannes Kron0854eb62018-10-10 22:33:20 +0200911 // Mixed one- and two-byte header extension.
Johannes Kron9581bc42018-10-23 10:17:39 +0200912 if (desc->extmap_allow_mixed()) {
Johannes Kron0854eb62018-10-10 22:33:20 +0200913 InitAttrLine(kAttributeExtmapAllowMixed, &os);
914 AddLine(os.str(), &message);
915 }
916
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000917 // MediaStream semantics
918 InitAttrLine(kAttributeMsidSemantics, &os);
919 os << kSdpDelimiterColon << " " << kMediaStreamSemantic;
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000920
Seth Hampson845e8782018-03-02 11:34:10 -0800921 std::set<std::string> media_stream_ids;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000922 const ContentInfo* audio_content = GetFirstAudioContent(desc);
923 if (audio_content)
Seth Hampson845e8782018-03-02 11:34:10 -0800924 GetMediaStreamIds(audio_content, &media_stream_ids);
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000925
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000926 const ContentInfo* video_content = GetFirstVideoContent(desc);
927 if (video_content)
Seth Hampson845e8782018-03-02 11:34:10 -0800928 GetMediaStreamIds(video_content, &media_stream_ids);
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000929
Steve Anton4daf66e2018-09-07 14:55:53 -0700930 for (const std::string& id : media_stream_ids) {
931 os << " " << id;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000932 }
933 AddLine(os.str(), &message);
934
Taylor Brandstetter2f65ec52018-05-24 11:37:28 -0700935 // a=ice-lite
936 //
937 // TODO(deadbeef): It's weird that we need to iterate TransportInfos for
938 // this, when it's a session-level attribute. It really should be moved to a
939 // session-level structure like SessionDescription.
940 for (const cricket::TransportInfo& transport : desc->transport_infos()) {
941 if (transport.description.ice_mode == cricket::ICEMODE_LITE) {
942 InitAttrLine(kAttributeIceLite, &os);
943 AddLine(os.str(), &message);
944 break;
945 }
946 }
947
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000948 // Preserve the order of the media contents.
949 int mline_index = -1;
Steve Anton4daf66e2018-09-07 14:55:53 -0700950 for (const ContentInfo& content : desc->contents()) {
wu@webrtc.org4c3e9912014-07-16 21:03:13 +0000951 std::vector<Candidate> candidates;
952 GetCandidatesByMindex(jdesc, ++mline_index, &candidates);
Steve Anton4daf66e2018-09-07 14:55:53 -0700953 BuildMediaDescription(&content, desc->GetTransportInfoByName(content.name),
954 content.media_description()->type(), candidates,
955 desc->msid_signaling(), &message);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000956 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000957 return message;
958}
959
960// Serializes the passed in IceCandidateInterface to a SDP string.
961// candidate - The candidate to be serialized.
Honghai Zhang7fb69db2016-03-14 11:59:18 -0700962std::string SdpSerializeCandidate(const IceCandidateInterface& candidate) {
963 return SdpSerializeCandidate(candidate.candidate());
964}
965
966// Serializes a cricket Candidate.
967std::string SdpSerializeCandidate(const cricket::Candidate& candidate) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000968 std::string message;
Honghai Zhang7fb69db2016-03-14 11:59:18 -0700969 std::vector<cricket::Candidate> candidates(1, candidate);
honghaiza54a0802015-12-16 18:37:23 -0800970 BuildCandidate(candidates, true, &message);
wu@webrtc.orgec9f5fb2014-06-24 17:05:10 +0000971 // From WebRTC draft section 4.8.1.1 candidate-attribute will be
972 // just candidate:<candidate> not a=candidate:<blah>CRLF
nisseede5da42017-01-12 05:15:36 -0800973 RTC_DCHECK(message.find("a=") == 0);
wu@webrtc.orgec9f5fb2014-06-24 17:05:10 +0000974 message.erase(0, 2);
nisseede5da42017-01-12 05:15:36 -0800975 RTC_DCHECK(message.find(kLineBreak) == message.size() - 2);
wu@webrtc.orgec9f5fb2014-06-24 17:05:10 +0000976 message.resize(message.size() - 2);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000977 return message;
978}
979
980bool SdpDeserialize(const std::string& message,
981 JsepSessionDescription* jdesc,
982 SdpParseError* error) {
983 std::string session_id;
984 std::string session_version;
Peter Thatcher7cbd1882015-09-17 18:54:52 -0700985 TransportDescription session_td("", "");
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000986 RtpHeaderExtensions session_extmaps;
zhihuang38989e52017-03-21 11:04:53 -0700987 rtc::SocketAddress session_connection_addr;
Mirko Bonadei317a1f02019-09-17 17:06:18 +0200988 auto desc = std::make_unique<cricket::SessionDescription>();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000989 size_t current_pos = 0;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000990
991 // Session Description
992 if (!ParseSessionDescription(message, &current_pos, &session_id,
deadbeefc80741f2015-10-22 13:14:45 -0700993 &session_version, &session_td, &session_extmaps,
Steve Anton5a1de872018-12-18 11:25:55 -0800994 &session_connection_addr, desc.get(), error)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000995 return false;
996 }
997
998 // Media Description
Steve Anton5a1de872018-12-18 11:25:55 -0800999 std::vector<std::unique_ptr<JsepIceCandidate>> candidates;
deadbeefc80741f2015-10-22 13:14:45 -07001000 if (!ParseMediaDescription(message, session_td, session_extmaps, &current_pos,
Steve Anton5a1de872018-12-18 11:25:55 -08001001 session_connection_addr, desc.get(), &candidates,
zhihuang38989e52017-03-21 11:04:53 -07001002 error)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001003 return false;
1004 }
1005
Harald Alvestrand4d7160e2019-04-12 07:01:29 +02001006 jdesc->Initialize(std::move(desc), session_id, session_version);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001007
Steve Anton5a1de872018-12-18 11:25:55 -08001008 for (const auto& candidate : candidates) {
1009 jdesc->AddCandidate(candidate.get());
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001010 }
1011 return true;
1012}
1013
1014bool SdpDeserializeCandidate(const std::string& message,
1015 JsepIceCandidate* jcandidate,
1016 SdpParseError* error) {
nisseede5da42017-01-12 05:15:36 -08001017 RTC_DCHECK(jcandidate != NULL);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001018 Candidate candidate;
1019 if (!ParseCandidate(message, &candidate, error, true)) {
1020 return false;
1021 }
1022 jcandidate->SetCandidate(candidate);
1023 return true;
1024}
1025
Honghai Zhang7fb69db2016-03-14 11:59:18 -07001026bool SdpDeserializeCandidate(const std::string& transport_name,
1027 const std::string& message,
1028 cricket::Candidate* candidate,
1029 SdpParseError* error) {
nisseede5da42017-01-12 05:15:36 -08001030 RTC_DCHECK(candidate != nullptr);
Honghai Zhang7fb69db2016-03-14 11:59:18 -07001031 if (!ParseCandidate(message, candidate, error, true)) {
1032 return false;
1033 }
1034 candidate->set_transport_name(transport_name);
1035 return true;
1036}
1037
Yves Gerey665174f2018-06-19 15:03:05 +02001038bool ParseCandidate(const std::string& message,
1039 Candidate* candidate,
1040 SdpParseError* error,
1041 bool is_raw) {
nisseede5da42017-01-12 05:15:36 -08001042 RTC_DCHECK(candidate != NULL);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001043
Artem Titov880fa812021-07-30 22:30:23 +02001044 // Get the first line from `message`.
jiayl@webrtc.org7ec3f9f2014-08-08 23:09:15 +00001045 std::string first_line = message;
1046 size_t pos = 0;
1047 GetLine(message, &pos, &first_line);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001048
Artem Titov880fa812021-07-30 22:30:23 +02001049 // Makes sure `message` contains only one line.
jiayl@webrtc.org7ec3f9f2014-08-08 23:09:15 +00001050 if (message.size() > first_line.size()) {
1051 std::string left, right;
Jonas Olssonec9e4922018-09-05 09:53:49 +02001052 if (rtc::tokenize_first(message, kNewLineChar, &left, &right) &&
Donald Curtis0e07f922015-05-15 09:21:23 -07001053 !right.empty()) {
jiayl@webrtc.org7ec3f9f2014-08-08 23:09:15 +00001054 return ParseFailed(message, 0, "Expect one line only", error);
1055 }
1056 }
1057
1058 // From WebRTC draft section 4.8.1.1 candidate-attribute should be
1059 // candidate:<candidate> when trickled, but we still support
1060 // a=candidate:<blah>CRLF for backward compatibility and for parsing a line
1061 // from the SDP.
1062 if (IsLineType(first_line, kLineTypeAttributes)) {
1063 first_line = first_line.substr(kLinePrefixLength);
1064 }
1065
1066 std::string attribute_candidate;
1067 std::string candidate_value;
1068
Artem Titov880fa812021-07-30 22:30:23 +02001069 // `first_line` must be in the form of "candidate:<value>".
Jonas Olssonec9e4922018-09-05 09:53:49 +02001070 if (!rtc::tokenize_first(first_line, kSdpDelimiterColonChar,
1071 &attribute_candidate, &candidate_value) ||
jiayl@webrtc.org7ec3f9f2014-08-08 23:09:15 +00001072 attribute_candidate != kAttributeCandidate) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001073 if (is_raw) {
Jonas Olssonec9e4922018-09-05 09:53:49 +02001074 rtc::StringBuilder description;
Jonas Olssonb2b20312020-01-14 12:11:31 +01001075 description << "Expect line: " << kAttributeCandidate
1076 << ":"
1077 "<candidate-str>";
Niels Möllera820cc22021-08-11 10:07:45 +02001078 return ParseFailed(first_line, 0, description.Release(), error);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001079 } else {
1080 return ParseFailedExpectLine(first_line, 0, kLineTypeAttributes,
1081 kAttributeCandidate, error);
1082 }
1083 }
1084
1085 std::vector<std::string> fields;
Jonas Olssonec9e4922018-09-05 09:53:49 +02001086 rtc::split(candidate_value, kSdpDelimiterSpaceChar, &fields);
jiayl@webrtc.org7ec3f9f2014-08-08 23:09:15 +00001087
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001088 // RFC 5245
1089 // a=candidate:<foundation> <component-id> <transport> <priority>
1090 // <connection-address> <port> typ <candidate-types>
1091 // [raddr <connection-address>] [rport <port>]
1092 // *(SP extension-att-name SP extension-att-value)
1093 const size_t expected_min_fields = 8;
1094 if (fields.size() < expected_min_fields ||
1095 (fields[6] != kAttributeCandidateTyp)) {
1096 return ParseFailedExpectMinFieldNum(first_line, expected_min_fields, error);
1097 }
jbauch083b73f2015-07-16 02:46:32 -07001098 const std::string& foundation = fields[0];
jiayl@webrtc.org7ec3f9f2014-08-08 23:09:15 +00001099
wu@webrtc.org5e760e72014-04-02 23:19:09 +00001100 int component_id = 0;
1101 if (!GetValueFromString(first_line, fields[1], &component_id, error)) {
1102 return false;
1103 }
jbauch083b73f2015-07-16 02:46:32 -07001104 const std::string& transport = fields[2];
Peter Boström0c4e06b2015-10-07 12:23:21 +02001105 uint32_t priority = 0;
wu@webrtc.org5e760e72014-04-02 23:19:09 +00001106 if (!GetValueFromString(first_line, fields[3], &priority, error)) {
1107 return false;
1108 }
jbauch083b73f2015-07-16 02:46:32 -07001109 const std::string& connection_address = fields[4];
wu@webrtc.org5e760e72014-04-02 23:19:09 +00001110 int port = 0;
1111 if (!GetValueFromString(first_line, fields[5], &port, error)) {
1112 return false;
1113 }
deadbeef90f1e1e2017-02-10 12:35:05 -08001114 if (!IsValidPort(port)) {
1115 return ParseFailed(first_line, "Invalid port number.", error);
1116 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001117 SocketAddress address(connection_address, port);
1118
1119 cricket::ProtocolType protocol;
hnslb68cc752016-12-13 10:33:41 -08001120 if (!StringToProto(transport.c_str(), &protocol)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001121 return ParseFailed(first_line, "Unsupported transport type.", error);
1122 }
Taylor Brandstetter8206bc02020-04-02 12:36:38 -07001123 bool tcp_protocol = false;
hnslb68cc752016-12-13 10:33:41 -08001124 switch (protocol) {
Taylor Brandstetter8206bc02020-04-02 12:36:38 -07001125 // Supported protocols.
hnslb68cc752016-12-13 10:33:41 -08001126 case cricket::PROTO_UDP:
Taylor Brandstetter8206bc02020-04-02 12:36:38 -07001127 break;
hnslb68cc752016-12-13 10:33:41 -08001128 case cricket::PROTO_TCP:
1129 case cricket::PROTO_SSLTCP:
Taylor Brandstetter8206bc02020-04-02 12:36:38 -07001130 tcp_protocol = true;
hnslb68cc752016-12-13 10:33:41 -08001131 break;
1132 default:
1133 return ParseFailed(first_line, "Unsupported transport type.", error);
1134 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001135
1136 std::string candidate_type;
jbauch083b73f2015-07-16 02:46:32 -07001137 const std::string& type = fields[7];
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001138 if (type == kCandidateHost) {
1139 candidate_type = cricket::LOCAL_PORT_TYPE;
1140 } else if (type == kCandidateSrflx) {
1141 candidate_type = cricket::STUN_PORT_TYPE;
1142 } else if (type == kCandidateRelay) {
1143 candidate_type = cricket::RELAY_PORT_TYPE;
Honghai Zhang7fb69db2016-03-14 11:59:18 -07001144 } else if (type == kCandidatePrflx) {
1145 candidate_type = cricket::PRFLX_PORT_TYPE;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001146 } else {
1147 return ParseFailed(first_line, "Unsupported candidate type.", error);
1148 }
1149
1150 size_t current_position = expected_min_fields;
1151 SocketAddress related_address;
1152 // The 2 optional fields for related address
1153 // [raddr <connection-address>] [rport <port>]
1154 if (fields.size() >= (current_position + 2) &&
1155 fields[current_position] == kAttributeCandidateRaddr) {
1156 related_address.SetIP(fields[++current_position]);
1157 ++current_position;
1158 }
1159 if (fields.size() >= (current_position + 2) &&
1160 fields[current_position] == kAttributeCandidateRport) {
wu@webrtc.org5e760e72014-04-02 23:19:09 +00001161 int port = 0;
Yves Gerey665174f2018-06-19 15:03:05 +02001162 if (!GetValueFromString(first_line, fields[++current_position], &port,
1163 error)) {
wu@webrtc.org5e760e72014-04-02 23:19:09 +00001164 return false;
1165 }
deadbeef90f1e1e2017-02-10 12:35:05 -08001166 if (!IsValidPort(port)) {
1167 return ParseFailed(first_line, "Invalid port number.", error);
1168 }
wu@webrtc.org5e760e72014-04-02 23:19:09 +00001169 related_address.SetPort(port);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001170 ++current_position;
1171 }
1172
mallinath@webrtc.org2d60c5e2014-08-08 22:29:20 +00001173 // If this is a TCP candidate, it has additional extension as defined in
1174 // RFC 6544.
1175 std::string tcptype;
1176 if (fields.size() >= (current_position + 2) &&
1177 fields[current_position] == kTcpCandidateType) {
1178 tcptype = fields[++current_position];
1179 ++current_position;
1180
1181 if (tcptype != cricket::TCPTYPE_ACTIVE_STR &&
1182 tcptype != cricket::TCPTYPE_PASSIVE_STR &&
1183 tcptype != cricket::TCPTYPE_SIMOPEN_STR) {
1184 return ParseFailed(first_line, "Invalid TCP candidate type.", error);
1185 }
1186
Taylor Brandstetter8206bc02020-04-02 12:36:38 -07001187 if (!tcp_protocol) {
mallinath@webrtc.org2d60c5e2014-08-08 22:29:20 +00001188 return ParseFailed(first_line, "Invalid non-TCP candidate", error);
1189 }
Taylor Brandstetter8206bc02020-04-02 12:36:38 -07001190 } else if (tcp_protocol) {
1191 // We allow the tcptype to be missing, for backwards compatibility,
1192 // treating it as a passive candidate.
1193 // TODO(bugs.webrtc.org/11466): Treat a missing tcptype as an error?
1194 tcptype = cricket::TCPTYPE_PASSIVE_STR;
mallinath@webrtc.org2d60c5e2014-08-08 22:29:20 +00001195 }
1196
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001197 // Extension
honghaiza54a0802015-12-16 18:37:23 -08001198 // Though non-standard, we support the ICE ufrag and pwd being signaled on
1199 // the candidate to avoid issues with confusing which generation a candidate
1200 // belongs to when trickling multiple generations at the same time.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001201 std::string username;
1202 std::string password;
Peter Boström0c4e06b2015-10-07 12:23:21 +02001203 uint32_t generation = 0;
honghaiza0c44ea2016-03-23 16:07:48 -07001204 uint16_t network_id = 0;
1205 uint16_t network_cost = 0;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001206 for (size_t i = current_position; i + 1 < fields.size(); ++i) {
1207 // RFC 5245
1208 // *(SP extension-att-name SP extension-att-value)
1209 if (fields[i] == kAttributeCandidateGeneration) {
wu@webrtc.org5e760e72014-04-02 23:19:09 +00001210 if (!GetValueFromString(first_line, fields[++i], &generation, error)) {
1211 return false;
1212 }
honghaiza54a0802015-12-16 18:37:23 -08001213 } else if (fields[i] == kAttributeCandidateUfrag) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001214 username = fields[++i];
honghaiza54a0802015-12-16 18:37:23 -08001215 } else if (fields[i] == kAttributeCandidatePwd) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001216 password = fields[++i];
honghaiza0c44ea2016-03-23 16:07:48 -07001217 } else if (fields[i] == kAttributeCandidateNetworkId) {
1218 if (!GetValueFromString(first_line, fields[++i], &network_id, error)) {
1219 return false;
1220 }
honghaize1a0c942016-02-16 14:54:56 -08001221 } else if (fields[i] == kAttributeCandidateNetworkCost) {
1222 if (!GetValueFromString(first_line, fields[++i], &network_cost, error)) {
1223 return false;
1224 }
Honghai Zhang351d77b2016-05-20 15:08:29 -07001225 network_cost = std::min(network_cost, rtc::kNetworkCostMax);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001226 } else {
1227 // Skip the unknown extension.
1228 ++i;
1229 }
1230 }
1231
guoweis@webrtc.org61c12472015-01-15 06:53:07 +00001232 *candidate = Candidate(component_id, cricket::ProtoToString(protocol),
guoweis@webrtc.org950c5182014-12-16 23:01:31 +00001233 address, priority, username, password, candidate_type,
honghaiza0c44ea2016-03-23 16:07:48 -07001234 generation, foundation, network_id, network_cost);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001235 candidate->set_related_address(related_address);
mallinath@webrtc.org2d60c5e2014-08-08 22:29:20 +00001236 candidate->set_tcptype(tcptype);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001237 return true;
1238}
1239
1240bool ParseIceOptions(const std::string& line,
1241 std::vector<std::string>* transport_options,
1242 SdpParseError* error) {
1243 std::string ice_options;
1244 if (!GetValue(line, kAttributeIceOption, &ice_options, error)) {
1245 return false;
1246 }
1247 std::vector<std::string> fields;
Jonas Olssonec9e4922018-09-05 09:53:49 +02001248 rtc::split(ice_options, kSdpDelimiterSpaceChar, &fields);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001249 for (size_t i = 0; i < fields.size(); ++i) {
1250 transport_options->push_back(fields[i]);
1251 }
1252 return true;
1253}
1254
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00001255bool ParseSctpPort(const std::string& line,
1256 int* sctp_port,
1257 SdpParseError* error) {
Harald Alvestrand48cce4d2019-04-11 10:41:24 +02001258 // draft-ietf-mmusic-sctp-sdp-26
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00001259 // a=sctp-port
1260 std::vector<std::string> fields;
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00001261 const size_t expected_min_fields = 2;
Jonas Olssonec9e4922018-09-05 09:53:49 +02001262 rtc::split(line.substr(kLinePrefixLength), kSdpDelimiterColonChar, &fields);
lally69f57602015-10-08 10:15:04 -07001263 if (fields.size() < expected_min_fields) {
1264 fields.resize(0);
Jonas Olssonec9e4922018-09-05 09:53:49 +02001265 rtc::split(line.substr(kLinePrefixLength), kSdpDelimiterSpaceChar, &fields);
lally69f57602015-10-08 10:15:04 -07001266 }
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00001267 if (fields.size() < expected_min_fields) {
1268 return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
1269 }
1270 if (!rtc::FromString(fields[1], sctp_port)) {
lally69f57602015-10-08 10:15:04 -07001271 return ParseFailed(line, "Invalid sctp port value.", error);
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00001272 }
1273 return true;
1274}
1275
Harald Alvestrand48cce4d2019-04-11 10:41:24 +02001276bool ParseSctpMaxMessageSize(const std::string& line,
1277 int* max_message_size,
1278 SdpParseError* error) {
1279 // draft-ietf-mmusic-sctp-sdp-26
1280 // a=max-message-size:199999
1281 std::vector<std::string> fields;
1282 const size_t expected_min_fields = 2;
1283 rtc::split(line.substr(kLinePrefixLength), kSdpDelimiterColonChar, &fields);
1284 if (fields.size() < expected_min_fields) {
1285 return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
1286 }
1287 if (!rtc::FromString(fields[1], max_message_size)) {
1288 return ParseFailed(line, "Invalid SCTP max message size.", error);
1289 }
1290 return true;
1291}
1292
isheriff6f8d6862016-05-26 11:24:55 -07001293bool ParseExtmap(const std::string& line,
1294 RtpExtension* extmap,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001295 SdpParseError* error) {
1296 // RFC 5285
1297 // a=extmap:<value>["/"<direction>] <URI> <extensionattributes>
1298 std::vector<std::string> fields;
Jonas Olssonec9e4922018-09-05 09:53:49 +02001299 rtc::split(line.substr(kLinePrefixLength), kSdpDelimiterSpaceChar, &fields);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001300 const size_t expected_min_fields = 2;
1301 if (fields.size() < expected_min_fields) {
1302 return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
1303 }
1304 std::string uri = fields[1];
1305
1306 std::string value_direction;
1307 if (!GetValue(fields[0], kAttributeExtmap, &value_direction, error)) {
1308 return false;
1309 }
1310 std::vector<std::string> sub_fields;
Jonas Olssonec9e4922018-09-05 09:53:49 +02001311 rtc::split(value_direction, kSdpDelimiterSlashChar, &sub_fields);
wu@webrtc.org5e760e72014-04-02 23:19:09 +00001312 int value = 0;
1313 if (!GetValueFromString(line, sub_fields[0], &value, error)) {
1314 return false;
1315 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001316
jbauch5869f502017-06-29 12:31:36 -07001317 bool encrypted = false;
1318 if (uri == RtpExtension::kEncryptHeaderExtensionsUri) {
1319 // RFC 6904
Steve Anton36b29d12017-10-30 09:57:42 -07001320 // a=extmap:<value["/"<direction>] urn:ietf:params:rtp-hdrext:encrypt <URI>
1321 // <extensionattributes>
jbauch5869f502017-06-29 12:31:36 -07001322 const size_t expected_min_fields_encrypted = expected_min_fields + 1;
1323 if (fields.size() < expected_min_fields_encrypted) {
1324 return ParseFailedExpectMinFieldNum(line, expected_min_fields_encrypted,
Yves Gerey665174f2018-06-19 15:03:05 +02001325 error);
jbauch5869f502017-06-29 12:31:36 -07001326 }
1327
1328 encrypted = true;
1329 uri = fields[2];
1330 if (uri == RtpExtension::kEncryptHeaderExtensionsUri) {
1331 return ParseFailed(line, "Recursive encrypted header.", error);
1332 }
1333 }
1334
1335 *extmap = RtpExtension(uri, value, encrypted);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001336 return true;
1337}
1338
Harald Alvestrandfbb45bd2019-05-15 08:07:47 +02001339static void BuildSctpContentAttributes(
1340 std::string* message,
1341 const cricket::SctpDataContentDescription* data_desc) {
1342 rtc::StringBuilder os;
1343 if (data_desc->use_sctpmap()) {
1344 // draft-ietf-mmusic-sctp-sdp-04
1345 // a=sctpmap:sctpmap-number protocol [streams]
1346 rtc::StringBuilder os;
1347 InitAttrLine(kAttributeSctpmap, &os);
1348 os << kSdpDelimiterColon << data_desc->port() << kSdpDelimiterSpace
1349 << kDefaultSctpmapProtocol << kSdpDelimiterSpace
1350 << cricket::kMaxSctpStreams;
1351 AddLine(os.str(), message);
1352 } else {
1353 // draft-ietf-mmusic-sctp-sdp-23
1354 // a=sctp-port:<port>
1355 InitAttrLine(kAttributeSctpPort, &os);
1356 os << kSdpDelimiterColon << data_desc->port();
1357 AddLine(os.str(), message);
1358 if (data_desc->max_message_size() != kDefaultSctpMaxMessageSize) {
1359 InitAttrLine(kAttributeMaxMessageSize, &os);
1360 os << kSdpDelimiterColon << data_desc->max_message_size();
1361 AddLine(os.str(), message);
1362 }
1363 }
1364}
1365
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001366void BuildMediaDescription(const ContentInfo* content_info,
1367 const TransportInfo* transport_info,
Patrik Höglundb6b29e02018-06-21 16:58:01 +02001368 const cricket::MediaType media_type,
wu@webrtc.org4c3e9912014-07-16 21:03:13 +00001369 const std::vector<Candidate>& candidates,
Steve Antone831b8c2018-02-01 12:22:16 -08001370 int msid_signaling,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001371 std::string* message) {
nisseede5da42017-01-12 05:15:36 -08001372 RTC_DCHECK(message != NULL);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001373 if (content_info == NULL || message == NULL) {
1374 return;
1375 }
Jonas Olssonec9e4922018-09-05 09:53:49 +02001376 rtc::StringBuilder os;
Steve Antonb1c1de12017-12-21 15:14:30 -08001377 const MediaContentDescription* media_desc = content_info->media_description();
1378 RTC_DCHECK(media_desc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001379
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001380 // RFC 4566
1381 // m=<media> <port> <proto> <fmt>
1382 // fmt is a list of payload type numbers that MAY be used in the session.
Philipp Hancke4e8c1152020-10-13 12:43:15 +02001383 std::string type;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001384 std::string fmt;
1385 if (media_type == cricket::MEDIA_TYPE_VIDEO) {
Philipp Hancke4e8c1152020-10-13 12:43:15 +02001386 type = kMediaTypeVideo;
Steve Antonb1c1de12017-12-21 15:14:30 -08001387 const VideoContentDescription* video_desc = media_desc->as_video();
Steve Anton4daf66e2018-09-07 14:55:53 -07001388 for (const cricket::VideoCodec& codec : video_desc->codecs()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001389 fmt.append(" ");
Steve Anton4daf66e2018-09-07 14:55:53 -07001390 fmt.append(rtc::ToString(codec.id));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001391 }
1392 } else if (media_type == cricket::MEDIA_TYPE_AUDIO) {
Philipp Hancke4e8c1152020-10-13 12:43:15 +02001393 type = kMediaTypeAudio;
Steve Antonb1c1de12017-12-21 15:14:30 -08001394 const AudioContentDescription* audio_desc = media_desc->as_audio();
Steve Anton4daf66e2018-09-07 14:55:53 -07001395 for (const cricket::AudioCodec& codec : audio_desc->codecs()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001396 fmt.append(" ");
Steve Anton4daf66e2018-09-07 14:55:53 -07001397 fmt.append(rtc::ToString(codec.id));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001398 }
1399 } else if (media_type == cricket::MEDIA_TYPE_DATA) {
Philipp Hancke4e8c1152020-10-13 12:43:15 +02001400 type = kMediaTypeData;
Harald Alvestrand5fc28b12019-05-13 13:36:16 +02001401 const cricket::SctpDataContentDescription* sctp_data_desc =
1402 media_desc->as_sctp();
1403 if (sctp_data_desc) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001404 fmt.append(" ");
jiayl@webrtc.org9c16c392014-05-01 18:30:30 +00001405
Harald Alvestrand5fc28b12019-05-13 13:36:16 +02001406 if (sctp_data_desc->use_sctpmap()) {
1407 fmt.append(rtc::ToString(sctp_data_desc->port()));
zstein4b2e0822017-02-17 19:48:38 -08001408 } else {
1409 fmt.append(kDefaultSctpmapProtocol);
1410 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001411 } else {
Artem Titovd3251962021-11-15 16:57:07 +01001412 RTC_DCHECK_NOTREACHED() << "Data description without SCTP";
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001413 }
Philipp Hancke4e8c1152020-10-13 12:43:15 +02001414 } else if (media_type == cricket::MEDIA_TYPE_UNSUPPORTED) {
1415 const UnsupportedContentDescription* unsupported_desc =
1416 media_desc->as_unsupported();
1417 type = unsupported_desc->media_type();
1418 } else {
Artem Titovd3251962021-11-15 16:57:07 +01001419 RTC_DCHECK_NOTREACHED();
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001420 }
1421 // The fmt must never be empty. If no codecs are found, set the fmt attribute
1422 // to 0.
1423 if (fmt.empty()) {
1424 fmt = " 0";
1425 }
1426
deadbeef25ed4352016-12-12 18:37:36 -08001427 // The port number in the m line will be updated later when associated with
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001428 // the candidates.
deadbeef25ed4352016-12-12 18:37:36 -08001429 //
1430 // A port value of 0 indicates that the m= section is rejected.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001431 // RFC 3264
1432 // To reject an offered stream, the port number in the corresponding stream in
1433 // the answer MUST be set to zero.
deadbeef25ed4352016-12-12 18:37:36 -08001434 //
1435 // However, the BUNDLE draft adds a new meaning to port zero, when used along
1436 // with a=bundle-only.
zhihuang38989e52017-03-21 11:04:53 -07001437 std::string port = kDummyPort;
1438 if (content_info->rejected || content_info->bundle_only) {
1439 port = kMediaPortRejected;
1440 } else if (!media_desc->connection_address().IsNil()) {
1441 port = rtc::ToString(media_desc->connection_address().port());
1442 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001443
Yves Gerey665174f2018-06-19 15:03:05 +02001444 rtc::SSLFingerprint* fp =
1445 (transport_info) ? transport_info->description.identity_fingerprint.get()
1446 : NULL;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001447
wu@webrtc.org4c3e9912014-07-16 21:03:13 +00001448 // Add the m and c lines.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001449 InitLine(kLineTypeMedia, type, &os);
1450 os << " " << port << " " << media_desc->protocol() << fmt;
zhihuang38989e52017-03-21 11:04:53 -07001451 AddLine(os.str(), message);
1452
1453 InitLine(kLineTypeConnection, kConnectionNettype, &os);
1454 if (media_desc->connection_address().IsNil()) {
1455 os << " " << kConnectionIpv4Addrtype << " " << kDummyAddress;
1456 } else if (media_desc->connection_address().family() == AF_INET) {
1457 os << " " << kConnectionIpv4Addrtype << " "
1458 << media_desc->connection_address().ipaddr().ToString();
Qingsi Wang56991422019-02-08 12:53:06 -08001459 } else if (media_desc->connection_address().family() == AF_INET6) {
zhihuang38989e52017-03-21 11:04:53 -07001460 os << " " << kConnectionIpv6Addrtype << " "
1461 << media_desc->connection_address().ipaddr().ToString();
Qingsi Wang56991422019-02-08 12:53:06 -08001462 } else {
1463 os << " " << kConnectionIpv4Addrtype << " " << kDummyAddress;
zhihuang38989e52017-03-21 11:04:53 -07001464 }
1465 AddLine(os.str(), message);
wu@webrtc.org4c3e9912014-07-16 21:03:13 +00001466
1467 // RFC 4566
Taylor Brandstetteree8c2462020-07-27 15:52:02 -07001468 // b=AS:<bandwidth> or
1469 // b=TIAS:<bandwidth>
1470 int bandwidth = media_desc->bandwidth();
1471 std::string bandwidth_type = media_desc->bandwidth_type();
1472 if (bandwidth_type == kApplicationSpecificBandwidth && bandwidth >= 1000) {
1473 InitLine(kLineTypeSessionBandwidth, bandwidth_type, &os);
1474 bandwidth /= 1000;
1475 os << kSdpDelimiterColon << bandwidth;
1476 AddLine(os.str(), message);
1477 } else if (bandwidth_type == kTransportSpecificBandwidth && bandwidth > 0) {
1478 InitLine(kLineTypeSessionBandwidth, bandwidth_type, &os);
1479 os << kSdpDelimiterColon << bandwidth;
wu@webrtc.org4c3e9912014-07-16 21:03:13 +00001480 AddLine(os.str(), message);
1481 }
1482
deadbeef25ed4352016-12-12 18:37:36 -08001483 // Add the a=bundle-only line.
1484 if (content_info->bundle_only) {
1485 InitAttrLine(kAttributeBundleOnly, &os);
1486 AddLine(os.str(), message);
1487 }
1488
wu@webrtc.org4c3e9912014-07-16 21:03:13 +00001489 // Add the a=rtcp line.
Harald Alvestrand5fc28b12019-05-13 13:36:16 +02001490 if (cricket::IsRtpProtocol(media_desc->protocol())) {
wu@webrtc.org4c3e9912014-07-16 21:03:13 +00001491 std::string rtcp_line = GetRtcpLine(candidates);
1492 if (!rtcp_line.empty()) {
1493 AddLine(rtcp_line, message);
1494 }
1495 }
1496
honghaiza54a0802015-12-16 18:37:23 -08001497 // Build the a=candidate lines. We don't include ufrag and pwd in the
1498 // candidates in the SDP to avoid redundancy.
1499 BuildCandidate(candidates, false, message);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001500
1501 // Use the transport_info to build the media level ice-ufrag and ice-pwd.
1502 if (transport_info) {
1503 // RFC 5245
1504 // ice-pwd-att = "ice-pwd" ":" password
1505 // ice-ufrag-att = "ice-ufrag" ":" ufrag
1506 // ice-ufrag
deadbeef3f7219b2015-12-28 15:17:14 -08001507 if (!transport_info->description.ice_ufrag.empty()) {
1508 InitAttrLine(kAttributeIceUfrag, &os);
1509 os << kSdpDelimiterColon << transport_info->description.ice_ufrag;
1510 AddLine(os.str(), message);
1511 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001512 // ice-pwd
deadbeef3f7219b2015-12-28 15:17:14 -08001513 if (!transport_info->description.ice_pwd.empty()) {
1514 InitAttrLine(kAttributeIcePwd, &os);
1515 os << kSdpDelimiterColon << transport_info->description.ice_pwd;
1516 AddLine(os.str(), message);
1517 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001518
1519 // draft-petithuguenin-mmusic-ice-attributes-level-03
1520 BuildIceOptions(transport_info->description.transport_options, message);
1521
1522 // RFC 4572
1523 // fingerprint-attribute =
1524 // "fingerprint" ":" hash-func SP fingerprint
1525 if (fp) {
1526 // Insert the fingerprint attribute.
1527 InitAttrLine(kAttributeFingerprint, &os);
Yves Gerey665174f2018-06-19 15:03:05 +02001528 os << kSdpDelimiterColon << fp->algorithm << kSdpDelimiterSpace
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001529 << fp->GetRfc4572Fingerprint();
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001530 AddLine(os.str(), message);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001531
1532 // Inserting setup attribute.
1533 if (transport_info->description.connection_role !=
Yves Gerey665174f2018-06-19 15:03:05 +02001534 cricket::CONNECTIONROLE_NONE) {
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001535 // Making sure we are not using "passive" mode.
1536 cricket::ConnectionRole role =
1537 transport_info->description.connection_role;
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +00001538 std::string dtls_role_str;
nissec16fa5e2017-02-07 07:18:43 -08001539 const bool success =
1540 cricket::ConnectionRoleToString(role, &dtls_role_str);
1541 RTC_DCHECK(success);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001542 InitAttrLine(kAttributeSetup, &os);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001543 os << kSdpDelimiterColon << dtls_role_str;
1544 AddLine(os.str(), message);
1545 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001546 }
1547 }
1548
1549 // RFC 3388
1550 // mid-attribute = "a=mid:" identification-tag
1551 // identification-tag = token
1552 // Use the content name as the mid identification-tag.
1553 InitAttrLine(kAttributeMid, &os);
1554 os << kSdpDelimiterColon << content_info->name;
1555 AddLine(os.str(), message);
1556
Harald Alvestrand5fc28b12019-05-13 13:36:16 +02001557 if (cricket::IsDtlsSctp(media_desc->protocol())) {
1558 const cricket::SctpDataContentDescription* data_desc =
1559 media_desc->as_sctp();
Harald Alvestrandfbb45bd2019-05-15 08:07:47 +02001560 BuildSctpContentAttributes(message, data_desc);
Harald Alvestrand5fc28b12019-05-13 13:36:16 +02001561 } else if (cricket::IsRtpProtocol(media_desc->protocol())) {
Steve Antone831b8c2018-02-01 12:22:16 -08001562 BuildRtpContentAttributes(media_desc, media_type, msid_signaling, message);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001563 }
1564}
1565
deadbeef9d3584c2016-02-16 17:54:10 -08001566void BuildRtpContentAttributes(const MediaContentDescription* media_desc,
Patrik Höglundb6b29e02018-06-21 16:58:01 +02001567 const cricket::MediaType media_type,
Steve Antone831b8c2018-02-01 12:22:16 -08001568 int msid_signaling,
deadbeef9d3584c2016-02-16 17:54:10 -08001569 std::string* message) {
Amit Hilbuchc57d5732018-12-11 15:30:11 -08001570 SdpSerializer serializer;
Jonas Olssonec9e4922018-09-05 09:53:49 +02001571 rtc::StringBuilder os;
Johannes Kron0854eb62018-10-10 22:33:20 +02001572 // RFC 8285
1573 // a=extmap-allow-mixed
1574 // The attribute MUST be either on session level or media level. We support
1575 // responding on both levels, however, we don't respond on media level if it's
1576 // set on session level.
Johannes Kron9581bc42018-10-23 10:17:39 +02001577 if (media_desc->extmap_allow_mixed_enum() ==
Johannes Kron0854eb62018-10-10 22:33:20 +02001578 MediaContentDescription::kMedia) {
1579 InitAttrLine(kAttributeExtmapAllowMixed, &os);
1580 AddLine(os.str(), message);
1581 }
1582 // RFC 8285
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001583 // a=extmap:<value>["/"<direction>] <URI> <extensionattributes>
1584 // The definitions MUST be either all session level or all media level. This
1585 // implementation uses all media level.
1586 for (size_t i = 0; i < media_desc->rtp_header_extensions().size(); ++i) {
jbauch5869f502017-06-29 12:31:36 -07001587 const RtpExtension& extension = media_desc->rtp_header_extensions()[i];
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001588 InitAttrLine(kAttributeExtmap, &os);
jbauch5869f502017-06-29 12:31:36 -07001589 os << kSdpDelimiterColon << extension.id;
1590 if (extension.encrypt) {
1591 os << kSdpDelimiterSpace << RtpExtension::kEncryptHeaderExtensionsUri;
1592 }
1593 os << kSdpDelimiterSpace << extension.uri;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001594 AddLine(os.str(), message);
1595 }
1596
1597 // RFC 3264
1598 // a=sendrecv || a=sendonly || a=sendrecv || a=inactive
deadbeefc80741f2015-10-22 13:14:45 -07001599 switch (media_desc->direction()) {
Harald Alvestrand6060df52020-08-11 09:54:02 +02001600 // Special case that for sdp purposes should be treated same as inactive.
1601 case RtpTransceiverDirection::kStopped:
Steve Anton4e70a722017-11-28 14:57:10 -08001602 case RtpTransceiverDirection::kInactive:
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001603 InitAttrLine(kAttributeInactive, &os);
1604 break;
Steve Anton4e70a722017-11-28 14:57:10 -08001605 case RtpTransceiverDirection::kSendOnly:
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001606 InitAttrLine(kAttributeSendOnly, &os);
1607 break;
Steve Anton4e70a722017-11-28 14:57:10 -08001608 case RtpTransceiverDirection::kRecvOnly:
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001609 InitAttrLine(kAttributeRecvOnly, &os);
1610 break;
Steve Anton4e70a722017-11-28 14:57:10 -08001611 case RtpTransceiverDirection::kSendRecv:
Markus Handell45c104b2020-03-11 10:51:13 +01001612 InitAttrLine(kAttributeSendRecv, &os);
1613 break;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001614 default:
Artem Titovd3251962021-11-15 16:57:07 +01001615 RTC_DCHECK_NOTREACHED();
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001616 InitAttrLine(kAttributeSendRecv, &os);
1617 break;
1618 }
1619 AddLine(os.str(), message);
1620
Seth Hampson5b4f0752018-04-02 16:31:36 -07001621 // Specified in https://datatracker.ietf.org/doc/draft-ietf-mmusic-msid/16/
1622 // a=msid:<msid-id> <msid-appdata>
1623 // The msid-id is a 1*64 token char representing the media stream id, and the
1624 // msid-appdata is a 1*64 token char representing the track id. There is a
1625 // line for every media stream, with a special msid-id value of "-"
1626 // representing no streams. The value of "msid-appdata" MUST be identical for
1627 // all lines.
Steve Antone831b8c2018-02-01 12:22:16 -08001628 if (msid_signaling & cricket::kMsidSignalingMediaSection) {
1629 const StreamParamsVec& streams = media_desc->streams();
1630 if (streams.size() == 1u) {
1631 const StreamParams& track = streams[0];
Seth Hampson5b4f0752018-04-02 16:31:36 -07001632 std::vector<std::string> stream_ids = track.stream_ids();
1633 if (stream_ids.empty()) {
1634 stream_ids.push_back(kNoStreamMsid);
1635 }
1636 for (const std::string& stream_id : stream_ids) {
1637 InitAttrLine(kAttributeMsid, &os);
1638 os << kSdpDelimiterColon << stream_id << kSdpDelimiterSpace << track.id;
1639 AddLine(os.str(), message);
1640 }
Steve Antone831b8c2018-02-01 12:22:16 -08001641 } else if (streams.size() > 1u) {
1642 RTC_LOG(LS_WARNING)
1643 << "Trying to serialize Unified Plan SDP with more than "
Jonas Olsson45cc8902018-02-13 10:37:07 +01001644 "one track in a media section. Omitting 'a=msid'.";
deadbeef9d3584c2016-02-16 17:54:10 -08001645 }
1646 }
1647
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001648 // RFC 5761
1649 // a=rtcp-mux
1650 if (media_desc->rtcp_mux()) {
1651 InitAttrLine(kAttributeRtcpMux, &os);
1652 AddLine(os.str(), message);
1653 }
1654
deadbeef13871492015-12-09 12:37:51 -08001655 // RFC 5506
1656 // a=rtcp-rsize
1657 if (media_desc->rtcp_reduced_size()) {
1658 InitAttrLine(kAttributeRtcpReducedSize, &os);
1659 AddLine(os.str(), message);
1660 }
1661
deadbeefd45aea82017-09-16 01:24:29 -07001662 if (media_desc->conference_mode()) {
1663 InitAttrLine(kAttributeXGoogleFlag, &os);
1664 os << kSdpDelimiterColon << kValueConference;
1665 AddLine(os.str(), message);
1666 }
1667
Sebastian Janssone1795f42019-07-24 11:38:03 +02001668 if (media_desc->remote_estimate()) {
1669 InitAttrLine(kAttributeRtcpRemoteEstimate, &os);
1670 AddLine(os.str(), message);
1671 }
1672
Harald Alvestrand0d018412021-11-04 13:52:31 +00001673 // RFC 4568
1674 // a=crypto:<tag> <crypto-suite> <key-params> [<session-params>]
1675 for (const CryptoParams& crypto_params : media_desc->cryptos()) {
1676 InitAttrLine(kAttributeCrypto, &os);
1677 os << kSdpDelimiterColon << crypto_params.tag << " "
1678 << crypto_params.cipher_suite << " " << crypto_params.key_params;
1679 if (!crypto_params.session_params.empty()) {
1680 os << " " << crypto_params.session_params;
1681 }
1682 AddLine(os.str(), message);
1683 }
1684
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001685 // RFC 4566
1686 // a=rtpmap:<payload type> <encoding name>/<clock rate>
1687 // [/<encodingparameters>]
1688 BuildRtpMap(media_desc, media_type, message);
1689
Steve Anton4daf66e2018-09-07 14:55:53 -07001690 for (const StreamParams& track : media_desc->streams()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001691 // Build the ssrc-group lines.
Steve Anton4daf66e2018-09-07 14:55:53 -07001692 for (const SsrcGroup& ssrc_group : track.ssrc_groups) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001693 // RFC 5576
1694 // a=ssrc-group:<semantics> <ssrc-id> ...
Steve Anton4daf66e2018-09-07 14:55:53 -07001695 if (ssrc_group.ssrcs.empty()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001696 continue;
1697 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001698 InitAttrLine(kAttributeSsrcGroup, &os);
Steve Anton4daf66e2018-09-07 14:55:53 -07001699 os << kSdpDelimiterColon << ssrc_group.semantics;
1700 for (uint32_t ssrc : ssrc_group.ssrcs) {
1701 os << kSdpDelimiterSpace << rtc::ToString(ssrc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001702 }
1703 AddLine(os.str(), message);
1704 }
1705 // Build the ssrc lines for each ssrc.
Steve Anton4daf66e2018-09-07 14:55:53 -07001706 for (uint32_t ssrc : track.ssrcs) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001707 // RFC 5576
1708 // a=ssrc:<ssrc-id> cname:<value>
Steve Anton4daf66e2018-09-07 14:55:53 -07001709 AddSsrcLine(ssrc, kSsrcAttributeCname, track.cname, message);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001710
Steve Antone831b8c2018-02-01 12:22:16 -08001711 if (msid_signaling & cricket::kMsidSignalingSsrcAttribute) {
1712 // draft-alvestrand-mmusic-msid-00
1713 // a=ssrc:<ssrc-id> msid:identifier [appdata]
1714 // The appdata consists of the "id" attribute of a MediaStreamTrack,
1715 // which corresponds to the "id" attribute of StreamParams.
Seth Hampson5b4f0752018-04-02 16:31:36 -07001716 // Since a=ssrc msid signaling is used in Plan B SDP semantics, and
1717 // multiple stream ids are not supported for Plan B, we are only adding
1718 // a line for the first media stream id here.
Seth Hampson7fa6ee62018-10-17 10:25:28 -07001719 const std::string& track_stream_id = track.first_stream_id();
1720 // We use a special msid-id value of "-" to represent no streams,
1721 // for Unified Plan compatibility. Plan B will always have a
1722 // track_stream_id.
1723 const std::string& stream_id =
1724 track_stream_id.empty() ? kNoStreamMsid : track_stream_id;
Steve Antone831b8c2018-02-01 12:22:16 -08001725 InitAttrLine(kAttributeSsrc, &os);
1726 os << kSdpDelimiterColon << ssrc << kSdpDelimiterSpace
1727 << kSsrcAttributeMsid << kSdpDelimiterColon << stream_id
Steve Anton4daf66e2018-09-07 14:55:53 -07001728 << kSdpDelimiterSpace << track.id;
Steve Antone831b8c2018-02-01 12:22:16 -08001729 AddLine(os.str(), message);
Steve Antone831b8c2018-02-01 12:22:16 -08001730 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001731 }
Amit Hilbuchc57d5732018-12-11 15:30:11 -08001732
1733 // Build the rid lines for each layer of the track
1734 for (const RidDescription& rid_description : track.rids()) {
1735 InitAttrLine(kAttributeRid, &os);
1736 os << kSdpDelimiterColon
1737 << serializer.SerializeRidDescription(rid_description);
1738 AddLine(os.str(), message);
1739 }
1740 }
1741
Florent Castellib60141b2019-07-03 12:47:54 +02001742 for (const RidDescription& rid_description : media_desc->receive_rids()) {
1743 InitAttrLine(kAttributeRid, &os);
1744 os << kSdpDelimiterColon
1745 << serializer.SerializeRidDescription(rid_description);
1746 AddLine(os.str(), message);
1747 }
1748
Amit Hilbucha2012042018-12-03 11:35:05 -08001749 // Simulcast (a=simulcast)
1750 // https://tools.ietf.org/html/draft-ietf-mmusic-sdp-simulcast-13#section-5.1
Amit Hilbuchc57d5732018-12-11 15:30:11 -08001751 if (media_desc->HasSimulcast()) {
1752 const auto& simulcast = media_desc->simulcast_description();
Amit Hilbucha2012042018-12-03 11:35:05 -08001753 InitAttrLine(kAttributeSimulcast, &os);
Amit Hilbucha2012042018-12-03 11:35:05 -08001754 os << kSdpDelimiterColon
1755 << serializer.SerializeSimulcastDescription(simulcast);
1756 AddLine(os.str(), message);
1757 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001758}
1759
Jonas Olssonec9e4922018-09-05 09:53:49 +02001760void WriteFmtpHeader(int payload_type, rtc::StringBuilder* os) {
Artem Titov880fa812021-07-30 22:30:23 +02001761 // fmtp header: a=fmtp:`payload_type` <parameters>
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001762 // Add a=fmtp
1763 InitAttrLine(kAttributeFmtp, os);
Artem Titov880fa812021-07-30 22:30:23 +02001764 // Add :`payload_type`
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001765 *os << kSdpDelimiterColon << payload_type;
1766}
1767
Mirta Dvornicic479a3c02019-06-04 15:38:50 +02001768void WritePacketizationHeader(int payload_type, rtc::StringBuilder* os) {
Artem Titov880fa812021-07-30 22:30:23 +02001769 // packetization header: a=packetization:`payload_type` <packetization_format>
Mirta Dvornicic479a3c02019-06-04 15:38:50 +02001770 // Add a=packetization
1771 InitAttrLine(kAttributePacketization, os);
Artem Titov880fa812021-07-30 22:30:23 +02001772 // Add :`payload_type`
Mirta Dvornicic479a3c02019-06-04 15:38:50 +02001773 *os << kSdpDelimiterColon << payload_type;
1774}
1775
Jonas Olssonec9e4922018-09-05 09:53:49 +02001776void WriteRtcpFbHeader(int payload_type, rtc::StringBuilder* os) {
Artem Titov880fa812021-07-30 22:30:23 +02001777 // rtcp-fb header: a=rtcp-fb:`payload_type`
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001778 // <parameters>/<ccm <ccm_parameters>>
1779 // Add a=rtcp-fb
1780 InitAttrLine(kAttributeRtcpFb, os);
1781 // Add :
1782 *os << kSdpDelimiterColon;
1783 if (payload_type == kWildcardPayloadType) {
1784 *os << "*";
1785 } else {
1786 *os << payload_type;
1787 }
1788}
1789
1790void WriteFmtpParameter(const std::string& parameter_name,
1791 const std::string& parameter_value,
Jonas Olssonec9e4922018-09-05 09:53:49 +02001792 rtc::StringBuilder* os) {
Philipp Hanckef2a4ec12020-07-01 21:07:32 +02001793 if (parameter_name == "") {
1794 // RFC 2198 and RFC 4733 don't use key-value pairs.
1795 *os << parameter_value;
1796 } else {
Artem Titov880fa812021-07-30 22:30:23 +02001797 // fmtp parameters: `parameter_name`=`parameter_value`
Philipp Hanckef2a4ec12020-07-01 21:07:32 +02001798 *os << parameter_name << kSdpDelimiterEqual << parameter_value;
1799 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001800}
1801
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001802bool IsFmtpParam(const std::string& name) {
ossuaa4b0772017-01-30 07:41:18 -08001803 // RFC 4855, section 3 specifies the mapping of media format parameters to SDP
1804 // parameters. Only ptime, maxptime, channels and rate are placed outside of
1805 // the fmtp line. In WebRTC, channels and rate are already handled separately
1806 // and thus not included in the CodecParameterMap.
1807 return name != kCodecParamPTime && name != kCodecParamMaxPTime;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001808}
1809
Johannes Kron72d69152020-02-10 14:05:55 +01001810bool WriteFmtpParameters(const cricket::CodecParameterMap& parameters,
1811 rtc::StringBuilder* os) {
1812 bool empty = true;
1813 const char* delimiter = ""; // No delimiter before first parameter.
1814 for (const auto& entry : parameters) {
Steve Anton4daf66e2018-09-07 14:55:53 -07001815 const std::string& key = entry.first;
1816 const std::string& value = entry.second;
Johannes Kron72d69152020-02-10 14:05:55 +01001817
Steve Anton4daf66e2018-09-07 14:55:53 -07001818 if (IsFmtpParam(key)) {
Johannes Kron72d69152020-02-10 14:05:55 +01001819 *os << delimiter;
1820 // A semicolon before each subsequent parameter.
1821 delimiter = kSdpDelimiterSemicolon;
1822 WriteFmtpParameter(key, value, os);
1823 empty = false;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001824 }
1825 }
Johannes Kron72d69152020-02-10 14:05:55 +01001826
1827 return !empty;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001828}
1829
1830template <class T>
1831void AddFmtpLine(const T& codec, std::string* message) {
Jonas Olssonec9e4922018-09-05 09:53:49 +02001832 rtc::StringBuilder os;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001833 WriteFmtpHeader(codec.id, &os);
Johannes Kron72d69152020-02-10 14:05:55 +01001834 os << kSdpDelimiterSpace;
1835 // Create FMTP line and check that it's nonempty.
1836 if (WriteFmtpParameters(codec.params, &os)) {
1837 AddLine(os.str(), message);
1838 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001839 return;
1840}
1841
1842template <class T>
Mirta Dvornicic479a3c02019-06-04 15:38:50 +02001843void AddPacketizationLine(const T& codec, std::string* message) {
1844 if (!codec.packetization) {
1845 return;
1846 }
1847 rtc::StringBuilder os;
1848 WritePacketizationHeader(codec.id, &os);
1849 os << " " << *codec.packetization;
1850 AddLine(os.str(), message);
1851}
1852
1853template <class T>
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001854void AddRtcpFbLines(const T& codec, std::string* message) {
Steve Anton4daf66e2018-09-07 14:55:53 -07001855 for (const cricket::FeedbackParam& param : codec.feedback_params.params()) {
Jonas Olssonec9e4922018-09-05 09:53:49 +02001856 rtc::StringBuilder os;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001857 WriteRtcpFbHeader(codec.id, &os);
Steve Anton4daf66e2018-09-07 14:55:53 -07001858 os << " " << param.id();
1859 if (!param.param().empty()) {
1860 os << " " << param.param();
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001861 }
1862 AddLine(os.str(), message);
1863 }
1864}
1865
1866bool GetMinValue(const std::vector<int>& values, int* value) {
1867 if (values.empty()) {
1868 return false;
1869 }
Steve Anton64b626b2019-01-28 17:25:26 -08001870 auto it = absl::c_min_element(values);
1871 *value = *it;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001872 return true;
1873}
1874
1875bool GetParameter(const std::string& name,
Yves Gerey665174f2018-06-19 15:03:05 +02001876 const cricket::CodecParameterMap& params,
1877 int* value) {
1878 std::map<std::string, std::string>::const_iterator found = params.find(name);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001879 if (found == params.end()) {
1880 return false;
1881 }
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00001882 if (!rtc::FromString(found->second, value)) {
wu@webrtc.org5e760e72014-04-02 23:19:09 +00001883 return false;
1884 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001885 return true;
1886}
1887
1888void BuildRtpMap(const MediaContentDescription* media_desc,
Patrik Höglundb6b29e02018-06-21 16:58:01 +02001889 const cricket::MediaType media_type,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001890 std::string* message) {
nisseede5da42017-01-12 05:15:36 -08001891 RTC_DCHECK(message != NULL);
1892 RTC_DCHECK(media_desc != NULL);
Jonas Olssonec9e4922018-09-05 09:53:49 +02001893 rtc::StringBuilder os;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001894 if (media_type == cricket::MEDIA_TYPE_VIDEO) {
Steve Anton4daf66e2018-09-07 14:55:53 -07001895 for (const cricket::VideoCodec& codec : media_desc->as_video()->codecs()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001896 // RFC 4566
1897 // a=rtpmap:<payload type> <encoding name>/<clock rate>
1898 // [/<encodingparameters>]
Steve Anton4daf66e2018-09-07 14:55:53 -07001899 if (codec.id != kWildcardPayloadType) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001900 InitAttrLine(kAttributeRtpmap, &os);
Steve Anton4daf66e2018-09-07 14:55:53 -07001901 os << kSdpDelimiterColon << codec.id << " " << codec.name << "/"
deadbeefe814a0d2017-02-25 18:15:09 -08001902 << cricket::kVideoCodecClockrate;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001903 AddLine(os.str(), message);
1904 }
Mirta Dvornicic479a3c02019-06-04 15:38:50 +02001905 AddPacketizationLine(codec, message);
Steve Anton4daf66e2018-09-07 14:55:53 -07001906 AddRtcpFbLines(codec, message);
1907 AddFmtpLine(codec, message);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001908 }
1909 } else if (media_type == cricket::MEDIA_TYPE_AUDIO) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001910 std::vector<int> ptimes;
1911 std::vector<int> maxptimes;
1912 int max_minptime = 0;
Steve Anton4daf66e2018-09-07 14:55:53 -07001913 for (const cricket::AudioCodec& codec : media_desc->as_audio()->codecs()) {
1914 RTC_DCHECK(!codec.name.empty());
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001915 // RFC 4566
1916 // a=rtpmap:<payload type> <encoding name>/<clock rate>
1917 // [/<encodingparameters>]
1918 InitAttrLine(kAttributeRtpmap, &os);
Steve Anton4daf66e2018-09-07 14:55:53 -07001919 os << kSdpDelimiterColon << codec.id << " ";
1920 os << codec.name << "/" << codec.clockrate;
1921 if (codec.channels != 1) {
1922 os << "/" << codec.channels;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001923 }
1924 AddLine(os.str(), message);
Steve Anton4daf66e2018-09-07 14:55:53 -07001925 AddRtcpFbLines(codec, message);
1926 AddFmtpLine(codec, message);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001927 int minptime = 0;
Steve Anton4daf66e2018-09-07 14:55:53 -07001928 if (GetParameter(kCodecParamMinPTime, codec.params, &minptime)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001929 max_minptime = std::max(minptime, max_minptime);
1930 }
1931 int ptime;
Steve Anton4daf66e2018-09-07 14:55:53 -07001932 if (GetParameter(kCodecParamPTime, codec.params, &ptime)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001933 ptimes.push_back(ptime);
1934 }
1935 int maxptime;
Steve Anton4daf66e2018-09-07 14:55:53 -07001936 if (GetParameter(kCodecParamMaxPTime, codec.params, &maxptime)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001937 maxptimes.push_back(maxptime);
1938 }
1939 }
1940 // Populate the maxptime attribute with the smallest maxptime of all codecs
1941 // under the same m-line.
1942 int min_maxptime = INT_MAX;
1943 if (GetMinValue(maxptimes, &min_maxptime)) {
1944 AddAttributeLine(kCodecParamMaxPTime, min_maxptime, message);
1945 }
nisseede5da42017-01-12 05:15:36 -08001946 RTC_DCHECK(min_maxptime > max_minptime);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001947 // Populate the ptime attribute with the smallest ptime or the largest
1948 // minptime, whichever is the largest, for all codecs under the same m-line.
1949 int ptime = INT_MAX;
1950 if (GetMinValue(ptimes, &ptime)) {
1951 ptime = std::min(ptime, min_maxptime);
1952 ptime = std::max(ptime, max_minptime);
1953 AddAttributeLine(kCodecParamPTime, ptime, message);
1954 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001955 }
1956}
1957
1958void BuildCandidate(const std::vector<Candidate>& candidates,
honghaiza54a0802015-12-16 18:37:23 -08001959 bool include_ufrag,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001960 std::string* message) {
Jonas Olssonec9e4922018-09-05 09:53:49 +02001961 rtc::StringBuilder os;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001962
Steve Anton4daf66e2018-09-07 14:55:53 -07001963 for (const Candidate& candidate : candidates) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001964 // RFC 5245
1965 // a=candidate:<foundation> <component-id> <transport> <priority>
1966 // <connection-address> <port> typ <candidate-types>
1967 // [raddr <connection-address>] [rport <port>]
1968 // *(SP extension-att-name SP extension-att-value)
1969 std::string type;
1970 // Map the cricket candidate type to "host" / "srflx" / "prflx" / "relay"
Steve Anton4daf66e2018-09-07 14:55:53 -07001971 if (candidate.type() == cricket::LOCAL_PORT_TYPE) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001972 type = kCandidateHost;
Steve Anton4daf66e2018-09-07 14:55:53 -07001973 } else if (candidate.type() == cricket::STUN_PORT_TYPE) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001974 type = kCandidateSrflx;
Steve Anton4daf66e2018-09-07 14:55:53 -07001975 } else if (candidate.type() == cricket::RELAY_PORT_TYPE) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001976 type = kCandidateRelay;
Steve Anton4daf66e2018-09-07 14:55:53 -07001977 } else if (candidate.type() == cricket::PRFLX_PORT_TYPE) {
Honghai Zhang7fb69db2016-03-14 11:59:18 -07001978 type = kCandidatePrflx;
1979 // Peer reflexive candidate may be signaled for being removed.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001980 } else {
Artem Titovd3251962021-11-15 16:57:07 +01001981 RTC_DCHECK_NOTREACHED();
Peter Thatcher019087f2015-04-28 09:06:26 -07001982 // Never write out candidates if we don't know the type.
1983 continue;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001984 }
1985
1986 InitAttrLine(kAttributeCandidate, &os);
Steve Anton4daf66e2018-09-07 14:55:53 -07001987 os << kSdpDelimiterColon << candidate.foundation() << " "
1988 << candidate.component() << " " << candidate.protocol() << " "
1989 << candidate.priority() << " "
1990 << (candidate.address().ipaddr().IsNil()
1991 ? candidate.address().hostname()
1992 : candidate.address().ipaddr().ToString())
1993 << " " << candidate.address().PortAsString() << " "
1994 << kAttributeCandidateTyp << " " << type << " ";
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001995
1996 // Related address
Steve Anton4daf66e2018-09-07 14:55:53 -07001997 if (!candidate.related_address().IsNil()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001998 os << kAttributeCandidateRaddr << " "
Steve Anton4daf66e2018-09-07 14:55:53 -07001999 << candidate.related_address().ipaddr().ToString() << " "
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002000 << kAttributeCandidateRport << " "
Steve Anton4daf66e2018-09-07 14:55:53 -07002001 << candidate.related_address().PortAsString() << " ";
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002002 }
2003
Taylor Brandstetter8206bc02020-04-02 12:36:38 -07002004 // Note that we allow the tcptype to be missing, for backwards
2005 // compatibility; the implementation treats this as a passive candidate.
2006 // TODO(bugs.webrtc.org/11466): Treat a missing tcptype as an error?
2007 if (candidate.protocol() == cricket::TCP_PROTOCOL_NAME &&
2008 !candidate.tcptype().empty()) {
Steve Anton4daf66e2018-09-07 14:55:53 -07002009 os << kTcpCandidateType << " " << candidate.tcptype() << " ";
mallinath@webrtc.org2d60c5e2014-08-08 22:29:20 +00002010 }
2011
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002012 // Extensions
Steve Anton4daf66e2018-09-07 14:55:53 -07002013 os << kAttributeCandidateGeneration << " " << candidate.generation();
2014 if (include_ufrag && !candidate.username().empty()) {
2015 os << " " << kAttributeCandidateUfrag << " " << candidate.username();
honghaiza54a0802015-12-16 18:37:23 -08002016 }
Steve Anton4daf66e2018-09-07 14:55:53 -07002017 if (candidate.network_id() > 0) {
2018 os << " " << kAttributeCandidateNetworkId << " "
2019 << candidate.network_id();
honghaiza0c44ea2016-03-23 16:07:48 -07002020 }
Steve Anton4daf66e2018-09-07 14:55:53 -07002021 if (candidate.network_cost() > 0) {
2022 os << " " << kAttributeCandidateNetworkCost << " "
2023 << candidate.network_cost();
honghaize1a0c942016-02-16 14:54:56 -08002024 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002025
2026 AddLine(os.str(), message);
2027 }
2028}
2029
2030void BuildIceOptions(const std::vector<std::string>& transport_options,
2031 std::string* message) {
2032 if (!transport_options.empty()) {
Jonas Olssonec9e4922018-09-05 09:53:49 +02002033 rtc::StringBuilder os;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002034 InitAttrLine(kAttributeIceOption, &os);
2035 os << kSdpDelimiterColon << transport_options[0];
2036 for (size_t i = 1; i < transport_options.size(); ++i) {
2037 os << kSdpDelimiterSpace << transport_options[i];
2038 }
2039 AddLine(os.str(), message);
2040 }
2041}
2042
zhihuang38989e52017-03-21 11:04:53 -07002043bool ParseConnectionData(const std::string& line,
2044 rtc::SocketAddress* addr,
2045 SdpParseError* error) {
2046 // Parse the line from left to right.
2047 std::string token;
2048 std::string rightpart;
2049 // RFC 4566
2050 // c=<nettype> <addrtype> <connection-address>
2051 // Skip the "c="
Jonas Olssonec9e4922018-09-05 09:53:49 +02002052 if (!rtc::tokenize_first(line, kSdpDelimiterEqualChar, &token, &rightpart)) {
zhihuang38989e52017-03-21 11:04:53 -07002053 return ParseFailed(line, "Failed to parse the network type.", error);
2054 }
2055
2056 // Extract and verify the <nettype>
Jonas Olssonec9e4922018-09-05 09:53:49 +02002057 if (!rtc::tokenize_first(rightpart, kSdpDelimiterSpaceChar, &token,
2058 &rightpart) ||
zhihuang38989e52017-03-21 11:04:53 -07002059 token != kConnectionNettype) {
2060 return ParseFailed(line,
2061 "Failed to parse the connection data. The network type "
2062 "is not currently supported.",
2063 error);
2064 }
2065
2066 // Extract the "<addrtype>" and "<connection-address>".
Jonas Olssonec9e4922018-09-05 09:53:49 +02002067 if (!rtc::tokenize_first(rightpart, kSdpDelimiterSpaceChar, &token,
2068 &rightpart)) {
zhihuang38989e52017-03-21 11:04:53 -07002069 return ParseFailed(line, "Failed to parse the address type.", error);
2070 }
2071
2072 // The rightpart part should be the IP address without the slash which is used
2073 // for multicast.
2074 if (rightpart.find('/') != std::string::npos) {
2075 return ParseFailed(line,
2076 "Failed to parse the connection data. Multicast is not "
2077 "currently supported.",
2078 error);
2079 }
2080 addr->SetIP(rightpart);
2081
2082 // Verify that the addrtype matches the type of the parsed address.
2083 if ((addr->family() == AF_INET && token != "IP4") ||
2084 (addr->family() == AF_INET6 && token != "IP6")) {
2085 addr->Clear();
2086 return ParseFailed(
2087 line,
2088 "Failed to parse the connection data. The address type is mismatching.",
2089 error);
2090 }
2091 return true;
2092}
2093
2094bool ParseSessionDescription(const std::string& message,
2095 size_t* pos,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002096 std::string* session_id,
2097 std::string* session_version,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002098 TransportDescription* session_td,
2099 RtpHeaderExtensions* session_extmaps,
zhihuang38989e52017-03-21 11:04:53 -07002100 rtc::SocketAddress* connection_addr,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002101 cricket::SessionDescription* desc,
2102 SdpParseError* error) {
2103 std::string line;
2104
deadbeefc80741f2015-10-22 13:14:45 -07002105 desc->set_msid_supported(false);
Johannes Kron9581bc42018-10-23 10:17:39 +02002106 desc->set_extmap_allow_mixed(false);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002107 // RFC 4566
2108 // v= (protocol version)
2109 if (!GetLineWithType(message, pos, &line, kLineTypeVersion)) {
Yves Gerey665174f2018-06-19 15:03:05 +02002110 return ParseFailedExpectLine(message, *pos, kLineTypeVersion, std::string(),
2111 error);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002112 }
2113 // RFC 4566
2114 // o=<username> <sess-id> <sess-version> <nettype> <addrtype>
2115 // <unicast-address>
2116 if (!GetLineWithType(message, pos, &line, kLineTypeOrigin)) {
Yves Gerey665174f2018-06-19 15:03:05 +02002117 return ParseFailedExpectLine(message, *pos, kLineTypeOrigin, std::string(),
2118 error);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002119 }
2120 std::vector<std::string> fields;
Jonas Olssonec9e4922018-09-05 09:53:49 +02002121 rtc::split(line.substr(kLinePrefixLength), kSdpDelimiterSpaceChar, &fields);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002122 const size_t expected_fields = 6;
2123 if (fields.size() != expected_fields) {
2124 return ParseFailedExpectFieldNum(line, expected_fields, error);
2125 }
2126 *session_id = fields[1];
2127 *session_version = fields[2];
2128
2129 // RFC 4566
2130 // s= (session name)
2131 if (!GetLineWithType(message, pos, &line, kLineTypeSessionName)) {
2132 return ParseFailedExpectLine(message, *pos, kLineTypeSessionName,
2133 std::string(), error);
2134 }
2135
Danil Chapovalov66cadcc2018-06-19 16:47:43 +02002136 // absl::optional lines
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002137 // Those are the optional lines, so shouldn't return false if not present.
2138 // RFC 4566
2139 // i=* (session information)
2140 GetLineWithType(message, pos, &line, kLineTypeSessionInfo);
2141
2142 // RFC 4566
2143 // u=* (URI of description)
2144 GetLineWithType(message, pos, &line, kLineTypeSessionUri);
2145
2146 // RFC 4566
2147 // e=* (email address)
2148 GetLineWithType(message, pos, &line, kLineTypeSessionEmail);
2149
2150 // RFC 4566
2151 // p=* (phone number)
2152 GetLineWithType(message, pos, &line, kLineTypeSessionPhone);
2153
2154 // RFC 4566
2155 // c=* (connection information -- not required if included in
2156 // all media)
zhihuang38989e52017-03-21 11:04:53 -07002157 if (GetLineWithType(message, pos, &line, kLineTypeConnection)) {
2158 if (!ParseConnectionData(line, connection_addr, error)) {
2159 return false;
2160 }
2161 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002162
2163 // RFC 4566
2164 // b=* (zero or more bandwidth information lines)
2165 while (GetLineWithType(message, pos, &line, kLineTypeSessionBandwidth)) {
2166 // By pass zero or more b lines.
2167 }
2168
2169 // RFC 4566
2170 // One or more time descriptions ("t=" and "r=" lines; see below)
2171 // t= (time the session is active)
2172 // r=* (zero or more repeat times)
2173 // Ensure there's at least one time description
2174 if (!GetLineWithType(message, pos, &line, kLineTypeTiming)) {
2175 return ParseFailedExpectLine(message, *pos, kLineTypeTiming, std::string(),
2176 error);
2177 }
2178
2179 while (GetLineWithType(message, pos, &line, kLineTypeRepeatTimes)) {
2180 // By pass zero or more r lines.
2181 }
2182
2183 // Go through the rest of the time descriptions
2184 while (GetLineWithType(message, pos, &line, kLineTypeTiming)) {
2185 while (GetLineWithType(message, pos, &line, kLineTypeRepeatTimes)) {
2186 // By pass zero or more r lines.
2187 }
2188 }
2189
2190 // RFC 4566
2191 // z=* (time zone adjustments)
2192 GetLineWithType(message, pos, &line, kLineTypeTimeZone);
2193
2194 // RFC 4566
2195 // k=* (encryption key)
2196 GetLineWithType(message, pos, &line, kLineTypeEncryptionKey);
2197
2198 // RFC 4566
2199 // a=* (zero or more session attribute lines)
2200 while (GetLineWithType(message, pos, &line, kLineTypeAttributes)) {
2201 if (HasAttribute(line, kAttributeGroup)) {
2202 if (!ParseGroupAttribute(line, desc, error)) {
2203 return false;
2204 }
2205 } else if (HasAttribute(line, kAttributeIceUfrag)) {
Yves Gerey665174f2018-06-19 15:03:05 +02002206 if (!GetValue(line, kAttributeIceUfrag, &(session_td->ice_ufrag),
2207 error)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002208 return false;
2209 }
2210 } else if (HasAttribute(line, kAttributeIcePwd)) {
2211 if (!GetValue(line, kAttributeIcePwd, &(session_td->ice_pwd), error)) {
2212 return false;
2213 }
2214 } else if (HasAttribute(line, kAttributeIceLite)) {
2215 session_td->ice_mode = cricket::ICEMODE_LITE;
2216 } else if (HasAttribute(line, kAttributeIceOption)) {
2217 if (!ParseIceOptions(line, &(session_td->transport_options), error)) {
2218 return false;
2219 }
2220 } else if (HasAttribute(line, kAttributeFingerprint)) {
2221 if (session_td->identity_fingerprint.get()) {
2222 return ParseFailed(
2223 line,
2224 "Can't have multiple fingerprint attributes at the same level.",
2225 error);
2226 }
Steve Anton4905edb2018-10-15 19:27:44 -07002227 std::unique_ptr<rtc::SSLFingerprint> fingerprint;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002228 if (!ParseFingerprintAttribute(line, &fingerprint, error)) {
2229 return false;
2230 }
Steve Anton4905edb2018-10-15 19:27:44 -07002231 session_td->identity_fingerprint = std::move(fingerprint);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00002232 } else if (HasAttribute(line, kAttributeSetup)) {
2233 if (!ParseDtlsSetup(line, &(session_td->connection_role), error)) {
2234 return false;
2235 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002236 } else if (HasAttribute(line, kAttributeMsidSemantics)) {
2237 std::string semantics;
2238 if (!GetValue(line, kAttributeMsidSemantics, &semantics, error)) {
2239 return false;
2240 }
deadbeefc80741f2015-10-22 13:14:45 -07002241 desc->set_msid_supported(
2242 CaseInsensitiveFind(semantics, kMediaStreamSemantic));
Johannes Kron0854eb62018-10-10 22:33:20 +02002243 } else if (HasAttribute(line, kAttributeExtmapAllowMixed)) {
Johannes Kron9581bc42018-10-23 10:17:39 +02002244 desc->set_extmap_allow_mixed(true);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002245 } else if (HasAttribute(line, kAttributeExtmap)) {
isheriff6f8d6862016-05-26 11:24:55 -07002246 RtpExtension extmap;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002247 if (!ParseExtmap(line, &extmap, error)) {
2248 return false;
2249 }
2250 session_extmaps->push_back(extmap);
2251 }
2252 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002253 return true;
2254}
2255
2256bool ParseGroupAttribute(const std::string& line,
2257 cricket::SessionDescription* desc,
2258 SdpParseError* error) {
nisseede5da42017-01-12 05:15:36 -08002259 RTC_DCHECK(desc != NULL);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002260
2261 // RFC 5888 and draft-holmberg-mmusic-sdp-bundle-negotiation-00
2262 // a=group:BUNDLE video voice
2263 std::vector<std::string> fields;
Jonas Olssonec9e4922018-09-05 09:53:49 +02002264 rtc::split(line.substr(kLinePrefixLength), kSdpDelimiterSpaceChar, &fields);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002265 std::string semantics;
2266 if (!GetValue(fields[0], kAttributeGroup, &semantics, error)) {
2267 return false;
2268 }
2269 cricket::ContentGroup group(semantics);
2270 for (size_t i = 1; i < fields.size(); ++i) {
2271 group.AddContentName(fields[i]);
2272 }
2273 desc->AddGroup(group);
2274 return true;
2275}
2276
Steve Anton4905edb2018-10-15 19:27:44 -07002277static bool ParseFingerprintAttribute(
2278 const std::string& line,
2279 std::unique_ptr<rtc::SSLFingerprint>* fingerprint,
2280 SdpParseError* error) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002281 std::vector<std::string> fields;
Jonas Olssonec9e4922018-09-05 09:53:49 +02002282 rtc::split(line.substr(kLinePrefixLength), kSdpDelimiterSpaceChar, &fields);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002283 const size_t expected_fields = 2;
2284 if (fields.size() != expected_fields) {
2285 return ParseFailedExpectFieldNum(line, expected_fields, error);
2286 }
2287
2288 // The first field here is "fingerprint:<hash>.
2289 std::string algorithm;
2290 if (!GetValue(fields[0], kAttributeFingerprint, &algorithm, error)) {
2291 return false;
2292 }
2293
2294 // Downcase the algorithm. Note that we don't need to downcase the
2295 // fingerprint because hex_decode can handle upper-case.
Steve Anton64b626b2019-01-28 17:25:26 -08002296 absl::c_transform(algorithm, algorithm.begin(), ::tolower);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002297
2298 // The second field is the digest value. De-hexify it.
Steve Anton4905edb2018-10-15 19:27:44 -07002299 *fingerprint =
2300 rtc::SSLFingerprint::CreateUniqueFromRfc4572(algorithm, fields[1]);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002301 if (!*fingerprint) {
Yves Gerey665174f2018-06-19 15:03:05 +02002302 return ParseFailed(line, "Failed to create fingerprint from the digest.",
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002303 error);
2304 }
2305
2306 return true;
2307}
2308
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00002309static bool ParseDtlsSetup(const std::string& line,
2310 cricket::ConnectionRole* role,
2311 SdpParseError* error) {
2312 // setup-attr = "a=setup:" role
2313 // role = "active" / "passive" / "actpass" / "holdconn"
2314 std::vector<std::string> fields;
Jonas Olssonec9e4922018-09-05 09:53:49 +02002315 rtc::split(line.substr(kLinePrefixLength), kSdpDelimiterColonChar, &fields);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00002316 const size_t expected_fields = 2;
2317 if (fields.size() != expected_fields) {
2318 return ParseFailedExpectFieldNum(line, expected_fields, error);
2319 }
2320 std::string role_str = fields[1];
2321 if (!cricket::StringToConnectionRole(role_str, role)) {
2322 return ParseFailed(line, "Invalid attribute value.", error);
2323 }
2324 return true;
2325}
2326
deadbeef9d3584c2016-02-16 17:54:10 -08002327static bool ParseMsidAttribute(const std::string& line,
Seth Hampson5b4f0752018-04-02 16:31:36 -07002328 std::vector<std::string>* stream_ids,
deadbeef9d3584c2016-02-16 17:54:10 -08002329 std::string* track_id,
2330 SdpParseError* error) {
Seth Hampson5b4f0752018-04-02 16:31:36 -07002331 // https://datatracker.ietf.org/doc/draft-ietf-mmusic-msid/16/
deadbeef9d3584c2016-02-16 17:54:10 -08002332 // a=msid:<stream id> <track id>
2333 // msid-value = msid-id [ SP msid-appdata ]
2334 // msid-id = 1*64token-char ; see RFC 4566
2335 // msid-appdata = 1*64token-char ; see RFC 4566
2336 std::string field1;
Seth Hampson5b4f0752018-04-02 16:31:36 -07002337 std::string new_stream_id;
2338 std::string new_track_id;
Jonas Olssonec9e4922018-09-05 09:53:49 +02002339 if (!rtc::tokenize_first(line.substr(kLinePrefixLength),
2340 kSdpDelimiterSpaceChar, &field1, &new_track_id)) {
deadbeef9d3584c2016-02-16 17:54:10 -08002341 const size_t expected_fields = 2;
2342 return ParseFailedExpectFieldNum(line, expected_fields, error);
2343 }
2344
Seth Hampson5b4f0752018-04-02 16:31:36 -07002345 if (new_track_id.empty()) {
deadbeefa4549d62017-02-10 17:26:22 -08002346 return ParseFailed(line, "Missing track ID in msid attribute.", error);
2347 }
Seth Hampson5b4f0752018-04-02 16:31:36 -07002348 // All track ids should be the same within an m section in a Unified Plan SDP.
2349 if (!track_id->empty() && new_track_id.compare(*track_id) != 0) {
2350 return ParseFailed(
2351 line, "Two different track IDs in msid attribute in one m= section",
2352 error);
2353 }
2354 *track_id = new_track_id;
deadbeefa4549d62017-02-10 17:26:22 -08002355
deadbeef9d3584c2016-02-16 17:54:10 -08002356 // msid:<msid-id>
Seth Hampson5b4f0752018-04-02 16:31:36 -07002357 if (!GetValue(field1, kAttributeMsid, &new_stream_id, error)) {
deadbeef9d3584c2016-02-16 17:54:10 -08002358 return false;
2359 }
Seth Hampson5b4f0752018-04-02 16:31:36 -07002360 if (new_stream_id.empty()) {
deadbeefa4549d62017-02-10 17:26:22 -08002361 return ParseFailed(line, "Missing stream ID in msid attribute.", error);
2362 }
Seth Hampson5b4f0752018-04-02 16:31:36 -07002363 // The special value "-" indicates "no MediaStream".
2364 if (new_stream_id.compare(kNoStreamMsid) != 0) {
2365 stream_ids->push_back(new_stream_id);
2366 }
deadbeef9d3584c2016-02-16 17:54:10 -08002367 return true;
2368}
2369
Amit Hilbuchc57d5732018-12-11 15:30:11 -08002370static void RemoveInvalidRidDescriptions(const std::vector<int>& payload_types,
2371 std::vector<RidDescription>* rids) {
2372 RTC_DCHECK(rids);
2373 std::set<std::string> to_remove;
2374 std::set<std::string> unique_rids;
2375
2376 // Check the rids to see which ones should be removed.
2377 for (RidDescription& rid : *rids) {
2378 // In the case of a duplicate, the entire "a=rid" line, and all "a=rid"
2379 // lines with rid-ids that duplicate this line, are discarded and MUST NOT
2380 // be included in the SDP Answer.
2381 auto pair = unique_rids.insert(rid.rid);
2382 // Insert will "fail" if element already exists.
2383 if (!pair.second) {
2384 to_remove.insert(rid.rid);
2385 continue;
2386 }
2387
2388 // If the "a=rid" line contains a "pt=", the list of payload types
2389 // is verified against the list of valid payload types for the media
2390 // section (that is, those listed on the "m=" line). Any PT missing
2391 // from the "m=" line is discarded from the set of values in the
2392 // "pt=". If no values are left in the "pt=" parameter after this
2393 // processing, then the "a=rid" line is discarded.
2394 if (rid.payload_types.empty()) {
2395 // If formats were not specified, rid should not be removed.
2396 continue;
2397 }
2398
2399 // Note: Spec does not mention how to handle duplicate formats.
2400 // Media section does not handle duplicates either.
2401 std::set<int> removed_formats;
2402 for (int payload_type : rid.payload_types) {
Steve Anton64b626b2019-01-28 17:25:26 -08002403 if (!absl::c_linear_search(payload_types, payload_type)) {
Amit Hilbuchc57d5732018-12-11 15:30:11 -08002404 removed_formats.insert(payload_type);
2405 }
2406 }
2407
2408 rid.payload_types.erase(
2409 std::remove_if(rid.payload_types.begin(), rid.payload_types.end(),
2410 [&removed_formats](int format) {
2411 return removed_formats.count(format) > 0;
2412 }),
2413 rid.payload_types.end());
2414
2415 // If all formats were removed then remove the rid alogether.
2416 if (rid.payload_types.empty()) {
2417 to_remove.insert(rid.rid);
2418 }
2419 }
2420
2421 // Remove every rid description that appears in the to_remove list.
2422 if (!to_remove.empty()) {
2423 rids->erase(std::remove_if(rids->begin(), rids->end(),
2424 [&to_remove](const RidDescription& rid) {
2425 return to_remove.count(rid.rid) > 0;
2426 }),
2427 rids->end());
2428 }
2429}
2430
2431// Create a new list (because SimulcastLayerList is immutable) without any
2432// layers that have a rid in the to_remove list.
2433// If a group of alternatives is empty after removing layers, the group should
2434// be removed altogether.
2435static SimulcastLayerList RemoveRidsFromSimulcastLayerList(
2436 const std::set<std::string>& to_remove,
2437 const SimulcastLayerList& layers) {
2438 SimulcastLayerList result;
2439 for (const std::vector<SimulcastLayer>& vector : layers) {
2440 std::vector<SimulcastLayer> new_layers;
2441 for (const SimulcastLayer& layer : vector) {
2442 if (to_remove.find(layer.rid) == to_remove.end()) {
2443 new_layers.push_back(layer);
2444 }
2445 }
2446 // If all layers were removed, do not add an entry.
2447 if (!new_layers.empty()) {
2448 result.AddLayerWithAlternatives(new_layers);
2449 }
2450 }
2451
2452 return result;
2453}
2454
2455// Will remove Simulcast Layers if:
2456// 1. They appear in both send and receive directions.
Artem Titov880fa812021-07-30 22:30:23 +02002457// 2. They do not appear in the list of `valid_rids`.
Amit Hilbuchc57d5732018-12-11 15:30:11 -08002458static void RemoveInvalidRidsFromSimulcast(
2459 const std::vector<RidDescription>& valid_rids,
2460 SimulcastDescription* simulcast) {
2461 RTC_DCHECK(simulcast);
2462 std::set<std::string> to_remove;
2463 std::vector<SimulcastLayer> all_send_layers =
2464 simulcast->send_layers().GetAllLayers();
2465 std::vector<SimulcastLayer> all_receive_layers =
2466 simulcast->receive_layers().GetAllLayers();
2467
2468 // If a rid appears in both send and receive directions, remove it from both.
2469 // This algorithm runs in O(n^2) time, but for small n (as is the case with
2470 // simulcast layers) it should still perform well.
2471 for (const SimulcastLayer& send_layer : all_send_layers) {
Steve Anton64b626b2019-01-28 17:25:26 -08002472 if (absl::c_any_of(all_receive_layers,
2473 [&send_layer](const SimulcastLayer& layer) {
2474 return layer.rid == send_layer.rid;
2475 })) {
Amit Hilbuchc57d5732018-12-11 15:30:11 -08002476 to_remove.insert(send_layer.rid);
2477 }
2478 }
2479
2480 // Add any rid that is not in the valid list to the remove set.
2481 for (const SimulcastLayer& send_layer : all_send_layers) {
Steve Anton64b626b2019-01-28 17:25:26 -08002482 if (absl::c_none_of(valid_rids, [&send_layer](const RidDescription& rid) {
Florent Castellic4421972019-07-02 20:27:42 +02002483 return send_layer.rid == rid.rid &&
2484 rid.direction == cricket::RidDirection::kSend;
Steve Anton64b626b2019-01-28 17:25:26 -08002485 })) {
Amit Hilbuchc57d5732018-12-11 15:30:11 -08002486 to_remove.insert(send_layer.rid);
2487 }
2488 }
2489
2490 // Add any rid that is not in the valid list to the remove set.
2491 for (const SimulcastLayer& receive_layer : all_receive_layers) {
Florent Castellic4421972019-07-02 20:27:42 +02002492 if (absl::c_none_of(
2493 valid_rids, [&receive_layer](const RidDescription& rid) {
2494 return receive_layer.rid == rid.rid &&
2495 rid.direction == cricket::RidDirection::kReceive;
2496 })) {
Amit Hilbuchc57d5732018-12-11 15:30:11 -08002497 to_remove.insert(receive_layer.rid);
2498 }
2499 }
2500
2501 simulcast->send_layers() =
2502 RemoveRidsFromSimulcastLayerList(to_remove, simulcast->send_layers());
2503 simulcast->receive_layers() =
2504 RemoveRidsFromSimulcastLayerList(to_remove, simulcast->receive_layers());
2505}
2506
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002507// RFC 3551
2508// PT encoding media type clock rate channels
2509// name (Hz)
2510// 0 PCMU A 8,000 1
2511// 1 reserved A
2512// 2 reserved A
2513// 3 GSM A 8,000 1
2514// 4 G723 A 8,000 1
2515// 5 DVI4 A 8,000 1
2516// 6 DVI4 A 16,000 1
2517// 7 LPC A 8,000 1
2518// 8 PCMA A 8,000 1
2519// 9 G722 A 8,000 1
2520// 10 L16 A 44,100 2
2521// 11 L16 A 44,100 1
2522// 12 QCELP A 8,000 1
2523// 13 CN A 8,000 1
2524// 14 MPA A 90,000 (see text)
2525// 15 G728 A 8,000 1
2526// 16 DVI4 A 11,025 1
2527// 17 DVI4 A 22,050 1
2528// 18 G729 A 8,000 1
2529struct StaticPayloadAudioCodec {
2530 const char* name;
2531 int clockrate;
Peter Kasting69558702016-01-12 16:26:35 -08002532 size_t channels;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002533};
2534static const StaticPayloadAudioCodec kStaticPayloadAudioCodecs[] = {
Yves Gerey665174f2018-06-19 15:03:05 +02002535 {"PCMU", 8000, 1}, {"reserved", 0, 0}, {"reserved", 0, 0},
2536 {"GSM", 8000, 1}, {"G723", 8000, 1}, {"DVI4", 8000, 1},
2537 {"DVI4", 16000, 1}, {"LPC", 8000, 1}, {"PCMA", 8000, 1},
2538 {"G722", 8000, 1}, {"L16", 44100, 2}, {"L16", 44100, 1},
2539 {"QCELP", 8000, 1}, {"CN", 8000, 1}, {"MPA", 90000, 1},
2540 {"G728", 8000, 1}, {"DVI4", 11025, 1}, {"DVI4", 22050, 1},
2541 {"G729", 8000, 1},
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002542};
2543
Yves Gerey665174f2018-06-19 15:03:05 +02002544void MaybeCreateStaticPayloadAudioCodecs(const std::vector<int>& fmts,
2545 AudioContentDescription* media_desc) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002546 if (!media_desc) {
2547 return;
2548 }
deadbeef67cf2c12016-04-13 10:07:16 -07002549 RTC_DCHECK(media_desc->codecs().empty());
solenberg9fa49752016-10-08 13:02:44 -07002550 for (int payload_type : fmts) {
Yves Gerey665174f2018-06-19 15:03:05 +02002551 if (!media_desc->HasCodec(payload_type) && payload_type >= 0 &&
kjellander3e33bfe2016-06-20 07:04:09 -07002552 static_cast<uint32_t>(payload_type) <
2553 arraysize(kStaticPayloadAudioCodecs)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002554 std::string encoding_name = kStaticPayloadAudioCodecs[payload_type].name;
2555 int clock_rate = kStaticPayloadAudioCodecs[payload_type].clockrate;
Peter Kasting69558702016-01-12 16:26:35 -08002556 size_t channels = kStaticPayloadAudioCodecs[payload_type].channels;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002557 media_desc->AddCodec(cricket::AudioCodec(payload_type, encoding_name,
deadbeef67cf2c12016-04-13 10:07:16 -07002558 clock_rate, 0, channels));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002559 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002560 }
2561}
2562
2563template <class C>
Steve Anton5a1de872018-12-18 11:25:55 -08002564static std::unique_ptr<C> ParseContentDescription(
2565 const std::string& message,
2566 const cricket::MediaType media_type,
2567 int mline_index,
2568 const std::string& protocol,
2569 const std::vector<int>& payload_types,
2570 size_t* pos,
2571 std::string* content_name,
2572 bool* bundle_only,
2573 int* msid_signaling,
2574 TransportDescription* transport,
2575 std::vector<std::unique_ptr<JsepIceCandidate>>* candidates,
2576 webrtc::SdpParseError* error) {
Mirko Bonadei317a1f02019-09-17 17:06:18 +02002577 auto media_desc = std::make_unique<C>();
Emil Lundmark801c9992021-01-19 13:06:32 +01002578 media_desc->set_extmap_allow_mixed_enum(MediaContentDescription::kNo);
deadbeef67cf2c12016-04-13 10:07:16 -07002579 if (!ParseContent(message, media_type, mline_index, protocol, payload_types,
Steve Anton5a1de872018-12-18 11:25:55 -08002580 pos, content_name, bundle_only, msid_signaling,
2581 media_desc.get(), transport, candidates, error)) {
zhihuang38989e52017-03-21 11:04:53 -07002582 return nullptr;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002583 }
2584 // Sort the codecs according to the m-line fmt list.
deadbeef67cf2c12016-04-13 10:07:16 -07002585 std::unordered_map<int, int> payload_type_preferences;
2586 // "size + 1" so that the lowest preference payload type has a preference of
2587 // 1, which is greater than the default (0) for payload types not in the fmt
2588 // list.
2589 int preference = static_cast<int>(payload_types.size() + 1);
2590 for (int pt : payload_types) {
2591 payload_type_preferences[pt] = preference--;
2592 }
2593 std::vector<typename C::CodecType> codecs = media_desc->codecs();
Steve Anton64b626b2019-01-28 17:25:26 -08002594 absl::c_sort(
2595 codecs, [&payload_type_preferences](const typename C::CodecType& a,
2596 const typename C::CodecType& b) {
2597 return payload_type_preferences[a.id] > payload_type_preferences[b.id];
2598 });
deadbeef67cf2c12016-04-13 10:07:16 -07002599 media_desc->set_codecs(codecs);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002600 return media_desc;
2601}
2602
Steve Anton5a1de872018-12-18 11:25:55 -08002603bool ParseMediaDescription(
2604 const std::string& message,
2605 const TransportDescription& session_td,
2606 const RtpHeaderExtensions& session_extmaps,
2607 size_t* pos,
2608 const rtc::SocketAddress& session_connection_addr,
2609 cricket::SessionDescription* desc,
2610 std::vector<std::unique_ptr<JsepIceCandidate>>* candidates,
2611 SdpParseError* error) {
nisseede5da42017-01-12 05:15:36 -08002612 RTC_DCHECK(desc != NULL);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002613 std::string line;
2614 int mline_index = -1;
Steve Antone831b8c2018-02-01 12:22:16 -08002615 int msid_signaling = 0;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002616
2617 // Zero or more media descriptions
2618 // RFC 4566
2619 // m=<media> <port> <proto> <fmt>
2620 while (GetLineWithType(message, pos, &line, kLineTypeMedia)) {
2621 ++mline_index;
2622
2623 std::vector<std::string> fields;
Jonas Olssonec9e4922018-09-05 09:53:49 +02002624 rtc::split(line.substr(kLinePrefixLength), kSdpDelimiterSpaceChar, &fields);
zstein4b2e0822017-02-17 19:48:38 -08002625
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002626 const size_t expected_min_fields = 4;
2627 if (fields.size() < expected_min_fields) {
2628 return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
2629 }
deadbeef25ed4352016-12-12 18:37:36 -08002630 bool port_rejected = false;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002631 // RFC 3264
2632 // To reject an offered stream, the port number in the corresponding stream
2633 // in the answer MUST be set to zero.
2634 if (fields[1] == kMediaPortRejected) {
deadbeef25ed4352016-12-12 18:37:36 -08002635 port_rejected = true;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002636 }
2637
zhihuang38989e52017-03-21 11:04:53 -07002638 int port = 0;
2639 if (!rtc::FromString<int>(fields[1], &port) || !IsValidPort(port)) {
2640 return ParseFailed(line, "The port number is invalid", error);
2641 }
Philipp Hancke6fb70042020-06-29 12:32:08 +02002642 const std::string& protocol = fields[2];
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002643
2644 // <fmt>
deadbeef67cf2c12016-04-13 10:07:16 -07002645 std::vector<int> payload_types;
Harald Alvestrand5fc28b12019-05-13 13:36:16 +02002646 if (cricket::IsRtpProtocol(protocol)) {
Yves Gerey665174f2018-06-19 15:03:05 +02002647 for (size_t j = 3; j < fields.size(); ++j) {
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00002648 int pl = 0;
pkasting@chromium.orge9facf82015-02-17 20:36:28 +00002649 if (!GetPayloadTypeFromString(line, fields[j], &pl, error)) {
jiayl@webrtc.orgddb85ab2014-09-05 16:31:56 +00002650 return false;
2651 }
deadbeef67cf2c12016-04-13 10:07:16 -07002652 payload_types.push_back(pl);
wu@webrtc.org5e760e72014-04-02 23:19:09 +00002653 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002654 }
2655
Artem Titov880fa812021-07-30 22:30:23 +02002656 // Make a temporary TransportDescription based on `session_td`.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002657 // Some of this gets overwritten by ParseContent.
deadbeef46eed762016-01-28 13:24:37 -08002658 TransportDescription transport(
2659 session_td.transport_options, session_td.ice_ufrag, session_td.ice_pwd,
2660 session_td.ice_mode, session_td.connection_role,
2661 session_td.identity_fingerprint.get());
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002662
kwibergd1fe2812016-04-27 06:47:29 -07002663 std::unique_ptr<MediaContentDescription> content;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002664 std::string content_name;
deadbeef25ed4352016-12-12 18:37:36 -08002665 bool bundle_only = false;
Steve Antone831b8c2018-02-01 12:22:16 -08002666 int section_msid_signaling = 0;
Philipp Hancke6fb70042020-06-29 12:32:08 +02002667 const std::string& media_type = fields[0];
Philipp Hanckeb70c9532021-01-13 10:40:06 +01002668 if ((media_type == kMediaTypeVideo || media_type == kMediaTypeAudio) &&
2669 !cricket::IsRtpProtocol(protocol)) {
2670 return ParseFailed(line, "Unsupported protocol for media type", error);
2671 }
Philipp Hancke6fb70042020-06-29 12:32:08 +02002672 if (media_type == kMediaTypeVideo) {
Steve Anton5a1de872018-12-18 11:25:55 -08002673 content = ParseContentDescription<VideoContentDescription>(
deadbeef67cf2c12016-04-13 10:07:16 -07002674 message, cricket::MEDIA_TYPE_VIDEO, mline_index, protocol,
Steve Antone831b8c2018-02-01 12:22:16 -08002675 payload_types, pos, &content_name, &bundle_only,
Steve Anton5a1de872018-12-18 11:25:55 -08002676 &section_msid_signaling, &transport, candidates, error);
Philipp Hancke6fb70042020-06-29 12:32:08 +02002677 } else if (media_type == kMediaTypeAudio) {
Steve Anton5a1de872018-12-18 11:25:55 -08002678 content = ParseContentDescription<AudioContentDescription>(
deadbeef67cf2c12016-04-13 10:07:16 -07002679 message, cricket::MEDIA_TYPE_AUDIO, mline_index, protocol,
Steve Antone831b8c2018-02-01 12:22:16 -08002680 payload_types, pos, &content_name, &bundle_only,
Steve Anton5a1de872018-12-18 11:25:55 -08002681 &section_msid_signaling, &transport, candidates, error);
Philipp Hancke6fb70042020-06-29 12:32:08 +02002682 } else if (media_type == kMediaTypeData) {
Harald Alvestrand5fc28b12019-05-13 13:36:16 +02002683 if (cricket::IsDtlsSctp(protocol)) {
2684 // The draft-03 format is:
2685 // m=application <port> DTLS/SCTP <sctp-port>...
2686 // use_sctpmap should be false.
2687 // The draft-26 format is:
2688 // m=application <port> UDP/DTLS/SCTP webrtc-datachannel
2689 // use_sctpmap should be false.
Mirko Bonadei317a1f02019-09-17 17:06:18 +02002690 auto data_desc = std::make_unique<SctpDataContentDescription>();
Harald Alvestrandfbb45bd2019-05-15 08:07:47 +02002691 // Default max message size is 64K
2692 // according to draft-ietf-mmusic-sctp-sdp-26
2693 data_desc->set_max_message_size(kDefaultSctpMaxMessageSize);
zstein4b2e0822017-02-17 19:48:38 -08002694 int p;
2695 if (rtc::FromString(fields[3], &p)) {
Harald Alvestrand5fc28b12019-05-13 13:36:16 +02002696 data_desc->set_port(p);
zstein4b2e0822017-02-17 19:48:38 -08002697 } else if (fields[3] == kDefaultSctpmapProtocol) {
2698 data_desc->set_use_sctpmap(false);
2699 }
Harald Alvestrand5fc28b12019-05-13 13:36:16 +02002700 if (!ParseContent(message, cricket::MEDIA_TYPE_DATA, mline_index,
2701 protocol, payload_types, pos, &content_name,
2702 &bundle_only, &section_msid_signaling,
2703 data_desc.get(), &transport, candidates, error)) {
2704 return false;
2705 }
2706 data_desc->set_protocol(protocol);
2707 content = std::move(data_desc);
Philipp Hanckeb70c9532021-01-13 10:40:06 +01002708 } else {
2709 return ParseFailed(line, "Unsupported protocol for media type", error);
wu@webrtc.org78187522013-10-07 23:32:02 +00002710 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002711 } else {
Mirko Bonadei675513b2017-11-09 11:09:25 +01002712 RTC_LOG(LS_WARNING) << "Unsupported media type: " << line;
Philipp Hancke4e8c1152020-10-13 12:43:15 +02002713 auto unsupported_desc =
2714 std::make_unique<UnsupportedContentDescription>(media_type);
2715 if (!ParseContent(message, cricket::MEDIA_TYPE_UNSUPPORTED, mline_index,
2716 protocol, payload_types, pos, &content_name,
2717 &bundle_only, &section_msid_signaling,
2718 unsupported_desc.get(), &transport, candidates,
2719 error)) {
2720 return false;
2721 }
2722 unsupported_desc->set_protocol(protocol);
2723 content = std::move(unsupported_desc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002724 }
2725 if (!content.get()) {
2726 // ParseContentDescription returns NULL if failed.
2727 return false;
2728 }
2729
Steve Antone831b8c2018-02-01 12:22:16 -08002730 msid_signaling |= section_msid_signaling;
2731
deadbeef25ed4352016-12-12 18:37:36 -08002732 bool content_rejected = false;
deadbeef12771a12017-01-03 13:53:47 -08002733 // A port of 0 is not interpreted as a rejected m= section when it's
2734 // used along with a=bundle-only.
deadbeef25ed4352016-12-12 18:37:36 -08002735 if (bundle_only) {
deadbeef25ed4352016-12-12 18:37:36 -08002736 if (!port_rejected) {
deadbeef12771a12017-01-03 13:53:47 -08002737 // Usage of bundle-only with a nonzero port is unspecified. So just
2738 // ignore bundle-only if we see this.
2739 bundle_only = false;
Mirko Bonadei675513b2017-11-09 11:09:25 +01002740 RTC_LOG(LS_WARNING)
deadbeef12771a12017-01-03 13:53:47 -08002741 << "a=bundle-only attribute observed with a nonzero "
Jonas Olsson45cc8902018-02-13 10:37:07 +01002742 "port; this usage is unspecified so the attribute is being "
2743 "ignored.";
deadbeef25ed4352016-12-12 18:37:36 -08002744 }
2745 } else {
2746 // If not using bundle-only, interpret port 0 in the normal way; the m=
2747 // section is being rejected.
2748 content_rejected = port_rejected;
2749 }
2750
Philipp Hancke4e8c1152020-10-13 12:43:15 +02002751 if (content->as_unsupported()) {
2752 content_rejected = true;
2753 } else if (cricket::IsRtpProtocol(protocol) && !content->as_sctp()) {
Harald Alvestrand5fc28b12019-05-13 13:36:16 +02002754 content->set_protocol(protocol);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002755 // Set the extmap.
2756 if (!session_extmaps.empty() &&
2757 !content->rtp_header_extensions().empty()) {
2758 return ParseFailed("",
2759 "The a=extmap MUST be either all session level or "
2760 "all media level.",
2761 error);
2762 }
2763 for (size_t i = 0; i < session_extmaps.size(); ++i) {
2764 content->AddRtpHeaderExtension(session_extmaps[i]);
2765 }
Harald Alvestrand5fc28b12019-05-13 13:36:16 +02002766 } else if (content->as_sctp()) {
2767 // Do nothing, it's OK
2768 } else {
2769 RTC_LOG(LS_WARNING) << "Parse failed with unknown protocol " << protocol;
2770 return false;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002771 }
zhihuang38989e52017-03-21 11:04:53 -07002772
2773 // Use the session level connection address if the media level addresses are
2774 // not specified.
2775 rtc::SocketAddress address;
2776 address = content->connection_address().IsNil()
2777 ? session_connection_addr
2778 : content->connection_address();
2779 address.SetPort(port);
2780 content->set_connection_address(address);
2781
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002782 desc->AddContent(content_name,
Harald Alvestrand5fc28b12019-05-13 13:36:16 +02002783 cricket::IsDtlsSctp(protocol) ? MediaProtocolType::kSctp
2784 : MediaProtocolType::kRtp,
Harald Alvestrand1716d392019-06-03 20:35:45 +02002785 content_rejected, bundle_only, std::move(content));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002786 // Create TransportInfo with the media level "ice-pwd" and "ice-ufrag".
Steve Anton06817cd2018-12-18 15:55:30 -08002787 desc->AddTransportInfo(TransportInfo(content_name, transport));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002788 }
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00002789
Steve Antone831b8c2018-02-01 12:22:16 -08002790 desc->set_msid_signaling(msid_signaling);
2791
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00002792 size_t end_of_message = message.size();
2793 if (mline_index == -1 && *pos != end_of_message) {
2794 ParseFailed(message, *pos, "Expects m line.", error);
2795 return false;
2796 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002797 return true;
2798}
2799
2800bool VerifyCodec(const cricket::Codec& codec) {
2801 // Codec has not been populated correctly unless the name has been set. This
2802 // can happen if an SDP has an fmtp or rtcp-fb with a payload type but doesn't
2803 // have a corresponding "rtpmap" line.
htab39db842016-12-08 01:50:48 -08002804 return !codec.name.empty();
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002805}
2806
2807bool VerifyAudioCodecs(const AudioContentDescription* audio_desc) {
Steve Anton64b626b2019-01-28 17:25:26 -08002808 return absl::c_all_of(audio_desc->codecs(), &VerifyCodec);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002809}
2810
2811bool VerifyVideoCodecs(const VideoContentDescription* video_desc) {
Steve Anton64b626b2019-01-28 17:25:26 -08002812 return absl::c_all_of(video_desc->codecs(), &VerifyCodec);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002813}
2814
2815void AddParameters(const cricket::CodecParameterMap& parameters,
2816 cricket::Codec* codec) {
Steve Anton4daf66e2018-09-07 14:55:53 -07002817 for (const auto& entry : parameters) {
2818 const std::string& key = entry.first;
2819 const std::string& value = entry.second;
2820 codec->SetParam(key, value);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002821 }
2822}
2823
2824void AddFeedbackParameter(const cricket::FeedbackParam& feedback_param,
2825 cricket::Codec* codec) {
2826 codec->AddFeedbackParam(feedback_param);
2827}
2828
2829void AddFeedbackParameters(const cricket::FeedbackParams& feedback_params,
2830 cricket::Codec* codec) {
Steve Anton4daf66e2018-09-07 14:55:53 -07002831 for (const cricket::FeedbackParam& param : feedback_params.params()) {
2832 codec->AddFeedbackParam(param);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002833 }
2834}
2835
Artem Titov880fa812021-07-30 22:30:23 +02002836// Gets the current codec setting associated with `payload_type`. If there
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +00002837// is no Codec associated with that payload type it returns an empty codec
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002838// with that payload type.
2839template <class T>
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +00002840T GetCodecWithPayloadType(const std::vector<T>& codecs, int payload_type) {
magjedb05fa242016-11-11 04:00:16 -08002841 const T* codec = FindCodecById(codecs, payload_type);
2842 if (codec)
2843 return *codec;
Artem Titov880fa812021-07-30 22:30:23 +02002844 // Return empty codec with `payload_type`.
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +00002845 T ret_val;
magjedb05fa242016-11-11 04:00:16 -08002846 ret_val.id = payload_type;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002847 return ret_val;
2848}
2849
2850// Updates or creates a new codec entry in the audio description.
2851template <class T, class U>
2852void AddOrReplaceCodec(MediaContentDescription* content_desc, const U& codec) {
2853 T* desc = static_cast<T*>(content_desc);
2854 std::vector<U> codecs = desc->codecs();
2855 bool found = false;
Steve Anton4daf66e2018-09-07 14:55:53 -07002856 for (U& existing_codec : codecs) {
2857 if (codec.id == existing_codec.id) {
2858 // Overwrite existing codec with the new codec.
2859 existing_codec = codec;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002860 found = true;
2861 break;
2862 }
2863 }
2864 if (!found) {
2865 desc->AddCodec(codec);
2866 return;
2867 }
2868 desc->set_codecs(codecs);
2869}
2870
Artem Titov880fa812021-07-30 22:30:23 +02002871// Adds or updates existing codec corresponding to `payload_type` according
2872// to `parameters`.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002873template <class T, class U>
Yves Gerey665174f2018-06-19 15:03:05 +02002874void UpdateCodec(MediaContentDescription* content_desc,
2875 int payload_type,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002876 const cricket::CodecParameterMap& parameters) {
2877 // Codec might already have been populated (from rtpmap).
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +00002878 U new_codec = GetCodecWithPayloadType(static_cast<T*>(content_desc)->codecs(),
2879 payload_type);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002880 AddParameters(parameters, &new_codec);
2881 AddOrReplaceCodec<T, U>(content_desc, new_codec);
2882}
2883
Artem Titov880fa812021-07-30 22:30:23 +02002884// Adds or updates existing codec corresponding to `payload_type` according
2885// to `feedback_param`.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002886template <class T, class U>
Yves Gerey665174f2018-06-19 15:03:05 +02002887void UpdateCodec(MediaContentDescription* content_desc,
2888 int payload_type,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002889 const cricket::FeedbackParam& feedback_param) {
2890 // Codec might already have been populated (from rtpmap).
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +00002891 U new_codec = GetCodecWithPayloadType(static_cast<T*>(content_desc)->codecs(),
2892 payload_type);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002893 AddFeedbackParameter(feedback_param, &new_codec);
2894 AddOrReplaceCodec<T, U>(content_desc, new_codec);
2895}
2896
Artem Titov880fa812021-07-30 22:30:23 +02002897// Adds or updates existing video codec corresponding to `payload_type`
2898// according to `packetization`.
Mirta Dvornicic479a3c02019-06-04 15:38:50 +02002899void UpdateVideoCodecPacketization(VideoContentDescription* video_desc,
2900 int payload_type,
2901 const std::string& packetization) {
2902 if (packetization != cricket::kPacketizationParamRaw) {
2903 // Ignore unsupported packetization attribute.
2904 return;
2905 }
2906
2907 // Codec might already have been populated (from rtpmap).
2908 cricket::VideoCodec codec =
2909 GetCodecWithPayloadType(video_desc->codecs(), payload_type);
2910 codec.packetization = packetization;
2911 AddOrReplaceCodec<VideoContentDescription, cricket::VideoCodec>(video_desc,
2912 codec);
2913}
2914
jlmiller@webrtc.orga744a282015-02-18 21:37:46 +00002915template <class T>
2916bool PopWildcardCodec(std::vector<T>* codecs, T* wildcard_codec) {
2917 for (auto iter = codecs->begin(); iter != codecs->end(); ++iter) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002918 if (iter->id == kWildcardPayloadType) {
2919 *wildcard_codec = *iter;
2920 codecs->erase(iter);
2921 return true;
2922 }
2923 }
2924 return false;
2925}
2926
Yves Gerey665174f2018-06-19 15:03:05 +02002927template <class T>
jlmiller@webrtc.orga744a282015-02-18 21:37:46 +00002928void UpdateFromWildcardCodecs(cricket::MediaContentDescriptionImpl<T>* desc) {
2929 auto codecs = desc->codecs();
2930 T wildcard_codec;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002931 if (!PopWildcardCodec(&codecs, &wildcard_codec)) {
2932 return;
2933 }
jlmiller@webrtc.orga744a282015-02-18 21:37:46 +00002934 for (auto& codec : codecs) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002935 AddFeedbackParameters(wildcard_codec.feedback_params, &codec);
2936 }
jlmiller@webrtc.orga744a282015-02-18 21:37:46 +00002937 desc->set_codecs(codecs);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002938}
2939
Yves Gerey665174f2018-06-19 15:03:05 +02002940void AddAudioAttribute(const std::string& name,
2941 const std::string& value,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002942 AudioContentDescription* audio_desc) {
2943 if (value.empty()) {
2944 return;
2945 }
2946 std::vector<cricket::AudioCodec> codecs = audio_desc->codecs();
Steve Anton4daf66e2018-09-07 14:55:53 -07002947 for (cricket::AudioCodec& codec : codecs) {
2948 codec.params[name] = value;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002949 }
2950 audio_desc->set_codecs(codecs);
2951}
2952
2953bool ParseContent(const std::string& message,
Patrik Höglundb6b29e02018-06-21 16:58:01 +02002954 const cricket::MediaType media_type,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002955 int mline_index,
2956 const std::string& protocol,
deadbeef67cf2c12016-04-13 10:07:16 -07002957 const std::vector<int>& payload_types,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002958 size_t* pos,
2959 std::string* content_name,
deadbeef25ed4352016-12-12 18:37:36 -08002960 bool* bundle_only,
Steve Antone831b8c2018-02-01 12:22:16 -08002961 int* msid_signaling,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002962 MediaContentDescription* media_desc,
2963 TransportDescription* transport,
Steve Anton5a1de872018-12-18 11:25:55 -08002964 std::vector<std::unique_ptr<JsepIceCandidate>>* candidates,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002965 SdpParseError* error) {
nisseede5da42017-01-12 05:15:36 -08002966 RTC_DCHECK(media_desc != NULL);
2967 RTC_DCHECK(content_name != NULL);
2968 RTC_DCHECK(transport != NULL);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002969
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +00002970 if (media_type == cricket::MEDIA_TYPE_AUDIO) {
Steve Antonb1c1de12017-12-21 15:14:30 -08002971 MaybeCreateStaticPayloadAudioCodecs(payload_types, media_desc->as_audio());
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +00002972 }
2973
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002974 // The media level "ice-ufrag" and "ice-pwd".
2975 // The candidates before update the media level "ice-pwd" and "ice-ufrag".
2976 Candidates candidates_orig;
2977 std::string line;
2978 std::string mline_id;
2979 // Tracks created out of the ssrc attributes.
2980 StreamParamsVec tracks;
2981 SsrcInfoVec ssrc_infos;
2982 SsrcGroupVec ssrc_groups;
2983 std::string maxptime_as_string;
2984 std::string ptime_as_string;
Seth Hampson5b4f0752018-04-02 16:31:36 -07002985 std::vector<std::string> stream_ids;
deadbeef9d3584c2016-02-16 17:54:10 -08002986 std::string track_id;
Amit Hilbuchc57d5732018-12-11 15:30:11 -08002987 SdpSerializer deserializer;
2988 std::vector<RidDescription> rids;
Amit Hilbucha2012042018-12-03 11:35:05 -08002989 SimulcastDescription simulcast;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002990
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002991 // Loop until the next m line
2992 while (!IsLineType(message, kLineTypeMedia, *pos)) {
2993 if (!GetLine(message, pos, &line)) {
2994 if (*pos >= message.size()) {
2995 break; // Done parsing
2996 } else {
sergeyu@chromium.org5bc25c42013-12-05 00:24:06 +00002997 return ParseFailed(message, *pos, "Invalid SDP line.", error);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00002998 }
2999 }
3000
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003001 // RFC 4566
3002 // b=* (zero or more bandwidth information lines)
3003 if (IsLineType(line, kLineTypeSessionBandwidth)) {
3004 std::string bandwidth;
Taylor Brandstetteree8c2462020-07-27 15:52:02 -07003005 std::string bandwidth_type;
Philipp Hanckefbbfc022020-07-31 08:30:50 +02003006 if (!rtc::tokenize_first(line.substr(kLinePrefixLength),
3007 kSdpDelimiterColonChar, &bandwidth_type,
3008 &bandwidth)) {
3009 return ParseFailed(
3010 line,
3011 "b= syntax error, does not match b=<modifier>:<bandwidth-value>.",
3012 error);
3013 }
3014 if (!(bandwidth_type == kApplicationSpecificBandwidth ||
3015 bandwidth_type == kTransportSpecificBandwidth)) {
3016 // Ignore unknown bandwidth types.
Taylor Brandstetteree8c2462020-07-27 15:52:02 -07003017 continue;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003018 }
Taylor Brandstetteree8c2462020-07-27 15:52:02 -07003019 int b = 0;
3020 if (!GetValueFromString(line, bandwidth, &b, error)) {
3021 return false;
3022 }
3023 // TODO(deadbeef): Historically, applications may be setting a value
3024 // of -1 to mean "unset any previously set bandwidth limit", even
3025 // though ommitting the "b=AS" entirely will do just that. Once we've
3026 // transitioned applications to doing the right thing, it would be
3027 // better to treat this as a hard error instead of just ignoring it.
3028 if (bandwidth_type == kApplicationSpecificBandwidth && b == -1) {
3029 RTC_LOG(LS_WARNING) << "Ignoring \"b=AS:-1\"; will be treated as \"no "
3030 "bandwidth limit\".";
3031 continue;
3032 }
3033 if (b < 0) {
3034 return ParseFailed(
3035 line, "b=" + bandwidth_type + " value can't be negative.", error);
3036 }
Taylor Brandstetteree8c2462020-07-27 15:52:02 -07003037 // Convert values. Prevent integer overflow.
3038 if (bandwidth_type == kApplicationSpecificBandwidth) {
3039 b = std::min(b, INT_MAX / 1000) * 1000;
3040 } else {
3041 b = std::min(b, INT_MAX);
3042 }
3043 media_desc->set_bandwidth(b);
3044 media_desc->set_bandwidth_type(bandwidth_type);
Philipp Hancke20ecd8f2020-12-09 10:42:40 +01003045 continue;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003046 }
3047
zhihuang38989e52017-03-21 11:04:53 -07003048 // Parse the media level connection data.
3049 if (IsLineType(line, kLineTypeConnection)) {
3050 rtc::SocketAddress addr;
3051 if (!ParseConnectionData(line, &addr, error)) {
3052 return false;
3053 }
3054 media_desc->set_connection_address(addr);
3055 continue;
3056 }
3057
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003058 if (!IsLineType(line, kLineTypeAttributes)) {
Steve Anton36b29d12017-10-30 09:57:42 -07003059 // TODO(deadbeef): Handle other lines if needed.
henrikad62c19e2020-12-07 17:52:34 +01003060 RTC_LOG(LS_VERBOSE) << "Ignored line: " << line;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003061 continue;
3062 }
3063
3064 // Handle attributes common to SCTP and RTP.
3065 if (HasAttribute(line, kAttributeMid)) {
3066 // RFC 3388
3067 // mid-attribute = "a=mid:" identification-tag
3068 // identification-tag = token
3069 // Use the mid identification-tag as the content name.
Harald Alvestrand99bcf602021-03-03 07:44:39 +00003070 if (!GetSingleTokenValue(line, kAttributeMid, &mline_id, error)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003071 return false;
3072 }
3073 *content_name = mline_id;
deadbeef25ed4352016-12-12 18:37:36 -08003074 } else if (HasAttribute(line, kAttributeBundleOnly)) {
3075 *bundle_only = true;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003076 } else if (HasAttribute(line, kAttributeCandidate)) {
3077 Candidate candidate;
3078 if (!ParseCandidate(line, &candidate, error, false)) {
3079 return false;
3080 }
deadbeef7bcdb692017-01-20 12:43:58 -08003081 // ParseCandidate will parse non-standard ufrag and password attributes,
3082 // since it's used for candidate trickling, but we only want to process
3083 // the "a=ice-ufrag"/"a=ice-pwd" values in a session description, so
3084 // strip them off at this point.
3085 candidate.set_username(std::string());
3086 candidate.set_password(std::string());
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003087 candidates_orig.push_back(candidate);
3088 } else if (HasAttribute(line, kAttributeIceUfrag)) {
3089 if (!GetValue(line, kAttributeIceUfrag, &transport->ice_ufrag, error)) {
3090 return false;
3091 }
3092 } else if (HasAttribute(line, kAttributeIcePwd)) {
3093 if (!GetValue(line, kAttributeIcePwd, &transport->ice_pwd, error)) {
3094 return false;
3095 }
3096 } else if (HasAttribute(line, kAttributeIceOption)) {
3097 if (!ParseIceOptions(line, &transport->transport_options, error)) {
3098 return false;
3099 }
3100 } else if (HasAttribute(line, kAttributeFmtp)) {
3101 if (!ParseFmtpAttributes(line, media_type, media_desc, error)) {
3102 return false;
3103 }
3104 } else if (HasAttribute(line, kAttributeFingerprint)) {
Steve Anton4905edb2018-10-15 19:27:44 -07003105 std::unique_ptr<rtc::SSLFingerprint> fingerprint;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003106 if (!ParseFingerprintAttribute(line, &fingerprint, error)) {
3107 return false;
3108 }
Steve Anton4905edb2018-10-15 19:27:44 -07003109 transport->identity_fingerprint = std::move(fingerprint);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00003110 } else if (HasAttribute(line, kAttributeSetup)) {
3111 if (!ParseDtlsSetup(line, &(transport->connection_role), error)) {
3112 return false;
3113 }
Philipp Hanckecd467b52021-01-29 09:29:47 +01003114 } else if (cricket::IsDtlsSctp(protocol) &&
3115 media_type == cricket::MEDIA_TYPE_DATA) {
Philipp Hancke4793e9e2020-12-15 12:40:57 +01003116 //
3117 // SCTP specific attributes
3118 //
3119 if (HasAttribute(line, kAttributeSctpPort)) {
Philipp Hancke4793e9e2020-12-15 12:40:57 +01003120 if (media_desc->as_sctp()->use_sctpmap()) {
3121 return ParseFailed(
3122 line, "sctp-port attribute can't be used with sctpmap.", error);
3123 }
3124 int sctp_port;
3125 if (!ParseSctpPort(line, &sctp_port, error)) {
3126 return false;
3127 }
3128 media_desc->as_sctp()->set_port(sctp_port);
3129 } else if (HasAttribute(line, kAttributeMaxMessageSize)) {
Philipp Hancke4793e9e2020-12-15 12:40:57 +01003130 int max_message_size;
3131 if (!ParseSctpMaxMessageSize(line, &max_message_size, error)) {
3132 return false;
3133 }
3134 media_desc->as_sctp()->set_max_message_size(max_message_size);
3135 } else if (HasAttribute(line, kAttributeSctpmap)) {
3136 // Ignore a=sctpmap: from early versions of draft-ietf-mmusic-sctp-sdp
3137 continue;
deadbeef7e146cb2016-09-28 10:04:34 -07003138 }
Harald Alvestrand5fc28b12019-05-13 13:36:16 +02003139 } else if (cricket::IsRtpProtocol(protocol)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003140 //
Philipp Hancke4793e9e2020-12-15 12:40:57 +01003141 // RTP specific attributes
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003142 //
3143 if (HasAttribute(line, kAttributeRtcpMux)) {
3144 media_desc->set_rtcp_mux(true);
deadbeef13871492015-12-09 12:37:51 -08003145 } else if (HasAttribute(line, kAttributeRtcpReducedSize)) {
3146 media_desc->set_rtcp_reduced_size(true);
Sebastian Janssone1795f42019-07-24 11:38:03 +02003147 } else if (HasAttribute(line, kAttributeRtcpRemoteEstimate)) {
3148 media_desc->set_remote_estimate(true);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003149 } else if (HasAttribute(line, kAttributeSsrcGroup)) {
3150 if (!ParseSsrcGroupAttribute(line, &ssrc_groups, error)) {
3151 return false;
3152 }
3153 } else if (HasAttribute(line, kAttributeSsrc)) {
Steve Antone831b8c2018-02-01 12:22:16 -08003154 if (!ParseSsrcAttribute(line, &ssrc_infos, msid_signaling, error)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003155 return false;
3156 }
Harald Alvestrand0d018412021-11-04 13:52:31 +00003157 } else if (HasAttribute(line, kAttributeCrypto)) {
3158 if (!ParseCryptoAttribute(line, media_desc, error)) {
3159 return false;
3160 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003161 } else if (HasAttribute(line, kAttributeRtpmap)) {
deadbeef67cf2c12016-04-13 10:07:16 -07003162 if (!ParseRtpmapAttribute(line, media_type, payload_types, media_desc,
3163 error)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003164 return false;
3165 }
3166 } else if (HasAttribute(line, kCodecParamMaxPTime)) {
3167 if (!GetValue(line, kCodecParamMaxPTime, &maxptime_as_string, error)) {
3168 return false;
3169 }
Mirta Dvornicic479a3c02019-06-04 15:38:50 +02003170 } else if (HasAttribute(line, kAttributePacketization)) {
3171 if (!ParsePacketizationAttribute(line, media_type, media_desc, error)) {
3172 return false;
3173 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003174 } else if (HasAttribute(line, kAttributeRtcpFb)) {
3175 if (!ParseRtcpFbAttribute(line, media_type, media_desc, error)) {
3176 return false;
3177 }
3178 } else if (HasAttribute(line, kCodecParamPTime)) {
3179 if (!GetValue(line, kCodecParamPTime, &ptime_as_string, error)) {
3180 return false;
3181 }
3182 } else if (HasAttribute(line, kAttributeSendOnly)) {
Steve Anton4e70a722017-11-28 14:57:10 -08003183 media_desc->set_direction(RtpTransceiverDirection::kSendOnly);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003184 } else if (HasAttribute(line, kAttributeRecvOnly)) {
Steve Anton4e70a722017-11-28 14:57:10 -08003185 media_desc->set_direction(RtpTransceiverDirection::kRecvOnly);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003186 } else if (HasAttribute(line, kAttributeInactive)) {
Steve Anton4e70a722017-11-28 14:57:10 -08003187 media_desc->set_direction(RtpTransceiverDirection::kInactive);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003188 } else if (HasAttribute(line, kAttributeSendRecv)) {
Steve Anton4e70a722017-11-28 14:57:10 -08003189 media_desc->set_direction(RtpTransceiverDirection::kSendRecv);
Johannes Kron0854eb62018-10-10 22:33:20 +02003190 } else if (HasAttribute(line, kAttributeExtmapAllowMixed)) {
Johannes Kron9581bc42018-10-23 10:17:39 +02003191 media_desc->set_extmap_allow_mixed_enum(
Johannes Kron0854eb62018-10-10 22:33:20 +02003192 MediaContentDescription::kMedia);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003193 } else if (HasAttribute(line, kAttributeExtmap)) {
isheriff6f8d6862016-05-26 11:24:55 -07003194 RtpExtension extmap;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003195 if (!ParseExtmap(line, &extmap, error)) {
3196 return false;
3197 }
3198 media_desc->AddRtpHeaderExtension(extmap);
3199 } else if (HasAttribute(line, kAttributeXGoogleFlag)) {
3200 // Experimental attribute. Conference mode activates more aggressive
3201 // AEC and NS settings.
Steve Anton36b29d12017-10-30 09:57:42 -07003202 // TODO(deadbeef): expose API to set these directly.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003203 std::string flag_value;
3204 if (!GetValue(line, kAttributeXGoogleFlag, &flag_value, error)) {
3205 return false;
3206 }
3207 if (flag_value.compare(kValueConference) == 0)
3208 media_desc->set_conference_mode(true);
deadbeef9d3584c2016-02-16 17:54:10 -08003209 } else if (HasAttribute(line, kAttributeMsid)) {
Seth Hampson5b4f0752018-04-02 16:31:36 -07003210 if (!ParseMsidAttribute(line, &stream_ids, &track_id, error)) {
deadbeef9d3584c2016-02-16 17:54:10 -08003211 return false;
3212 }
Steve Antone831b8c2018-02-01 12:22:16 -08003213 *msid_signaling |= cricket::kMsidSignalingMediaSection;
Amit Hilbuchc57d5732018-12-11 15:30:11 -08003214 } else if (HasAttribute(line, kAttributeRid)) {
3215 const size_t kRidPrefixLength =
3216 kLinePrefixLength + arraysize(kAttributeRid);
3217 if (line.size() <= kRidPrefixLength) {
3218 RTC_LOG(LS_INFO) << "Ignoring empty RID attribute: " << line;
3219 continue;
3220 }
3221 RTCErrorOr<RidDescription> error_or_rid_description =
3222 deserializer.DeserializeRidDescription(
3223 line.substr(kRidPrefixLength));
3224
3225 // Malformed a=rid lines are discarded.
3226 if (!error_or_rid_description.ok()) {
3227 RTC_LOG(LS_INFO) << "Ignoring malformed RID line: '" << line
3228 << "'. Error: "
3229 << error_or_rid_description.error().message();
3230 continue;
3231 }
3232
3233 rids.push_back(error_or_rid_description.MoveValue());
Amit Hilbucha2012042018-12-03 11:35:05 -08003234 } else if (HasAttribute(line, kAttributeSimulcast)) {
3235 const size_t kSimulcastPrefixLength =
3236 kLinePrefixLength + arraysize(kAttributeSimulcast);
3237 if (line.size() <= kSimulcastPrefixLength) {
3238 return ParseFailed(line, "Simulcast attribute is empty.", error);
3239 }
3240
3241 if (!simulcast.empty()) {
3242 return ParseFailed(line, "Multiple Simulcast attributes specified.",
3243 error);
3244 }
3245
Amit Hilbucha2012042018-12-03 11:35:05 -08003246 RTCErrorOr<SimulcastDescription> error_or_simulcast =
3247 deserializer.DeserializeSimulcastDescription(
3248 line.substr(kSimulcastPrefixLength));
3249 if (!error_or_simulcast.ok()) {
3250 return ParseFailed(line,
3251 std::string("Malformed simulcast line: ") +
3252 error_or_simulcast.error().message(),
3253 error);
3254 }
3255
3256 simulcast = error_or_simulcast.value();
Harald Alvestrandba90b7f2020-12-08 12:56:56 +00003257 } else if (HasAttribute(line, kAttributeRtcp)) {
3258 // Ignore and do not log a=rtcp line.
3259 // JSEP section 5.8.2 (media section parsing) says to ignore it.
3260 continue;
Amit Hilbucha2012042018-12-03 11:35:05 -08003261 } else {
3262 // Unrecognized attribute in RTP protocol.
Philipp Hanckedf9245c2020-12-08 14:00:37 +01003263 RTC_LOG(LS_VERBOSE) << "Ignored line: " << line;
Amit Hilbucha2012042018-12-03 11:35:05 -08003264 continue;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003265 }
3266 } else {
3267 // Only parse lines that we are interested of.
Philipp Hanckedf9245c2020-12-08 14:00:37 +01003268 RTC_LOG(LS_VERBOSE) << "Ignored line: " << line;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003269 continue;
3270 }
3271 }
3272
Amit Hilbuchc57d5732018-12-11 15:30:11 -08003273 // Remove duplicate or inconsistent rids.
3274 RemoveInvalidRidDescriptions(payload_types, &rids);
3275
3276 // If simulcast is specifed, split the rids into send and receive.
3277 // Rids that do not appear in simulcast attribute will be removed.
3278 // If it is not specified, we assume that all rids are for send layers.
Amit Hilbuchb7446ed2019-01-28 12:25:25 -08003279 std::vector<RidDescription> send_rids;
Florent Castellib60141b2019-07-03 12:47:54 +02003280 std::vector<RidDescription> receive_rids;
Amit Hilbuchc57d5732018-12-11 15:30:11 -08003281 if (!simulcast.empty()) {
3282 // Verify that the rids in simulcast match rids in sdp.
3283 RemoveInvalidRidsFromSimulcast(rids, &simulcast);
3284
3285 // Use simulcast description to figure out Send / Receive RIDs.
3286 std::map<std::string, RidDescription> rid_map;
3287 for (const RidDescription& rid : rids) {
3288 rid_map[rid.rid] = rid;
3289 }
3290
3291 for (const auto& layer : simulcast.send_layers().GetAllLayers()) {
3292 auto iter = rid_map.find(layer.rid);
3293 RTC_DCHECK(iter != rid_map.end());
3294 send_rids.push_back(iter->second);
3295 }
3296
Florent Castellib60141b2019-07-03 12:47:54 +02003297 for (const auto& layer : simulcast.receive_layers().GetAllLayers()) {
3298 auto iter = rid_map.find(layer.rid);
3299 RTC_DCHECK(iter != rid_map.end());
3300 receive_rids.push_back(iter->second);
3301 }
3302
Amit Hilbuchc57d5732018-12-11 15:30:11 -08003303 media_desc->set_simulcast_description(simulcast);
3304 } else {
3305 send_rids = rids;
3306 }
3307
Florent Castellib60141b2019-07-03 12:47:54 +02003308 media_desc->set_receive_rids(receive_rids);
3309
Artem Titov880fa812021-07-30 22:30:23 +02003310 // Create tracks from the `ssrc_infos`.
Taylor Brandstetter5de6b752016-03-09 17:02:30 -08003311 // If the stream_id/track_id for all SSRCS are identical, one StreamParams
3312 // will be created in CreateTracksFromSsrcInfos, containing all the SSRCs from
3313 // the m= section.
Seth Hampson5897a6e2018-04-03 11:16:33 -07003314 if (!ssrc_infos.empty()) {
3315 CreateTracksFromSsrcInfos(ssrc_infos, stream_ids, track_id, &tracks,
3316 *msid_signaling);
3317 } else if (media_type != cricket::MEDIA_TYPE_DATA &&
3318 (*msid_signaling & cricket::kMsidSignalingMediaSection)) {
3319 // If the stream_ids/track_id was signaled but SSRCs were unsignaled we
3320 // still create a track. This isn't done for data media types because
3321 // StreamParams aren't used for SCTP streams, and RTP data channels don't
3322 // support unsignaled SSRCs.
Amit Hilbuchc57d5732018-12-11 15:30:11 -08003323 CreateTrackWithNoSsrcs(stream_ids, track_id, send_rids, &tracks);
3324 }
3325
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003326 // Add the ssrc group to the track.
Steve Anton4daf66e2018-09-07 14:55:53 -07003327 for (const SsrcGroup& ssrc_group : ssrc_groups) {
3328 if (ssrc_group.ssrcs.empty()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003329 continue;
3330 }
Steve Anton4daf66e2018-09-07 14:55:53 -07003331 uint32_t ssrc = ssrc_group.ssrcs.front();
3332 for (StreamParams& track : tracks) {
3333 if (track.has_ssrc(ssrc)) {
3334 track.ssrc_groups.push_back(ssrc_group);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003335 }
3336 }
3337 }
3338
Artem Titov880fa812021-07-30 22:30:23 +02003339 // Add the new tracks to the `media_desc`.
deadbeef9d3584c2016-02-16 17:54:10 -08003340 for (StreamParams& track : tracks) {
3341 media_desc->AddStream(track);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003342 }
3343
3344 if (media_type == cricket::MEDIA_TYPE_AUDIO) {
Steve Antonb1c1de12017-12-21 15:14:30 -08003345 AudioContentDescription* audio_desc = media_desc->as_audio();
jlmiller@webrtc.orga744a282015-02-18 21:37:46 +00003346 UpdateFromWildcardCodecs(audio_desc);
3347
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003348 // Verify audio codec ensures that no audio codec has been populated with
3349 // only fmtp.
3350 if (!VerifyAudioCodecs(audio_desc)) {
3351 return ParseFailed("Failed to parse audio codecs correctly.", error);
3352 }
3353 AddAudioAttribute(kCodecParamMaxPTime, maxptime_as_string, audio_desc);
3354 AddAudioAttribute(kCodecParamPTime, ptime_as_string, audio_desc);
3355 }
3356
3357 if (media_type == cricket::MEDIA_TYPE_VIDEO) {
Steve Antonb1c1de12017-12-21 15:14:30 -08003358 VideoContentDescription* video_desc = media_desc->as_video();
jlmiller@webrtc.orga744a282015-02-18 21:37:46 +00003359 UpdateFromWildcardCodecs(video_desc);
3360 // Verify video codec ensures that no video codec has been populated with
3361 // only rtcp-fb.
3362 if (!VerifyVideoCodecs(video_desc)) {
3363 return ParseFailed("Failed to parse video codecs correctly.", error);
3364 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003365 }
3366
3367 // RFC 5245
3368 // Update the candidates with the media level "ice-pwd" and "ice-ufrag".
Steve Anton4daf66e2018-09-07 14:55:53 -07003369 for (Candidate& candidate : candidates_orig) {
3370 RTC_DCHECK(candidate.username().empty() ||
3371 candidate.username() == transport->ice_ufrag);
3372 candidate.set_username(transport->ice_ufrag);
3373 RTC_DCHECK(candidate.password().empty());
3374 candidate.set_password(transport->ice_pwd);
3375 candidates->push_back(
Mirko Bonadei317a1f02019-09-17 17:06:18 +02003376 std::make_unique<JsepIceCandidate>(mline_id, mline_index, candidate));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003377 }
Amit Hilbucha2012042018-12-03 11:35:05 -08003378
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003379 return true;
3380}
3381
Steve Antone831b8c2018-02-01 12:22:16 -08003382bool ParseSsrcAttribute(const std::string& line,
3383 SsrcInfoVec* ssrc_infos,
3384 int* msid_signaling,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003385 SdpParseError* error) {
nisseede5da42017-01-12 05:15:36 -08003386 RTC_DCHECK(ssrc_infos != NULL);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003387 // RFC 5576
3388 // a=ssrc:<ssrc-id> <attribute>
3389 // a=ssrc:<ssrc-id> <attribute>:<value>
3390 std::string field1, field2;
Jonas Olssonec9e4922018-09-05 09:53:49 +02003391 if (!rtc::tokenize_first(line.substr(kLinePrefixLength),
3392 kSdpDelimiterSpaceChar, &field1, &field2)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003393 const size_t expected_fields = 2;
3394 return ParseFailedExpectFieldNum(line, expected_fields, error);
3395 }
3396
3397 // ssrc:<ssrc-id>
3398 std::string ssrc_id_s;
3399 if (!GetValue(field1, kAttributeSsrc, &ssrc_id_s, error)) {
3400 return false;
3401 }
Peter Boström0c4e06b2015-10-07 12:23:21 +02003402 uint32_t ssrc_id = 0;
wu@webrtc.org5e760e72014-04-02 23:19:09 +00003403 if (!GetValueFromString(line, ssrc_id_s, &ssrc_id, error)) {
3404 return false;
3405 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003406
3407 std::string attribute;
3408 std::string value;
Jonas Olssonec9e4922018-09-05 09:53:49 +02003409 if (!rtc::tokenize_first(field2, kSdpDelimiterColonChar, &attribute,
3410 &value)) {
3411 rtc::StringBuilder description;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003412 description << "Failed to get the ssrc attribute value from " << field2
3413 << ". Expected format <attribute>:<value>.";
Niels Möllera820cc22021-08-11 10:07:45 +02003414 return ParseFailed(line, description.Release(), error);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003415 }
3416
Artem Titov880fa812021-07-30 22:30:23 +02003417 // Check if there's already an item for this `ssrc_id`. Create a new one if
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003418 // there isn't.
Steve Anton64b626b2019-01-28 17:25:26 -08003419 auto ssrc_info_it =
3420 absl::c_find_if(*ssrc_infos, [ssrc_id](const SsrcInfo& ssrc_info) {
3421 return ssrc_info.ssrc_id == ssrc_id;
3422 });
Steve Anton4daf66e2018-09-07 14:55:53 -07003423 if (ssrc_info_it == ssrc_infos->end()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003424 SsrcInfo info;
3425 info.ssrc_id = ssrc_id;
3426 ssrc_infos->push_back(info);
Steve Anton4daf66e2018-09-07 14:55:53 -07003427 ssrc_info_it = ssrc_infos->end() - 1;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003428 }
Steve Anton4daf66e2018-09-07 14:55:53 -07003429 SsrcInfo& ssrc_info = *ssrc_info_it;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003430
Artem Titov880fa812021-07-30 22:30:23 +02003431 // Store the info to the `ssrc_info`.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003432 if (attribute == kSsrcAttributeCname) {
3433 // RFC 5576
3434 // cname:<value>
Steve Anton4daf66e2018-09-07 14:55:53 -07003435 ssrc_info.cname = value;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003436 } else if (attribute == kSsrcAttributeMsid) {
3437 // draft-alvestrand-mmusic-msid-00
Seth Hampson5b4f0752018-04-02 16:31:36 -07003438 // msid:identifier [appdata]
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003439 std::vector<std::string> fields;
Jonas Olssonec9e4922018-09-05 09:53:49 +02003440 rtc::split(value, kSdpDelimiterSpaceChar, &fields);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003441 if (fields.size() < 1 || fields.size() > 2) {
Yves Gerey665174f2018-06-19 15:03:05 +02003442 return ParseFailed(
3443 line, "Expected format \"msid:<identifier>[ <appdata>]\".", error);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003444 }
Steve Anton4daf66e2018-09-07 14:55:53 -07003445 ssrc_info.stream_id = fields[0];
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003446 if (fields.size() == 2) {
Steve Anton4daf66e2018-09-07 14:55:53 -07003447 ssrc_info.track_id = fields[1];
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003448 }
Steve Antone831b8c2018-02-01 12:22:16 -08003449 *msid_signaling |= cricket::kMsidSignalingSsrcAttribute;
Harald Alvestrand88b8dec2022-04-07 09:54:51 +00003450 } else {
3451 RTC_LOG(LS_INFO) << "Ignored unknown ssrc-specific attribute: " << line;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003452 }
3453 return true;
3454}
3455
3456bool ParseSsrcGroupAttribute(const std::string& line,
3457 SsrcGroupVec* ssrc_groups,
3458 SdpParseError* error) {
nisseede5da42017-01-12 05:15:36 -08003459 RTC_DCHECK(ssrc_groups != NULL);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003460 // RFC 5576
3461 // a=ssrc-group:<semantics> <ssrc-id> ...
3462 std::vector<std::string> fields;
Jonas Olssonec9e4922018-09-05 09:53:49 +02003463 rtc::split(line.substr(kLinePrefixLength), kSdpDelimiterSpaceChar, &fields);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003464 const size_t expected_min_fields = 2;
3465 if (fields.size() < expected_min_fields) {
3466 return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
3467 }
3468 std::string semantics;
3469 if (!GetValue(fields[0], kAttributeSsrcGroup, &semantics, error)) {
3470 return false;
3471 }
Peter Boström0c4e06b2015-10-07 12:23:21 +02003472 std::vector<uint32_t> ssrcs;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003473 for (size_t i = 1; i < fields.size(); ++i) {
Peter Boström0c4e06b2015-10-07 12:23:21 +02003474 uint32_t ssrc = 0;
wu@webrtc.org5e760e72014-04-02 23:19:09 +00003475 if (!GetValueFromString(line, fields[i], &ssrc, error)) {
3476 return false;
3477 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003478 ssrcs.push_back(ssrc);
3479 }
3480 ssrc_groups->push_back(SsrcGroup(semantics, ssrcs));
3481 return true;
3482}
3483
Harald Alvestrand0d018412021-11-04 13:52:31 +00003484bool ParseCryptoAttribute(const std::string& line,
3485 MediaContentDescription* media_desc,
3486 SdpParseError* error) {
3487 std::vector<std::string> fields;
3488 rtc::split(line.substr(kLinePrefixLength), kSdpDelimiterSpaceChar, &fields);
3489 // RFC 4568
3490 // a=crypto:<tag> <crypto-suite> <key-params> [<session-params>]
3491 const size_t expected_min_fields = 3;
3492 if (fields.size() < expected_min_fields) {
3493 return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
3494 }
3495 std::string tag_value;
3496 if (!GetValue(fields[0], kAttributeCrypto, &tag_value, error)) {
3497 return false;
3498 }
3499 int tag = 0;
3500 if (!GetValueFromString(line, tag_value, &tag, error)) {
3501 return false;
3502 }
3503 const std::string& crypto_suite = fields[1];
3504 const std::string& key_params = fields[2];
3505 std::string session_params;
3506 if (fields.size() > 3) {
3507 session_params = fields[3];
3508 }
3509 media_desc->AddCrypto(
3510 CryptoParams(tag, crypto_suite, key_params, session_params));
3511 return true;
3512}
3513
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003514// Updates or creates a new codec entry in the audio description with according
Artem Titov880fa812021-07-30 22:30:23 +02003515// to `name`, `clockrate`, `bitrate`, and `channels`.
deadbeef67cf2c12016-04-13 10:07:16 -07003516void UpdateCodec(int payload_type,
3517 const std::string& name,
3518 int clockrate,
3519 int bitrate,
3520 size_t channels,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003521 AudioContentDescription* audio_desc) {
3522 // Codec may already be populated with (only) optional parameters
3523 // (from an fmtp).
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +00003524 cricket::AudioCodec codec =
3525 GetCodecWithPayloadType(audio_desc->codecs(), payload_type);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003526 codec.name = name;
3527 codec.clockrate = clockrate;
3528 codec.bitrate = bitrate;
3529 codec.channels = channels;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003530 AddOrReplaceCodec<AudioContentDescription, cricket::AudioCodec>(audio_desc,
3531 codec);
3532}
3533
3534// Updates or creates a new codec entry in the video description according to
Artem Titov880fa812021-07-30 22:30:23 +02003535// `name`, `width`, `height`, and `framerate`.
deadbeef67cf2c12016-04-13 10:07:16 -07003536void UpdateCodec(int payload_type,
3537 const std::string& name,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003538 VideoContentDescription* video_desc) {
3539 // Codec may already be populated with (only) optional parameters
3540 // (from an fmtp).
changbin.shao@webrtc.org2d25b442015-03-16 04:14:34 +00003541 cricket::VideoCodec codec =
3542 GetCodecWithPayloadType(video_desc->codecs(), payload_type);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003543 codec.name = name;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003544 AddOrReplaceCodec<VideoContentDescription, cricket::VideoCodec>(video_desc,
3545 codec);
3546}
3547
3548bool ParseRtpmapAttribute(const std::string& line,
Patrik Höglundb6b29e02018-06-21 16:58:01 +02003549 const cricket::MediaType media_type,
deadbeef67cf2c12016-04-13 10:07:16 -07003550 const std::vector<int>& payload_types,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003551 MediaContentDescription* media_desc,
3552 SdpParseError* error) {
3553 std::vector<std::string> fields;
Jonas Olssonec9e4922018-09-05 09:53:49 +02003554 rtc::split(line.substr(kLinePrefixLength), kSdpDelimiterSpaceChar, &fields);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003555 // RFC 4566
3556 // a=rtpmap:<payload type> <encoding name>/<clock rate>[/<encodingparameters>]
3557 const size_t expected_min_fields = 2;
3558 if (fields.size() < expected_min_fields) {
3559 return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
3560 }
3561 std::string payload_type_value;
3562 if (!GetValue(fields[0], kAttributeRtpmap, &payload_type_value, error)) {
3563 return false;
3564 }
wu@webrtc.org5e760e72014-04-02 23:19:09 +00003565 int payload_type = 0;
pkasting@chromium.orge9facf82015-02-17 20:36:28 +00003566 if (!GetPayloadTypeFromString(line, payload_type_value, &payload_type,
3567 error)) {
wu@webrtc.org5e760e72014-04-02 23:19:09 +00003568 return false;
3569 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003570
Steve Anton64b626b2019-01-28 17:25:26 -08003571 if (!absl::c_linear_search(payload_types, payload_type)) {
Mirko Bonadei675513b2017-11-09 11:09:25 +01003572 RTC_LOG(LS_WARNING) << "Ignore rtpmap line that did not appear in the "
Jonas Olsson45cc8902018-02-13 10:37:07 +01003573 "<fmt> of the m-line: "
3574 << line;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003575 return true;
3576 }
jbauch083b73f2015-07-16 02:46:32 -07003577 const std::string& encoder = fields[1];
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003578 std::vector<std::string> codec_params;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +00003579 rtc::split(encoder, '/', &codec_params);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003580 // <encoding name>/<clock rate>[/<encodingparameters>]
3581 // 2 mandatory fields
3582 if (codec_params.size() < 2 || codec_params.size() > 3) {
3583 return ParseFailed(line,
3584 "Expected format \"<encoding name>/<clock rate>"
3585 "[/<encodingparameters>]\".",
3586 error);
3587 }
jbauch083b73f2015-07-16 02:46:32 -07003588 const std::string& encoding_name = codec_params[0];
wu@webrtc.org5e760e72014-04-02 23:19:09 +00003589 int clock_rate = 0;
3590 if (!GetValueFromString(line, codec_params[1], &clock_rate, error)) {
3591 return false;
3592 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003593 if (media_type == cricket::MEDIA_TYPE_VIDEO) {
Steve Antonb1c1de12017-12-21 15:14:30 -08003594 VideoContentDescription* video_desc = media_desc->as_video();
Yves Gerey665174f2018-06-19 15:03:05 +02003595 UpdateCodec(payload_type, encoding_name, video_desc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003596 } else if (media_type == cricket::MEDIA_TYPE_AUDIO) {
3597 // RFC 4566
3598 // For audio streams, <encoding parameters> indicates the number
3599 // of audio channels. This parameter is OPTIONAL and may be
3600 // omitted if the number of channels is one, provided that no
3601 // additional parameters are needed.
Peter Kasting69558702016-01-12 16:26:35 -08003602 size_t channels = 1;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003603 if (codec_params.size() == 3) {
wu@webrtc.org5e760e72014-04-02 23:19:09 +00003604 if (!GetValueFromString(line, codec_params[2], &channels, error)) {
3605 return false;
3606 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003607 }
Philipp Hancked58ac5a2021-11-25 08:57:54 +01003608 if (channels > kMaxNumberOfChannels) {
3609 return ParseFailed(line, "At most 24 channels are supported.", error);
3610 }
3611
Steve Antonb1c1de12017-12-21 15:14:30 -08003612 AudioContentDescription* audio_desc = media_desc->as_audio();
ossue1405ad2017-01-23 08:55:48 -08003613 UpdateCodec(payload_type, encoding_name, clock_rate, 0, channels,
deadbeef67cf2c12016-04-13 10:07:16 -07003614 audio_desc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003615 }
3616 return true;
3617}
3618
Niels Möller658b88a2022-03-17 10:47:42 +01003619bool ParseFmtpParam(absl::string_view line,
Yves Gerey665174f2018-06-19 15:03:05 +02003620 std::string* parameter,
3621 std::string* value,
3622 SdpParseError* error) {
Jonas Olssonec9e4922018-09-05 09:53:49 +02003623 if (!rtc::tokenize_first(line, kSdpDelimiterEqualChar, parameter, value)) {
Philipp Hanckef2a4ec12020-07-01 21:07:32 +02003624 // Support for non-key-value lines like RFC 2198 or RFC 4733.
3625 *parameter = "";
Niels Möller658b88a2022-03-17 10:47:42 +01003626 *value = std::string(line);
Philipp Hanckef2a4ec12020-07-01 21:07:32 +02003627 return true;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003628 }
3629 // a=fmtp:<payload_type> <param1>=<value1>; <param2>=<value2>; ...
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003630 return true;
3631}
3632
Yves Gerey665174f2018-06-19 15:03:05 +02003633bool ParseFmtpAttributes(const std::string& line,
Patrik Höglundb6b29e02018-06-21 16:58:01 +02003634 const cricket::MediaType media_type,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003635 MediaContentDescription* media_desc,
3636 SdpParseError* error) {
3637 if (media_type != cricket::MEDIA_TYPE_AUDIO &&
3638 media_type != cricket::MEDIA_TYPE_VIDEO) {
3639 return true;
3640 }
Donald Curtis0e07f922015-05-15 09:21:23 -07003641
3642 std::string line_payload;
3643 std::string line_params;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003644
Philipp Hanckef2a4ec12020-07-01 21:07:32 +02003645 // https://tools.ietf.org/html/rfc4566#section-6
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003646 // a=fmtp:<format> <format specific parameters>
3647 // At least two fields, whereas the second one is any of the optional
3648 // parameters.
Jonas Olssonec9e4922018-09-05 09:53:49 +02003649 if (!rtc::tokenize_first(line.substr(kLinePrefixLength),
3650 kSdpDelimiterSpaceChar, &line_payload,
3651 &line_params)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003652 ParseFailedExpectMinFieldNum(line, 2, error);
3653 return false;
3654 }
3655
Donald Curtis0e07f922015-05-15 09:21:23 -07003656 // Parse out the payload information.
pkasting@chromium.orgd3245462015-02-23 21:28:22 +00003657 std::string payload_type_str;
Donald Curtis0e07f922015-05-15 09:21:23 -07003658 if (!GetValue(line_payload, kAttributeFmtp, &payload_type_str, error)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003659 return false;
3660 }
3661
Donald Curtis0e07f922015-05-15 09:21:23 -07003662 int payload_type = 0;
Donald Curtis144d0182015-05-15 13:14:24 -07003663 if (!GetPayloadTypeFromString(line_payload, payload_type_str, &payload_type,
3664 error)) {
Donald Curtis0e07f922015-05-15 09:21:23 -07003665 return false;
3666 }
3667
3668 // Parse out format specific parameters.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003669 cricket::CodecParameterMap codec_params;
Niels Möller658b88a2022-03-17 10:47:42 +01003670 for (absl::string_view param :
3671 rtc::split(line_params, kSdpDelimiterSemicolonChar)) {
Donald Curtis0e07f922015-05-15 09:21:23 -07003672 std::string name;
3673 std::string value;
Niels Möller658b88a2022-03-17 10:47:42 +01003674 if (!ParseFmtpParam(absl::StripAsciiWhitespace(param), &name, &value,
3675 error)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003676 return false;
3677 }
Philipp Hanckef2a4ec12020-07-01 21:07:32 +02003678 if (codec_params.find(name) != codec_params.end()) {
3679 RTC_LOG(LS_INFO) << "Overwriting duplicate fmtp parameter with key \""
3680 << name << "\".";
3681 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003682 codec_params[name] = value;
3683 }
3684
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003685 if (media_type == cricket::MEDIA_TYPE_AUDIO) {
3686 UpdateCodec<AudioContentDescription, cricket::AudioCodec>(
pkasting@chromium.orgd3245462015-02-23 21:28:22 +00003687 media_desc, payload_type, codec_params);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003688 } else if (media_type == cricket::MEDIA_TYPE_VIDEO) {
3689 UpdateCodec<VideoContentDescription, cricket::VideoCodec>(
pkasting@chromium.orgd3245462015-02-23 21:28:22 +00003690 media_desc, payload_type, codec_params);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003691 }
3692 return true;
3693}
3694
Mirta Dvornicic479a3c02019-06-04 15:38:50 +02003695bool ParsePacketizationAttribute(const std::string& line,
3696 const cricket::MediaType media_type,
3697 MediaContentDescription* media_desc,
3698 SdpParseError* error) {
3699 if (media_type != cricket::MEDIA_TYPE_VIDEO) {
3700 return true;
3701 }
3702 std::vector<std::string> packetization_fields;
3703 rtc::split(line.c_str(), kSdpDelimiterSpaceChar, &packetization_fields);
3704 if (packetization_fields.size() < 2) {
3705 return ParseFailedGetValue(line, kAttributePacketization, error);
3706 }
3707 std::string payload_type_string;
3708 if (!GetValue(packetization_fields[0], kAttributePacketization,
3709 &payload_type_string, error)) {
3710 return false;
3711 }
3712 int payload_type;
3713 if (!GetPayloadTypeFromString(line, payload_type_string, &payload_type,
3714 error)) {
3715 return false;
3716 }
3717 std::string packetization = packetization_fields[1];
3718 UpdateVideoCodecPacketization(media_desc->as_video(), payload_type,
3719 packetization);
3720 return true;
3721}
3722
Yves Gerey665174f2018-06-19 15:03:05 +02003723bool ParseRtcpFbAttribute(const std::string& line,
Patrik Höglundb6b29e02018-06-21 16:58:01 +02003724 const cricket::MediaType media_type,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003725 MediaContentDescription* media_desc,
3726 SdpParseError* error) {
3727 if (media_type != cricket::MEDIA_TYPE_AUDIO &&
3728 media_type != cricket::MEDIA_TYPE_VIDEO) {
3729 return true;
3730 }
3731 std::vector<std::string> rtcp_fb_fields;
Jonas Olssonec9e4922018-09-05 09:53:49 +02003732 rtc::split(line.c_str(), kSdpDelimiterSpaceChar, &rtcp_fb_fields);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003733 if (rtcp_fb_fields.size() < 2) {
3734 return ParseFailedGetValue(line, kAttributeRtcpFb, error);
3735 }
3736 std::string payload_type_string;
3737 if (!GetValue(rtcp_fb_fields[0], kAttributeRtcpFb, &payload_type_string,
3738 error)) {
3739 return false;
3740 }
wu@webrtc.org5e760e72014-04-02 23:19:09 +00003741 int payload_type = kWildcardPayloadType;
3742 if (payload_type_string != "*") {
pkasting@chromium.orge9facf82015-02-17 20:36:28 +00003743 if (!GetPayloadTypeFromString(line, payload_type_string, &payload_type,
3744 error)) {
wu@webrtc.org5e760e72014-04-02 23:19:09 +00003745 return false;
3746 }
3747 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003748 std::string id = rtcp_fb_fields[1];
3749 std::string param = "";
3750 for (std::vector<std::string>::iterator iter = rtcp_fb_fields.begin() + 2;
3751 iter != rtcp_fb_fields.end(); ++iter) {
3752 param.append(*iter);
3753 }
3754 const cricket::FeedbackParam feedback_param(id, param);
3755
3756 if (media_type == cricket::MEDIA_TYPE_AUDIO) {
pkasting@chromium.orgd3245462015-02-23 21:28:22 +00003757 UpdateCodec<AudioContentDescription, cricket::AudioCodec>(
3758 media_desc, payload_type, feedback_param);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003759 } else if (media_type == cricket::MEDIA_TYPE_VIDEO) {
pkasting@chromium.orgd3245462015-02-23 21:28:22 +00003760 UpdateCodec<VideoContentDescription, cricket::VideoCodec>(
3761 media_desc, payload_type, feedback_param);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003762 }
3763 return true;
3764}
3765
3766} // namespace webrtc