Bug 1864652 - Expose settings for Global Privacy Control. r=geckoview-reviewers,ohall...
[gecko.git] / third_party / libwebrtc / pc / sdp_serializer.cc
blob31c624b12c96580c56f68bce4065d702065fb779
1 /*
2 * Copyright 2018 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/sdp_serializer.h"
13 #include <map>
14 #include <string>
15 #include <type_traits>
16 #include <utility>
17 #include <vector>
19 #include "absl/algorithm/container.h"
20 #include "absl/strings/string_view.h"
21 #include "absl/types/optional.h"
22 #include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
23 #include "rtc_base/checks.h"
24 #include "rtc_base/string_encode.h"
25 #include "rtc_base/string_to_number.h"
26 #include "rtc_base/strings/string_builder.h"
28 using cricket::RidDescription;
29 using cricket::RidDirection;
30 using cricket::SimulcastDescription;
31 using cricket::SimulcastLayer;
32 using cricket::SimulcastLayerList;
34 namespace webrtc {
36 namespace {
38 // delimiters
39 const char kDelimiterComma[] = ",";
40 const char kDelimiterCommaChar = ',';
41 const char kDelimiterEqual[] = "=";
42 const char kDelimiterEqualChar = '=';
43 const char kDelimiterSemicolon[] = ";";
44 const char kDelimiterSemicolonChar = ';';
45 const char kDelimiterSpace[] = " ";
46 const char kDelimiterSpaceChar = ' ';
48 // https://tools.ietf.org/html/draft-ietf-mmusic-sdp-simulcast-13#section-5.1
49 // https://tools.ietf.org/html/draft-ietf-mmusic-rid-15#section-10
50 const char kSimulcastPausedStream[] = "~";
51 const char kSimulcastPausedStreamChar = '~';
52 const char kSendDirection[] = "send";
53 const char kReceiveDirection[] = "recv";
54 const char kPayloadType[] = "pt";
56 RTCError ParseError(absl::string_view message) {
57 return RTCError(RTCErrorType::SYNTAX_ERROR, message);
60 // These methods serialize simulcast according to the specification:
61 // https://tools.ietf.org/html/draft-ietf-mmusic-sdp-simulcast-13#section-5.1
62 rtc::StringBuilder& operator<<(rtc::StringBuilder& builder,
63 const SimulcastLayer& simulcast_layer) {
64 if (simulcast_layer.is_paused) {
65 builder << kSimulcastPausedStream;
67 builder << simulcast_layer.rid;
68 return builder;
71 rtc::StringBuilder& operator<<(
72 rtc::StringBuilder& builder,
73 const std::vector<SimulcastLayer>& layer_alternatives) {
74 bool first = true;
75 for (const SimulcastLayer& rid : layer_alternatives) {
76 if (!first) {
77 builder << kDelimiterComma;
79 builder << rid;
80 first = false;
82 return builder;
85 rtc::StringBuilder& operator<<(rtc::StringBuilder& builder,
86 const SimulcastLayerList& simulcast_layers) {
87 bool first = true;
88 for (const auto& alternatives : simulcast_layers) {
89 if (!first) {
90 builder << kDelimiterSemicolon;
92 builder << alternatives;
93 first = false;
95 return builder;
97 // This method deserializes simulcast according to the specification:
98 // https://tools.ietf.org/html/draft-ietf-mmusic-sdp-simulcast-13#section-5.1
99 // sc-str-list = sc-alt-list *( ";" sc-alt-list )
100 // sc-alt-list = sc-id *( "," sc-id )
101 // sc-id-paused = "~"
102 // sc-id = [sc-id-paused] rid-id
103 // rid-id = 1*(alpha-numeric / "-" / "_") ; see: I-D.ietf-mmusic-rid
104 RTCErrorOr<SimulcastLayerList> ParseSimulcastLayerList(const std::string& str) {
105 std::vector<absl::string_view> tokens =
106 rtc::split(str, kDelimiterSemicolonChar);
107 if (tokens.empty()) {
108 return ParseError("Layer list cannot be empty.");
111 SimulcastLayerList result;
112 for (const absl::string_view& token : tokens) {
113 if (token.empty()) {
114 return ParseError("Simulcast alternative layer list is empty.");
117 std::vector<absl::string_view> rid_tokens =
118 rtc::split(token, kDelimiterCommaChar);
120 if (rid_tokens.empty()) {
121 return ParseError("Simulcast alternative layer list is malformed.");
124 std::vector<SimulcastLayer> layers;
125 for (const absl::string_view& rid_token : rid_tokens) {
126 if (rid_token.empty() || rid_token == kSimulcastPausedStream) {
127 return ParseError("Rid must not be empty.");
130 bool paused = rid_token[0] == kSimulcastPausedStreamChar;
131 absl::string_view rid = paused ? rid_token.substr(1) : rid_token;
132 layers.push_back(SimulcastLayer(rid, paused));
135 result.AddLayerWithAlternatives(layers);
138 return std::move(result);
141 webrtc::RTCError ParseRidPayloadList(const std::string& payload_list,
142 RidDescription* rid_description) {
143 RTC_DCHECK(rid_description);
144 std::vector<int>& payload_types = rid_description->payload_types;
145 // Check that the description doesn't have any payload types or restrictions.
146 // If the pt= field is specified, it must be first and must not repeat.
147 if (!payload_types.empty()) {
148 return ParseError("Multiple pt= found in RID Description.");
150 if (!rid_description->restrictions.empty()) {
151 return ParseError("Payload list must appear first in the restrictions.");
154 // If the pt= field is specified, it must have a value.
155 if (payload_list.empty()) {
156 return ParseError("Payload list must have at least one value.");
159 // Tokenize the ',' delimited list
160 std::vector<std::string> string_payloads;
161 rtc::tokenize(payload_list, kDelimiterCommaChar, &string_payloads);
162 if (string_payloads.empty()) {
163 return ParseError("Payload list must have at least one value.");
166 for (const std::string& payload_type : string_payloads) {
167 absl::optional<int> value = rtc::StringToNumber<int>(payload_type);
168 if (!value.has_value()) {
169 return ParseError("Invalid payload type: " + payload_type);
172 // Check if the value already appears in the payload list.
173 if (absl::c_linear_search(payload_types, value.value())) {
174 return ParseError("Duplicate payload type in list: " + payload_type);
176 payload_types.push_back(value.value());
179 return RTCError::OK();
182 } // namespace
184 std::string SdpSerializer::SerializeSimulcastDescription(
185 const cricket::SimulcastDescription& simulcast) const {
186 rtc::StringBuilder sb;
187 std::string delimiter;
189 if (!simulcast.send_layers().empty()) {
190 sb << kSendDirection << kDelimiterSpace << simulcast.send_layers();
191 delimiter = kDelimiterSpace;
194 if (!simulcast.receive_layers().empty()) {
195 sb << delimiter << kReceiveDirection << kDelimiterSpace
196 << simulcast.receive_layers();
199 return sb.str();
202 // https://tools.ietf.org/html/draft-ietf-mmusic-sdp-simulcast-13#section-5.1
203 // a:simulcast:<send> <streams> <recv> <streams>
204 // Formal Grammar
205 // sc-value = ( sc-send [SP sc-recv] ) / ( sc-recv [SP sc-send] )
206 // sc-send = %s"send" SP sc-str-list
207 // sc-recv = %s"recv" SP sc-str-list
208 // sc-str-list = sc-alt-list *( ";" sc-alt-list )
209 // sc-alt-list = sc-id *( "," sc-id )
210 // sc-id-paused = "~"
211 // sc-id = [sc-id-paused] rid-id
212 // rid-id = 1*(alpha-numeric / "-" / "_") ; see: I-D.ietf-mmusic-rid
213 RTCErrorOr<SimulcastDescription> SdpSerializer::DeserializeSimulcastDescription(
214 absl::string_view string) const {
215 std::vector<std::string> tokens;
216 rtc::tokenize(std::string(string), kDelimiterSpaceChar, &tokens);
218 if (tokens.size() != 2 && tokens.size() != 4) {
219 return ParseError("Must have one or two <direction, streams> pairs.");
222 bool bidirectional = tokens.size() == 4; // indicates both send and recv
224 // Tokens 0, 2 (if exists) should be send / recv
225 if ((tokens[0] != kSendDirection && tokens[0] != kReceiveDirection) ||
226 (bidirectional && tokens[2] != kSendDirection &&
227 tokens[2] != kReceiveDirection) ||
228 (bidirectional && tokens[0] == tokens[2])) {
229 return ParseError("Valid values: send / recv.");
232 // Tokens 1, 3 (if exists) should be alternative layer lists
233 RTCErrorOr<SimulcastLayerList> list1, list2;
234 list1 = ParseSimulcastLayerList(tokens[1]);
235 if (!list1.ok()) {
236 return list1.MoveError();
239 if (bidirectional) {
240 list2 = ParseSimulcastLayerList(tokens[3]);
241 if (!list2.ok()) {
242 return list2.MoveError();
246 // Set the layers so that list1 is for send and list2 is for recv
247 if (tokens[0] != kSendDirection) {
248 std::swap(list1, list2);
251 // Set the layers according to which pair is send and which is recv
252 // At this point if the simulcast is unidirectional then
253 // either `list1` or `list2` will be in 'error' state indicating that
254 // the value should not be used.
255 SimulcastDescription simulcast;
256 if (list1.ok()) {
257 simulcast.send_layers() = list1.MoveValue();
260 if (list2.ok()) {
261 simulcast.receive_layers() = list2.MoveValue();
264 return std::move(simulcast);
267 std::string SdpSerializer::SerializeRidDescription(
268 const RidDescription& rid_description) const {
269 RTC_DCHECK(!rid_description.rid.empty());
270 RTC_DCHECK(rid_description.direction == RidDirection::kSend ||
271 rid_description.direction == RidDirection::kReceive);
273 rtc::StringBuilder builder;
274 builder << rid_description.rid << kDelimiterSpace
275 << (rid_description.direction == RidDirection::kSend
276 ? kSendDirection
277 : kReceiveDirection);
279 const auto& payload_types = rid_description.payload_types;
280 const auto& restrictions = rid_description.restrictions;
282 // First property is separated by ' ', the next ones by ';'.
283 const char* propertyDelimiter = kDelimiterSpace;
285 // Serialize any codecs in the description.
286 if (!payload_types.empty()) {
287 builder << propertyDelimiter << kPayloadType << kDelimiterEqual;
288 propertyDelimiter = kDelimiterSemicolon;
289 const char* formatDelimiter = "";
290 for (int payload_type : payload_types) {
291 builder << formatDelimiter << payload_type;
292 formatDelimiter = kDelimiterComma;
296 // Serialize any restrictions in the description.
297 for (const auto& pair : restrictions) {
298 // Serialize key=val pairs. =val part is ommitted if val is empty.
299 builder << propertyDelimiter << pair.first;
300 if (!pair.second.empty()) {
301 builder << kDelimiterEqual << pair.second;
304 propertyDelimiter = kDelimiterSemicolon;
307 return builder.str();
310 // https://tools.ietf.org/html/draft-ietf-mmusic-rid-15#section-10
311 // Formal Grammar
312 // rid-syntax = %s"a=rid:" rid-id SP rid-dir
313 // [ rid-pt-param-list / rid-param-list ]
314 // rid-id = 1*(alpha-numeric / "-" / "_")
315 // rid-dir = %s"send" / %s"recv"
316 // rid-pt-param-list = SP rid-fmt-list *( ";" rid-param )
317 // rid-param-list = SP rid-param *( ";" rid-param )
318 // rid-fmt-list = %s"pt=" fmt *( "," fmt )
319 // rid-param = 1*(alpha-numeric / "-") [ "=" param-val ]
320 // param-val = *( %x20-58 / %x60-7E )
321 // ; Any printable character except semicolon
322 RTCErrorOr<RidDescription> SdpSerializer::DeserializeRidDescription(
323 absl::string_view string) const {
324 std::vector<std::string> tokens;
325 rtc::tokenize(std::string(string), kDelimiterSpaceChar, &tokens);
327 if (tokens.size() < 2) {
328 return ParseError("RID Description must contain <RID> <direction>.");
331 if (tokens.size() > 3) {
332 return ParseError("Invalid RID Description format. Too many arguments.");
335 if (!IsLegalRsidName(tokens[0])) {
336 return ParseError("Invalid RID value: " + tokens[0] + ".");
339 if (tokens[1] != kSendDirection && tokens[1] != kReceiveDirection) {
340 return ParseError("Invalid RID direction. Supported values: send / recv.");
343 RidDirection direction = tokens[1] == kSendDirection ? RidDirection::kSend
344 : RidDirection::kReceive;
346 RidDescription rid_description(tokens[0], direction);
348 // If there is a third argument it is a payload list and/or restriction list.
349 if (tokens.size() == 3) {
350 std::vector<std::string> restrictions;
351 rtc::tokenize(tokens[2], kDelimiterSemicolonChar, &restrictions);
353 // Check for malformed restriction list, such as ';' or ';;;' etc.
354 if (restrictions.empty()) {
355 return ParseError("Invalid RID restriction list: " + tokens[2]);
358 // Parse the restrictions. The payload indicator (pt) can only appear first.
359 for (const std::string& restriction : restrictions) {
360 std::vector<std::string> parts;
361 rtc::tokenize(restriction, kDelimiterEqualChar, &parts);
362 if (parts.empty() || parts.size() > 2) {
363 return ParseError("Invalid format for restriction: " + restriction);
366 // `parts` contains at least one value and it does not contain a space.
367 // Note: `parts` and other values might still contain tab, newline,
368 // unprintable characters, etc. which will not generate errors here but
369 // will (most-likely) be ignored by components down stream.
370 if (parts[0] == kPayloadType) {
371 RTCError error = ParseRidPayloadList(
372 parts.size() > 1 ? parts[1] : std::string(), &rid_description);
373 if (!error.ok()) {
374 return std::move(error);
377 continue;
380 // Parse `parts` as a key=value pair which allows unspecified values.
381 if (rid_description.restrictions.find(parts[0]) !=
382 rid_description.restrictions.end()) {
383 return ParseError("Duplicate restriction specified: " + parts[0]);
386 rid_description.restrictions[parts[0]] =
387 parts.size() > 1 ? parts[1] : std::string();
391 return std::move(rid_description);
394 } // namespace webrtc