xref: /aosp_15_r20/external/webrtc/pc/sdp_serializer.cc (revision d9f758449e529ab9291ac668be2861e7a55c2422)
1*d9f75844SAndroid Build Coastguard Worker /*
2*d9f75844SAndroid Build Coastguard Worker  *  Copyright 2018 The WebRTC project authors. All Rights Reserved.
3*d9f75844SAndroid Build Coastguard Worker  *
4*d9f75844SAndroid Build Coastguard Worker  *  Use of this source code is governed by a BSD-style license
5*d9f75844SAndroid Build Coastguard Worker  *  that can be found in the LICENSE file in the root of the source
6*d9f75844SAndroid Build Coastguard Worker  *  tree. An additional intellectual property rights grant can be found
7*d9f75844SAndroid Build Coastguard Worker  *  in the file PATENTS.  All contributing project authors may
8*d9f75844SAndroid Build Coastguard Worker  *  be found in the AUTHORS file in the root of the source tree.
9*d9f75844SAndroid Build Coastguard Worker  */
10*d9f75844SAndroid Build Coastguard Worker 
11*d9f75844SAndroid Build Coastguard Worker #include "pc/sdp_serializer.h"
12*d9f75844SAndroid Build Coastguard Worker 
13*d9f75844SAndroid Build Coastguard Worker #include <map>
14*d9f75844SAndroid Build Coastguard Worker #include <string>
15*d9f75844SAndroid Build Coastguard Worker #include <type_traits>
16*d9f75844SAndroid Build Coastguard Worker #include <utility>
17*d9f75844SAndroid Build Coastguard Worker #include <vector>
18*d9f75844SAndroid Build Coastguard Worker 
19*d9f75844SAndroid Build Coastguard Worker #include "absl/algorithm/container.h"
20*d9f75844SAndroid Build Coastguard Worker #include "absl/types/optional.h"
21*d9f75844SAndroid Build Coastguard Worker #include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
22*d9f75844SAndroid Build Coastguard Worker #include "rtc_base/checks.h"
23*d9f75844SAndroid Build Coastguard Worker #include "rtc_base/string_encode.h"
24*d9f75844SAndroid Build Coastguard Worker #include "rtc_base/string_to_number.h"
25*d9f75844SAndroid Build Coastguard Worker #include "rtc_base/strings/string_builder.h"
26*d9f75844SAndroid Build Coastguard Worker 
27*d9f75844SAndroid Build Coastguard Worker using cricket::RidDescription;
28*d9f75844SAndroid Build Coastguard Worker using cricket::RidDirection;
29*d9f75844SAndroid Build Coastguard Worker using cricket::SimulcastDescription;
30*d9f75844SAndroid Build Coastguard Worker using cricket::SimulcastLayer;
31*d9f75844SAndroid Build Coastguard Worker using cricket::SimulcastLayerList;
32*d9f75844SAndroid Build Coastguard Worker 
33*d9f75844SAndroid Build Coastguard Worker namespace webrtc {
34*d9f75844SAndroid Build Coastguard Worker 
35*d9f75844SAndroid Build Coastguard Worker namespace {
36*d9f75844SAndroid Build Coastguard Worker 
37*d9f75844SAndroid Build Coastguard Worker // delimiters
38*d9f75844SAndroid Build Coastguard Worker const char kDelimiterComma[] = ",";
39*d9f75844SAndroid Build Coastguard Worker const char kDelimiterCommaChar = ',';
40*d9f75844SAndroid Build Coastguard Worker const char kDelimiterEqual[] = "=";
41*d9f75844SAndroid Build Coastguard Worker const char kDelimiterEqualChar = '=';
42*d9f75844SAndroid Build Coastguard Worker const char kDelimiterSemicolon[] = ";";
43*d9f75844SAndroid Build Coastguard Worker const char kDelimiterSemicolonChar = ';';
44*d9f75844SAndroid Build Coastguard Worker const char kDelimiterSpace[] = " ";
45*d9f75844SAndroid Build Coastguard Worker const char kDelimiterSpaceChar = ' ';
46*d9f75844SAndroid Build Coastguard Worker 
47*d9f75844SAndroid Build Coastguard Worker // https://tools.ietf.org/html/draft-ietf-mmusic-sdp-simulcast-13#section-5.1
48*d9f75844SAndroid Build Coastguard Worker // https://tools.ietf.org/html/draft-ietf-mmusic-rid-15#section-10
49*d9f75844SAndroid Build Coastguard Worker const char kSimulcastPausedStream[] = "~";
50*d9f75844SAndroid Build Coastguard Worker const char kSimulcastPausedStreamChar = '~';
51*d9f75844SAndroid Build Coastguard Worker const char kSendDirection[] = "send";
52*d9f75844SAndroid Build Coastguard Worker const char kReceiveDirection[] = "recv";
53*d9f75844SAndroid Build Coastguard Worker const char kPayloadType[] = "pt";
54*d9f75844SAndroid Build Coastguard Worker 
ParseError(const std::string & message)55*d9f75844SAndroid Build Coastguard Worker RTCError ParseError(const std::string& message) {
56*d9f75844SAndroid Build Coastguard Worker   return RTCError(RTCErrorType::SYNTAX_ERROR, message);
57*d9f75844SAndroid Build Coastguard Worker }
58*d9f75844SAndroid Build Coastguard Worker 
59*d9f75844SAndroid Build Coastguard Worker // These methods serialize simulcast according to the specification:
60*d9f75844SAndroid Build Coastguard Worker // https://tools.ietf.org/html/draft-ietf-mmusic-sdp-simulcast-13#section-5.1
operator <<(rtc::StringBuilder & builder,const SimulcastLayer & simulcast_layer)61*d9f75844SAndroid Build Coastguard Worker rtc::StringBuilder& operator<<(rtc::StringBuilder& builder,
62*d9f75844SAndroid Build Coastguard Worker                                const SimulcastLayer& simulcast_layer) {
63*d9f75844SAndroid Build Coastguard Worker   if (simulcast_layer.is_paused) {
64*d9f75844SAndroid Build Coastguard Worker     builder << kSimulcastPausedStream;
65*d9f75844SAndroid Build Coastguard Worker   }
66*d9f75844SAndroid Build Coastguard Worker   builder << simulcast_layer.rid;
67*d9f75844SAndroid Build Coastguard Worker   return builder;
68*d9f75844SAndroid Build Coastguard Worker }
69*d9f75844SAndroid Build Coastguard Worker 
operator <<(rtc::StringBuilder & builder,const std::vector<SimulcastLayer> & layer_alternatives)70*d9f75844SAndroid Build Coastguard Worker rtc::StringBuilder& operator<<(
71*d9f75844SAndroid Build Coastguard Worker     rtc::StringBuilder& builder,
72*d9f75844SAndroid Build Coastguard Worker     const std::vector<SimulcastLayer>& layer_alternatives) {
73*d9f75844SAndroid Build Coastguard Worker   bool first = true;
74*d9f75844SAndroid Build Coastguard Worker   for (const SimulcastLayer& rid : layer_alternatives) {
75*d9f75844SAndroid Build Coastguard Worker     if (!first) {
76*d9f75844SAndroid Build Coastguard Worker       builder << kDelimiterComma;
77*d9f75844SAndroid Build Coastguard Worker     }
78*d9f75844SAndroid Build Coastguard Worker     builder << rid;
79*d9f75844SAndroid Build Coastguard Worker     first = false;
80*d9f75844SAndroid Build Coastguard Worker   }
81*d9f75844SAndroid Build Coastguard Worker   return builder;
82*d9f75844SAndroid Build Coastguard Worker }
83*d9f75844SAndroid Build Coastguard Worker 
operator <<(rtc::StringBuilder & builder,const SimulcastLayerList & simulcast_layers)84*d9f75844SAndroid Build Coastguard Worker rtc::StringBuilder& operator<<(rtc::StringBuilder& builder,
85*d9f75844SAndroid Build Coastguard Worker                                const SimulcastLayerList& simulcast_layers) {
86*d9f75844SAndroid Build Coastguard Worker   bool first = true;
87*d9f75844SAndroid Build Coastguard Worker   for (const auto& alternatives : simulcast_layers) {
88*d9f75844SAndroid Build Coastguard Worker     if (!first) {
89*d9f75844SAndroid Build Coastguard Worker       builder << kDelimiterSemicolon;
90*d9f75844SAndroid Build Coastguard Worker     }
91*d9f75844SAndroid Build Coastguard Worker     builder << alternatives;
92*d9f75844SAndroid Build Coastguard Worker     first = false;
93*d9f75844SAndroid Build Coastguard Worker   }
94*d9f75844SAndroid Build Coastguard Worker   return builder;
95*d9f75844SAndroid Build Coastguard Worker }
96*d9f75844SAndroid Build Coastguard Worker // This method deserializes simulcast according to the specification:
97*d9f75844SAndroid Build Coastguard Worker // https://tools.ietf.org/html/draft-ietf-mmusic-sdp-simulcast-13#section-5.1
98*d9f75844SAndroid Build Coastguard Worker // sc-str-list  = sc-alt-list *( ";" sc-alt-list )
99*d9f75844SAndroid Build Coastguard Worker // sc-alt-list  = sc-id *( "," sc-id )
100*d9f75844SAndroid Build Coastguard Worker // sc-id-paused = "~"
101*d9f75844SAndroid Build Coastguard Worker // sc-id        = [sc-id-paused] rid-id
102*d9f75844SAndroid Build Coastguard Worker // rid-id       = 1*(alpha-numeric / "-" / "_") ; see: I-D.ietf-mmusic-rid
ParseSimulcastLayerList(const std::string & str)103*d9f75844SAndroid Build Coastguard Worker RTCErrorOr<SimulcastLayerList> ParseSimulcastLayerList(const std::string& str) {
104*d9f75844SAndroid Build Coastguard Worker   std::vector<absl::string_view> tokens =
105*d9f75844SAndroid Build Coastguard Worker       rtc::split(str, kDelimiterSemicolonChar);
106*d9f75844SAndroid Build Coastguard Worker   if (tokens.empty()) {
107*d9f75844SAndroid Build Coastguard Worker     return ParseError("Layer list cannot be empty.");
108*d9f75844SAndroid Build Coastguard Worker   }
109*d9f75844SAndroid Build Coastguard Worker 
110*d9f75844SAndroid Build Coastguard Worker   SimulcastLayerList result;
111*d9f75844SAndroid Build Coastguard Worker   for (const absl::string_view& token : tokens) {
112*d9f75844SAndroid Build Coastguard Worker     if (token.empty()) {
113*d9f75844SAndroid Build Coastguard Worker       return ParseError("Simulcast alternative layer list is empty.");
114*d9f75844SAndroid Build Coastguard Worker     }
115*d9f75844SAndroid Build Coastguard Worker 
116*d9f75844SAndroid Build Coastguard Worker     std::vector<absl::string_view> rid_tokens =
117*d9f75844SAndroid Build Coastguard Worker         rtc::split(token, kDelimiterCommaChar);
118*d9f75844SAndroid Build Coastguard Worker 
119*d9f75844SAndroid Build Coastguard Worker     if (rid_tokens.empty()) {
120*d9f75844SAndroid Build Coastguard Worker       return ParseError("Simulcast alternative layer list is malformed.");
121*d9f75844SAndroid Build Coastguard Worker     }
122*d9f75844SAndroid Build Coastguard Worker 
123*d9f75844SAndroid Build Coastguard Worker     std::vector<SimulcastLayer> layers;
124*d9f75844SAndroid Build Coastguard Worker     for (const absl::string_view& rid_token : rid_tokens) {
125*d9f75844SAndroid Build Coastguard Worker       if (rid_token.empty() || rid_token == kSimulcastPausedStream) {
126*d9f75844SAndroid Build Coastguard Worker         return ParseError("Rid must not be empty.");
127*d9f75844SAndroid Build Coastguard Worker       }
128*d9f75844SAndroid Build Coastguard Worker 
129*d9f75844SAndroid Build Coastguard Worker       bool paused = rid_token[0] == kSimulcastPausedStreamChar;
130*d9f75844SAndroid Build Coastguard Worker       absl::string_view rid = paused ? rid_token.substr(1) : rid_token;
131*d9f75844SAndroid Build Coastguard Worker       layers.push_back(SimulcastLayer(rid, paused));
132*d9f75844SAndroid Build Coastguard Worker     }
133*d9f75844SAndroid Build Coastguard Worker 
134*d9f75844SAndroid Build Coastguard Worker     result.AddLayerWithAlternatives(layers);
135*d9f75844SAndroid Build Coastguard Worker   }
136*d9f75844SAndroid Build Coastguard Worker 
137*d9f75844SAndroid Build Coastguard Worker   return std::move(result);
138*d9f75844SAndroid Build Coastguard Worker }
139*d9f75844SAndroid Build Coastguard Worker 
ParseRidPayloadList(const std::string & payload_list,RidDescription * rid_description)140*d9f75844SAndroid Build Coastguard Worker webrtc::RTCError ParseRidPayloadList(const std::string& payload_list,
141*d9f75844SAndroid Build Coastguard Worker                                      RidDescription* rid_description) {
142*d9f75844SAndroid Build Coastguard Worker   RTC_DCHECK(rid_description);
143*d9f75844SAndroid Build Coastguard Worker   std::vector<int>& payload_types = rid_description->payload_types;
144*d9f75844SAndroid Build Coastguard Worker   // Check that the description doesn't have any payload types or restrictions.
145*d9f75844SAndroid Build Coastguard Worker   // If the pt= field is specified, it must be first and must not repeat.
146*d9f75844SAndroid Build Coastguard Worker   if (!payload_types.empty()) {
147*d9f75844SAndroid Build Coastguard Worker     return ParseError("Multiple pt= found in RID Description.");
148*d9f75844SAndroid Build Coastguard Worker   }
149*d9f75844SAndroid Build Coastguard Worker   if (!rid_description->restrictions.empty()) {
150*d9f75844SAndroid Build Coastguard Worker     return ParseError("Payload list must appear first in the restrictions.");
151*d9f75844SAndroid Build Coastguard Worker   }
152*d9f75844SAndroid Build Coastguard Worker 
153*d9f75844SAndroid Build Coastguard Worker   // If the pt= field is specified, it must have a value.
154*d9f75844SAndroid Build Coastguard Worker   if (payload_list.empty()) {
155*d9f75844SAndroid Build Coastguard Worker     return ParseError("Payload list must have at least one value.");
156*d9f75844SAndroid Build Coastguard Worker   }
157*d9f75844SAndroid Build Coastguard Worker 
158*d9f75844SAndroid Build Coastguard Worker   // Tokenize the ',' delimited list
159*d9f75844SAndroid Build Coastguard Worker   std::vector<std::string> string_payloads;
160*d9f75844SAndroid Build Coastguard Worker   rtc::tokenize(payload_list, kDelimiterCommaChar, &string_payloads);
161*d9f75844SAndroid Build Coastguard Worker   if (string_payloads.empty()) {
162*d9f75844SAndroid Build Coastguard Worker     return ParseError("Payload list must have at least one value.");
163*d9f75844SAndroid Build Coastguard Worker   }
164*d9f75844SAndroid Build Coastguard Worker 
165*d9f75844SAndroid Build Coastguard Worker   for (const std::string& payload_type : string_payloads) {
166*d9f75844SAndroid Build Coastguard Worker     absl::optional<int> value = rtc::StringToNumber<int>(payload_type);
167*d9f75844SAndroid Build Coastguard Worker     if (!value.has_value()) {
168*d9f75844SAndroid Build Coastguard Worker       return ParseError("Invalid payload type: " + payload_type);
169*d9f75844SAndroid Build Coastguard Worker     }
170*d9f75844SAndroid Build Coastguard Worker 
171*d9f75844SAndroid Build Coastguard Worker     // Check if the value already appears in the payload list.
172*d9f75844SAndroid Build Coastguard Worker     if (absl::c_linear_search(payload_types, value.value())) {
173*d9f75844SAndroid Build Coastguard Worker       return ParseError("Duplicate payload type in list: " + payload_type);
174*d9f75844SAndroid Build Coastguard Worker     }
175*d9f75844SAndroid Build Coastguard Worker     payload_types.push_back(value.value());
176*d9f75844SAndroid Build Coastguard Worker   }
177*d9f75844SAndroid Build Coastguard Worker 
178*d9f75844SAndroid Build Coastguard Worker   return RTCError::OK();
179*d9f75844SAndroid Build Coastguard Worker }
180*d9f75844SAndroid Build Coastguard Worker 
181*d9f75844SAndroid Build Coastguard Worker }  // namespace
182*d9f75844SAndroid Build Coastguard Worker 
SerializeSimulcastDescription(const cricket::SimulcastDescription & simulcast) const183*d9f75844SAndroid Build Coastguard Worker std::string SdpSerializer::SerializeSimulcastDescription(
184*d9f75844SAndroid Build Coastguard Worker     const cricket::SimulcastDescription& simulcast) const {
185*d9f75844SAndroid Build Coastguard Worker   rtc::StringBuilder sb;
186*d9f75844SAndroid Build Coastguard Worker   std::string delimiter;
187*d9f75844SAndroid Build Coastguard Worker 
188*d9f75844SAndroid Build Coastguard Worker   if (!simulcast.send_layers().empty()) {
189*d9f75844SAndroid Build Coastguard Worker     sb << kSendDirection << kDelimiterSpace << simulcast.send_layers();
190*d9f75844SAndroid Build Coastguard Worker     delimiter = kDelimiterSpace;
191*d9f75844SAndroid Build Coastguard Worker   }
192*d9f75844SAndroid Build Coastguard Worker 
193*d9f75844SAndroid Build Coastguard Worker   if (!simulcast.receive_layers().empty()) {
194*d9f75844SAndroid Build Coastguard Worker     sb << delimiter << kReceiveDirection << kDelimiterSpace
195*d9f75844SAndroid Build Coastguard Worker        << simulcast.receive_layers();
196*d9f75844SAndroid Build Coastguard Worker   }
197*d9f75844SAndroid Build Coastguard Worker 
198*d9f75844SAndroid Build Coastguard Worker   return sb.str();
199*d9f75844SAndroid Build Coastguard Worker }
200*d9f75844SAndroid Build Coastguard Worker 
201*d9f75844SAndroid Build Coastguard Worker // https://tools.ietf.org/html/draft-ietf-mmusic-sdp-simulcast-13#section-5.1
202*d9f75844SAndroid Build Coastguard Worker // a:simulcast:<send> <streams> <recv> <streams>
203*d9f75844SAndroid Build Coastguard Worker // Formal Grammar
204*d9f75844SAndroid Build Coastguard Worker // sc-value     = ( sc-send [SP sc-recv] ) / ( sc-recv [SP sc-send] )
205*d9f75844SAndroid Build Coastguard Worker // sc-send      = %s"send" SP sc-str-list
206*d9f75844SAndroid Build Coastguard Worker // sc-recv      = %s"recv" SP sc-str-list
207*d9f75844SAndroid Build Coastguard Worker // sc-str-list  = sc-alt-list *( ";" sc-alt-list )
208*d9f75844SAndroid Build Coastguard Worker // sc-alt-list  = sc-id *( "," sc-id )
209*d9f75844SAndroid Build Coastguard Worker // sc-id-paused = "~"
210*d9f75844SAndroid Build Coastguard Worker // sc-id        = [sc-id-paused] rid-id
211*d9f75844SAndroid Build Coastguard Worker // rid-id       = 1*(alpha-numeric / "-" / "_") ; see: I-D.ietf-mmusic-rid
DeserializeSimulcastDescription(absl::string_view string) const212*d9f75844SAndroid Build Coastguard Worker RTCErrorOr<SimulcastDescription> SdpSerializer::DeserializeSimulcastDescription(
213*d9f75844SAndroid Build Coastguard Worker     absl::string_view string) const {
214*d9f75844SAndroid Build Coastguard Worker   std::vector<std::string> tokens;
215*d9f75844SAndroid Build Coastguard Worker   rtc::tokenize(std::string(string), kDelimiterSpaceChar, &tokens);
216*d9f75844SAndroid Build Coastguard Worker 
217*d9f75844SAndroid Build Coastguard Worker   if (tokens.size() != 2 && tokens.size() != 4) {
218*d9f75844SAndroid Build Coastguard Worker     return ParseError("Must have one or two <direction, streams> pairs.");
219*d9f75844SAndroid Build Coastguard Worker   }
220*d9f75844SAndroid Build Coastguard Worker 
221*d9f75844SAndroid Build Coastguard Worker   bool bidirectional = tokens.size() == 4;  // indicates both send and recv
222*d9f75844SAndroid Build Coastguard Worker 
223*d9f75844SAndroid Build Coastguard Worker   // Tokens 0, 2 (if exists) should be send / recv
224*d9f75844SAndroid Build Coastguard Worker   if ((tokens[0] != kSendDirection && tokens[0] != kReceiveDirection) ||
225*d9f75844SAndroid Build Coastguard Worker       (bidirectional && tokens[2] != kSendDirection &&
226*d9f75844SAndroid Build Coastguard Worker        tokens[2] != kReceiveDirection) ||
227*d9f75844SAndroid Build Coastguard Worker       (bidirectional && tokens[0] == tokens[2])) {
228*d9f75844SAndroid Build Coastguard Worker     return ParseError("Valid values: send / recv.");
229*d9f75844SAndroid Build Coastguard Worker   }
230*d9f75844SAndroid Build Coastguard Worker 
231*d9f75844SAndroid Build Coastguard Worker   // Tokens 1, 3 (if exists) should be alternative layer lists
232*d9f75844SAndroid Build Coastguard Worker   RTCErrorOr<SimulcastLayerList> list1, list2;
233*d9f75844SAndroid Build Coastguard Worker   list1 = ParseSimulcastLayerList(tokens[1]);
234*d9f75844SAndroid Build Coastguard Worker   if (!list1.ok()) {
235*d9f75844SAndroid Build Coastguard Worker     return list1.MoveError();
236*d9f75844SAndroid Build Coastguard Worker   }
237*d9f75844SAndroid Build Coastguard Worker 
238*d9f75844SAndroid Build Coastguard Worker   if (bidirectional) {
239*d9f75844SAndroid Build Coastguard Worker     list2 = ParseSimulcastLayerList(tokens[3]);
240*d9f75844SAndroid Build Coastguard Worker     if (!list2.ok()) {
241*d9f75844SAndroid Build Coastguard Worker       return list2.MoveError();
242*d9f75844SAndroid Build Coastguard Worker     }
243*d9f75844SAndroid Build Coastguard Worker   }
244*d9f75844SAndroid Build Coastguard Worker 
245*d9f75844SAndroid Build Coastguard Worker   // Set the layers so that list1 is for send and list2 is for recv
246*d9f75844SAndroid Build Coastguard Worker   if (tokens[0] != kSendDirection) {
247*d9f75844SAndroid Build Coastguard Worker     std::swap(list1, list2);
248*d9f75844SAndroid Build Coastguard Worker   }
249*d9f75844SAndroid Build Coastguard Worker 
250*d9f75844SAndroid Build Coastguard Worker   // Set the layers according to which pair is send and which is recv
251*d9f75844SAndroid Build Coastguard Worker   // At this point if the simulcast is unidirectional then
252*d9f75844SAndroid Build Coastguard Worker   // either `list1` or `list2` will be in 'error' state indicating that
253*d9f75844SAndroid Build Coastguard Worker   // the value should not be used.
254*d9f75844SAndroid Build Coastguard Worker   SimulcastDescription simulcast;
255*d9f75844SAndroid Build Coastguard Worker   if (list1.ok()) {
256*d9f75844SAndroid Build Coastguard Worker     simulcast.send_layers() = list1.MoveValue();
257*d9f75844SAndroid Build Coastguard Worker   }
258*d9f75844SAndroid Build Coastguard Worker 
259*d9f75844SAndroid Build Coastguard Worker   if (list2.ok()) {
260*d9f75844SAndroid Build Coastguard Worker     simulcast.receive_layers() = list2.MoveValue();
261*d9f75844SAndroid Build Coastguard Worker   }
262*d9f75844SAndroid Build Coastguard Worker 
263*d9f75844SAndroid Build Coastguard Worker   return std::move(simulcast);
264*d9f75844SAndroid Build Coastguard Worker }
265*d9f75844SAndroid Build Coastguard Worker 
SerializeRidDescription(const RidDescription & rid_description) const266*d9f75844SAndroid Build Coastguard Worker std::string SdpSerializer::SerializeRidDescription(
267*d9f75844SAndroid Build Coastguard Worker     const RidDescription& rid_description) const {
268*d9f75844SAndroid Build Coastguard Worker   RTC_DCHECK(!rid_description.rid.empty());
269*d9f75844SAndroid Build Coastguard Worker   RTC_DCHECK(rid_description.direction == RidDirection::kSend ||
270*d9f75844SAndroid Build Coastguard Worker              rid_description.direction == RidDirection::kReceive);
271*d9f75844SAndroid Build Coastguard Worker 
272*d9f75844SAndroid Build Coastguard Worker   rtc::StringBuilder builder;
273*d9f75844SAndroid Build Coastguard Worker   builder << rid_description.rid << kDelimiterSpace
274*d9f75844SAndroid Build Coastguard Worker           << (rid_description.direction == RidDirection::kSend
275*d9f75844SAndroid Build Coastguard Worker                   ? kSendDirection
276*d9f75844SAndroid Build Coastguard Worker                   : kReceiveDirection);
277*d9f75844SAndroid Build Coastguard Worker 
278*d9f75844SAndroid Build Coastguard Worker   const auto& payload_types = rid_description.payload_types;
279*d9f75844SAndroid Build Coastguard Worker   const auto& restrictions = rid_description.restrictions;
280*d9f75844SAndroid Build Coastguard Worker 
281*d9f75844SAndroid Build Coastguard Worker   // First property is separated by ' ', the next ones by ';'.
282*d9f75844SAndroid Build Coastguard Worker   const char* propertyDelimiter = kDelimiterSpace;
283*d9f75844SAndroid Build Coastguard Worker 
284*d9f75844SAndroid Build Coastguard Worker   // Serialize any codecs in the description.
285*d9f75844SAndroid Build Coastguard Worker   if (!payload_types.empty()) {
286*d9f75844SAndroid Build Coastguard Worker     builder << propertyDelimiter << kPayloadType << kDelimiterEqual;
287*d9f75844SAndroid Build Coastguard Worker     propertyDelimiter = kDelimiterSemicolon;
288*d9f75844SAndroid Build Coastguard Worker     const char* formatDelimiter = "";
289*d9f75844SAndroid Build Coastguard Worker     for (int payload_type : payload_types) {
290*d9f75844SAndroid Build Coastguard Worker       builder << formatDelimiter << payload_type;
291*d9f75844SAndroid Build Coastguard Worker       formatDelimiter = kDelimiterComma;
292*d9f75844SAndroid Build Coastguard Worker     }
293*d9f75844SAndroid Build Coastguard Worker   }
294*d9f75844SAndroid Build Coastguard Worker 
295*d9f75844SAndroid Build Coastguard Worker   // Serialize any restrictions in the description.
296*d9f75844SAndroid Build Coastguard Worker   for (const auto& pair : restrictions) {
297*d9f75844SAndroid Build Coastguard Worker     // Serialize key=val pairs. =val part is ommitted if val is empty.
298*d9f75844SAndroid Build Coastguard Worker     builder << propertyDelimiter << pair.first;
299*d9f75844SAndroid Build Coastguard Worker     if (!pair.second.empty()) {
300*d9f75844SAndroid Build Coastguard Worker       builder << kDelimiterEqual << pair.second;
301*d9f75844SAndroid Build Coastguard Worker     }
302*d9f75844SAndroid Build Coastguard Worker 
303*d9f75844SAndroid Build Coastguard Worker     propertyDelimiter = kDelimiterSemicolon;
304*d9f75844SAndroid Build Coastguard Worker   }
305*d9f75844SAndroid Build Coastguard Worker 
306*d9f75844SAndroid Build Coastguard Worker   return builder.str();
307*d9f75844SAndroid Build Coastguard Worker }
308*d9f75844SAndroid Build Coastguard Worker 
309*d9f75844SAndroid Build Coastguard Worker // https://tools.ietf.org/html/draft-ietf-mmusic-rid-15#section-10
310*d9f75844SAndroid Build Coastguard Worker // Formal Grammar
311*d9f75844SAndroid Build Coastguard Worker // rid-syntax         = %s"a=rid:" rid-id SP rid-dir
312*d9f75844SAndroid Build Coastguard Worker //                      [ rid-pt-param-list / rid-param-list ]
313*d9f75844SAndroid Build Coastguard Worker // rid-id             = 1*(alpha-numeric / "-" / "_")
314*d9f75844SAndroid Build Coastguard Worker // rid-dir            = %s"send" / %s"recv"
315*d9f75844SAndroid Build Coastguard Worker // rid-pt-param-list  = SP rid-fmt-list *( ";" rid-param )
316*d9f75844SAndroid Build Coastguard Worker // rid-param-list     = SP rid-param *( ";" rid-param )
317*d9f75844SAndroid Build Coastguard Worker // rid-fmt-list       = %s"pt=" fmt *( "," fmt )
318*d9f75844SAndroid Build Coastguard Worker // rid-param          = 1*(alpha-numeric / "-") [ "=" param-val ]
319*d9f75844SAndroid Build Coastguard Worker // param-val          = *( %x20-58 / %x60-7E )
320*d9f75844SAndroid Build Coastguard Worker //                      ; Any printable character except semicolon
DeserializeRidDescription(absl::string_view string) const321*d9f75844SAndroid Build Coastguard Worker RTCErrorOr<RidDescription> SdpSerializer::DeserializeRidDescription(
322*d9f75844SAndroid Build Coastguard Worker     absl::string_view string) const {
323*d9f75844SAndroid Build Coastguard Worker   std::vector<std::string> tokens;
324*d9f75844SAndroid Build Coastguard Worker   rtc::tokenize(std::string(string), kDelimiterSpaceChar, &tokens);
325*d9f75844SAndroid Build Coastguard Worker 
326*d9f75844SAndroid Build Coastguard Worker   if (tokens.size() < 2) {
327*d9f75844SAndroid Build Coastguard Worker     return ParseError("RID Description must contain <RID> <direction>.");
328*d9f75844SAndroid Build Coastguard Worker   }
329*d9f75844SAndroid Build Coastguard Worker 
330*d9f75844SAndroid Build Coastguard Worker   if (tokens.size() > 3) {
331*d9f75844SAndroid Build Coastguard Worker     return ParseError("Invalid RID Description format. Too many arguments.");
332*d9f75844SAndroid Build Coastguard Worker   }
333*d9f75844SAndroid Build Coastguard Worker 
334*d9f75844SAndroid Build Coastguard Worker   if (!IsLegalRsidName(tokens[0])) {
335*d9f75844SAndroid Build Coastguard Worker     return ParseError("Invalid RID value: " + tokens[0] + ".");
336*d9f75844SAndroid Build Coastguard Worker   }
337*d9f75844SAndroid Build Coastguard Worker 
338*d9f75844SAndroid Build Coastguard Worker   if (tokens[1] != kSendDirection && tokens[1] != kReceiveDirection) {
339*d9f75844SAndroid Build Coastguard Worker     return ParseError("Invalid RID direction. Supported values: send / recv.");
340*d9f75844SAndroid Build Coastguard Worker   }
341*d9f75844SAndroid Build Coastguard Worker 
342*d9f75844SAndroid Build Coastguard Worker   RidDirection direction = tokens[1] == kSendDirection ? RidDirection::kSend
343*d9f75844SAndroid Build Coastguard Worker                                                        : RidDirection::kReceive;
344*d9f75844SAndroid Build Coastguard Worker 
345*d9f75844SAndroid Build Coastguard Worker   RidDescription rid_description(tokens[0], direction);
346*d9f75844SAndroid Build Coastguard Worker 
347*d9f75844SAndroid Build Coastguard Worker   // If there is a third argument it is a payload list and/or restriction list.
348*d9f75844SAndroid Build Coastguard Worker   if (tokens.size() == 3) {
349*d9f75844SAndroid Build Coastguard Worker     std::vector<std::string> restrictions;
350*d9f75844SAndroid Build Coastguard Worker     rtc::tokenize(tokens[2], kDelimiterSemicolonChar, &restrictions);
351*d9f75844SAndroid Build Coastguard Worker 
352*d9f75844SAndroid Build Coastguard Worker     // Check for malformed restriction list, such as ';' or ';;;' etc.
353*d9f75844SAndroid Build Coastguard Worker     if (restrictions.empty()) {
354*d9f75844SAndroid Build Coastguard Worker       return ParseError("Invalid RID restriction list: " + tokens[2]);
355*d9f75844SAndroid Build Coastguard Worker     }
356*d9f75844SAndroid Build Coastguard Worker 
357*d9f75844SAndroid Build Coastguard Worker     // Parse the restrictions. The payload indicator (pt) can only appear first.
358*d9f75844SAndroid Build Coastguard Worker     for (const std::string& restriction : restrictions) {
359*d9f75844SAndroid Build Coastguard Worker       std::vector<std::string> parts;
360*d9f75844SAndroid Build Coastguard Worker       rtc::tokenize(restriction, kDelimiterEqualChar, &parts);
361*d9f75844SAndroid Build Coastguard Worker       if (parts.empty() || parts.size() > 2) {
362*d9f75844SAndroid Build Coastguard Worker         return ParseError("Invalid format for restriction: " + restriction);
363*d9f75844SAndroid Build Coastguard Worker       }
364*d9f75844SAndroid Build Coastguard Worker 
365*d9f75844SAndroid Build Coastguard Worker       // `parts` contains at least one value and it does not contain a space.
366*d9f75844SAndroid Build Coastguard Worker       // Note: `parts` and other values might still contain tab, newline,
367*d9f75844SAndroid Build Coastguard Worker       // unprintable characters, etc. which will not generate errors here but
368*d9f75844SAndroid Build Coastguard Worker       // will (most-likely) be ignored by components down stream.
369*d9f75844SAndroid Build Coastguard Worker       if (parts[0] == kPayloadType) {
370*d9f75844SAndroid Build Coastguard Worker         RTCError error = ParseRidPayloadList(
371*d9f75844SAndroid Build Coastguard Worker             parts.size() > 1 ? parts[1] : std::string(), &rid_description);
372*d9f75844SAndroid Build Coastguard Worker         if (!error.ok()) {
373*d9f75844SAndroid Build Coastguard Worker           return std::move(error);
374*d9f75844SAndroid Build Coastguard Worker         }
375*d9f75844SAndroid Build Coastguard Worker 
376*d9f75844SAndroid Build Coastguard Worker         continue;
377*d9f75844SAndroid Build Coastguard Worker       }
378*d9f75844SAndroid Build Coastguard Worker 
379*d9f75844SAndroid Build Coastguard Worker       // Parse `parts` as a key=value pair which allows unspecified values.
380*d9f75844SAndroid Build Coastguard Worker       if (rid_description.restrictions.find(parts[0]) !=
381*d9f75844SAndroid Build Coastguard Worker           rid_description.restrictions.end()) {
382*d9f75844SAndroid Build Coastguard Worker         return ParseError("Duplicate restriction specified: " + parts[0]);
383*d9f75844SAndroid Build Coastguard Worker       }
384*d9f75844SAndroid Build Coastguard Worker 
385*d9f75844SAndroid Build Coastguard Worker       rid_description.restrictions[parts[0]] =
386*d9f75844SAndroid Build Coastguard Worker           parts.size() > 1 ? parts[1] : std::string();
387*d9f75844SAndroid Build Coastguard Worker     }
388*d9f75844SAndroid Build Coastguard Worker   }
389*d9f75844SAndroid Build Coastguard Worker 
390*d9f75844SAndroid Build Coastguard Worker   return std::move(rid_description);
391*d9f75844SAndroid Build Coastguard Worker }
392*d9f75844SAndroid Build Coastguard Worker 
393*d9f75844SAndroid Build Coastguard Worker }  // namespace webrtc
394