blob: b5a1d877f1f0e086ca55059aa947607678736082 [file] [log] [blame]
Amit Hilbucha2012042018-12-03 11:35:05 -08001/*
2 * Copyright 2018 The WebRTC project authors. All Rights Reserved.
3 *
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 */
10
11#include "pc/sdpserializer.h"
12
13#include <string>
14#include <utility>
15#include <vector>
16
17#include "api/jsep.h"
18#include "rtc_base/strings/string_builder.h"
19
20using cricket::SimulcastDescription;
21using cricket::SimulcastLayer;
22using cricket::SimulcastLayerList;
23
24namespace webrtc {
25
26namespace {
27
28// delimiters
29const char kDelimiterComma[] = ",";
30const char kDelimiterCommaChar = ',';
31const char kDelimiterSemicolon[] = ";";
32const char kDelimiterSemicolonChar = ';';
33const char kDelimiterSpace[] = " ";
34const char kDelimiterSpaceChar = ' ';
35
36// https://tools.ietf.org/html/draft-ietf-mmusic-sdp-simulcast-13#section-5.1
37const char kSimulcastPausedStream[] = "~";
38const char kSimulcastPausedStreamChar = '~';
39const char kSimulcastSendStreams[] = "send";
40const char kSimulcastReceiveStreams[] = "recv";
41
42RTCError ParseError(const std::string& message) {
43 return RTCError(RTCErrorType::SYNTAX_ERROR, message);
44}
45
46// These methods serialize simulcast according to the specification:
47// https://tools.ietf.org/html/draft-ietf-mmusic-sdp-simulcast-13#section-5.1
48rtc::StringBuilder& operator<<(rtc::StringBuilder& builder,
49 const SimulcastLayer& simulcast_layer) {
50 if (simulcast_layer.is_paused) {
51 builder << kSimulcastPausedStream;
52 }
53 builder << simulcast_layer.rid;
54 return builder;
55}
56
57rtc::StringBuilder& operator<<(
58 rtc::StringBuilder& builder,
59 const std::vector<SimulcastLayer>& layer_alternatives) {
60 bool first = true;
61 for (const SimulcastLayer& rid : layer_alternatives) {
62 if (!first) {
63 builder << kDelimiterComma;
64 }
65 builder << rid;
66 first = false;
67 }
68 return builder;
69}
70
71rtc::StringBuilder& operator<<(rtc::StringBuilder& builder,
72 const SimulcastLayerList& simulcast_layers) {
73 bool first = true;
74 for (auto alternatives : simulcast_layers) {
75 if (!first) {
76 builder << kDelimiterSemicolon;
77 }
78 builder << alternatives;
79 first = false;
80 }
81 return builder;
82}
83
84// These methods deserialize simulcast according to the specification:
85// https://tools.ietf.org/html/draft-ietf-mmusic-sdp-simulcast-13#section-5.1
86// sc-str-list = sc-alt-list *( ";" sc-alt-list )
87// sc-alt-list = sc-id *( "," sc-id )
88// sc-id-paused = "~"
89// sc-id = [sc-id-paused] rid-id
90// rid-id = 1*(alpha-numeric / "-" / "_") ; see: I-D.ietf-mmusic-rid
91RTCErrorOr<SimulcastLayerList> ParseSimulcastLayerList(const std::string& str) {
92 std::vector<std::string> tokens;
93 rtc::tokenize_with_empty_tokens(str, kDelimiterSemicolonChar, &tokens);
94 if (tokens.empty()) {
95 return ParseError("Layer list cannot be empty.");
96 }
97
98 SimulcastLayerList result;
99 for (const std::string& token : tokens) {
100 if (token.empty()) {
101 return ParseError("Simulcast alternative layer list is empty.");
102 }
103
104 std::vector<std::string> rid_tokens;
105 rtc::tokenize_with_empty_tokens(token, kDelimiterCommaChar, &rid_tokens);
106 if (rid_tokens.empty()) {
107 return ParseError("Simulcast alternative layer list is malformed.");
108 }
109
110 std::vector<SimulcastLayer> layers;
111 for (const auto& rid_token : rid_tokens) {
112 if (rid_token.empty() || rid_token == kSimulcastPausedStream) {
113 return ParseError("Rid must not be empty.");
114 }
115
116 bool paused = rid_token[0] == kSimulcastPausedStreamChar;
117 std::string rid = paused ? rid_token.substr(1) : rid_token;
118
119 // TODO(amithi, bugs.webrtc.org/10073):
120 // Validate the rid format.
121 // See also: https://github.com/w3c/webrtc-pc/issues/2013
122 layers.push_back(SimulcastLayer(rid, paused));
123 }
124
125 result.AddLayerWithAlternatives(layers);
126 }
127
128 return std::move(result);
129}
130
131} // namespace
132
133std::string SdpSerializer::SerializeSimulcastDescription(
134 const cricket::SimulcastDescription& simulcast) const {
135 rtc::StringBuilder sb;
136 std::string delimiter;
137
138 if (!simulcast.send_layers().empty()) {
139 sb << kSimulcastSendStreams << kDelimiterSpace << simulcast.send_layers();
140 delimiter = kDelimiterSpace;
141 }
142
143 if (!simulcast.receive_layers().empty()) {
144 sb << delimiter << kSimulcastReceiveStreams << kDelimiterSpace
145 << simulcast.receive_layers();
146 }
147
148 return sb.str();
149}
150
151// https://tools.ietf.org/html/draft-ietf-mmusic-sdp-simulcast-13#section-5.1
152// a:simulcast:<send> <streams> <recv> <streams>
153// Formal Grammar
154// sc-value = ( sc-send [SP sc-recv] ) / ( sc-recv [SP sc-send] )
155// sc-send = %s"send" SP sc-str-list
156// sc-recv = %s"recv" SP sc-str-list
157// sc-str-list = sc-alt-list *( ";" sc-alt-list )
158// sc-alt-list = sc-id *( "," sc-id )
159// sc-id-paused = "~"
160// sc-id = [sc-id-paused] rid-id
161// rid-id = 1*(alpha-numeric / "-" / "_") ; see: I-D.ietf-mmusic-rid
162RTCErrorOr<SimulcastDescription> SdpSerializer::DeserializeSimulcastDescription(
163 absl::string_view string) const {
164 std::vector<std::string> tokens;
165 rtc::tokenize(std::string(string), kDelimiterSpaceChar, &tokens);
166
167 if (tokens.size() != 2 && tokens.size() != 4) {
168 return ParseError("Must have one or two <direction, streams> pairs.");
169 }
170
171 bool bidirectional = tokens.size() == 4; // indicates both send and recv
172
173 // Tokens 0, 2 (if exists) should be send / recv
174 if ((tokens[0] != kSimulcastSendStreams &&
175 tokens[0] != kSimulcastReceiveStreams) ||
176 (bidirectional && tokens[2] != kSimulcastSendStreams &&
177 tokens[2] != kSimulcastReceiveStreams) ||
178 (bidirectional && tokens[0] == tokens[2])) {
179 return ParseError("Valid values: send / recv.");
180 }
181
182 // Tokens 1, 3 (if exists) should be alternative layer lists
183 RTCErrorOr<SimulcastLayerList> list1, list2;
184 list1 = ParseSimulcastLayerList(tokens[1]);
185 if (!list1.ok()) {
186 return list1.MoveError();
187 }
188
189 if (bidirectional) {
190 list2 = ParseSimulcastLayerList(tokens[3]);
191 if (!list2.ok()) {
192 return list2.MoveError();
193 }
194 }
195
196 // Set the layers so that list1 is for send and list2 is for recv
197 if (tokens[0] != kSimulcastSendStreams) {
198 std::swap(list1, list2);
199 }
200
201 // Set the layers according to which pair is send and which is recv
202 // At this point if the simulcast is unidirectional then
203 // either |list1| or |list2| will be in 'error' state indicating that
204 // the value should not be used.
205 SimulcastDescription simulcast;
206 if (list1.ok()) {
207 simulcast.send_layers() = list1.MoveValue();
208 }
209
210 if (list2.ok()) {
211 simulcast.receive_layers() = list2.MoveValue();
212 }
213
214 return std::move(simulcast);
215}
216
217} // namespace webrtc