Adding SDP parsing for Simulcast.

Parsing simulcast according to:
https://tools.ietf.org/html/draft-ietf-mmusic-sdp-simulcast-13#section-5.1
Created SdpSerializer for making serialized components more testable.
Simulcast functionality is still not accessible to users.

Bug: webrtc:10055
Change-Id: Ia6e4cef756cb954521dd19e22911f8eb6498880e
Reviewed-on: https://webrtc-review.googlesource.com/c/112160
Commit-Queue: Amit Hilbuch <amithi@webrtc.org>
Reviewed-by: Seth Hampson <shampson@webrtc.org>
Reviewed-by: Steve Anton <steveanton@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#25883}
diff --git a/pc/sdpserializer.cc b/pc/sdpserializer.cc
new file mode 100644
index 0000000..b5a1d87
--- /dev/null
+++ b/pc/sdpserializer.cc
@@ -0,0 +1,217 @@
+/*
+ *  Copyright 2018 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "pc/sdpserializer.h"
+
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "api/jsep.h"
+#include "rtc_base/strings/string_builder.h"
+
+using cricket::SimulcastDescription;
+using cricket::SimulcastLayer;
+using cricket::SimulcastLayerList;
+
+namespace webrtc {
+
+namespace {
+
+// delimiters
+const char kDelimiterComma[] = ",";
+const char kDelimiterCommaChar = ',';
+const char kDelimiterSemicolon[] = ";";
+const char kDelimiterSemicolonChar = ';';
+const char kDelimiterSpace[] = " ";
+const char kDelimiterSpaceChar = ' ';
+
+// https://tools.ietf.org/html/draft-ietf-mmusic-sdp-simulcast-13#section-5.1
+const char kSimulcastPausedStream[] = "~";
+const char kSimulcastPausedStreamChar = '~';
+const char kSimulcastSendStreams[] = "send";
+const char kSimulcastReceiveStreams[] = "recv";
+
+RTCError ParseError(const std::string& message) {
+  return RTCError(RTCErrorType::SYNTAX_ERROR, message);
+}
+
+// These methods serialize simulcast according to the specification:
+// https://tools.ietf.org/html/draft-ietf-mmusic-sdp-simulcast-13#section-5.1
+rtc::StringBuilder& operator<<(rtc::StringBuilder& builder,
+                               const SimulcastLayer& simulcast_layer) {
+  if (simulcast_layer.is_paused) {
+    builder << kSimulcastPausedStream;
+  }
+  builder << simulcast_layer.rid;
+  return builder;
+}
+
+rtc::StringBuilder& operator<<(
+    rtc::StringBuilder& builder,
+    const std::vector<SimulcastLayer>& layer_alternatives) {
+  bool first = true;
+  for (const SimulcastLayer& rid : layer_alternatives) {
+    if (!first) {
+      builder << kDelimiterComma;
+    }
+    builder << rid;
+    first = false;
+  }
+  return builder;
+}
+
+rtc::StringBuilder& operator<<(rtc::StringBuilder& builder,
+                               const SimulcastLayerList& simulcast_layers) {
+  bool first = true;
+  for (auto alternatives : simulcast_layers) {
+    if (!first) {
+      builder << kDelimiterSemicolon;
+    }
+    builder << alternatives;
+    first = false;
+  }
+  return builder;
+}
+
+// These methods deserialize simulcast according to the specification:
+// https://tools.ietf.org/html/draft-ietf-mmusic-sdp-simulcast-13#section-5.1
+// sc-str-list  = sc-alt-list *( ";" sc-alt-list )
+// sc-alt-list  = sc-id *( "," sc-id )
+// sc-id-paused = "~"
+// sc-id        = [sc-id-paused] rid-id
+// rid-id       = 1*(alpha-numeric / "-" / "_") ; see: I-D.ietf-mmusic-rid
+RTCErrorOr<SimulcastLayerList> ParseSimulcastLayerList(const std::string& str) {
+  std::vector<std::string> tokens;
+  rtc::tokenize_with_empty_tokens(str, kDelimiterSemicolonChar, &tokens);
+  if (tokens.empty()) {
+    return ParseError("Layer list cannot be empty.");
+  }
+
+  SimulcastLayerList result;
+  for (const std::string& token : tokens) {
+    if (token.empty()) {
+      return ParseError("Simulcast alternative layer list is empty.");
+    }
+
+    std::vector<std::string> rid_tokens;
+    rtc::tokenize_with_empty_tokens(token, kDelimiterCommaChar, &rid_tokens);
+    if (rid_tokens.empty()) {
+      return ParseError("Simulcast alternative layer list is malformed.");
+    }
+
+    std::vector<SimulcastLayer> layers;
+    for (const auto& rid_token : rid_tokens) {
+      if (rid_token.empty() || rid_token == kSimulcastPausedStream) {
+        return ParseError("Rid must not be empty.");
+      }
+
+      bool paused = rid_token[0] == kSimulcastPausedStreamChar;
+      std::string rid = paused ? rid_token.substr(1) : rid_token;
+
+      // TODO(amithi, bugs.webrtc.org/10073):
+      // Validate the rid format.
+      // See also: https://github.com/w3c/webrtc-pc/issues/2013
+      layers.push_back(SimulcastLayer(rid, paused));
+    }
+
+    result.AddLayerWithAlternatives(layers);
+  }
+
+  return std::move(result);
+}
+
+}  // namespace
+
+std::string SdpSerializer::SerializeSimulcastDescription(
+    const cricket::SimulcastDescription& simulcast) const {
+  rtc::StringBuilder sb;
+  std::string delimiter;
+
+  if (!simulcast.send_layers().empty()) {
+    sb << kSimulcastSendStreams << kDelimiterSpace << simulcast.send_layers();
+    delimiter = kDelimiterSpace;
+  }
+
+  if (!simulcast.receive_layers().empty()) {
+    sb << delimiter << kSimulcastReceiveStreams << kDelimiterSpace
+       << simulcast.receive_layers();
+  }
+
+  return sb.str();
+}
+
+// https://tools.ietf.org/html/draft-ietf-mmusic-sdp-simulcast-13#section-5.1
+// a:simulcast:<send> <streams> <recv> <streams>
+// Formal Grammar
+// sc-value     = ( sc-send [SP sc-recv] ) / ( sc-recv [SP sc-send] )
+// sc-send      = %s"send" SP sc-str-list
+// sc-recv      = %s"recv" SP sc-str-list
+// sc-str-list  = sc-alt-list *( ";" sc-alt-list )
+// sc-alt-list  = sc-id *( "," sc-id )
+// sc-id-paused = "~"
+// sc-id        = [sc-id-paused] rid-id
+// rid-id       = 1*(alpha-numeric / "-" / "_") ; see: I-D.ietf-mmusic-rid
+RTCErrorOr<SimulcastDescription> SdpSerializer::DeserializeSimulcastDescription(
+    absl::string_view string) const {
+  std::vector<std::string> tokens;
+  rtc::tokenize(std::string(string), kDelimiterSpaceChar, &tokens);
+
+  if (tokens.size() != 2 && tokens.size() != 4) {
+    return ParseError("Must have one or two <direction, streams> pairs.");
+  }
+
+  bool bidirectional = tokens.size() == 4;  // indicates both send and recv
+
+  // Tokens 0, 2 (if exists) should be send / recv
+  if ((tokens[0] != kSimulcastSendStreams &&
+       tokens[0] != kSimulcastReceiveStreams) ||
+      (bidirectional && tokens[2] != kSimulcastSendStreams &&
+       tokens[2] != kSimulcastReceiveStreams) ||
+      (bidirectional && tokens[0] == tokens[2])) {
+    return ParseError("Valid values: send / recv.");
+  }
+
+  // Tokens 1, 3 (if exists) should be alternative layer lists
+  RTCErrorOr<SimulcastLayerList> list1, list2;
+  list1 = ParseSimulcastLayerList(tokens[1]);
+  if (!list1.ok()) {
+    return list1.MoveError();
+  }
+
+  if (bidirectional) {
+    list2 = ParseSimulcastLayerList(tokens[3]);
+    if (!list2.ok()) {
+      return list2.MoveError();
+    }
+  }
+
+  // Set the layers so that list1 is for send and list2 is for recv
+  if (tokens[0] != kSimulcastSendStreams) {
+    std::swap(list1, list2);
+  }
+
+  // Set the layers according to which pair is send and which is recv
+  // At this point if the simulcast is unidirectional then
+  // either |list1| or |list2| will be in 'error' state indicating that
+  // the value should not be used.
+  SimulcastDescription simulcast;
+  if (list1.ok()) {
+    simulcast.send_layers() = list1.MoveValue();
+  }
+
+  if (list2.ok()) {
+    simulcast.receive_layers() = list2.MoveValue();
+  }
+
+  return std::move(simulcast);
+}
+
+}  // namespace webrtc