/* * 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 #include #include #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& 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 ParseSimulcastLayerList(const std::string& str) { std::vector 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 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 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: // 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 SdpSerializer::DeserializeSimulcastDescription( absl::string_view string) const { std::vector tokens; rtc::tokenize(std::string(string), kDelimiterSpaceChar, &tokens); if (tokens.size() != 2 && tokens.size() != 4) { return ParseError("Must have one or two 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 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