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