Bug 1864652 - Expose settings for Global Privacy Control. r=geckoview-reviewers,ohall...
[gecko.git] / third_party / libwebrtc / pc / webrtc_sdp.cc
blob71cd18cfb6825a748313baf17199761c44bd9d7f
1 /*
2 * Copyright 2011 The WebRTC project authors. All Rights Reserved.
4 * 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.
9 */
11 #include "pc/webrtc_sdp.h"
13 #include <ctype.h>
14 #include <limits.h>
16 #include <algorithm>
17 #include <cstddef>
18 #include <cstdint>
19 #include <map>
20 #include <memory>
21 #include <set>
22 #include <string>
23 #include <type_traits>
24 #include <unordered_map>
25 #include <utility>
26 #include <vector>
28 #include "absl/algorithm/container.h"
29 #include "absl/strings/ascii.h"
30 #include "absl/strings/match.h"
31 #include "api/candidate.h"
32 #include "api/crypto_params.h"
33 #include "api/jsep_ice_candidate.h"
34 #include "api/jsep_session_description.h"
35 #include "api/media_types.h"
36 // for RtpExtension
37 #include "absl/strings/string_view.h"
38 #include "absl/types/optional.h"
39 #include "api/rtc_error.h"
40 #include "api/rtp_parameters.h"
41 #include "api/rtp_transceiver_direction.h"
42 #include "media/base/codec.h"
43 #include "media/base/media_constants.h"
44 #include "media/base/rid_description.h"
45 #include "media/base/rtp_utils.h"
46 #include "media/base/stream_params.h"
47 #include "media/sctp/sctp_transport_internal.h"
48 #include "p2p/base/candidate_pair_interface.h"
49 #include "p2p/base/ice_transport_internal.h"
50 #include "p2p/base/p2p_constants.h"
51 #include "p2p/base/port.h"
52 #include "p2p/base/port_interface.h"
53 #include "p2p/base/transport_description.h"
54 #include "p2p/base/transport_info.h"
55 #include "pc/media_protocol_names.h"
56 #include "pc/media_session.h"
57 #include "pc/sdp_serializer.h"
58 #include "pc/session_description.h"
59 #include "pc/simulcast_description.h"
60 #include "rtc_base/arraysize.h"
61 #include "rtc_base/checks.h"
62 #include "rtc_base/helpers.h"
63 #include "rtc_base/ip_address.h"
64 #include "rtc_base/logging.h"
65 #include "rtc_base/net_helper.h"
66 #include "rtc_base/network_constants.h"
67 #include "rtc_base/socket_address.h"
68 #include "rtc_base/ssl_fingerprint.h"
69 #include "rtc_base/string_encode.h"
70 #include "rtc_base/string_utils.h"
71 #include "rtc_base/strings/string_builder.h"
73 using cricket::AudioContentDescription;
74 using cricket::Candidate;
75 using cricket::Candidates;
76 using cricket::ContentInfo;
77 using cricket::CryptoParams;
78 using cricket::ICE_CANDIDATE_COMPONENT_RTCP;
79 using cricket::ICE_CANDIDATE_COMPONENT_RTP;
80 using cricket::kApplicationSpecificBandwidth;
81 using cricket::kCodecParamMaxPTime;
82 using cricket::kCodecParamMinPTime;
83 using cricket::kCodecParamPTime;
84 using cricket::kTransportSpecificBandwidth;
85 using cricket::MediaContentDescription;
86 using cricket::MediaProtocolType;
87 using cricket::MediaType;
88 using cricket::RidDescription;
89 using cricket::RtpHeaderExtensions;
90 using cricket::SctpDataContentDescription;
91 using cricket::SimulcastDescription;
92 using cricket::SimulcastLayer;
93 using cricket::SimulcastLayerList;
94 using cricket::SsrcGroup;
95 using cricket::StreamParams;
96 using cricket::StreamParamsVec;
97 using cricket::TransportDescription;
98 using cricket::TransportInfo;
99 using cricket::UnsupportedContentDescription;
100 using cricket::VideoContentDescription;
101 using rtc::SocketAddress;
103 // TODO(deadbeef): Switch to using anonymous namespace rather than declaring
104 // everything "static".
105 namespace webrtc {
107 // Line type
108 // RFC 4566
109 // An SDP session description consists of a number of lines of text of
110 // the form:
111 // <type>=<value>
112 // where <type> MUST be exactly one case-significant character.
114 // Check if passed character is a "token-char" from RFC 4566.
115 // https://datatracker.ietf.org/doc/html/rfc4566#section-9
116 // token-char = %x21 / %x23-27 / %x2A-2B / %x2D-2E / %x30-39
117 // / %x41-5A / %x5E-7E
118 bool IsTokenChar(char ch) {
119 return ch == 0x21 || (ch >= 0x23 && ch <= 0x27) || ch == 0x2a || ch == 0x2b ||
120 ch == 0x2d || ch == 0x2e || (ch >= 0x30 && ch <= 0x39) ||
121 (ch >= 0x41 && ch <= 0x5a) || (ch >= 0x5e && ch <= 0x7e);
123 static const int kLinePrefixLength = 2; // Length of <type>=
124 static const char kLineTypeVersion = 'v';
125 static const char kLineTypeOrigin = 'o';
126 static const char kLineTypeSessionName = 's';
127 static const char kLineTypeSessionInfo = 'i';
128 static const char kLineTypeSessionUri = 'u';
129 static const char kLineTypeSessionEmail = 'e';
130 static const char kLineTypeSessionPhone = 'p';
131 static const char kLineTypeSessionBandwidth = 'b';
132 static const char kLineTypeTiming = 't';
133 static const char kLineTypeRepeatTimes = 'r';
134 static const char kLineTypeTimeZone = 'z';
135 static const char kLineTypeEncryptionKey = 'k';
136 static const char kLineTypeMedia = 'm';
137 static const char kLineTypeConnection = 'c';
138 static const char kLineTypeAttributes = 'a';
140 // Attributes
141 static const char kAttributeGroup[] = "group";
142 static const char kAttributeMid[] = "mid";
143 static const char kAttributeMsid[] = "msid";
144 static const char kAttributeBundleOnly[] = "bundle-only";
145 static const char kAttributeRtcpMux[] = "rtcp-mux";
146 static const char kAttributeRtcpReducedSize[] = "rtcp-rsize";
147 static const char kAttributeSsrc[] = "ssrc";
148 static const char kSsrcAttributeCname[] = "cname";
149 static const char kAttributeExtmapAllowMixed[] = "extmap-allow-mixed";
150 static const char kAttributeExtmap[] = "extmap";
151 // draft-alvestrand-mmusic-msid-01
152 // a=msid-semantic: WMS
153 // This is a legacy field supported only for Plan B semantics.
154 static const char kAttributeMsidSemantics[] = "msid-semantic";
155 static const char kMediaStreamSemantic[] = "WMS";
156 static const char kSsrcAttributeMsid[] = "msid";
157 static const char kDefaultMsid[] = "default";
158 static const char kNoStreamMsid[] = "-";
159 static const char kAttributeSsrcGroup[] = "ssrc-group";
160 static const char kAttributeCrypto[] = "crypto";
161 static const char kAttributeCandidate[] = "candidate";
162 static const char kAttributeCandidateTyp[] = "typ";
163 static const char kAttributeCandidateRaddr[] = "raddr";
164 static const char kAttributeCandidateRport[] = "rport";
165 static const char kAttributeCandidateUfrag[] = "ufrag";
166 static const char kAttributeCandidatePwd[] = "pwd";
167 static const char kAttributeCandidateGeneration[] = "generation";
168 static const char kAttributeCandidateNetworkId[] = "network-id";
169 static const char kAttributeCandidateNetworkCost[] = "network-cost";
170 static const char kAttributeFingerprint[] = "fingerprint";
171 static const char kAttributeSetup[] = "setup";
172 static const char kAttributeFmtp[] = "fmtp";
173 static const char kAttributeRtpmap[] = "rtpmap";
174 static const char kAttributeSctpmap[] = "sctpmap";
175 static const char kAttributeRtcp[] = "rtcp";
176 static const char kAttributeIceUfrag[] = "ice-ufrag";
177 static const char kAttributeIcePwd[] = "ice-pwd";
178 static const char kAttributeIceLite[] = "ice-lite";
179 static const char kAttributeIceOption[] = "ice-options";
180 static const char kAttributeSendOnly[] = "sendonly";
181 static const char kAttributeRecvOnly[] = "recvonly";
182 static const char kAttributeRtcpFb[] = "rtcp-fb";
183 static const char kAttributeSendRecv[] = "sendrecv";
184 static const char kAttributeInactive[] = "inactive";
185 // draft-ietf-mmusic-sctp-sdp-26
186 // a=sctp-port, a=max-message-size
187 static const char kAttributeSctpPort[] = "sctp-port";
188 static const char kAttributeMaxMessageSize[] = "max-message-size";
189 static const int kDefaultSctpMaxMessageSize = 65536;
190 // draft-ietf-mmusic-sdp-simulcast-13
191 // a=simulcast
192 static const char kAttributeSimulcast[] = "simulcast";
193 // draft-ietf-mmusic-rid-15
194 // a=rid
195 static const char kAttributeRid[] = "rid";
196 static const char kAttributePacketization[] = "packetization";
198 // Experimental flags
199 static const char kAttributeXGoogleFlag[] = "x-google-flag";
200 static const char kValueConference[] = "conference";
202 static const char kAttributeRtcpRemoteEstimate[] = "remote-net-estimate";
204 // Candidate
205 static const char kCandidateHost[] = "host";
206 static const char kCandidateSrflx[] = "srflx";
207 static const char kCandidatePrflx[] = "prflx";
208 static const char kCandidateRelay[] = "relay";
209 static const char kTcpCandidateType[] = "tcptype";
211 // rtc::StringBuilder doesn't have a << overload for chars, while rtc::split and
212 // rtc::tokenize_first both take a char delimiter. To handle both cases these
213 // constants come in pairs of a chars and length-one strings.
214 static const char kSdpDelimiterEqual[] = "=";
215 static const char kSdpDelimiterEqualChar = '=';
216 static const char kSdpDelimiterSpace[] = " ";
217 static const char kSdpDelimiterSpaceChar = ' ';
218 static const char kSdpDelimiterColon[] = ":";
219 static const char kSdpDelimiterColonChar = ':';
220 static const char kSdpDelimiterSemicolon[] = ";";
221 static const char kSdpDelimiterSemicolonChar = ';';
222 static const char kSdpDelimiterSlashChar = '/';
223 static const char kNewLineChar = '\n';
224 static const char kReturnChar = '\r';
225 static const char kLineBreak[] = "\r\n";
227 // TODO(deadbeef): Generate the Session and Time description
228 // instead of hardcoding.
229 static const char kSessionVersion[] = "v=0";
230 // RFC 4566
231 static const char kSessionOriginUsername[] = "-";
232 static const char kSessionOriginSessionId[] = "0";
233 static const char kSessionOriginSessionVersion[] = "0";
234 static const char kSessionOriginNettype[] = "IN";
235 static const char kSessionOriginAddrtype[] = "IP4";
236 static const char kSessionOriginAddress[] = "127.0.0.1";
237 static const char kSessionName[] = "s=-";
238 static const char kTimeDescription[] = "t=0 0";
239 static const char kAttrGroup[] = "a=group:BUNDLE";
240 static const char kConnectionNettype[] = "IN";
241 static const char kConnectionIpv4Addrtype[] = "IP4";
242 static const char kConnectionIpv6Addrtype[] = "IP6";
243 static const char kMediaTypeVideo[] = "video";
244 static const char kMediaTypeAudio[] = "audio";
245 static const char kMediaTypeData[] = "application";
246 static const char kMediaPortRejected[] = "0";
247 // draft-ietf-mmusic-trickle-ice-01
248 // When no candidates have been gathered, set the connection
249 // address to IP6 ::.
250 // TODO(perkj): FF can not parse IP6 ::. See http://crbug/430333
251 // Use IPV4 per default.
252 static const char kDummyAddress[] = "0.0.0.0";
253 static const char kDummyPort[] = "9";
255 static const char kDefaultSctpmapProtocol[] = "webrtc-datachannel";
257 // RTP payload type is in the 0-127 range. Use -1 to indicate "all" payload
258 // types.
259 const int kWildcardPayloadType = -1;
261 // Maximum number of channels allowed.
262 static const size_t kMaxNumberOfChannels = 24;
264 struct SsrcInfo {
265 uint32_t ssrc_id;
266 std::string cname;
267 std::string stream_id;
268 std::string track_id;
270 typedef std::vector<SsrcInfo> SsrcInfoVec;
271 typedef std::vector<SsrcGroup> SsrcGroupVec;
273 template <class T>
274 static void AddFmtpLine(const T& codec, std::string* message);
275 static void BuildMediaDescription(const ContentInfo* content_info,
276 const TransportInfo* transport_info,
277 const cricket::MediaType media_type,
278 const std::vector<Candidate>& candidates,
279 int msid_signaling,
280 std::string* message);
281 static void BuildMediaLine(const cricket::MediaType media_type,
282 const ContentInfo* content_info,
283 const MediaContentDescription* media_desc,
284 std::string* message);
285 static void BuildRtpContentAttributes(const MediaContentDescription* media_desc,
286 const cricket::MediaType media_type,
287 int msid_signaling,
288 std::string* message);
289 static void BuildRtpHeaderExtensions(const RtpHeaderExtensions& extensions,
290 std::string* message);
291 static void BuildRtpmap(const MediaContentDescription* media_desc,
292 const cricket::MediaType media_type,
293 std::string* message);
294 static void BuildCandidate(const std::vector<Candidate>& candidates,
295 bool include_ufrag,
296 std::string* message);
297 static void BuildIceUfragPwd(const TransportInfo* transport_info,
298 std::string* message);
299 static void BuildDtlsFingerprintSetup(const TransportInfo* transport_info,
300 std::string* message);
301 static void BuildIceOptions(const std::vector<std::string>& transport_options,
302 std::string* message);
303 static bool ParseSessionDescription(absl::string_view message,
304 size_t* pos,
305 std::string* session_id,
306 std::string* session_version,
307 TransportDescription* session_td,
308 RtpHeaderExtensions* session_extmaps,
309 rtc::SocketAddress* connection_addr,
310 cricket::SessionDescription* desc,
311 SdpParseError* error);
312 static bool ParseMediaDescription(
313 absl::string_view message,
314 const TransportDescription& session_td,
315 const RtpHeaderExtensions& session_extmaps,
316 size_t* pos,
317 const rtc::SocketAddress& session_connection_addr,
318 cricket::SessionDescription* desc,
319 std::vector<std::unique_ptr<JsepIceCandidate>>* candidates,
320 SdpParseError* error);
321 static bool ParseContent(
322 absl::string_view message,
323 const cricket::MediaType media_type,
324 int mline_index,
325 absl::string_view protocol,
326 const std::vector<int>& payload_types,
327 size_t* pos,
328 std::string* content_name,
329 bool* bundle_only,
330 int* msid_signaling,
331 MediaContentDescription* media_desc,
332 TransportDescription* transport,
333 std::vector<std::unique_ptr<JsepIceCandidate>>* candidates,
334 SdpParseError* error);
335 static bool ParseGroupAttribute(absl::string_view line,
336 cricket::SessionDescription* desc,
337 SdpParseError* error);
338 static bool ParseSsrcAttribute(absl::string_view line,
339 SsrcInfoVec* ssrc_infos,
340 int* msid_signaling,
341 SdpParseError* error);
342 static bool ParseSsrcGroupAttribute(absl::string_view line,
343 SsrcGroupVec* ssrc_groups,
344 SdpParseError* error);
345 static bool ParseCryptoAttribute(absl::string_view line,
346 MediaContentDescription* media_desc,
347 SdpParseError* error);
348 static bool ParseRtpmapAttribute(absl::string_view line,
349 const cricket::MediaType media_type,
350 const std::vector<int>& payload_types,
351 MediaContentDescription* media_desc,
352 SdpParseError* error);
353 static bool ParseFmtpAttributes(absl::string_view line,
354 const cricket::MediaType media_type,
355 MediaContentDescription* media_desc,
356 SdpParseError* error);
357 static bool ParseFmtpParam(absl::string_view line,
358 std::string* parameter,
359 std::string* value,
360 SdpParseError* error);
361 static bool ParsePacketizationAttribute(absl::string_view line,
362 const cricket::MediaType media_type,
363 MediaContentDescription* media_desc,
364 SdpParseError* error);
365 static bool ParseRtcpFbAttribute(absl::string_view line,
366 const cricket::MediaType media_type,
367 MediaContentDescription* media_desc,
368 SdpParseError* error);
369 static bool ParseIceOptions(absl::string_view line,
370 std::vector<std::string>* transport_options,
371 SdpParseError* error);
372 static bool ParseExtmap(absl::string_view line,
373 RtpExtension* extmap,
374 SdpParseError* error);
375 static bool ParseFingerprintAttribute(
376 absl::string_view line,
377 std::unique_ptr<rtc::SSLFingerprint>* fingerprint,
378 SdpParseError* error);
379 static bool ParseDtlsSetup(absl::string_view line,
380 cricket::ConnectionRole* role,
381 SdpParseError* error);
382 static bool ParseMsidAttribute(absl::string_view line,
383 std::vector<std::string>* stream_ids,
384 std::string* track_id,
385 SdpParseError* error);
387 static void RemoveInvalidRidDescriptions(const std::vector<int>& payload_types,
388 std::vector<RidDescription>* rids);
390 static SimulcastLayerList RemoveRidsFromSimulcastLayerList(
391 const std::set<std::string>& to_remove,
392 const SimulcastLayerList& layers);
394 static void RemoveInvalidRidsFromSimulcast(
395 const std::vector<RidDescription>& rids,
396 SimulcastDescription* simulcast);
398 // Helper functions
400 // Below ParseFailed*** functions output the line that caused the parsing
401 // failure and the detailed reason (`description`) of the failure to `error`.
402 // The functions always return false so that they can be used directly in the
403 // following way when error happens:
404 // "return ParseFailed***(...);"
406 // The line starting at `line_start` of `message` is the failing line.
407 // The reason for the failure should be provided in the `description`.
408 // An example of a description could be "unknown character".
409 static bool ParseFailed(absl::string_view message,
410 size_t line_start,
411 std::string description,
412 SdpParseError* error) {
413 // Get the first line of `message` from `line_start`.
414 absl::string_view first_line;
415 size_t line_end = message.find(kNewLineChar, line_start);
416 if (line_end != std::string::npos) {
417 if (line_end > 0 && (message.at(line_end - 1) == kReturnChar)) {
418 --line_end;
420 first_line = message.substr(line_start, (line_end - line_start));
421 } else {
422 first_line = message.substr(line_start);
425 RTC_LOG(LS_ERROR) << "Failed to parse: \"" << first_line
426 << "\". Reason: " << description;
427 if (error) {
428 // TODO(bugs.webrtc.org/13220): In C++17, we can use plain assignment, with
429 // a string_view on the right hand side.
430 error->line.assign(first_line.data(), first_line.size());
431 error->description = std::move(description);
433 return false;
436 // `line` is the failing line. The reason for the failure should be
437 // provided in the `description`.
438 static bool ParseFailed(absl::string_view line,
439 std::string description,
440 SdpParseError* error) {
441 return ParseFailed(line, 0, std::move(description), error);
444 // Parses failure where the failing SDP line isn't know or there are multiple
445 // failing lines.
446 static bool ParseFailed(std::string description, SdpParseError* error) {
447 return ParseFailed("", std::move(description), error);
450 // `line` is the failing line. The failure is due to the fact that `line`
451 // doesn't have `expected_fields` fields.
452 static bool ParseFailedExpectFieldNum(absl::string_view line,
453 int expected_fields,
454 SdpParseError* error) {
455 rtc::StringBuilder description;
456 description << "Expects " << expected_fields << " fields.";
457 return ParseFailed(line, description.Release(), error);
460 // `line` is the failing line. The failure is due to the fact that `line` has
461 // less than `expected_min_fields` fields.
462 static bool ParseFailedExpectMinFieldNum(absl::string_view line,
463 int expected_min_fields,
464 SdpParseError* error) {
465 rtc::StringBuilder description;
466 description << "Expects at least " << expected_min_fields << " fields.";
467 return ParseFailed(line, description.Release(), error);
470 // `line` is the failing line. The failure is due to the fact that it failed to
471 // get the value of `attribute`.
472 static bool ParseFailedGetValue(absl::string_view line,
473 absl::string_view attribute,
474 SdpParseError* error) {
475 rtc::StringBuilder description;
476 description << "Failed to get the value of attribute: " << attribute;
477 return ParseFailed(line, description.Release(), error);
480 // The line starting at `line_start` of `message` is the failing line. The
481 // failure is due to the line type (e.g. the "m" part of the "m-line")
482 // not matching what is expected. The expected line type should be
483 // provided as `line_type`.
484 static bool ParseFailedExpectLine(absl::string_view message,
485 size_t line_start,
486 const char line_type,
487 absl::string_view line_value,
488 SdpParseError* error) {
489 rtc::StringBuilder description;
490 description << "Expect line: " << std::string(1, line_type) << "="
491 << line_value;
492 return ParseFailed(message, line_start, description.Release(), error);
495 static bool AddLine(absl::string_view line, std::string* message) {
496 if (!message)
497 return false;
499 message->append(line.data(), line.size());
500 message->append(kLineBreak);
501 return true;
504 // Trim return character, if any.
505 static absl::string_view TrimReturnChar(absl::string_view line) {
506 if (!line.empty() && line.back() == kReturnChar) {
507 line.remove_suffix(1);
509 return line;
512 // Gets line of `message` starting at `pos`, and checks overall SDP syntax. On
513 // success, advances `pos` to the next line.
514 static absl::optional<absl::string_view> GetLine(absl::string_view message,
515 size_t* pos) {
516 size_t line_end = message.find(kNewLineChar, *pos);
517 if (line_end == absl::string_view::npos) {
518 return absl::nullopt;
520 absl::string_view line =
521 TrimReturnChar(message.substr(*pos, line_end - *pos));
523 // RFC 4566
524 // An SDP session description consists of a number of lines of text of
525 // the form:
526 // <type>=<value>
527 // where <type> MUST be exactly one case-significant character and
528 // <value> is structured text whose format depends on <type>.
529 // Whitespace MUST NOT be used on either side of the "=" sign.
531 // However, an exception to the whitespace rule is made for "s=", since
532 // RFC4566 also says:
534 // If a session has no meaningful name, the value "s= " SHOULD be used
535 // (i.e., a single space as the session name).
536 if (line.length() < 3 || !islower(static_cast<unsigned char>(line[0])) ||
537 line[1] != kSdpDelimiterEqualChar ||
538 (line[0] != kLineTypeSessionName && line[2] == kSdpDelimiterSpaceChar)) {
539 return absl::nullopt;
541 *pos = line_end + 1;
542 return line;
545 // Init `os` to "`type`=`value`".
546 static void InitLine(const char type,
547 absl::string_view value,
548 rtc::StringBuilder* os) {
549 os->Clear();
550 *os << std::string(1, type) << kSdpDelimiterEqual << value;
553 // Init `os` to "a=`attribute`".
554 static void InitAttrLine(absl::string_view attribute, rtc::StringBuilder* os) {
555 InitLine(kLineTypeAttributes, attribute, os);
558 // Writes a SDP attribute line based on `attribute` and `value` to `message`.
559 static void AddAttributeLine(absl::string_view attribute,
560 int value,
561 std::string* message) {
562 rtc::StringBuilder os;
563 InitAttrLine(attribute, &os);
564 os << kSdpDelimiterColon << value;
565 AddLine(os.str(), message);
568 static bool IsLineType(absl::string_view message,
569 const char type,
570 size_t line_start) {
571 if (message.size() < line_start + kLinePrefixLength) {
572 return false;
574 return (message[line_start] == type &&
575 message[line_start + 1] == kSdpDelimiterEqualChar);
578 static bool IsLineType(absl::string_view line, const char type) {
579 return IsLineType(line, type, 0);
582 static absl::optional<absl::string_view>
583 GetLineWithType(absl::string_view message, size_t* pos, const char type) {
584 if (IsLineType(message, type, *pos)) {
585 return GetLine(message, pos);
587 return absl::nullopt;
590 static bool HasAttribute(absl::string_view line, absl::string_view attribute) {
591 if (line.compare(kLinePrefixLength, attribute.size(), attribute) == 0) {
592 // Make sure that the match is not only a partial match. If length of
593 // strings doesn't match, the next character of the line must be ':' or ' '.
594 // This function is also used for media descriptions (e.g., "m=audio 9..."),
595 // hence the need to also allow space in the end.
596 RTC_CHECK_LE(kLinePrefixLength + attribute.size(), line.size());
597 if ((kLinePrefixLength + attribute.size()) == line.size() ||
598 line[kLinePrefixLength + attribute.size()] == kSdpDelimiterColonChar ||
599 line[kLinePrefixLength + attribute.size()] == kSdpDelimiterSpaceChar) {
600 return true;
603 return false;
606 static bool AddSsrcLine(uint32_t ssrc_id,
607 absl::string_view attribute,
608 absl::string_view value,
609 std::string* message) {
610 // RFC 5576
611 // a=ssrc:<ssrc-id> <attribute>:<value>
612 rtc::StringBuilder os;
613 InitAttrLine(kAttributeSsrc, &os);
614 os << kSdpDelimiterColon << ssrc_id << kSdpDelimiterSpace << attribute
615 << kSdpDelimiterColon << value;
616 return AddLine(os.str(), message);
619 // Get value only from <attribute>:<value>.
620 static bool GetValue(absl::string_view message,
621 absl::string_view attribute,
622 std::string* value,
623 SdpParseError* error) {
624 std::string leftpart;
625 if (!rtc::tokenize_first(message, kSdpDelimiterColonChar, &leftpart, value)) {
626 return ParseFailedGetValue(message, attribute, error);
628 // The left part should end with the expected attribute.
629 if (leftpart.length() < attribute.length() ||
630 absl::string_view(leftpart).compare(
631 leftpart.length() - attribute.length(), attribute.length(),
632 attribute) != 0) {
633 return ParseFailedGetValue(message, attribute, error);
635 return true;
638 // Get a single [token] from <attribute>:<token>
639 static bool GetSingleTokenValue(absl::string_view message,
640 absl::string_view attribute,
641 std::string* value,
642 SdpParseError* error) {
643 if (!GetValue(message, attribute, value, error)) {
644 return false;
646 if (!absl::c_all_of(absl::string_view(*value), IsTokenChar)) {
647 rtc::StringBuilder description;
648 description << "Illegal character found in the value of " << attribute;
649 return ParseFailed(message, description.Release(), error);
651 return true;
654 static bool CaseInsensitiveFind(std::string str1, std::string str2) {
655 absl::c_transform(str1, str1.begin(), ::tolower);
656 absl::c_transform(str2, str2.begin(), ::tolower);
657 return str1.find(str2) != std::string::npos;
660 template <class T>
661 static bool GetValueFromString(absl::string_view line,
662 absl::string_view s,
663 T* t,
664 SdpParseError* error) {
665 if (!rtc::FromString(s, t)) {
666 rtc::StringBuilder description;
667 description << "Invalid value: " << s << ".";
668 return ParseFailed(line, description.Release(), error);
670 return true;
673 static bool GetPayloadTypeFromString(absl::string_view line,
674 absl::string_view s,
675 int* payload_type,
676 SdpParseError* error) {
677 return GetValueFromString(line, s, payload_type, error) &&
678 cricket::IsValidRtpPayloadType(*payload_type);
681 // Creates a StreamParams track in the case when no SSRC lines are signaled.
682 // This is a track that does not contain SSRCs and only contains
683 // stream_ids/track_id if it's signaled with a=msid lines.
684 void CreateTrackWithNoSsrcs(const std::vector<std::string>& msid_stream_ids,
685 absl::string_view msid_track_id,
686 const std::vector<RidDescription>& rids,
687 StreamParamsVec* tracks) {
688 StreamParams track;
689 if (msid_track_id.empty() && rids.empty()) {
690 // We only create an unsignaled track if a=msid lines were signaled.
691 RTC_LOG(LS_INFO) << "MSID not signaled, skipping creation of StreamParams";
692 return;
694 track.set_stream_ids(msid_stream_ids);
695 track.id = std::string(msid_track_id);
696 track.set_rids(rids);
697 tracks->push_back(track);
700 // Creates the StreamParams tracks, for the case when SSRC lines are signaled.
701 // `msid_stream_ids` and `msid_track_id` represent the stream/track ID from the
702 // "a=msid" attribute, if it exists. They are empty if the attribute does not
703 // exist. We prioritize getting stream_ids/track_ids signaled in a=msid lines.
704 void CreateTracksFromSsrcInfos(const SsrcInfoVec& ssrc_infos,
705 const std::vector<std::string>& msid_stream_ids,
706 absl::string_view msid_track_id,
707 StreamParamsVec* tracks,
708 int msid_signaling) {
709 RTC_DCHECK(tracks);
710 for (const SsrcInfo& ssrc_info : ssrc_infos) {
711 // According to https://tools.ietf.org/html/rfc5576#section-6.1, the CNAME
712 // attribute is mandatory, but we relax that restriction.
713 if (ssrc_info.cname.empty()) {
714 RTC_LOG(LS_WARNING) << "CNAME attribute missing for SSRC "
715 << ssrc_info.ssrc_id;
717 std::vector<std::string> stream_ids;
718 std::string track_id;
719 if (msid_signaling & cricket::kMsidSignalingMediaSection) {
720 // This is the case with Unified Plan SDP msid signaling.
721 stream_ids = msid_stream_ids;
722 track_id = std::string(msid_track_id);
723 } else if (msid_signaling & cricket::kMsidSignalingSsrcAttribute) {
724 // This is the case with Plan B SDP msid signaling.
725 stream_ids.push_back(ssrc_info.stream_id);
726 track_id = ssrc_info.track_id;
727 } else {
728 // Since no media streams isn't supported with older SDP signaling, we
729 // use a default stream id.
730 stream_ids.push_back(kDefaultMsid);
733 auto track_it = absl::c_find_if(
734 *tracks,
735 [track_id](const StreamParams& track) { return track.id == track_id; });
736 if (track_it == tracks->end()) {
737 // If we don't find an existing track, create a new one.
738 tracks->push_back(StreamParams());
739 track_it = tracks->end() - 1;
741 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;
747 for (StreamParams& stream : *tracks) {
748 // If a track ID wasn't populated from the SSRC attributes OR the
749 // msid attribute, use default/random values. This happens after
750 // deduplication.
751 if (stream.id.empty()) {
752 stream.id = rtc::CreateRandomString(8);
757 void GetMediaStreamIds(const ContentInfo* content,
758 std::set<std::string>* labels) {
759 for (const StreamParams& stream_params :
760 content->media_description()->streams()) {
761 for (const std::string& stream_id : stream_params.stream_ids()) {
762 labels->insert(stream_id);
767 // RFC 5245
768 // It is RECOMMENDED that default candidates be chosen based on the
769 // likelihood of those candidates to work with the peer that is being
770 // contacted. It is RECOMMENDED that relayed > reflexive > host.
771 static const int kPreferenceUnknown = 0;
772 static const int kPreferenceHost = 1;
773 static const int kPreferenceReflexive = 2;
774 static const int kPreferenceRelayed = 3;
776 static int GetCandidatePreferenceFromType(absl::string_view type) {
777 int preference = kPreferenceUnknown;
778 if (type == cricket::LOCAL_PORT_TYPE) {
779 preference = kPreferenceHost;
780 } else if (type == cricket::STUN_PORT_TYPE) {
781 preference = kPreferenceReflexive;
782 } else if (type == cricket::RELAY_PORT_TYPE) {
783 preference = kPreferenceRelayed;
784 } else {
785 RTC_DCHECK_NOTREACHED();
787 return preference;
790 // Get ip and port of the default destination from the `candidates` with the
791 // given value of `component_id`. The default candidate should be the one most
792 // likely to work, typically IPv4 relay.
793 // RFC 5245
794 // The value of `component_id` currently supported are 1 (RTP) and 2 (RTCP).
795 // TODO(deadbeef): Decide the default destination in webrtcsession and
796 // pass it down via SessionDescription.
797 static void GetDefaultDestination(const std::vector<Candidate>& candidates,
798 int component_id,
799 std::string* port,
800 std::string* ip,
801 std::string* addr_type) {
802 *addr_type = kConnectionIpv4Addrtype;
803 *port = kDummyPort;
804 *ip = kDummyAddress;
805 int current_preference = kPreferenceUnknown;
806 int current_family = AF_UNSPEC;
807 for (const Candidate& candidate : candidates) {
808 if (candidate.component() != component_id) {
809 continue;
811 // Default destination should be UDP only.
812 if (candidate.protocol() != cricket::UDP_PROTOCOL_NAME) {
813 continue;
815 const int preference = GetCandidatePreferenceFromType(candidate.type());
816 const int family = candidate.address().ipaddr().family();
817 // See if this candidate is more preferable then the current one if it's the
818 // same family. Or if the current family is IPv4 already so we could safely
819 // ignore all IPv6 ones. WebRTC bug 4269.
820 // http://code.google.com/p/webrtc/issues/detail?id=4269
821 if ((preference <= current_preference && current_family == family) ||
822 (current_family == AF_INET && family == AF_INET6)) {
823 continue;
825 if (family == AF_INET) {
826 addr_type->assign(kConnectionIpv4Addrtype);
827 } else if (family == AF_INET6) {
828 addr_type->assign(kConnectionIpv6Addrtype);
830 current_preference = preference;
831 current_family = family;
832 *port = candidate.address().PortAsString();
833 *ip = candidate.address().ipaddr().ToString();
837 // Gets "a=rtcp" line if found default RTCP candidate from `candidates`.
838 static std::string GetRtcpLine(const std::vector<Candidate>& candidates) {
839 std::string rtcp_line, rtcp_port, rtcp_ip, addr_type;
840 GetDefaultDestination(candidates, ICE_CANDIDATE_COMPONENT_RTCP, &rtcp_port,
841 &rtcp_ip, &addr_type);
842 // Found default RTCP candidate.
843 // RFC 5245
844 // If the agent is utilizing RTCP, it MUST encode the RTCP candidate
845 // using the a=rtcp attribute as defined in RFC 3605.
847 // RFC 3605
848 // rtcp-attribute = "a=rtcp:" port [nettype space addrtype space
849 // connection-address] CRLF
850 rtc::StringBuilder os;
851 InitAttrLine(kAttributeRtcp, &os);
852 os << kSdpDelimiterColon << rtcp_port << " " << kConnectionNettype << " "
853 << addr_type << " " << rtcp_ip;
854 rtcp_line = os.str();
855 return rtcp_line;
858 // Get candidates according to the mline index from SessionDescriptionInterface.
859 static void GetCandidatesByMindex(const SessionDescriptionInterface& desci,
860 int mline_index,
861 std::vector<Candidate>* candidates) {
862 if (!candidates) {
863 return;
865 const IceCandidateCollection* cc = desci.candidates(mline_index);
866 for (size_t i = 0; i < cc->count(); ++i) {
867 const IceCandidateInterface* candidate = cc->at(i);
868 candidates->push_back(candidate->candidate());
872 static bool IsValidPort(int port) {
873 return port >= 0 && port <= 65535;
876 std::string SdpSerialize(const JsepSessionDescription& jdesc) {
877 const cricket::SessionDescription* desc = jdesc.description();
878 if (!desc) {
879 return "";
882 std::string message;
884 // Session Description.
885 AddLine(kSessionVersion, &message);
886 // Session Origin
887 // RFC 4566
888 // o=<username> <sess-id> <sess-version> <nettype> <addrtype>
889 // <unicast-address>
890 rtc::StringBuilder os;
891 InitLine(kLineTypeOrigin, kSessionOriginUsername, &os);
892 const std::string& session_id =
893 jdesc.session_id().empty() ? kSessionOriginSessionId : jdesc.session_id();
894 const std::string& session_version = jdesc.session_version().empty()
895 ? kSessionOriginSessionVersion
896 : jdesc.session_version();
897 os << " " << session_id << " " << session_version << " "
898 << kSessionOriginNettype << " " << kSessionOriginAddrtype << " "
899 << kSessionOriginAddress;
900 AddLine(os.str(), &message);
901 AddLine(kSessionName, &message);
903 // Time Description.
904 AddLine(kTimeDescription, &message);
906 // BUNDLE Groups
907 std::vector<const cricket::ContentGroup*> groups =
908 desc->GetGroupsByName(cricket::GROUP_TYPE_BUNDLE);
909 for (const cricket::ContentGroup* group : groups) {
910 std::string group_line = kAttrGroup;
911 RTC_DCHECK(group != NULL);
912 for (const std::string& content_name : group->content_names()) {
913 group_line.append(" ");
914 group_line.append(content_name);
916 AddLine(group_line, &message);
919 // Mixed one- and two-byte header extension.
920 if (desc->extmap_allow_mixed()) {
921 InitAttrLine(kAttributeExtmapAllowMixed, &os);
922 AddLine(os.str(), &message);
925 // MediaStream semantics
926 InitAttrLine(kAttributeMsidSemantics, &os);
927 os << kSdpDelimiterColon << " " << kMediaStreamSemantic;
929 std::set<std::string> media_stream_ids;
930 const ContentInfo* audio_content = GetFirstAudioContent(desc);
931 if (audio_content)
932 GetMediaStreamIds(audio_content, &media_stream_ids);
934 const ContentInfo* video_content = GetFirstVideoContent(desc);
935 if (video_content)
936 GetMediaStreamIds(video_content, &media_stream_ids);
938 for (const std::string& id : media_stream_ids) {
939 os << " " << id;
941 AddLine(os.str(), &message);
943 // a=ice-lite
945 // TODO(deadbeef): It's weird that we need to iterate TransportInfos for
946 // this, when it's a session-level attribute. It really should be moved to a
947 // session-level structure like SessionDescription.
948 for (const cricket::TransportInfo& transport : desc->transport_infos()) {
949 if (transport.description.ice_mode == cricket::ICEMODE_LITE) {
950 InitAttrLine(kAttributeIceLite, &os);
951 AddLine(os.str(), &message);
952 break;
956 // Preserve the order of the media contents.
957 int mline_index = -1;
958 for (const ContentInfo& content : desc->contents()) {
959 std::vector<Candidate> candidates;
960 GetCandidatesByMindex(jdesc, ++mline_index, &candidates);
961 BuildMediaDescription(&content, desc->GetTransportInfoByName(content.name),
962 content.media_description()->type(), candidates,
963 desc->msid_signaling(), &message);
965 return message;
968 // Serializes the passed in IceCandidateInterface to a SDP string.
969 // candidate - The candidate to be serialized.
970 std::string SdpSerializeCandidate(const IceCandidateInterface& candidate) {
971 return SdpSerializeCandidate(candidate.candidate());
974 // Serializes a cricket Candidate.
975 std::string SdpSerializeCandidate(const cricket::Candidate& candidate) {
976 std::string message;
977 std::vector<cricket::Candidate> candidates(1, candidate);
978 BuildCandidate(candidates, true, &message);
979 // From WebRTC draft section 4.8.1.1 candidate-attribute will be
980 // just candidate:<candidate> not a=candidate:<blah>CRLF
981 RTC_DCHECK(message.find("a=") == 0);
982 message.erase(0, 2);
983 RTC_DCHECK(message.find(kLineBreak) == message.size() - 2);
984 message.resize(message.size() - 2);
985 return message;
988 bool SdpDeserialize(absl::string_view message,
989 JsepSessionDescription* jdesc,
990 SdpParseError* error) {
991 std::string session_id;
992 std::string session_version;
993 TransportDescription session_td("", "");
994 RtpHeaderExtensions session_extmaps;
995 rtc::SocketAddress session_connection_addr;
996 auto desc = std::make_unique<cricket::SessionDescription>();
997 size_t current_pos = 0;
999 // Session Description
1000 if (!ParseSessionDescription(message, &current_pos, &session_id,
1001 &session_version, &session_td, &session_extmaps,
1002 &session_connection_addr, desc.get(), error)) {
1003 return false;
1006 // Media Description
1007 std::vector<std::unique_ptr<JsepIceCandidate>> candidates;
1008 if (!ParseMediaDescription(message, session_td, session_extmaps, &current_pos,
1009 session_connection_addr, desc.get(), &candidates,
1010 error)) {
1011 return false;
1014 jdesc->Initialize(std::move(desc), session_id, session_version);
1016 for (const auto& candidate : candidates) {
1017 jdesc->AddCandidate(candidate.get());
1019 return true;
1022 bool SdpDeserializeCandidate(absl::string_view message,
1023 JsepIceCandidate* jcandidate,
1024 SdpParseError* error) {
1025 RTC_DCHECK(jcandidate != NULL);
1026 Candidate candidate;
1027 if (!ParseCandidate(message, &candidate, error, true)) {
1028 return false;
1030 jcandidate->SetCandidate(candidate);
1031 return true;
1034 bool SdpDeserializeCandidate(absl::string_view transport_name,
1035 absl::string_view message,
1036 cricket::Candidate* candidate,
1037 SdpParseError* error) {
1038 RTC_DCHECK(candidate != nullptr);
1039 if (!ParseCandidate(message, candidate, error, true)) {
1040 return false;
1042 candidate->set_transport_name(transport_name);
1043 return true;
1046 bool ParseCandidate(absl::string_view message,
1047 Candidate* candidate,
1048 SdpParseError* error,
1049 bool is_raw) {
1050 RTC_DCHECK(candidate != NULL);
1052 // Makes sure `message` contains only one line.
1053 absl::string_view first_line;
1055 size_t line_end = message.find(kNewLineChar);
1056 if (line_end == absl::string_view::npos) {
1057 first_line = message;
1058 } else if (line_end + 1 == message.size()) {
1059 first_line = message.substr(0, line_end);
1060 } else {
1061 return ParseFailed(message, 0, "Expect one line only", error);
1064 // Trim return char, if any.
1065 first_line = TrimReturnChar(first_line);
1067 // From WebRTC draft section 4.8.1.1 candidate-attribute should be
1068 // candidate:<candidate> when trickled, but we still support
1069 // a=candidate:<blah>CRLF for backward compatibility and for parsing a line
1070 // from the SDP.
1071 if (IsLineType(first_line, kLineTypeAttributes)) {
1072 first_line = first_line.substr(kLinePrefixLength);
1075 std::string attribute_candidate;
1076 std::string candidate_value;
1078 // `first_line` must be in the form of "candidate:<value>".
1079 if (!rtc::tokenize_first(first_line, kSdpDelimiterColonChar,
1080 &attribute_candidate, &candidate_value) ||
1081 attribute_candidate != kAttributeCandidate) {
1082 if (is_raw) {
1083 rtc::StringBuilder description;
1084 description << "Expect line: " << kAttributeCandidate
1085 << ":"
1086 "<candidate-str>";
1087 return ParseFailed(first_line, 0, description.Release(), error);
1088 } else {
1089 return ParseFailedExpectLine(first_line, 0, kLineTypeAttributes,
1090 kAttributeCandidate, error);
1094 std::vector<absl::string_view> fields =
1095 rtc::split(candidate_value, kSdpDelimiterSpaceChar);
1097 // RFC 5245
1098 // a=candidate:<foundation> <component-id> <transport> <priority>
1099 // <connection-address> <port> typ <candidate-types>
1100 // [raddr <connection-address>] [rport <port>]
1101 // *(SP extension-att-name SP extension-att-value)
1102 const size_t expected_min_fields = 8;
1103 if (fields.size() < expected_min_fields ||
1104 (fields[6] != kAttributeCandidateTyp)) {
1105 return ParseFailedExpectMinFieldNum(first_line, expected_min_fields, error);
1107 const absl::string_view foundation = fields[0];
1109 int component_id = 0;
1110 if (!GetValueFromString(first_line, fields[1], &component_id, error)) {
1111 return false;
1113 const absl::string_view transport = fields[2];
1114 uint32_t priority = 0;
1115 if (!GetValueFromString(first_line, fields[3], &priority, error)) {
1116 return false;
1118 const absl::string_view connection_address = fields[4];
1119 int port = 0;
1120 if (!GetValueFromString(first_line, fields[5], &port, error)) {
1121 return false;
1123 if (!IsValidPort(port)) {
1124 return ParseFailed(first_line, "Invalid port number.", error);
1126 SocketAddress address(connection_address, port);
1128 absl::optional<cricket::ProtocolType> protocol =
1129 cricket::StringToProto(transport);
1130 if (!protocol) {
1131 return ParseFailed(first_line, "Unsupported transport type.", error);
1133 bool tcp_protocol = false;
1134 switch (*protocol) {
1135 // Supported protocols.
1136 case cricket::PROTO_UDP:
1137 break;
1138 case cricket::PROTO_TCP:
1139 case cricket::PROTO_SSLTCP:
1140 tcp_protocol = true;
1141 break;
1142 default:
1143 return ParseFailed(first_line, "Unsupported transport type.", error);
1146 std::string candidate_type;
1147 const absl::string_view type = fields[7];
1148 if (type == kCandidateHost) {
1149 candidate_type = cricket::LOCAL_PORT_TYPE;
1150 } else if (type == kCandidateSrflx) {
1151 candidate_type = cricket::STUN_PORT_TYPE;
1152 } else if (type == kCandidateRelay) {
1153 candidate_type = cricket::RELAY_PORT_TYPE;
1154 } else if (type == kCandidatePrflx) {
1155 candidate_type = cricket::PRFLX_PORT_TYPE;
1156 } else {
1157 return ParseFailed(first_line, "Unsupported candidate type.", error);
1160 size_t current_position = expected_min_fields;
1161 SocketAddress related_address;
1162 // The 2 optional fields for related address
1163 // [raddr <connection-address>] [rport <port>]
1164 if (fields.size() >= (current_position + 2) &&
1165 fields[current_position] == kAttributeCandidateRaddr) {
1166 related_address.SetIP(fields[++current_position]);
1167 ++current_position;
1169 if (fields.size() >= (current_position + 2) &&
1170 fields[current_position] == kAttributeCandidateRport) {
1171 int port = 0;
1172 if (!GetValueFromString(first_line, fields[++current_position], &port,
1173 error)) {
1174 return false;
1176 if (!IsValidPort(port)) {
1177 return ParseFailed(first_line, "Invalid port number.", error);
1179 related_address.SetPort(port);
1180 ++current_position;
1183 // If this is a TCP candidate, it has additional extension as defined in
1184 // RFC 6544.
1185 absl::string_view tcptype;
1186 if (fields.size() >= (current_position + 2) &&
1187 fields[current_position] == kTcpCandidateType) {
1188 tcptype = fields[++current_position];
1189 ++current_position;
1191 if (tcptype != cricket::TCPTYPE_ACTIVE_STR &&
1192 tcptype != cricket::TCPTYPE_PASSIVE_STR &&
1193 tcptype != cricket::TCPTYPE_SIMOPEN_STR) {
1194 return ParseFailed(first_line, "Invalid TCP candidate type.", error);
1197 if (!tcp_protocol) {
1198 return ParseFailed(first_line, "Invalid non-TCP candidate", error);
1200 } else if (tcp_protocol) {
1201 // We allow the tcptype to be missing, for backwards compatibility,
1202 // treating it as a passive candidate.
1203 // TODO(bugs.webrtc.org/11466): Treat a missing tcptype as an error?
1204 tcptype = cricket::TCPTYPE_PASSIVE_STR;
1207 // Extension
1208 // Though non-standard, we support the ICE ufrag and pwd being signaled on
1209 // the candidate to avoid issues with confusing which generation a candidate
1210 // belongs to when trickling multiple generations at the same time.
1211 absl::string_view username;
1212 absl::string_view password;
1213 uint32_t generation = 0;
1214 uint16_t network_id = 0;
1215 uint16_t network_cost = 0;
1216 for (size_t i = current_position; i + 1 < fields.size(); ++i) {
1217 // RFC 5245
1218 // *(SP extension-att-name SP extension-att-value)
1219 if (fields[i] == kAttributeCandidateGeneration) {
1220 if (!GetValueFromString(first_line, fields[++i], &generation, error)) {
1221 return false;
1223 } else if (fields[i] == kAttributeCandidateUfrag) {
1224 username = fields[++i];
1225 } else if (fields[i] == kAttributeCandidatePwd) {
1226 password = fields[++i];
1227 } else if (fields[i] == kAttributeCandidateNetworkId) {
1228 if (!GetValueFromString(first_line, fields[++i], &network_id, error)) {
1229 return false;
1231 } else if (fields[i] == kAttributeCandidateNetworkCost) {
1232 if (!GetValueFromString(first_line, fields[++i], &network_cost, error)) {
1233 return false;
1235 network_cost = std::min(network_cost, rtc::kNetworkCostMax);
1236 } else {
1237 // Skip the unknown extension.
1238 ++i;
1242 *candidate = Candidate(component_id, cricket::ProtoToString(*protocol),
1243 address, priority, username, password, candidate_type,
1244 generation, foundation, network_id, network_cost);
1245 candidate->set_related_address(related_address);
1246 candidate->set_tcptype(tcptype);
1247 return true;
1250 bool ParseIceOptions(absl::string_view line,
1251 std::vector<std::string>* transport_options,
1252 SdpParseError* error) {
1253 std::string ice_options;
1254 if (!GetValue(line, kAttributeIceOption, &ice_options, error)) {
1255 return false;
1257 std::vector<absl::string_view> fields =
1258 rtc::split(ice_options, kSdpDelimiterSpaceChar);
1259 for (size_t i = 0; i < fields.size(); ++i) {
1260 transport_options->emplace_back(fields[i]);
1262 return true;
1265 bool ParseSctpPort(absl::string_view line,
1266 int* sctp_port,
1267 SdpParseError* error) {
1268 // draft-ietf-mmusic-sctp-sdp-26
1269 // a=sctp-port
1270 const size_t expected_min_fields = 2;
1271 std::vector<absl::string_view> fields =
1272 rtc::split(line.substr(kLinePrefixLength), kSdpDelimiterColonChar);
1273 if (fields.size() < expected_min_fields) {
1274 fields = rtc::split(line.substr(kLinePrefixLength), kSdpDelimiterSpaceChar);
1276 if (fields.size() < expected_min_fields) {
1277 return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
1279 if (!rtc::FromString(fields[1], sctp_port)) {
1280 return ParseFailed(line, "Invalid sctp port value.", error);
1282 return true;
1285 bool ParseSctpMaxMessageSize(absl::string_view line,
1286 int* max_message_size,
1287 SdpParseError* error) {
1288 // draft-ietf-mmusic-sctp-sdp-26
1289 // a=max-message-size:199999
1290 const size_t expected_min_fields = 2;
1291 std::vector<absl::string_view> fields =
1292 rtc::split(line.substr(kLinePrefixLength), kSdpDelimiterColonChar);
1293 if (fields.size() < expected_min_fields) {
1294 return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
1296 if (!rtc::FromString(fields[1], max_message_size)) {
1297 return ParseFailed(line, "Invalid SCTP max message size.", error);
1299 return true;
1302 bool ParseExtmap(absl::string_view line,
1303 RtpExtension* extmap,
1304 SdpParseError* error) {
1305 // RFC 5285
1306 // a=extmap:<value>["/"<direction>] <URI> <extensionattributes>
1307 std::vector<absl::string_view> fields =
1308 rtc::split(line.substr(kLinePrefixLength), kSdpDelimiterSpaceChar);
1309 const size_t expected_min_fields = 2;
1310 if (fields.size() < expected_min_fields) {
1311 return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
1313 absl::string_view uri = fields[1];
1315 std::string value_direction;
1316 if (!GetValue(fields[0], kAttributeExtmap, &value_direction, error)) {
1317 return false;
1319 std::vector<absl::string_view> sub_fields =
1320 rtc::split(value_direction, kSdpDelimiterSlashChar);
1321 int value = 0;
1322 if (!GetValueFromString(line, sub_fields[0], &value, error)) {
1323 return false;
1326 bool encrypted = false;
1327 if (uri == RtpExtension::kEncryptHeaderExtensionsUri) {
1328 // RFC 6904
1329 // a=extmap:<value["/"<direction>] urn:ietf:params:rtp-hdrext:encrypt <URI>
1330 // <extensionattributes>
1331 const size_t expected_min_fields_encrypted = expected_min_fields + 1;
1332 if (fields.size() < expected_min_fields_encrypted) {
1333 return ParseFailedExpectMinFieldNum(line, expected_min_fields_encrypted,
1334 error);
1337 encrypted = true;
1338 uri = fields[2];
1339 if (uri == RtpExtension::kEncryptHeaderExtensionsUri) {
1340 return ParseFailed(line, "Recursive encrypted header.", error);
1344 *extmap = RtpExtension(uri, value, encrypted);
1345 return true;
1348 static void BuildSctpContentAttributes(
1349 std::string* message,
1350 const cricket::SctpDataContentDescription* data_desc) {
1351 rtc::StringBuilder os;
1352 if (data_desc->use_sctpmap()) {
1353 // draft-ietf-mmusic-sctp-sdp-04
1354 // a=sctpmap:sctpmap-number protocol [streams]
1355 rtc::StringBuilder os;
1356 InitAttrLine(kAttributeSctpmap, &os);
1357 os << kSdpDelimiterColon << data_desc->port() << kSdpDelimiterSpace
1358 << kDefaultSctpmapProtocol << kSdpDelimiterSpace
1359 << cricket::kMaxSctpStreams;
1360 AddLine(os.str(), message);
1361 } else {
1362 // draft-ietf-mmusic-sctp-sdp-23
1363 // a=sctp-port:<port>
1364 InitAttrLine(kAttributeSctpPort, &os);
1365 os << kSdpDelimiterColon << data_desc->port();
1366 AddLine(os.str(), message);
1367 if (data_desc->max_message_size() != kDefaultSctpMaxMessageSize) {
1368 InitAttrLine(kAttributeMaxMessageSize, &os);
1369 os << kSdpDelimiterColon << data_desc->max_message_size();
1370 AddLine(os.str(), message);
1375 void BuildIceUfragPwd(const TransportInfo* transport_info,
1376 std::string* message) {
1377 RTC_DCHECK(transport_info);
1379 rtc::StringBuilder os;
1380 // RFC 5245
1381 // ice-pwd-att = "ice-pwd" ":" password
1382 // ice-ufrag-att = "ice-ufrag" ":" ufrag
1383 // ice-ufrag
1384 if (!transport_info->description.ice_ufrag.empty()) {
1385 InitAttrLine(kAttributeIceUfrag, &os);
1386 os << kSdpDelimiterColon << transport_info->description.ice_ufrag;
1387 AddLine(os.str(), message);
1389 // ice-pwd
1390 if (!transport_info->description.ice_pwd.empty()) {
1391 InitAttrLine(kAttributeIcePwd, &os);
1392 os << kSdpDelimiterColon << transport_info->description.ice_pwd;
1393 AddLine(os.str(), message);
1397 void BuildDtlsFingerprintSetup(const TransportInfo* transport_info,
1398 std::string* message) {
1399 RTC_DCHECK(transport_info);
1401 rtc::StringBuilder os;
1402 // RFC 4572
1403 // fingerprint-attribute =
1404 // "fingerprint" ":" hash-func SP fingerprint
1405 // When using max-bundle this is already included at session level.
1406 // Insert the fingerprint attribute.
1407 auto fingerprint = transport_info->description.identity_fingerprint.get();
1408 if (!fingerprint) {
1409 return;
1411 InitAttrLine(kAttributeFingerprint, &os);
1412 os << kSdpDelimiterColon << fingerprint->algorithm << kSdpDelimiterSpace
1413 << fingerprint->GetRfc4572Fingerprint();
1414 AddLine(os.str(), message);
1416 // Inserting setup attribute.
1417 if (transport_info->description.connection_role !=
1418 cricket::CONNECTIONROLE_NONE) {
1419 // Making sure we are not using "passive" mode.
1420 cricket::ConnectionRole role = transport_info->description.connection_role;
1421 std::string dtls_role_str;
1422 const bool success = cricket::ConnectionRoleToString(role, &dtls_role_str);
1423 RTC_DCHECK(success);
1424 InitAttrLine(kAttributeSetup, &os);
1425 os << kSdpDelimiterColon << dtls_role_str;
1426 AddLine(os.str(), message);
1430 void BuildMediaLine(const cricket::MediaType media_type,
1431 const ContentInfo* content_info,
1432 const MediaContentDescription* media_desc,
1433 std::string* message) {
1434 rtc::StringBuilder os;
1436 // RFC 4566
1437 // m=<media> <port> <proto> <fmt>
1438 // fmt is a list of payload type numbers that MAY be used in the session.
1439 std::string type;
1440 std::string fmt;
1441 if (media_type == cricket::MEDIA_TYPE_VIDEO) {
1442 type = kMediaTypeVideo;
1443 const VideoContentDescription* video_desc = media_desc->as_video();
1444 for (const cricket::VideoCodec& codec : video_desc->codecs()) {
1445 fmt.append(" ");
1446 fmt.append(rtc::ToString(codec.id));
1448 } else if (media_type == cricket::MEDIA_TYPE_AUDIO) {
1449 type = kMediaTypeAudio;
1450 const AudioContentDescription* audio_desc = media_desc->as_audio();
1451 for (const cricket::AudioCodec& codec : audio_desc->codecs()) {
1452 fmt.append(" ");
1453 fmt.append(rtc::ToString(codec.id));
1455 } else if (media_type == cricket::MEDIA_TYPE_DATA) {
1456 type = kMediaTypeData;
1457 const cricket::SctpDataContentDescription* sctp_data_desc =
1458 media_desc->as_sctp();
1459 if (sctp_data_desc) {
1460 fmt.append(" ");
1462 if (sctp_data_desc->use_sctpmap()) {
1463 fmt.append(rtc::ToString(sctp_data_desc->port()));
1464 } else {
1465 fmt.append(kDefaultSctpmapProtocol);
1467 } else {
1468 RTC_DCHECK_NOTREACHED() << "Data description without SCTP";
1470 } else if (media_type == cricket::MEDIA_TYPE_UNSUPPORTED) {
1471 const UnsupportedContentDescription* unsupported_desc =
1472 media_desc->as_unsupported();
1473 type = unsupported_desc->media_type();
1474 } else {
1475 RTC_DCHECK_NOTREACHED();
1477 // The fmt must never be empty. If no codecs are found, set the fmt attribute
1478 // to 0.
1479 if (fmt.empty()) {
1480 fmt = " 0";
1483 // The port number in the m line will be updated later when associated with
1484 // the candidates.
1486 // A port value of 0 indicates that the m= section is rejected.
1487 // RFC 3264
1488 // To reject an offered stream, the port number in the corresponding stream in
1489 // the answer MUST be set to zero.
1491 // However, the BUNDLE draft adds a new meaning to port zero, when used along
1492 // with a=bundle-only.
1493 std::string port = kDummyPort;
1494 if (content_info->rejected || content_info->bundle_only) {
1495 port = kMediaPortRejected;
1496 } else if (!media_desc->connection_address().IsNil()) {
1497 port = rtc::ToString(media_desc->connection_address().port());
1500 // Add the m and c lines.
1501 InitLine(kLineTypeMedia, type, &os);
1502 os << " " << port << " " << media_desc->protocol() << fmt;
1503 AddLine(os.str(), message);
1506 void BuildMediaDescription(const ContentInfo* content_info,
1507 const TransportInfo* transport_info,
1508 const cricket::MediaType media_type,
1509 const std::vector<Candidate>& candidates,
1510 int msid_signaling,
1511 std::string* message) {
1512 RTC_DCHECK(message);
1513 if (!content_info) {
1514 return;
1516 rtc::StringBuilder os;
1517 const MediaContentDescription* media_desc = content_info->media_description();
1518 RTC_DCHECK(media_desc);
1520 // Add the m line.
1521 BuildMediaLine(media_type, content_info, media_desc, message);
1522 // Add the c line.
1523 InitLine(kLineTypeConnection, kConnectionNettype, &os);
1524 if (media_desc->connection_address().IsNil()) {
1525 os << " " << kConnectionIpv4Addrtype << " " << kDummyAddress;
1526 } else if (media_desc->connection_address().family() == AF_INET) {
1527 os << " " << kConnectionIpv4Addrtype << " "
1528 << media_desc->connection_address().ipaddr().ToString();
1529 } else if (media_desc->connection_address().family() == AF_INET6) {
1530 os << " " << kConnectionIpv6Addrtype << " "
1531 << media_desc->connection_address().ipaddr().ToString();
1532 } else {
1533 os << " " << kConnectionIpv4Addrtype << " " << kDummyAddress;
1535 AddLine(os.str(), message);
1537 // RFC 4566
1538 // b=AS:<bandwidth> or
1539 // b=TIAS:<bandwidth>
1540 int bandwidth = media_desc->bandwidth();
1541 std::string bandwidth_type = media_desc->bandwidth_type();
1542 if (bandwidth_type == kApplicationSpecificBandwidth && bandwidth >= 1000) {
1543 InitLine(kLineTypeSessionBandwidth, bandwidth_type, &os);
1544 bandwidth /= 1000;
1545 os << kSdpDelimiterColon << bandwidth;
1546 AddLine(os.str(), message);
1547 } else if (bandwidth_type == kTransportSpecificBandwidth && bandwidth > 0) {
1548 InitLine(kLineTypeSessionBandwidth, bandwidth_type, &os);
1549 os << kSdpDelimiterColon << bandwidth;
1550 AddLine(os.str(), message);
1553 // Add the a=bundle-only line.
1554 if (content_info->bundle_only) {
1555 InitAttrLine(kAttributeBundleOnly, &os);
1556 AddLine(os.str(), message);
1559 // Add the a=rtcp line.
1560 if (cricket::IsRtpProtocol(media_desc->protocol())) {
1561 std::string rtcp_line = GetRtcpLine(candidates);
1562 if (!rtcp_line.empty()) {
1563 AddLine(rtcp_line, message);
1567 // Build the a=candidate lines. We don't include ufrag and pwd in the
1568 // candidates in the SDP to avoid redundancy.
1569 BuildCandidate(candidates, false, message);
1571 // Use the transport_info to build the media level ice-ufrag, ice-pwd
1572 // and DTLS fingerprint and setup attributes.
1573 if (transport_info) {
1574 BuildIceUfragPwd(transport_info, message);
1576 // draft-petithuguenin-mmusic-ice-attributes-level-03
1577 BuildIceOptions(transport_info->description.transport_options, message);
1579 // Also include the DTLS fingerprint and setup attribute if available.
1580 BuildDtlsFingerprintSetup(transport_info, message);
1583 // RFC 3388
1584 // mid-attribute = "a=mid:" identification-tag
1585 // identification-tag = token
1586 // Use the content name as the mid identification-tag.
1587 InitAttrLine(kAttributeMid, &os);
1588 os << kSdpDelimiterColon << content_info->name;
1589 AddLine(os.str(), message);
1591 if (cricket::IsDtlsSctp(media_desc->protocol())) {
1592 const cricket::SctpDataContentDescription* data_desc =
1593 media_desc->as_sctp();
1594 BuildSctpContentAttributes(message, data_desc);
1595 } else if (cricket::IsRtpProtocol(media_desc->protocol())) {
1596 BuildRtpContentAttributes(media_desc, media_type, msid_signaling, message);
1600 void BuildRtpContentAttributes(const MediaContentDescription* media_desc,
1601 const cricket::MediaType media_type,
1602 int msid_signaling,
1603 std::string* message) {
1604 SdpSerializer serializer;
1605 rtc::StringBuilder os;
1606 // RFC 8285
1607 // a=extmap-allow-mixed
1608 // The attribute MUST be either on session level or media level. We support
1609 // responding on both levels, however, we don't respond on media level if it's
1610 // set on session level.
1611 if (media_desc->extmap_allow_mixed_enum() ==
1612 MediaContentDescription::kMedia) {
1613 InitAttrLine(kAttributeExtmapAllowMixed, &os);
1614 AddLine(os.str(), message);
1616 BuildRtpHeaderExtensions(media_desc->rtp_header_extensions(), message);
1618 // RFC 3264
1619 // a=sendrecv || a=sendonly || a=sendrecv || a=inactive
1620 switch (media_desc->direction()) {
1621 // Special case that for sdp purposes should be treated same as inactive.
1622 case RtpTransceiverDirection::kStopped:
1623 case RtpTransceiverDirection::kInactive:
1624 InitAttrLine(kAttributeInactive, &os);
1625 break;
1626 case RtpTransceiverDirection::kSendOnly:
1627 InitAttrLine(kAttributeSendOnly, &os);
1628 break;
1629 case RtpTransceiverDirection::kRecvOnly:
1630 InitAttrLine(kAttributeRecvOnly, &os);
1631 break;
1632 case RtpTransceiverDirection::kSendRecv:
1633 InitAttrLine(kAttributeSendRecv, &os);
1634 break;
1635 default:
1636 RTC_DCHECK_NOTREACHED();
1637 InitAttrLine(kAttributeSendRecv, &os);
1638 break;
1640 AddLine(os.str(), message);
1642 // Specified in https://datatracker.ietf.org/doc/draft-ietf-mmusic-msid/16/
1643 // a=msid:<msid-id> <msid-appdata>
1644 // The msid-id is a 1*64 token char representing the media stream id, and the
1645 // msid-appdata is a 1*64 token char representing the track id. There is a
1646 // line for every media stream, with a special msid-id value of "-"
1647 // representing no streams. The value of "msid-appdata" MUST be identical for
1648 // all lines.
1649 if (msid_signaling & cricket::kMsidSignalingMediaSection) {
1650 const StreamParamsVec& streams = media_desc->streams();
1651 if (streams.size() == 1u) {
1652 const StreamParams& track = streams[0];
1653 std::vector<std::string> stream_ids = track.stream_ids();
1654 if (stream_ids.empty()) {
1655 stream_ids.push_back(kNoStreamMsid);
1657 for (const std::string& stream_id : stream_ids) {
1658 InitAttrLine(kAttributeMsid, &os);
1659 os << kSdpDelimiterColon << stream_id << kSdpDelimiterSpace << track.id;
1660 AddLine(os.str(), message);
1662 } else if (streams.size() > 1u) {
1663 RTC_LOG(LS_WARNING)
1664 << "Trying to serialize Unified Plan SDP with more than "
1665 "one track in a media section. Omitting 'a=msid'.";
1669 // RFC 5761
1670 // a=rtcp-mux
1671 if (media_desc->rtcp_mux()) {
1672 InitAttrLine(kAttributeRtcpMux, &os);
1673 AddLine(os.str(), message);
1676 // RFC 5506
1677 // a=rtcp-rsize
1678 if (media_desc->rtcp_reduced_size()) {
1679 InitAttrLine(kAttributeRtcpReducedSize, &os);
1680 AddLine(os.str(), message);
1683 if (media_desc->conference_mode()) {
1684 InitAttrLine(kAttributeXGoogleFlag, &os);
1685 os << kSdpDelimiterColon << kValueConference;
1686 AddLine(os.str(), message);
1689 if (media_desc->remote_estimate()) {
1690 InitAttrLine(kAttributeRtcpRemoteEstimate, &os);
1691 AddLine(os.str(), message);
1694 // RFC 4568
1695 // a=crypto:<tag> <crypto-suite> <key-params> [<session-params>]
1696 for (const CryptoParams& crypto_params : media_desc->cryptos()) {
1697 InitAttrLine(kAttributeCrypto, &os);
1698 os << kSdpDelimiterColon << crypto_params.tag << " "
1699 << crypto_params.crypto_suite << " " << crypto_params.key_params;
1700 if (!crypto_params.session_params.empty()) {
1701 os << " " << crypto_params.session_params;
1703 AddLine(os.str(), message);
1706 // RFC 4566
1707 // a=rtpmap:<payload type> <encoding name>/<clock rate>
1708 // [/<encodingparameters>]
1709 BuildRtpmap(media_desc, media_type, message);
1711 for (const StreamParams& track : media_desc->streams()) {
1712 // Build the ssrc-group lines.
1713 for (const SsrcGroup& ssrc_group : track.ssrc_groups) {
1714 // RFC 5576
1715 // a=ssrc-group:<semantics> <ssrc-id> ...
1716 if (ssrc_group.ssrcs.empty()) {
1717 continue;
1719 InitAttrLine(kAttributeSsrcGroup, &os);
1720 os << kSdpDelimiterColon << ssrc_group.semantics;
1721 for (uint32_t ssrc : ssrc_group.ssrcs) {
1722 os << kSdpDelimiterSpace << rtc::ToString(ssrc);
1724 AddLine(os.str(), message);
1726 // Build the ssrc lines for each ssrc.
1727 for (uint32_t ssrc : track.ssrcs) {
1728 // RFC 5576
1729 // a=ssrc:<ssrc-id> cname:<value>
1730 AddSsrcLine(ssrc, kSsrcAttributeCname, track.cname, message);
1732 if (msid_signaling & cricket::kMsidSignalingSsrcAttribute) {
1733 // draft-alvestrand-mmusic-msid-00
1734 // a=ssrc:<ssrc-id> msid:identifier [appdata]
1735 // The appdata consists of the "id" attribute of a MediaStreamTrack,
1736 // which corresponds to the "id" attribute of StreamParams.
1737 // Since a=ssrc msid signaling is used in Plan B SDP semantics, and
1738 // multiple stream ids are not supported for Plan B, we are only adding
1739 // a line for the first media stream id here.
1740 const std::string& track_stream_id = track.first_stream_id();
1741 // We use a special msid-id value of "-" to represent no streams,
1742 // for Unified Plan compatibility. Plan B will always have a
1743 // track_stream_id.
1744 const std::string& stream_id =
1745 track_stream_id.empty() ? kNoStreamMsid : track_stream_id;
1746 InitAttrLine(kAttributeSsrc, &os);
1747 os << kSdpDelimiterColon << ssrc << kSdpDelimiterSpace
1748 << kSsrcAttributeMsid << kSdpDelimiterColon << stream_id
1749 << kSdpDelimiterSpace << track.id;
1750 AddLine(os.str(), message);
1754 // Build the rid lines for each layer of the track
1755 for (const RidDescription& rid_description : track.rids()) {
1756 InitAttrLine(kAttributeRid, &os);
1757 os << kSdpDelimiterColon
1758 << serializer.SerializeRidDescription(rid_description);
1759 AddLine(os.str(), message);
1763 for (const RidDescription& rid_description : media_desc->receive_rids()) {
1764 InitAttrLine(kAttributeRid, &os);
1765 os << kSdpDelimiterColon
1766 << serializer.SerializeRidDescription(rid_description);
1767 AddLine(os.str(), message);
1770 // Simulcast (a=simulcast)
1771 // https://tools.ietf.org/html/draft-ietf-mmusic-sdp-simulcast-13#section-5.1
1772 if (media_desc->HasSimulcast()) {
1773 const auto& simulcast = media_desc->simulcast_description();
1774 InitAttrLine(kAttributeSimulcast, &os);
1775 os << kSdpDelimiterColon
1776 << serializer.SerializeSimulcastDescription(simulcast);
1777 AddLine(os.str(), message);
1781 void BuildRtpHeaderExtensions(const RtpHeaderExtensions& extensions,
1782 std::string* message) {
1783 rtc::StringBuilder os;
1785 // RFC 8285
1786 // a=extmap:<value>["/"<direction>] <URI> <extensionattributes>
1787 // The definitions MUST be either all session level or all media level. This
1788 // implementation uses all media level.
1789 for (const RtpExtension& extension : extensions) {
1790 InitAttrLine(kAttributeExtmap, &os);
1791 os << kSdpDelimiterColon << extension.id;
1792 if (extension.encrypt) {
1793 os << kSdpDelimiterSpace << RtpExtension::kEncryptHeaderExtensionsUri;
1795 os << kSdpDelimiterSpace << extension.uri;
1796 AddLine(os.str(), message);
1800 void WriteFmtpHeader(int payload_type, rtc::StringBuilder* os) {
1801 // fmtp header: a=fmtp:`payload_type` <parameters>
1802 // Add a=fmtp
1803 InitAttrLine(kAttributeFmtp, os);
1804 // Add :`payload_type`
1805 *os << kSdpDelimiterColon << payload_type;
1808 void WritePacketizationHeader(int payload_type, rtc::StringBuilder* os) {
1809 // packetization header: a=packetization:`payload_type` <packetization_format>
1810 // Add a=packetization
1811 InitAttrLine(kAttributePacketization, os);
1812 // Add :`payload_type`
1813 *os << kSdpDelimiterColon << payload_type;
1816 void WriteRtcpFbHeader(int payload_type, rtc::StringBuilder* os) {
1817 // rtcp-fb header: a=rtcp-fb:`payload_type`
1818 // <parameters>/<ccm <ccm_parameters>>
1819 // Add a=rtcp-fb
1820 InitAttrLine(kAttributeRtcpFb, os);
1821 // Add :
1822 *os << kSdpDelimiterColon;
1823 if (payload_type == kWildcardPayloadType) {
1824 *os << "*";
1825 } else {
1826 *os << payload_type;
1830 void WriteFmtpParameter(absl::string_view parameter_name,
1831 absl::string_view parameter_value,
1832 rtc::StringBuilder* os) {
1833 if (parameter_name.empty()) {
1834 // RFC 2198 and RFC 4733 don't use key-value pairs.
1835 *os << parameter_value;
1836 } else {
1837 // fmtp parameters: `parameter_name`=`parameter_value`
1838 *os << parameter_name << kSdpDelimiterEqual << parameter_value;
1842 bool IsFmtpParam(absl::string_view name) {
1843 // RFC 4855, section 3 specifies the mapping of media format parameters to SDP
1844 // parameters. Only ptime, maxptime, channels and rate are placed outside of
1845 // the fmtp line. In WebRTC, channels and rate are already handled separately
1846 // and thus not included in the CodecParameterMap.
1847 return name != kCodecParamPTime && name != kCodecParamMaxPTime;
1850 bool WriteFmtpParameters(const cricket::CodecParameterMap& parameters,
1851 rtc::StringBuilder* os) {
1852 bool empty = true;
1853 const char* delimiter = ""; // No delimiter before first parameter.
1854 for (const auto& entry : parameters) {
1855 const std::string& key = entry.first;
1856 const std::string& value = entry.second;
1858 if (IsFmtpParam(key)) {
1859 *os << delimiter;
1860 // A semicolon before each subsequent parameter.
1861 delimiter = kSdpDelimiterSemicolon;
1862 WriteFmtpParameter(key, value, os);
1863 empty = false;
1867 return !empty;
1870 template <class T>
1871 void AddFmtpLine(const T& codec, std::string* message) {
1872 rtc::StringBuilder os;
1873 WriteFmtpHeader(codec.id, &os);
1874 os << kSdpDelimiterSpace;
1875 // Create FMTP line and check that it's nonempty.
1876 if (WriteFmtpParameters(codec.params, &os)) {
1877 AddLine(os.str(), message);
1879 return;
1882 template <class T>
1883 void AddPacketizationLine(const T& codec, std::string* message) {
1884 if (!codec.packetization) {
1885 return;
1887 rtc::StringBuilder os;
1888 WritePacketizationHeader(codec.id, &os);
1889 os << " " << *codec.packetization;
1890 AddLine(os.str(), message);
1893 template <class T>
1894 void AddRtcpFbLines(const T& codec, std::string* message) {
1895 for (const cricket::FeedbackParam& param : codec.feedback_params.params()) {
1896 rtc::StringBuilder os;
1897 WriteRtcpFbHeader(codec.id, &os);
1898 os << " " << param.id();
1899 if (!param.param().empty()) {
1900 os << " " << param.param();
1902 AddLine(os.str(), message);
1906 bool GetMinValue(const std::vector<int>& values, int* value) {
1907 if (values.empty()) {
1908 return false;
1910 auto it = absl::c_min_element(values);
1911 *value = *it;
1912 return true;
1915 bool GetParameter(const std::string& name,
1916 const cricket::CodecParameterMap& params,
1917 int* value) {
1918 std::map<std::string, std::string>::const_iterator found = params.find(name);
1919 if (found == params.end()) {
1920 return false;
1922 if (!rtc::FromString(found->second, value)) {
1923 return false;
1925 return true;
1928 void BuildRtpmap(const MediaContentDescription* media_desc,
1929 const cricket::MediaType media_type,
1930 std::string* message) {
1931 RTC_DCHECK(message != NULL);
1932 RTC_DCHECK(media_desc != NULL);
1933 rtc::StringBuilder os;
1934 if (media_type == cricket::MEDIA_TYPE_VIDEO) {
1935 for (const cricket::VideoCodec& codec : media_desc->as_video()->codecs()) {
1936 // RFC 4566
1937 // a=rtpmap:<payload type> <encoding name>/<clock rate>
1938 // [/<encodingparameters>]
1939 if (codec.id != kWildcardPayloadType) {
1940 InitAttrLine(kAttributeRtpmap, &os);
1941 os << kSdpDelimiterColon << codec.id << " " << codec.name << "/"
1942 << cricket::kVideoCodecClockrate;
1943 AddLine(os.str(), message);
1945 AddPacketizationLine(codec, message);
1946 AddRtcpFbLines(codec, message);
1947 AddFmtpLine(codec, message);
1949 } else if (media_type == cricket::MEDIA_TYPE_AUDIO) {
1950 std::vector<int> ptimes;
1951 std::vector<int> maxptimes;
1952 int max_minptime = 0;
1953 for (const cricket::AudioCodec& codec : media_desc->as_audio()->codecs()) {
1954 RTC_DCHECK(!codec.name.empty());
1955 // RFC 4566
1956 // a=rtpmap:<payload type> <encoding name>/<clock rate>
1957 // [/<encodingparameters>]
1958 InitAttrLine(kAttributeRtpmap, &os);
1959 os << kSdpDelimiterColon << codec.id << " ";
1960 os << codec.name << "/" << codec.clockrate;
1961 if (codec.channels != 1) {
1962 os << "/" << codec.channels;
1964 AddLine(os.str(), message);
1965 AddRtcpFbLines(codec, message);
1966 AddFmtpLine(codec, message);
1967 int minptime = 0;
1968 if (GetParameter(kCodecParamMinPTime, codec.params, &minptime)) {
1969 max_minptime = std::max(minptime, max_minptime);
1971 int ptime;
1972 if (GetParameter(kCodecParamPTime, codec.params, &ptime)) {
1973 ptimes.push_back(ptime);
1975 int maxptime;
1976 if (GetParameter(kCodecParamMaxPTime, codec.params, &maxptime)) {
1977 maxptimes.push_back(maxptime);
1980 // Populate the maxptime attribute with the smallest maxptime of all codecs
1981 // under the same m-line.
1982 int min_maxptime = INT_MAX;
1983 if (GetMinValue(maxptimes, &min_maxptime)) {
1984 AddAttributeLine(kCodecParamMaxPTime, min_maxptime, message);
1986 RTC_DCHECK_GE(min_maxptime, max_minptime);
1987 // Populate the ptime attribute with the smallest ptime or the largest
1988 // minptime, whichever is the largest, for all codecs under the same m-line.
1989 int ptime = INT_MAX;
1990 if (GetMinValue(ptimes, &ptime)) {
1991 ptime = std::min(ptime, min_maxptime);
1992 ptime = std::max(ptime, max_minptime);
1993 AddAttributeLine(kCodecParamPTime, ptime, message);
1998 void BuildCandidate(const std::vector<Candidate>& candidates,
1999 bool include_ufrag,
2000 std::string* message) {
2001 rtc::StringBuilder os;
2003 for (const Candidate& candidate : candidates) {
2004 // RFC 5245
2005 // a=candidate:<foundation> <component-id> <transport> <priority>
2006 // <connection-address> <port> typ <candidate-types>
2007 // [raddr <connection-address>] [rport <port>]
2008 // *(SP extension-att-name SP extension-att-value)
2009 std::string type;
2010 // Map the cricket candidate type to "host" / "srflx" / "prflx" / "relay"
2011 if (candidate.type() == cricket::LOCAL_PORT_TYPE) {
2012 type = kCandidateHost;
2013 } else if (candidate.type() == cricket::STUN_PORT_TYPE) {
2014 type = kCandidateSrflx;
2015 } else if (candidate.type() == cricket::RELAY_PORT_TYPE) {
2016 type = kCandidateRelay;
2017 } else if (candidate.type() == cricket::PRFLX_PORT_TYPE) {
2018 type = kCandidatePrflx;
2019 // Peer reflexive candidate may be signaled for being removed.
2020 } else {
2021 RTC_DCHECK_NOTREACHED();
2022 // Never write out candidates if we don't know the type.
2023 continue;
2026 InitAttrLine(kAttributeCandidate, &os);
2027 os << kSdpDelimiterColon << candidate.foundation() << " "
2028 << candidate.component() << " " << candidate.protocol() << " "
2029 << candidate.priority() << " "
2030 << (candidate.address().ipaddr().IsNil()
2031 ? candidate.address().hostname()
2032 : candidate.address().ipaddr().ToString())
2033 << " " << candidate.address().PortAsString() << " "
2034 << kAttributeCandidateTyp << " " << type << " ";
2036 // Related address
2037 if (!candidate.related_address().IsNil()) {
2038 os << kAttributeCandidateRaddr << " "
2039 << candidate.related_address().ipaddr().ToString() << " "
2040 << kAttributeCandidateRport << " "
2041 << candidate.related_address().PortAsString() << " ";
2044 // Note that we allow the tcptype to be missing, for backwards
2045 // compatibility; the implementation treats this as a passive candidate.
2046 // TODO(bugs.webrtc.org/11466): Treat a missing tcptype as an error?
2047 if (candidate.protocol() == cricket::TCP_PROTOCOL_NAME &&
2048 !candidate.tcptype().empty()) {
2049 os << kTcpCandidateType << " " << candidate.tcptype() << " ";
2052 // Extensions
2053 os << kAttributeCandidateGeneration << " " << candidate.generation();
2054 if (include_ufrag && !candidate.username().empty()) {
2055 os << " " << kAttributeCandidateUfrag << " " << candidate.username();
2057 if (candidate.network_id() > 0) {
2058 os << " " << kAttributeCandidateNetworkId << " "
2059 << candidate.network_id();
2061 if (candidate.network_cost() > 0) {
2062 os << " " << kAttributeCandidateNetworkCost << " "
2063 << candidate.network_cost();
2066 AddLine(os.str(), message);
2070 void BuildIceOptions(const std::vector<std::string>& transport_options,
2071 std::string* message) {
2072 if (!transport_options.empty()) {
2073 rtc::StringBuilder os;
2074 InitAttrLine(kAttributeIceOption, &os);
2075 os << kSdpDelimiterColon << transport_options[0];
2076 for (size_t i = 1; i < transport_options.size(); ++i) {
2077 os << kSdpDelimiterSpace << transport_options[i];
2079 AddLine(os.str(), message);
2083 bool ParseConnectionData(absl::string_view line,
2084 rtc::SocketAddress* addr,
2085 SdpParseError* error) {
2086 // Parse the line from left to right.
2087 std::string token;
2088 std::string rightpart;
2089 // RFC 4566
2090 // c=<nettype> <addrtype> <connection-address>
2091 // Skip the "c="
2092 if (!rtc::tokenize_first(line, kSdpDelimiterEqualChar, &token, &rightpart)) {
2093 return ParseFailed(line, "Failed to parse the network type.", error);
2096 // Extract and verify the <nettype>
2097 if (!rtc::tokenize_first(rightpart, kSdpDelimiterSpaceChar, &token,
2098 &rightpart) ||
2099 token != kConnectionNettype) {
2100 return ParseFailed(line,
2101 "Failed to parse the connection data. The network type "
2102 "is not currently supported.",
2103 error);
2106 // Extract the "<addrtype>" and "<connection-address>".
2107 if (!rtc::tokenize_first(rightpart, kSdpDelimiterSpaceChar, &token,
2108 &rightpart)) {
2109 return ParseFailed(line, "Failed to parse the address type.", error);
2112 // The rightpart part should be the IP address without the slash which is used
2113 // for multicast.
2114 if (rightpart.find('/') != std::string::npos) {
2115 return ParseFailed(line,
2116 "Failed to parse the connection data. Multicast is not "
2117 "currently supported.",
2118 error);
2120 addr->SetIP(rightpart);
2122 // Verify that the addrtype matches the type of the parsed address.
2123 if ((addr->family() == AF_INET && token != "IP4") ||
2124 (addr->family() == AF_INET6 && token != "IP6")) {
2125 addr->Clear();
2126 return ParseFailed(
2127 line,
2128 "Failed to parse the connection data. The address type is mismatching.",
2129 error);
2131 return true;
2134 bool ParseSessionDescription(absl::string_view message,
2135 size_t* pos,
2136 std::string* session_id,
2137 std::string* session_version,
2138 TransportDescription* session_td,
2139 RtpHeaderExtensions* session_extmaps,
2140 rtc::SocketAddress* connection_addr,
2141 cricket::SessionDescription* desc,
2142 SdpParseError* error) {
2143 absl::optional<absl::string_view> line;
2145 desc->set_msid_supported(false);
2146 desc->set_extmap_allow_mixed(false);
2147 // RFC 4566
2148 // v= (protocol version)
2149 line = GetLineWithType(message, pos, kLineTypeVersion);
2150 if (!line) {
2151 return ParseFailedExpectLine(message, *pos, kLineTypeVersion, std::string(),
2152 error);
2154 // RFC 4566
2155 // o=<username> <sess-id> <sess-version> <nettype> <addrtype>
2156 // <unicast-address>
2157 line = GetLineWithType(message, pos, kLineTypeOrigin);
2158 if (!line) {
2159 return ParseFailedExpectLine(message, *pos, kLineTypeOrigin, std::string(),
2160 error);
2162 std::vector<absl::string_view> fields =
2163 rtc::split(line->substr(kLinePrefixLength), kSdpDelimiterSpaceChar);
2164 const size_t expected_fields = 6;
2165 if (fields.size() != expected_fields) {
2166 return ParseFailedExpectFieldNum(*line, expected_fields, error);
2168 *session_id = std::string(fields[1]);
2169 *session_version = std::string(fields[2]);
2171 // RFC 4566
2172 // s= (session name)
2173 line = GetLineWithType(message, pos, kLineTypeSessionName);
2174 if (!line) {
2175 return ParseFailedExpectLine(message, *pos, kLineTypeSessionName,
2176 std::string(), error);
2179 // optional lines
2180 // Those are the optional lines, so shouldn't return false if not present.
2181 // RFC 4566
2182 // i=* (session information)
2183 GetLineWithType(message, pos, kLineTypeSessionInfo);
2185 // RFC 4566
2186 // u=* (URI of description)
2187 GetLineWithType(message, pos, kLineTypeSessionUri);
2189 // RFC 4566
2190 // e=* (email address)
2191 GetLineWithType(message, pos, kLineTypeSessionEmail);
2193 // RFC 4566
2194 // p=* (phone number)
2195 GetLineWithType(message, pos, kLineTypeSessionPhone);
2197 // RFC 4566
2198 // c=* (connection information -- not required if included in
2199 // all media)
2200 if (absl::optional<absl::string_view> cline =
2201 GetLineWithType(message, pos, kLineTypeConnection);
2202 cline.has_value()) {
2203 if (!ParseConnectionData(*cline, connection_addr, error)) {
2204 return false;
2208 // RFC 4566
2209 // b=* (zero or more bandwidth information lines)
2210 while (GetLineWithType(message, pos, kLineTypeSessionBandwidth).has_value()) {
2211 // By pass zero or more b lines.
2214 // RFC 4566
2215 // One or more time descriptions ("t=" and "r=" lines; see below)
2216 // t= (time the session is active)
2217 // r=* (zero or more repeat times)
2218 // Ensure there's at least one time description
2219 if (!GetLineWithType(message, pos, kLineTypeTiming).has_value()) {
2220 return ParseFailedExpectLine(message, *pos, kLineTypeTiming, std::string(),
2221 error);
2224 while (GetLineWithType(message, pos, kLineTypeRepeatTimes).has_value()) {
2225 // By pass zero or more r lines.
2228 // Go through the rest of the time descriptions
2229 while (GetLineWithType(message, pos, kLineTypeTiming).has_value()) {
2230 while (GetLineWithType(message, pos, kLineTypeRepeatTimes).has_value()) {
2231 // By pass zero or more r lines.
2235 // RFC 4566
2236 // z=* (time zone adjustments)
2237 GetLineWithType(message, pos, kLineTypeTimeZone);
2239 // RFC 4566
2240 // k=* (encryption key)
2241 GetLineWithType(message, pos, kLineTypeEncryptionKey);
2243 // RFC 4566
2244 // a=* (zero or more session attribute lines)
2245 while (absl::optional<absl::string_view> aline =
2246 GetLineWithType(message, pos, kLineTypeAttributes)) {
2247 if (HasAttribute(*aline, kAttributeGroup)) {
2248 if (!ParseGroupAttribute(*aline, desc, error)) {
2249 return false;
2251 } else if (HasAttribute(*aline, kAttributeIceUfrag)) {
2252 if (!GetValue(*aline, kAttributeIceUfrag, &(session_td->ice_ufrag),
2253 error)) {
2254 return false;
2256 } else if (HasAttribute(*aline, kAttributeIcePwd)) {
2257 if (!GetValue(*aline, kAttributeIcePwd, &(session_td->ice_pwd), error)) {
2258 return false;
2260 } else if (HasAttribute(*aline, kAttributeIceLite)) {
2261 session_td->ice_mode = cricket::ICEMODE_LITE;
2262 } else if (HasAttribute(*aline, kAttributeIceOption)) {
2263 if (!ParseIceOptions(*aline, &(session_td->transport_options), error)) {
2264 return false;
2266 } else if (HasAttribute(*aline, kAttributeFingerprint)) {
2267 if (session_td->identity_fingerprint.get()) {
2268 return ParseFailed(
2269 *aline,
2270 "Can't have multiple fingerprint attributes at the same level.",
2271 error);
2273 std::unique_ptr<rtc::SSLFingerprint> fingerprint;
2274 if (!ParseFingerprintAttribute(*aline, &fingerprint, error)) {
2275 return false;
2277 session_td->identity_fingerprint = std::move(fingerprint);
2278 } else if (HasAttribute(*aline, kAttributeSetup)) {
2279 if (!ParseDtlsSetup(*aline, &(session_td->connection_role), error)) {
2280 return false;
2282 } else if (HasAttribute(*aline, kAttributeMsidSemantics)) {
2283 std::string semantics;
2284 if (!GetValue(*aline, kAttributeMsidSemantics, &semantics, error)) {
2285 return false;
2287 desc->set_msid_supported(
2288 CaseInsensitiveFind(semantics, kMediaStreamSemantic));
2289 } else if (HasAttribute(*aline, kAttributeExtmapAllowMixed)) {
2290 desc->set_extmap_allow_mixed(true);
2291 } else if (HasAttribute(*aline, kAttributeExtmap)) {
2292 RtpExtension extmap;
2293 if (!ParseExtmap(*aline, &extmap, error)) {
2294 return false;
2296 session_extmaps->push_back(extmap);
2299 return true;
2302 bool ParseGroupAttribute(absl::string_view line,
2303 cricket::SessionDescription* desc,
2304 SdpParseError* error) {
2305 RTC_DCHECK(desc != NULL);
2307 // RFC 5888 and draft-holmberg-mmusic-sdp-bundle-negotiation-00
2308 // a=group:BUNDLE video voice
2309 std::vector<absl::string_view> fields =
2310 rtc::split(line.substr(kLinePrefixLength), kSdpDelimiterSpaceChar);
2311 std::string semantics;
2312 if (!GetValue(fields[0], kAttributeGroup, &semantics, error)) {
2313 return false;
2315 cricket::ContentGroup group(semantics);
2316 for (size_t i = 1; i < fields.size(); ++i) {
2317 group.AddContentName(fields[i]);
2319 desc->AddGroup(group);
2320 return true;
2323 static bool ParseFingerprintAttribute(
2324 absl::string_view line,
2325 std::unique_ptr<rtc::SSLFingerprint>* fingerprint,
2326 SdpParseError* error) {
2327 std::vector<absl::string_view> fields =
2328 rtc::split(line.substr(kLinePrefixLength), kSdpDelimiterSpaceChar);
2329 const size_t expected_fields = 2;
2330 if (fields.size() != expected_fields) {
2331 return ParseFailedExpectFieldNum(line, expected_fields, error);
2334 // The first field here is "fingerprint:<hash>.
2335 std::string algorithm;
2336 if (!GetValue(fields[0], kAttributeFingerprint, &algorithm, error)) {
2337 return false;
2340 // Downcase the algorithm. Note that we don't need to downcase the
2341 // fingerprint because hex_decode can handle upper-case.
2342 absl::c_transform(algorithm, algorithm.begin(), ::tolower);
2344 // The second field is the digest value. De-hexify it.
2345 *fingerprint =
2346 rtc::SSLFingerprint::CreateUniqueFromRfc4572(algorithm, fields[1]);
2347 if (!*fingerprint) {
2348 return ParseFailed(line, "Failed to create fingerprint from the digest.",
2349 error);
2352 return true;
2355 static bool ParseDtlsSetup(absl::string_view line,
2356 cricket::ConnectionRole* role_ptr,
2357 SdpParseError* error) {
2358 // setup-attr = "a=setup:" role
2359 // role = "active" / "passive" / "actpass" / "holdconn"
2360 std::vector<absl::string_view> fields =
2361 rtc::split(line.substr(kLinePrefixLength), kSdpDelimiterColonChar);
2362 const size_t expected_fields = 2;
2363 if (fields.size() != expected_fields) {
2364 return ParseFailedExpectFieldNum(line, expected_fields, error);
2366 if (absl::optional<cricket::ConnectionRole> role =
2367 cricket::StringToConnectionRole(fields[1]);
2368 role.has_value()) {
2369 *role_ptr = *role;
2370 return true;
2372 return ParseFailed(line, "Invalid attribute value.", error);
2375 static bool ParseMsidAttribute(absl::string_view line,
2376 std::vector<std::string>* stream_ids,
2377 std::string* track_id,
2378 SdpParseError* error) {
2379 // https://datatracker.ietf.org/doc/rfc8830/
2380 // a=msid:<msid-value>
2381 // msid-value = msid-id [ SP msid-appdata ]
2382 // msid-id = 1*64token-char ; see RFC 4566
2383 // msid-appdata = 1*64token-char ; see RFC 4566
2384 // Note that JSEP stipulates not sending msid-appdata so
2385 // a=msid:<stream id> <track id>
2386 // is supported for backward compability reasons only.
2387 std::vector<std::string> fields;
2388 size_t num_fields = rtc::tokenize(line.substr(kLinePrefixLength),
2389 kSdpDelimiterSpaceChar, &fields);
2390 if (num_fields < 1 || num_fields > 2) {
2391 return ParseFailed(line, "Expected a stream ID and optionally a track ID",
2392 error);
2394 if (num_fields == 1) {
2395 if (line.back() == kSdpDelimiterSpaceChar) {
2396 return ParseFailed(line, "Missing track ID in msid attribute.", error);
2398 if (!track_id->empty()) {
2399 fields.push_back(*track_id);
2400 } else {
2401 // Ending with an empty string track will cause a random track id
2402 // to be generated later in the process.
2403 fields.push_back("");
2406 RTC_DCHECK_EQ(fields.size(), 2);
2408 // All track ids should be the same within an m section in a Unified Plan SDP.
2409 if (!track_id->empty() && track_id->compare(fields[1]) != 0) {
2410 return ParseFailed(
2411 line, "Two different track IDs in msid attribute in one m= section",
2412 error);
2414 *track_id = fields[1];
2416 // msid:<msid-id>
2417 std::string new_stream_id;
2418 if (!GetValue(fields[0], kAttributeMsid, &new_stream_id, error)) {
2419 return false;
2421 if (new_stream_id.empty()) {
2422 return ParseFailed(line, "Missing stream ID in msid attribute.", error);
2424 // The special value "-" indicates "no MediaStream".
2425 if (new_stream_id.compare(kNoStreamMsid) != 0 &&
2426 !absl::c_any_of(*stream_ids,
2427 [&new_stream_id](const std::string& existing_stream_id) {
2428 return new_stream_id == existing_stream_id;
2429 })) {
2430 stream_ids->push_back(new_stream_id);
2432 return true;
2435 static void RemoveInvalidRidDescriptions(const std::vector<int>& payload_types,
2436 std::vector<RidDescription>* rids) {
2437 RTC_DCHECK(rids);
2438 std::set<std::string> to_remove;
2439 std::set<std::string> unique_rids;
2441 // Check the rids to see which ones should be removed.
2442 for (RidDescription& rid : *rids) {
2443 // In the case of a duplicate, the entire "a=rid" line, and all "a=rid"
2444 // lines with rid-ids that duplicate this line, are discarded and MUST NOT
2445 // be included in the SDP Answer.
2446 auto pair = unique_rids.insert(rid.rid);
2447 // Insert will "fail" if element already exists.
2448 if (!pair.second) {
2449 to_remove.insert(rid.rid);
2450 continue;
2453 // If the "a=rid" line contains a "pt=", the list of payload types
2454 // is verified against the list of valid payload types for the media
2455 // section (that is, those listed on the "m=" line). Any PT missing
2456 // from the "m=" line is discarded from the set of values in the
2457 // "pt=". If no values are left in the "pt=" parameter after this
2458 // processing, then the "a=rid" line is discarded.
2459 if (rid.payload_types.empty()) {
2460 // If formats were not specified, rid should not be removed.
2461 continue;
2464 // Note: Spec does not mention how to handle duplicate formats.
2465 // Media section does not handle duplicates either.
2466 std::set<int> removed_formats;
2467 for (int payload_type : rid.payload_types) {
2468 if (!absl::c_linear_search(payload_types, payload_type)) {
2469 removed_formats.insert(payload_type);
2473 rid.payload_types.erase(
2474 std::remove_if(rid.payload_types.begin(), rid.payload_types.end(),
2475 [&removed_formats](int format) {
2476 return removed_formats.count(format) > 0;
2478 rid.payload_types.end());
2480 // If all formats were removed then remove the rid alogether.
2481 if (rid.payload_types.empty()) {
2482 to_remove.insert(rid.rid);
2486 // Remove every rid description that appears in the to_remove list.
2487 if (!to_remove.empty()) {
2488 rids->erase(std::remove_if(rids->begin(), rids->end(),
2489 [&to_remove](const RidDescription& rid) {
2490 return to_remove.count(rid.rid) > 0;
2492 rids->end());
2496 // Create a new list (because SimulcastLayerList is immutable) without any
2497 // layers that have a rid in the to_remove list.
2498 // If a group of alternatives is empty after removing layers, the group should
2499 // be removed altogether.
2500 static SimulcastLayerList RemoveRidsFromSimulcastLayerList(
2501 const std::set<std::string>& to_remove,
2502 const SimulcastLayerList& layers) {
2503 SimulcastLayerList result;
2504 for (const std::vector<SimulcastLayer>& vector : layers) {
2505 std::vector<SimulcastLayer> new_layers;
2506 for (const SimulcastLayer& layer : vector) {
2507 if (to_remove.find(layer.rid) == to_remove.end()) {
2508 new_layers.push_back(layer);
2511 // If all layers were removed, do not add an entry.
2512 if (!new_layers.empty()) {
2513 result.AddLayerWithAlternatives(new_layers);
2517 return result;
2520 // Will remove Simulcast Layers if:
2521 // 1. They appear in both send and receive directions.
2522 // 2. They do not appear in the list of `valid_rids`.
2523 static void RemoveInvalidRidsFromSimulcast(
2524 const std::vector<RidDescription>& valid_rids,
2525 SimulcastDescription* simulcast) {
2526 RTC_DCHECK(simulcast);
2527 std::set<std::string> to_remove;
2528 std::vector<SimulcastLayer> all_send_layers =
2529 simulcast->send_layers().GetAllLayers();
2530 std::vector<SimulcastLayer> all_receive_layers =
2531 simulcast->receive_layers().GetAllLayers();
2533 // If a rid appears in both send and receive directions, remove it from both.
2534 // This algorithm runs in O(n^2) time, but for small n (as is the case with
2535 // simulcast layers) it should still perform well.
2536 for (const SimulcastLayer& send_layer : all_send_layers) {
2537 if (absl::c_any_of(all_receive_layers,
2538 [&send_layer](const SimulcastLayer& layer) {
2539 return layer.rid == send_layer.rid;
2540 })) {
2541 to_remove.insert(send_layer.rid);
2545 // Add any rid that is not in the valid list to the remove set.
2546 for (const SimulcastLayer& send_layer : all_send_layers) {
2547 if (absl::c_none_of(valid_rids, [&send_layer](const RidDescription& rid) {
2548 return send_layer.rid == rid.rid &&
2549 rid.direction == cricket::RidDirection::kSend;
2550 })) {
2551 to_remove.insert(send_layer.rid);
2555 // Add any rid that is not in the valid list to the remove set.
2556 for (const SimulcastLayer& receive_layer : all_receive_layers) {
2557 if (absl::c_none_of(
2558 valid_rids, [&receive_layer](const RidDescription& rid) {
2559 return receive_layer.rid == rid.rid &&
2560 rid.direction == cricket::RidDirection::kReceive;
2561 })) {
2562 to_remove.insert(receive_layer.rid);
2566 simulcast->send_layers() =
2567 RemoveRidsFromSimulcastLayerList(to_remove, simulcast->send_layers());
2568 simulcast->receive_layers() =
2569 RemoveRidsFromSimulcastLayerList(to_remove, simulcast->receive_layers());
2572 // RFC 3551
2573 // PT encoding media type clock rate channels
2574 // name (Hz)
2575 // 0 PCMU A 8,000 1
2576 // 1 reserved A
2577 // 2 reserved A
2578 // 3 GSM A 8,000 1
2579 // 4 G723 A 8,000 1
2580 // 5 DVI4 A 8,000 1
2581 // 6 DVI4 A 16,000 1
2582 // 7 LPC A 8,000 1
2583 // 8 PCMA A 8,000 1
2584 // 9 G722 A 8,000 1
2585 // 10 L16 A 44,100 2
2586 // 11 L16 A 44,100 1
2587 // 12 QCELP A 8,000 1
2588 // 13 CN A 8,000 1
2589 // 14 MPA A 90,000 (see text)
2590 // 15 G728 A 8,000 1
2591 // 16 DVI4 A 11,025 1
2592 // 17 DVI4 A 22,050 1
2593 // 18 G729 A 8,000 1
2594 struct StaticPayloadAudioCodec {
2595 const char* name;
2596 int clockrate;
2597 size_t channels;
2599 static const StaticPayloadAudioCodec kStaticPayloadAudioCodecs[] = {
2600 {"PCMU", 8000, 1}, {"reserved", 0, 0}, {"reserved", 0, 0},
2601 {"GSM", 8000, 1}, {"G723", 8000, 1}, {"DVI4", 8000, 1},
2602 {"DVI4", 16000, 1}, {"LPC", 8000, 1}, {"PCMA", 8000, 1},
2603 {"G722", 8000, 1}, {"L16", 44100, 2}, {"L16", 44100, 1},
2604 {"QCELP", 8000, 1}, {"CN", 8000, 1}, {"MPA", 90000, 1},
2605 {"G728", 8000, 1}, {"DVI4", 11025, 1}, {"DVI4", 22050, 1},
2606 {"G729", 8000, 1},
2609 void MaybeCreateStaticPayloadAudioCodecs(const std::vector<int>& fmts,
2610 AudioContentDescription* media_desc) {
2611 if (!media_desc) {
2612 return;
2614 RTC_DCHECK(media_desc->codecs().empty());
2615 for (int payload_type : fmts) {
2616 if (!media_desc->HasCodec(payload_type) && payload_type >= 0 &&
2617 static_cast<uint32_t>(payload_type) <
2618 arraysize(kStaticPayloadAudioCodecs)) {
2619 std::string encoding_name = kStaticPayloadAudioCodecs[payload_type].name;
2620 int clock_rate = kStaticPayloadAudioCodecs[payload_type].clockrate;
2621 size_t channels = kStaticPayloadAudioCodecs[payload_type].channels;
2622 media_desc->AddCodec(cricket::CreateAudioCodec(
2623 payload_type, encoding_name, clock_rate, channels));
2628 template <class C>
2629 static std::unique_ptr<C> ParseContentDescription(
2630 absl::string_view message,
2631 const cricket::MediaType media_type,
2632 int mline_index,
2633 absl::string_view protocol,
2634 const std::vector<int>& payload_types,
2635 size_t* pos,
2636 std::string* content_name,
2637 bool* bundle_only,
2638 int* msid_signaling,
2639 TransportDescription* transport,
2640 std::vector<std::unique_ptr<JsepIceCandidate>>* candidates,
2641 webrtc::SdpParseError* error) {
2642 auto media_desc = std::make_unique<C>();
2643 media_desc->set_extmap_allow_mixed_enum(MediaContentDescription::kNo);
2644 if (!ParseContent(message, media_type, mline_index, protocol, payload_types,
2645 pos, content_name, bundle_only, msid_signaling,
2646 media_desc.get(), transport, candidates, error)) {
2647 return nullptr;
2649 // Sort the codecs according to the m-line fmt list.
2650 std::unordered_map<int, int> payload_type_preferences;
2651 // "size + 1" so that the lowest preference payload type has a preference of
2652 // 1, which is greater than the default (0) for payload types not in the fmt
2653 // list.
2654 int preference = static_cast<int>(payload_types.size() + 1);
2655 for (int pt : payload_types) {
2656 payload_type_preferences[pt] = preference--;
2658 std::vector<cricket::Codec> codecs = media_desc->codecs();
2659 absl::c_sort(codecs, [&payload_type_preferences](const cricket::Codec& a,
2660 const cricket::Codec& b) {
2661 return payload_type_preferences[a.id] > payload_type_preferences[b.id];
2663 media_desc->set_codecs(codecs);
2664 return media_desc;
2667 bool ParseMediaDescription(
2668 absl::string_view message,
2669 const TransportDescription& session_td,
2670 const RtpHeaderExtensions& session_extmaps,
2671 size_t* pos,
2672 const rtc::SocketAddress& session_connection_addr,
2673 cricket::SessionDescription* desc,
2674 std::vector<std::unique_ptr<JsepIceCandidate>>* candidates,
2675 SdpParseError* error) {
2676 RTC_DCHECK(desc != NULL);
2677 int mline_index = -1;
2678 int msid_signaling = 0;
2680 // Zero or more media descriptions
2681 // RFC 4566
2682 // m=<media> <port> <proto> <fmt>
2683 while (absl::optional<absl::string_view> mline =
2684 GetLineWithType(message, pos, kLineTypeMedia)) {
2685 ++mline_index;
2687 std::vector<absl::string_view> fields =
2688 rtc::split(mline->substr(kLinePrefixLength), kSdpDelimiterSpaceChar);
2690 const size_t expected_min_fields = 4;
2691 if (fields.size() < expected_min_fields) {
2692 return ParseFailedExpectMinFieldNum(*mline, expected_min_fields, error);
2694 bool port_rejected = false;
2695 // RFC 3264
2696 // To reject an offered stream, the port number in the corresponding stream
2697 // in the answer MUST be set to zero.
2698 if (fields[1] == kMediaPortRejected) {
2699 port_rejected = true;
2702 int port = 0;
2703 if (!rtc::FromString<int>(fields[1], &port) || !IsValidPort(port)) {
2704 return ParseFailed(*mline, "The port number is invalid", error);
2706 absl::string_view protocol = fields[2];
2708 // <fmt>
2709 std::vector<int> payload_types;
2710 if (cricket::IsRtpProtocol(protocol)) {
2711 for (size_t j = 3; j < fields.size(); ++j) {
2712 int pl = 0;
2713 if (!GetPayloadTypeFromString(*mline, fields[j], &pl, error)) {
2714 return false;
2716 payload_types.push_back(pl);
2720 // Make a temporary TransportDescription based on `session_td`.
2721 // Some of this gets overwritten by ParseContent.
2722 TransportDescription transport(
2723 session_td.transport_options, session_td.ice_ufrag, session_td.ice_pwd,
2724 session_td.ice_mode, session_td.connection_role,
2725 session_td.identity_fingerprint.get());
2727 std::unique_ptr<MediaContentDescription> content;
2728 std::string content_name;
2729 bool bundle_only = false;
2730 int section_msid_signaling = 0;
2731 absl::string_view media_type = fields[0];
2732 if ((media_type == kMediaTypeVideo || media_type == kMediaTypeAudio) &&
2733 !cricket::IsRtpProtocol(protocol)) {
2734 return ParseFailed(*mline, "Unsupported protocol for media type", error);
2736 if (media_type == kMediaTypeVideo) {
2737 content = ParseContentDescription<VideoContentDescription>(
2738 message, cricket::MEDIA_TYPE_VIDEO, mline_index, protocol,
2739 payload_types, pos, &content_name, &bundle_only,
2740 &section_msid_signaling, &transport, candidates, error);
2741 } else if (media_type == kMediaTypeAudio) {
2742 content = ParseContentDescription<AudioContentDescription>(
2743 message, cricket::MEDIA_TYPE_AUDIO, mline_index, protocol,
2744 payload_types, pos, &content_name, &bundle_only,
2745 &section_msid_signaling, &transport, candidates, error);
2746 } else if (media_type == kMediaTypeData && cricket::IsDtlsSctp(protocol)) {
2747 // The draft-03 format is:
2748 // m=application <port> DTLS/SCTP <sctp-port>...
2749 // use_sctpmap should be false.
2750 // The draft-26 format is:
2751 // m=application <port> UDP/DTLS/SCTP webrtc-datachannel
2752 // use_sctpmap should be false.
2753 auto data_desc = std::make_unique<SctpDataContentDescription>();
2754 // Default max message size is 64K
2755 // according to draft-ietf-mmusic-sctp-sdp-26
2756 data_desc->set_max_message_size(kDefaultSctpMaxMessageSize);
2757 int p;
2758 if (rtc::FromString(fields[3], &p)) {
2759 data_desc->set_port(p);
2760 } else if (fields[3] == kDefaultSctpmapProtocol) {
2761 data_desc->set_use_sctpmap(false);
2763 if (!ParseContent(message, cricket::MEDIA_TYPE_DATA, mline_index,
2764 protocol, payload_types, pos, &content_name,
2765 &bundle_only, &section_msid_signaling, data_desc.get(),
2766 &transport, candidates, error)) {
2767 return false;
2769 data_desc->set_protocol(protocol);
2770 content = std::move(data_desc);
2771 } else {
2772 RTC_LOG(LS_WARNING) << "Unsupported media type: " << *mline;
2773 auto unsupported_desc =
2774 std::make_unique<UnsupportedContentDescription>(media_type);
2775 if (!ParseContent(message, cricket::MEDIA_TYPE_UNSUPPORTED, mline_index,
2776 protocol, payload_types, pos, &content_name,
2777 &bundle_only, &section_msid_signaling,
2778 unsupported_desc.get(), &transport, candidates,
2779 error)) {
2780 return false;
2782 unsupported_desc->set_protocol(protocol);
2783 content = std::move(unsupported_desc);
2785 if (!content.get()) {
2786 // ParseContentDescription returns NULL if failed.
2787 return false;
2790 msid_signaling |= section_msid_signaling;
2792 bool content_rejected = false;
2793 // A port of 0 is not interpreted as a rejected m= section when it's
2794 // used along with a=bundle-only.
2795 if (bundle_only) {
2796 if (!port_rejected) {
2797 // Usage of bundle-only with a nonzero port is unspecified. So just
2798 // ignore bundle-only if we see this.
2799 bundle_only = false;
2800 RTC_LOG(LS_WARNING)
2801 << "a=bundle-only attribute observed with a nonzero "
2802 "port; this usage is unspecified so the attribute is being "
2803 "ignored.";
2805 } else {
2806 // If not using bundle-only, interpret port 0 in the normal way; the m=
2807 // section is being rejected.
2808 content_rejected = port_rejected;
2811 if (content->as_unsupported()) {
2812 content_rejected = true;
2813 } else if (cricket::IsRtpProtocol(protocol) && !content->as_sctp()) {
2814 content->set_protocol(std::string(protocol));
2815 // Set the extmap.
2816 if (!session_extmaps.empty() &&
2817 !content->rtp_header_extensions().empty()) {
2818 return ParseFailed("",
2819 "The a=extmap MUST be either all session level or "
2820 "all media level.",
2821 error);
2823 for (size_t i = 0; i < session_extmaps.size(); ++i) {
2824 content->AddRtpHeaderExtension(session_extmaps[i]);
2826 } else if (content->as_sctp()) {
2827 // Do nothing, it's OK
2828 } else {
2829 RTC_LOG(LS_WARNING) << "Parse failed with unknown protocol " << protocol;
2830 return false;
2833 // Use the session level connection address if the media level addresses are
2834 // not specified.
2835 rtc::SocketAddress address;
2836 address = content->connection_address().IsNil()
2837 ? session_connection_addr
2838 : content->connection_address();
2839 address.SetPort(port);
2840 content->set_connection_address(address);
2842 desc->AddContent(content_name,
2843 cricket::IsDtlsSctp(protocol) ? MediaProtocolType::kSctp
2844 : MediaProtocolType::kRtp,
2845 content_rejected, bundle_only, std::move(content));
2846 // Create TransportInfo with the media level "ice-pwd" and "ice-ufrag".
2847 desc->AddTransportInfo(TransportInfo(content_name, transport));
2850 desc->set_msid_signaling(msid_signaling);
2852 size_t end_of_message = message.size();
2853 if (mline_index == -1 && *pos != end_of_message) {
2854 ParseFailed(message, *pos, "Expects m line.", error);
2855 return false;
2857 return true;
2860 bool VerifyCodec(const cricket::Codec& codec) {
2861 // Codec has not been populated correctly unless the name has been set. This
2862 // can happen if an SDP has an fmtp or rtcp-fb with a payload type but doesn't
2863 // have a corresponding "rtpmap" line.
2864 return !codec.name.empty();
2867 bool VerifyAudioCodecs(const AudioContentDescription* audio_desc) {
2868 return absl::c_all_of(audio_desc->codecs(), &VerifyCodec);
2871 bool VerifyVideoCodecs(const VideoContentDescription* video_desc) {
2872 return absl::c_all_of(video_desc->codecs(), &VerifyCodec);
2875 void AddParameters(const cricket::CodecParameterMap& parameters,
2876 cricket::Codec* codec) {
2877 for (const auto& entry : parameters) {
2878 const std::string& key = entry.first;
2879 const std::string& value = entry.second;
2880 codec->SetParam(key, value);
2884 void AddFeedbackParameter(const cricket::FeedbackParam& feedback_param,
2885 cricket::Codec* codec) {
2886 codec->AddFeedbackParam(feedback_param);
2889 void AddFeedbackParameters(const cricket::FeedbackParams& feedback_params,
2890 cricket::Codec* codec) {
2891 for (const cricket::FeedbackParam& param : feedback_params.params()) {
2892 codec->AddFeedbackParam(param);
2896 // Gets the current codec setting associated with `payload_type`. If there
2897 // is no Codec associated with that payload type it returns an empty codec
2898 // with that payload type.
2899 template <class T>
2900 T GetCodecWithPayloadType(cricket::MediaType type,
2901 const std::vector<T>& codecs,
2902 int payload_type) {
2903 const T* codec = FindCodecById(codecs, payload_type);
2904 if (codec)
2905 return *codec;
2906 // Return empty codec with `payload_type`.
2907 if (type == cricket::MEDIA_TYPE_AUDIO) {
2908 return cricket::CreateAudioCodec(payload_type, "", 0, 0);
2909 } else {
2910 return cricket::CreateVideoCodec(payload_type, "");
2914 // Updates or creates a new codec entry in the media description.
2915 template <class T, class U>
2916 void AddOrReplaceCodec(MediaContentDescription* content_desc, const U& codec) {
2917 T* desc = static_cast<T*>(content_desc);
2918 std::vector<U> codecs = desc->codecs();
2919 bool found = false;
2920 for (U& existing_codec : codecs) {
2921 if (codec.id == existing_codec.id) {
2922 // Overwrite existing codec with the new codec.
2923 existing_codec = codec;
2924 found = true;
2925 break;
2928 if (!found) {
2929 desc->AddCodec(codec);
2930 return;
2932 desc->set_codecs(codecs);
2935 // Adds or updates existing codec corresponding to `payload_type` according
2936 // to `parameters`.
2937 template <class T, class U>
2938 void UpdateCodec(MediaContentDescription* content_desc,
2939 int payload_type,
2940 const cricket::CodecParameterMap& parameters) {
2941 // Codec might already have been populated (from rtpmap).
2942 U new_codec = GetCodecWithPayloadType(content_desc->type(),
2943 static_cast<T*>(content_desc)->codecs(),
2944 payload_type);
2945 AddParameters(parameters, &new_codec);
2946 AddOrReplaceCodec<T, U>(content_desc, new_codec);
2949 // Adds or updates existing codec corresponding to `payload_type` according
2950 // to `feedback_param`.
2951 template <class T, class U>
2952 void UpdateCodec(MediaContentDescription* content_desc,
2953 int payload_type,
2954 const cricket::FeedbackParam& feedback_param) {
2955 // Codec might already have been populated (from rtpmap).
2956 U new_codec = GetCodecWithPayloadType(content_desc->type(),
2957 static_cast<T*>(content_desc)->codecs(),
2958 payload_type);
2959 AddFeedbackParameter(feedback_param, &new_codec);
2960 AddOrReplaceCodec<T, U>(content_desc, new_codec);
2963 // Adds or updates existing video codec corresponding to `payload_type`
2964 // according to `packetization`.
2965 void UpdateVideoCodecPacketization(VideoContentDescription* video_desc,
2966 int payload_type,
2967 absl::string_view packetization) {
2968 if (packetization != cricket::kPacketizationParamRaw) {
2969 // Ignore unsupported packetization attribute.
2970 return;
2973 // Codec might already have been populated (from rtpmap).
2974 cricket::VideoCodec codec = GetCodecWithPayloadType(
2975 video_desc->type(), video_desc->codecs(), payload_type);
2976 codec.packetization = std::string(packetization);
2977 AddOrReplaceCodec<VideoContentDescription, cricket::VideoCodec>(video_desc,
2978 codec);
2981 template <class T>
2982 absl::optional<T> PopWildcardCodec(std::vector<T>* codecs) {
2983 for (auto iter = codecs->begin(); iter != codecs->end(); ++iter) {
2984 if (iter->id == kWildcardPayloadType) {
2985 T wildcard_codec = *iter;
2986 codecs->erase(iter);
2987 return wildcard_codec;
2990 return absl::nullopt;
2993 template <class T>
2994 void UpdateFromWildcardCodecs(cricket::MediaContentDescriptionImpl<T>* desc) {
2995 auto codecs = desc->codecs();
2996 absl::optional<T> wildcard_codec = PopWildcardCodec(&codecs);
2997 if (!wildcard_codec) {
2998 return;
3000 for (auto& codec : codecs) {
3001 AddFeedbackParameters(wildcard_codec->feedback_params, &codec);
3003 desc->set_codecs(codecs);
3006 void AddAudioAttribute(const std::string& name,
3007 absl::string_view value,
3008 AudioContentDescription* audio_desc) {
3009 if (value.empty()) {
3010 return;
3012 std::vector<cricket::AudioCodec> codecs = audio_desc->codecs();
3013 for (cricket::AudioCodec& codec : codecs) {
3014 codec.params[name] = std::string(value);
3016 audio_desc->set_codecs(codecs);
3019 bool ParseContent(absl::string_view message,
3020 const cricket::MediaType media_type,
3021 int mline_index,
3022 absl::string_view protocol,
3023 const std::vector<int>& payload_types,
3024 size_t* pos,
3025 std::string* content_name,
3026 bool* bundle_only,
3027 int* msid_signaling,
3028 MediaContentDescription* media_desc,
3029 TransportDescription* transport,
3030 std::vector<std::unique_ptr<JsepIceCandidate>>* candidates,
3031 SdpParseError* error) {
3032 RTC_DCHECK(media_desc != NULL);
3033 RTC_DCHECK(content_name != NULL);
3034 RTC_DCHECK(transport != NULL);
3036 if (media_type == cricket::MEDIA_TYPE_AUDIO) {
3037 MaybeCreateStaticPayloadAudioCodecs(payload_types, media_desc->as_audio());
3040 // The media level "ice-ufrag" and "ice-pwd".
3041 // The candidates before update the media level "ice-pwd" and "ice-ufrag".
3042 Candidates candidates_orig;
3043 std::string mline_id;
3044 // Tracks created out of the ssrc attributes.
3045 StreamParamsVec tracks;
3046 SsrcInfoVec ssrc_infos;
3047 SsrcGroupVec ssrc_groups;
3048 std::string maxptime_as_string;
3049 std::string ptime_as_string;
3050 std::vector<std::string> stream_ids;
3051 std::string track_id;
3052 SdpSerializer deserializer;
3053 std::vector<RidDescription> rids;
3054 SimulcastDescription simulcast;
3056 // Loop until the next m line
3057 while (!IsLineType(message, kLineTypeMedia, *pos)) {
3058 absl::optional<absl::string_view> line = GetLine(message, pos);
3059 if (!line.has_value()) {
3060 if (*pos >= message.size()) {
3061 break; // Done parsing
3062 } else {
3063 return ParseFailed(message, *pos, "Invalid SDP line.", error);
3067 // RFC 4566
3068 // b=* (zero or more bandwidth information lines)
3069 if (IsLineType(*line, kLineTypeSessionBandwidth)) {
3070 std::string bandwidth;
3071 std::string bandwidth_type;
3072 if (!rtc::tokenize_first(line->substr(kLinePrefixLength),
3073 kSdpDelimiterColonChar, &bandwidth_type,
3074 &bandwidth)) {
3075 return ParseFailed(
3076 *line,
3077 "b= syntax error, does not match b=<modifier>:<bandwidth-value>.",
3078 error);
3080 if (!(bandwidth_type == kApplicationSpecificBandwidth ||
3081 bandwidth_type == kTransportSpecificBandwidth)) {
3082 // Ignore unknown bandwidth types.
3083 continue;
3085 int b = 0;
3086 if (!GetValueFromString(*line, bandwidth, &b, error)) {
3087 return false;
3089 // TODO(deadbeef): Historically, applications may be setting a value
3090 // of -1 to mean "unset any previously set bandwidth limit", even
3091 // though ommitting the "b=AS" entirely will do just that. Once we've
3092 // transitioned applications to doing the right thing, it would be
3093 // better to treat this as a hard error instead of just ignoring it.
3094 if (bandwidth_type == kApplicationSpecificBandwidth && b == -1) {
3095 RTC_LOG(LS_WARNING) << "Ignoring \"b=AS:-1\"; will be treated as \"no "
3096 "bandwidth limit\".";
3097 continue;
3099 if (b < 0) {
3100 return ParseFailed(
3101 *line, "b=" + bandwidth_type + " value can't be negative.", error);
3103 // Convert values. Prevent integer overflow.
3104 if (bandwidth_type == kApplicationSpecificBandwidth) {
3105 b = std::min(b, INT_MAX / 1000) * 1000;
3106 } else {
3107 b = std::min(b, INT_MAX);
3109 media_desc->set_bandwidth(b);
3110 media_desc->set_bandwidth_type(bandwidth_type);
3111 continue;
3114 // Parse the media level connection data.
3115 if (IsLineType(*line, kLineTypeConnection)) {
3116 rtc::SocketAddress addr;
3117 if (!ParseConnectionData(*line, &addr, error)) {
3118 return false;
3120 media_desc->set_connection_address(addr);
3121 continue;
3124 if (!IsLineType(*line, kLineTypeAttributes)) {
3125 // TODO(deadbeef): Handle other lines if needed.
3126 RTC_LOG(LS_VERBOSE) << "Ignored line: " << *line;
3127 continue;
3130 // Handle attributes common to SCTP and RTP.
3131 if (HasAttribute(*line, kAttributeMid)) {
3132 // RFC 3388
3133 // mid-attribute = "a=mid:" identification-tag
3134 // identification-tag = token
3135 // Use the mid identification-tag as the content name.
3136 if (!GetSingleTokenValue(*line, kAttributeMid, &mline_id, error)) {
3137 return false;
3139 *content_name = mline_id;
3140 } else if (HasAttribute(*line, kAttributeBundleOnly)) {
3141 *bundle_only = true;
3142 } else if (HasAttribute(*line, kAttributeCandidate)) {
3143 Candidate candidate;
3144 if (!ParseCandidate(*line, &candidate, error, false)) {
3145 return false;
3147 // ParseCandidate will parse non-standard ufrag and password attributes,
3148 // since it's used for candidate trickling, but we only want to process
3149 // the "a=ice-ufrag"/"a=ice-pwd" values in a session description, so
3150 // strip them off at this point.
3151 candidate.set_username(std::string());
3152 candidate.set_password(std::string());
3153 candidates_orig.push_back(candidate);
3154 } else if (HasAttribute(*line, kAttributeIceUfrag)) {
3155 if (!GetValue(*line, kAttributeIceUfrag, &transport->ice_ufrag, error)) {
3156 return false;
3158 } else if (HasAttribute(*line, kAttributeIcePwd)) {
3159 if (!GetValue(*line, kAttributeIcePwd, &transport->ice_pwd, error)) {
3160 return false;
3162 } else if (HasAttribute(*line, kAttributeIceOption)) {
3163 if (!ParseIceOptions(*line, &transport->transport_options, error)) {
3164 return false;
3166 } else if (HasAttribute(*line, kAttributeFmtp)) {
3167 if (!ParseFmtpAttributes(*line, media_type, media_desc, error)) {
3168 return false;
3170 } else if (HasAttribute(*line, kAttributeFingerprint)) {
3171 std::unique_ptr<rtc::SSLFingerprint> fingerprint;
3172 if (!ParseFingerprintAttribute(*line, &fingerprint, error)) {
3173 return false;
3175 transport->identity_fingerprint = std::move(fingerprint);
3176 } else if (HasAttribute(*line, kAttributeSetup)) {
3177 if (!ParseDtlsSetup(*line, &(transport->connection_role), error)) {
3178 return false;
3180 } else if (cricket::IsDtlsSctp(protocol) &&
3181 media_type == cricket::MEDIA_TYPE_DATA) {
3183 // SCTP specific attributes
3185 if (HasAttribute(*line, kAttributeSctpPort)) {
3186 if (media_desc->as_sctp()->use_sctpmap()) {
3187 return ParseFailed(
3188 *line, "sctp-port attribute can't be used with sctpmap.", error);
3190 int sctp_port;
3191 if (!ParseSctpPort(*line, &sctp_port, error)) {
3192 return false;
3194 media_desc->as_sctp()->set_port(sctp_port);
3195 } else if (HasAttribute(*line, kAttributeMaxMessageSize)) {
3196 int max_message_size;
3197 if (!ParseSctpMaxMessageSize(*line, &max_message_size, error)) {
3198 return false;
3200 media_desc->as_sctp()->set_max_message_size(max_message_size);
3201 } else if (HasAttribute(*line, kAttributeSctpmap)) {
3202 // Ignore a=sctpmap: from early versions of draft-ietf-mmusic-sctp-sdp
3203 continue;
3205 } else if (cricket::IsRtpProtocol(protocol)) {
3207 // RTP specific attributes
3209 if (HasAttribute(*line, kAttributeRtcpMux)) {
3210 media_desc->set_rtcp_mux(true);
3211 } else if (HasAttribute(*line, kAttributeRtcpReducedSize)) {
3212 media_desc->set_rtcp_reduced_size(true);
3213 } else if (HasAttribute(*line, kAttributeRtcpRemoteEstimate)) {
3214 media_desc->set_remote_estimate(true);
3215 } else if (HasAttribute(*line, kAttributeSsrcGroup)) {
3216 if (!ParseSsrcGroupAttribute(*line, &ssrc_groups, error)) {
3217 return false;
3219 } else if (HasAttribute(*line, kAttributeSsrc)) {
3220 if (!ParseSsrcAttribute(*line, &ssrc_infos, msid_signaling, error)) {
3221 return false;
3223 } else if (HasAttribute(*line, kAttributeCrypto)) {
3224 if (!ParseCryptoAttribute(*line, media_desc, error)) {
3225 return false;
3227 } else if (HasAttribute(*line, kAttributeRtpmap)) {
3228 if (!ParseRtpmapAttribute(*line, media_type, payload_types, media_desc,
3229 error)) {
3230 return false;
3232 } else if (HasAttribute(*line, kCodecParamMaxPTime)) {
3233 if (!GetValue(*line, kCodecParamMaxPTime, &maxptime_as_string, error)) {
3234 return false;
3236 } else if (HasAttribute(*line, kAttributePacketization)) {
3237 if (!ParsePacketizationAttribute(*line, media_type, media_desc,
3238 error)) {
3239 return false;
3241 } else if (HasAttribute(*line, kAttributeRtcpFb)) {
3242 if (!ParseRtcpFbAttribute(*line, media_type, media_desc, error)) {
3243 return false;
3245 } else if (HasAttribute(*line, kCodecParamPTime)) {
3246 if (!GetValue(*line, kCodecParamPTime, &ptime_as_string, error)) {
3247 return false;
3249 } else if (HasAttribute(*line, kAttributeSendOnly)) {
3250 media_desc->set_direction(RtpTransceiverDirection::kSendOnly);
3251 } else if (HasAttribute(*line, kAttributeRecvOnly)) {
3252 media_desc->set_direction(RtpTransceiverDirection::kRecvOnly);
3253 } else if (HasAttribute(*line, kAttributeInactive)) {
3254 media_desc->set_direction(RtpTransceiverDirection::kInactive);
3255 } else if (HasAttribute(*line, kAttributeSendRecv)) {
3256 media_desc->set_direction(RtpTransceiverDirection::kSendRecv);
3257 } else if (HasAttribute(*line, kAttributeExtmapAllowMixed)) {
3258 media_desc->set_extmap_allow_mixed_enum(
3259 MediaContentDescription::kMedia);
3260 } else if (HasAttribute(*line, kAttributeExtmap)) {
3261 RtpExtension extmap;
3262 if (!ParseExtmap(*line, &extmap, error)) {
3263 return false;
3265 media_desc->AddRtpHeaderExtension(extmap);
3266 } else if (HasAttribute(*line, kAttributeXGoogleFlag)) {
3267 // Experimental attribute. Conference mode activates more aggressive
3268 // AEC and NS settings.
3269 // TODO(deadbeef): expose API to set these directly.
3270 std::string flag_value;
3271 if (!GetValue(*line, kAttributeXGoogleFlag, &flag_value, error)) {
3272 return false;
3274 if (flag_value.compare(kValueConference) == 0)
3275 media_desc->set_conference_mode(true);
3276 } else if (HasAttribute(*line, kAttributeMsid)) {
3277 if (!ParseMsidAttribute(*line, &stream_ids, &track_id, error)) {
3278 return false;
3280 *msid_signaling |= cricket::kMsidSignalingMediaSection;
3281 } else if (HasAttribute(*line, kAttributeRid)) {
3282 const size_t kRidPrefixLength =
3283 kLinePrefixLength + arraysize(kAttributeRid);
3284 if (line->size() <= kRidPrefixLength) {
3285 RTC_LOG(LS_INFO) << "Ignoring empty RID attribute: " << *line;
3286 continue;
3288 RTCErrorOr<RidDescription> error_or_rid_description =
3289 deserializer.DeserializeRidDescription(
3290 line->substr(kRidPrefixLength));
3292 // Malformed a=rid lines are discarded.
3293 if (!error_or_rid_description.ok()) {
3294 RTC_LOG(LS_INFO) << "Ignoring malformed RID line: '" << *line
3295 << "'. Error: "
3296 << error_or_rid_description.error().message();
3297 continue;
3300 rids.push_back(error_or_rid_description.MoveValue());
3301 } else if (HasAttribute(*line, kAttributeSimulcast)) {
3302 const size_t kSimulcastPrefixLength =
3303 kLinePrefixLength + arraysize(kAttributeSimulcast);
3304 if (line->size() <= kSimulcastPrefixLength) {
3305 return ParseFailed(*line, "Simulcast attribute is empty.", error);
3308 if (!simulcast.empty()) {
3309 return ParseFailed(*line, "Multiple Simulcast attributes specified.",
3310 error);
3313 RTCErrorOr<SimulcastDescription> error_or_simulcast =
3314 deserializer.DeserializeSimulcastDescription(
3315 line->substr(kSimulcastPrefixLength));
3316 if (!error_or_simulcast.ok()) {
3317 return ParseFailed(*line,
3318 std::string("Malformed simulcast line: ") +
3319 error_or_simulcast.error().message(),
3320 error);
3323 simulcast = error_or_simulcast.value();
3324 } else if (HasAttribute(*line, kAttributeRtcp)) {
3325 // Ignore and do not log a=rtcp line.
3326 // JSEP section 5.8.2 (media section parsing) says to ignore it.
3327 continue;
3328 } else {
3329 // Unrecognized attribute in RTP protocol.
3330 RTC_LOG(LS_VERBOSE) << "Ignored line: " << *line;
3331 continue;
3333 } else {
3334 // Only parse lines that we are interested of.
3335 RTC_LOG(LS_VERBOSE) << "Ignored line: " << *line;
3336 continue;
3340 // Remove duplicate or inconsistent rids.
3341 RemoveInvalidRidDescriptions(payload_types, &rids);
3343 // If simulcast is specifed, split the rids into send and receive.
3344 // Rids that do not appear in simulcast attribute will be removed.
3345 std::vector<RidDescription> send_rids;
3346 std::vector<RidDescription> receive_rids;
3347 if (!simulcast.empty()) {
3348 // Verify that the rids in simulcast match rids in sdp.
3349 RemoveInvalidRidsFromSimulcast(rids, &simulcast);
3351 // Use simulcast description to figure out Send / Receive RIDs.
3352 std::map<std::string, RidDescription> rid_map;
3353 for (const RidDescription& rid : rids) {
3354 rid_map[rid.rid] = rid;
3357 for (const auto& layer : simulcast.send_layers().GetAllLayers()) {
3358 auto iter = rid_map.find(layer.rid);
3359 RTC_DCHECK(iter != rid_map.end());
3360 send_rids.push_back(iter->second);
3363 for (const auto& layer : simulcast.receive_layers().GetAllLayers()) {
3364 auto iter = rid_map.find(layer.rid);
3365 RTC_DCHECK(iter != rid_map.end());
3366 receive_rids.push_back(iter->second);
3369 media_desc->set_simulcast_description(simulcast);
3370 } else {
3371 // RID is specified in RFC 8851, which identifies a lot of usages.
3372 // We only support RFC 8853 usage of RID, not anything else.
3373 // Ignore all RID parameters when a=simulcast is missing.
3374 // In particular do NOT do send_rids = rids;
3375 RTC_LOG(LS_VERBOSE) << "Ignoring send_rids without simulcast";
3378 media_desc->set_receive_rids(receive_rids);
3380 // Create tracks from the `ssrc_infos`.
3381 // If the stream_id/track_id for all SSRCS are identical, one StreamParams
3382 // will be created in CreateTracksFromSsrcInfos, containing all the SSRCs from
3383 // the m= section.
3384 if (!ssrc_infos.empty()) {
3385 CreateTracksFromSsrcInfos(ssrc_infos, stream_ids, track_id, &tracks,
3386 *msid_signaling);
3387 } else if (media_type != cricket::MEDIA_TYPE_DATA &&
3388 (*msid_signaling & cricket::kMsidSignalingMediaSection)) {
3389 // If the stream_ids/track_id was signaled but SSRCs were unsignaled we
3390 // still create a track. This isn't done for data media types because
3391 // StreamParams aren't used for SCTP streams, and RTP data channels don't
3392 // support unsignaled SSRCs.
3393 // If track id was not specified, create a random one.
3394 if (track_id.empty()) {
3395 track_id = rtc::CreateRandomString(8);
3397 CreateTrackWithNoSsrcs(stream_ids, track_id, send_rids, &tracks);
3400 // Add the ssrc group to the track.
3401 for (const SsrcGroup& ssrc_group : ssrc_groups) {
3402 if (ssrc_group.ssrcs.empty()) {
3403 continue;
3405 uint32_t ssrc = ssrc_group.ssrcs.front();
3406 for (StreamParams& track : tracks) {
3407 if (track.has_ssrc(ssrc)) {
3408 track.ssrc_groups.push_back(ssrc_group);
3413 // Add the new tracks to the `media_desc`.
3414 for (StreamParams& track : tracks) {
3415 media_desc->AddStream(track);
3418 if (media_type == cricket::MEDIA_TYPE_AUDIO) {
3419 AudioContentDescription* audio_desc = media_desc->as_audio();
3420 UpdateFromWildcardCodecs(audio_desc);
3422 // Verify audio codec ensures that no audio codec has been populated with
3423 // only fmtp.
3424 if (!VerifyAudioCodecs(audio_desc)) {
3425 return ParseFailed("Failed to parse audio codecs correctly.", error);
3427 AddAudioAttribute(kCodecParamMaxPTime, maxptime_as_string, audio_desc);
3428 AddAudioAttribute(kCodecParamPTime, ptime_as_string, audio_desc);
3431 if (media_type == cricket::MEDIA_TYPE_VIDEO) {
3432 VideoContentDescription* video_desc = media_desc->as_video();
3433 UpdateFromWildcardCodecs(video_desc);
3434 // Verify video codec ensures that no video codec has been populated with
3435 // only rtcp-fb.
3436 if (!VerifyVideoCodecs(video_desc)) {
3437 return ParseFailed("Failed to parse video codecs correctly.", error);
3441 // RFC 5245
3442 // Update the candidates with the media level "ice-pwd" and "ice-ufrag".
3443 for (Candidate& candidate : candidates_orig) {
3444 RTC_DCHECK(candidate.username().empty() ||
3445 candidate.username() == transport->ice_ufrag);
3446 candidate.set_username(transport->ice_ufrag);
3447 RTC_DCHECK(candidate.password().empty());
3448 candidate.set_password(transport->ice_pwd);
3449 candidates->push_back(
3450 std::make_unique<JsepIceCandidate>(mline_id, mline_index, candidate));
3453 return true;
3456 bool ParseSsrcAttribute(absl::string_view line,
3457 SsrcInfoVec* ssrc_infos,
3458 int* msid_signaling,
3459 SdpParseError* error) {
3460 RTC_DCHECK(ssrc_infos != NULL);
3461 // RFC 5576
3462 // a=ssrc:<ssrc-id> <attribute>
3463 // a=ssrc:<ssrc-id> <attribute>:<value>
3464 std::string field1, field2;
3465 if (!rtc::tokenize_first(line.substr(kLinePrefixLength),
3466 kSdpDelimiterSpaceChar, &field1, &field2)) {
3467 const size_t expected_fields = 2;
3468 return ParseFailedExpectFieldNum(line, expected_fields, error);
3471 // ssrc:<ssrc-id>
3472 std::string ssrc_id_s;
3473 if (!GetValue(field1, kAttributeSsrc, &ssrc_id_s, error)) {
3474 return false;
3476 uint32_t ssrc_id = 0;
3477 if (!GetValueFromString(line, ssrc_id_s, &ssrc_id, error)) {
3478 return false;
3481 std::string attribute;
3482 std::string value;
3483 if (!rtc::tokenize_first(field2, kSdpDelimiterColonChar, &attribute,
3484 &value)) {
3485 rtc::StringBuilder description;
3486 description << "Failed to get the ssrc attribute value from " << field2
3487 << ". Expected format <attribute>:<value>.";
3488 return ParseFailed(line, description.Release(), error);
3491 // Check if there's already an item for this `ssrc_id`. Create a new one if
3492 // there isn't.
3493 auto ssrc_info_it =
3494 absl::c_find_if(*ssrc_infos, [ssrc_id](const SsrcInfo& ssrc_info) {
3495 return ssrc_info.ssrc_id == ssrc_id;
3497 if (ssrc_info_it == ssrc_infos->end()) {
3498 SsrcInfo info;
3499 info.ssrc_id = ssrc_id;
3500 ssrc_infos->push_back(info);
3501 ssrc_info_it = ssrc_infos->end() - 1;
3503 SsrcInfo& ssrc_info = *ssrc_info_it;
3505 // Store the info to the `ssrc_info`.
3506 if (attribute == kSsrcAttributeCname) {
3507 // RFC 5576
3508 // cname:<value>
3509 ssrc_info.cname = value;
3510 } else if (attribute == kSsrcAttributeMsid) {
3511 // draft-alvestrand-mmusic-msid-00
3512 // msid:identifier [appdata]
3513 std::vector<absl::string_view> fields =
3514 rtc::split(value, kSdpDelimiterSpaceChar);
3515 if (fields.size() < 1 || fields.size() > 2) {
3516 return ParseFailed(
3517 line, "Expected format \"msid:<identifier>[ <appdata>]\".", error);
3519 ssrc_info.stream_id = std::string(fields[0]);
3520 if (fields.size() == 2) {
3521 ssrc_info.track_id = std::string(fields[1]);
3523 *msid_signaling |= cricket::kMsidSignalingSsrcAttribute;
3524 } else {
3525 RTC_LOG(LS_INFO) << "Ignored unknown ssrc-specific attribute: " << line;
3527 return true;
3530 bool ParseSsrcGroupAttribute(absl::string_view line,
3531 SsrcGroupVec* ssrc_groups,
3532 SdpParseError* error) {
3533 RTC_DCHECK(ssrc_groups != NULL);
3534 // RFC 5576
3535 // a=ssrc-group:<semantics> <ssrc-id> ...
3536 std::vector<absl::string_view> fields =
3537 rtc::split(line.substr(kLinePrefixLength), kSdpDelimiterSpaceChar);
3538 const size_t expected_min_fields = 2;
3539 if (fields.size() < expected_min_fields) {
3540 return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
3542 std::string semantics;
3543 if (!GetValue(fields[0], kAttributeSsrcGroup, &semantics, error)) {
3544 return false;
3546 std::vector<uint32_t> ssrcs;
3547 for (size_t i = 1; i < fields.size(); ++i) {
3548 uint32_t ssrc = 0;
3549 if (!GetValueFromString(line, fields[i], &ssrc, error)) {
3550 return false;
3552 // Reject duplicates. While not forbidden by RFC 5576,
3553 // they don't make sense.
3554 if (absl::c_linear_search(ssrcs, ssrc)) {
3555 return ParseFailed(line, "Duplicate SSRC in ssrc-group", error);
3557 ssrcs.push_back(ssrc);
3559 ssrc_groups->push_back(SsrcGroup(semantics, ssrcs));
3560 return true;
3563 bool ParseCryptoAttribute(absl::string_view line,
3564 MediaContentDescription* media_desc,
3565 SdpParseError* error) {
3566 std::vector<absl::string_view> fields =
3567 rtc::split(line.substr(kLinePrefixLength), kSdpDelimiterSpaceChar);
3568 // RFC 4568
3569 // a=crypto:<tag> <crypto-suite> <key-params> [<session-params>]
3570 const size_t expected_min_fields = 3;
3571 if (fields.size() < expected_min_fields) {
3572 return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
3574 std::string tag_value;
3575 if (!GetValue(fields[0], kAttributeCrypto, &tag_value, error)) {
3576 return false;
3578 int tag = 0;
3579 if (!GetValueFromString(line, tag_value, &tag, error)) {
3580 return false;
3582 const absl::string_view crypto_suite = fields[1];
3583 const absl::string_view key_params = fields[2];
3584 absl::string_view session_params;
3585 if (fields.size() > 3) {
3586 session_params = fields[3];
3589 media_desc->AddCrypto(
3590 CryptoParams(tag, crypto_suite, key_params, session_params));
3591 return true;
3594 // Updates or creates a new codec entry in the audio description with according
3595 // to `name`, `clockrate`, `bitrate`, and `channels`.
3596 void UpdateCodec(int payload_type,
3597 absl::string_view name,
3598 int clockrate,
3599 int bitrate,
3600 size_t channels,
3601 AudioContentDescription* audio_desc) {
3602 // Codec may already be populated with (only) optional parameters
3603 // (from an fmtp).
3604 cricket::AudioCodec codec = GetCodecWithPayloadType(
3605 audio_desc->type(), audio_desc->codecs(), payload_type);
3606 codec.name = std::string(name);
3607 codec.clockrate = clockrate;
3608 codec.bitrate = bitrate;
3609 codec.channels = channels;
3610 AddOrReplaceCodec<AudioContentDescription, cricket::AudioCodec>(audio_desc,
3611 codec);
3614 // Updates or creates a new codec entry in the video description according to
3615 // `name`, `width`, `height`, and `framerate`.
3616 void UpdateCodec(int payload_type,
3617 absl::string_view name,
3618 VideoContentDescription* video_desc) {
3619 // Codec may already be populated with (only) optional parameters
3620 // (from an fmtp).
3621 cricket::VideoCodec codec = GetCodecWithPayloadType(
3622 video_desc->type(), video_desc->codecs(), payload_type);
3623 codec.name = std::string(name);
3624 AddOrReplaceCodec<VideoContentDescription, cricket::VideoCodec>(video_desc,
3625 codec);
3628 bool ParseRtpmapAttribute(absl::string_view line,
3629 const cricket::MediaType media_type,
3630 const std::vector<int>& payload_types,
3631 MediaContentDescription* media_desc,
3632 SdpParseError* error) {
3633 static const int kFirstDynamicPayloadTypeLowerRange = 35;
3634 std::vector<absl::string_view> fields =
3635 rtc::split(line.substr(kLinePrefixLength), kSdpDelimiterSpaceChar);
3636 // RFC 4566
3637 // a=rtpmap:<payload type> <encoding name>/<clock rate>[/<encodingparameters>]
3638 const size_t expected_min_fields = 2;
3639 if (fields.size() < expected_min_fields) {
3640 return ParseFailedExpectMinFieldNum(line, expected_min_fields, error);
3642 std::string payload_type_value;
3643 if (!GetValue(fields[0], kAttributeRtpmap, &payload_type_value, error)) {
3644 return false;
3646 int payload_type = 0;
3647 if (!GetPayloadTypeFromString(line, payload_type_value, &payload_type,
3648 error)) {
3649 return false;
3652 if (!absl::c_linear_search(payload_types, payload_type)) {
3653 RTC_LOG(LS_WARNING) << "Ignore rtpmap line that did not appear in the "
3654 "<fmt> of the m-line: "
3655 << line;
3656 return true;
3658 std::vector<absl::string_view> codec_params = rtc::split(fields[1], '/');
3659 // <encoding name>/<clock rate>[/<encodingparameters>]
3660 // 2 mandatory fields
3661 if (codec_params.size() < 2 || codec_params.size() > 3) {
3662 return ParseFailed(line,
3663 "Expected format \"<encoding name>/<clock rate>"
3664 "[/<encodingparameters>]\".",
3665 error);
3667 const absl::string_view encoding_name = codec_params[0];
3668 int clock_rate = 0;
3669 if (!GetValueFromString(line, codec_params[1], &clock_rate, error)) {
3670 return false;
3673 if (media_type == cricket::MEDIA_TYPE_VIDEO) {
3674 VideoContentDescription* video_desc = media_desc->as_video();
3675 for (const cricket::VideoCodec& existing_codec : video_desc->codecs()) {
3676 if (!existing_codec.name.empty() && payload_type == existing_codec.id &&
3677 (!absl::EqualsIgnoreCase(encoding_name, existing_codec.name) ||
3678 clock_rate != existing_codec.clockrate)) {
3679 rtc::StringBuilder description;
3680 description
3681 << "Duplicate "
3682 << (payload_type < kFirstDynamicPayloadTypeLowerRange
3683 ? "statically assigned"
3684 : "")
3685 << " payload type with conflicting codec name or clock rate.";
3686 return ParseFailed(line, description.Release(), error);
3689 UpdateCodec(payload_type, encoding_name, video_desc);
3690 } else if (media_type == cricket::MEDIA_TYPE_AUDIO) {
3691 // RFC 4566
3692 // For audio streams, <encoding parameters> indicates the number
3693 // of audio channels. This parameter is OPTIONAL and may be
3694 // omitted if the number of channels is one, provided that no
3695 // additional parameters are needed.
3696 size_t channels = 1;
3697 if (codec_params.size() == 3) {
3698 if (!GetValueFromString(line, codec_params[2], &channels, error)) {
3699 return false;
3702 if (channels > kMaxNumberOfChannels) {
3703 return ParseFailed(line, "At most 24 channels are supported.", error);
3706 AudioContentDescription* audio_desc = media_desc->as_audio();
3707 for (const cricket::AudioCodec& existing_codec : audio_desc->codecs()) {
3708 // TODO(crbug.com/1338902) re-add checks for clockrate and number of
3709 // channels.
3710 if (!existing_codec.name.empty() && payload_type == existing_codec.id &&
3711 (!absl::EqualsIgnoreCase(encoding_name, existing_codec.name))) {
3712 rtc::StringBuilder description;
3713 description
3714 << "Duplicate "
3715 << (payload_type < kFirstDynamicPayloadTypeLowerRange
3716 ? "statically assigned"
3717 : "")
3718 << " payload type with conflicting codec name or clock rate.";
3719 return ParseFailed(line, description.Release(), error);
3722 UpdateCodec(payload_type, encoding_name, clock_rate, 0, channels,
3723 audio_desc);
3725 return true;
3728 bool ParseFmtpParam(absl::string_view line,
3729 std::string* parameter,
3730 std::string* value,
3731 SdpParseError* error) {
3732 if (!rtc::tokenize_first(line, kSdpDelimiterEqualChar, parameter, value)) {
3733 // Support for non-key-value lines like RFC 2198 or RFC 4733.
3734 *parameter = "";
3735 *value = std::string(line);
3736 return true;
3738 // a=fmtp:<payload_type> <param1>=<value1>; <param2>=<value2>; ...
3739 return true;
3742 bool ParseFmtpAttributes(absl::string_view line,
3743 const cricket::MediaType media_type,
3744 MediaContentDescription* media_desc,
3745 SdpParseError* error) {
3746 if (media_type != cricket::MEDIA_TYPE_AUDIO &&
3747 media_type != cricket::MEDIA_TYPE_VIDEO) {
3748 return true;
3751 std::string line_payload;
3752 std::string line_params;
3754 // https://tools.ietf.org/html/rfc4566#section-6
3755 // a=fmtp:<format> <format specific parameters>
3756 // At least two fields, whereas the second one is any of the optional
3757 // parameters.
3758 if (!rtc::tokenize_first(line.substr(kLinePrefixLength),
3759 kSdpDelimiterSpaceChar, &line_payload,
3760 &line_params)) {
3761 ParseFailedExpectMinFieldNum(line, 2, error);
3762 return false;
3765 // Parse out the payload information.
3766 std::string payload_type_str;
3767 if (!GetValue(line_payload, kAttributeFmtp, &payload_type_str, error)) {
3768 return false;
3771 int payload_type = 0;
3772 if (!GetPayloadTypeFromString(line_payload, payload_type_str, &payload_type,
3773 error)) {
3774 return false;
3777 // Parse out format specific parameters.
3778 cricket::CodecParameterMap codec_params;
3779 for (absl::string_view param :
3780 rtc::split(line_params, kSdpDelimiterSemicolonChar)) {
3781 std::string name;
3782 std::string value;
3783 if (!ParseFmtpParam(absl::StripAsciiWhitespace(param), &name, &value,
3784 error)) {
3785 return false;
3787 if (codec_params.find(name) != codec_params.end()) {
3788 RTC_LOG(LS_INFO) << "Overwriting duplicate fmtp parameter with key \""
3789 << name << "\".";
3791 codec_params[name] = value;
3794 if (media_type == cricket::MEDIA_TYPE_AUDIO) {
3795 UpdateCodec<AudioContentDescription, cricket::AudioCodec>(
3796 media_desc, payload_type, codec_params);
3797 } else if (media_type == cricket::MEDIA_TYPE_VIDEO) {
3798 UpdateCodec<VideoContentDescription, cricket::VideoCodec>(
3799 media_desc, payload_type, codec_params);
3801 return true;
3804 bool ParsePacketizationAttribute(absl::string_view line,
3805 const cricket::MediaType media_type,
3806 MediaContentDescription* media_desc,
3807 SdpParseError* error) {
3808 if (media_type != cricket::MEDIA_TYPE_VIDEO) {
3809 return true;
3811 std::vector<absl::string_view> packetization_fields =
3812 rtc::split(line, kSdpDelimiterSpaceChar);
3813 if (packetization_fields.size() < 2) {
3814 return ParseFailedGetValue(line, kAttributePacketization, error);
3816 std::string payload_type_string;
3817 if (!GetValue(packetization_fields[0], kAttributePacketization,
3818 &payload_type_string, error)) {
3819 return false;
3821 int payload_type;
3822 if (!GetPayloadTypeFromString(line, payload_type_string, &payload_type,
3823 error)) {
3824 return false;
3826 absl::string_view packetization = packetization_fields[1];
3827 UpdateVideoCodecPacketization(media_desc->as_video(), payload_type,
3828 packetization);
3829 return true;
3832 bool ParseRtcpFbAttribute(absl::string_view line,
3833 const cricket::MediaType media_type,
3834 MediaContentDescription* media_desc,
3835 SdpParseError* error) {
3836 if (media_type != cricket::MEDIA_TYPE_AUDIO &&
3837 media_type != cricket::MEDIA_TYPE_VIDEO) {
3838 return true;
3840 std::vector<absl::string_view> rtcp_fb_fields =
3841 rtc::split(line, kSdpDelimiterSpaceChar);
3842 if (rtcp_fb_fields.size() < 2) {
3843 return ParseFailedGetValue(line, kAttributeRtcpFb, error);
3845 std::string payload_type_string;
3846 if (!GetValue(rtcp_fb_fields[0], kAttributeRtcpFb, &payload_type_string,
3847 error)) {
3848 return false;
3850 int payload_type = kWildcardPayloadType;
3851 if (payload_type_string != "*") {
3852 if (!GetPayloadTypeFromString(line, payload_type_string, &payload_type,
3853 error)) {
3854 return false;
3857 absl::string_view id = rtcp_fb_fields[1];
3858 std::string param = "";
3859 for (auto iter = rtcp_fb_fields.begin() + 2; iter != rtcp_fb_fields.end();
3860 ++iter) {
3861 param.append(iter->data(), iter->length());
3863 const cricket::FeedbackParam feedback_param(id, param);
3865 if (media_type == cricket::MEDIA_TYPE_AUDIO) {
3866 UpdateCodec<AudioContentDescription, cricket::AudioCodec>(
3867 media_desc, payload_type, feedback_param);
3868 } else if (media_type == cricket::MEDIA_TYPE_VIDEO) {
3869 UpdateCodec<VideoContentDescription, cricket::VideoCodec>(
3870 media_desc, payload_type, feedback_param);
3872 return true;
3875 } // namespace webrtc