RID parsing for Simulcast support.
Adding RidDescription to StreamParams that will contain the list of rids
for the track.
Adding receive_stream to MediaContentDescription to allow identifying
the stream that originates from the answerer (but is referenced by the
sender). For example, to signal that it will be received in Simulcast.
Bug: webrtc:10073.
Change-Id: Icd9a6b0a69d42bef51f525e673ce447255584334
Reviewed-on: https://webrtc-review.googlesource.com/c/113794
Commit-Queue: Amit Hilbuch <amithi@webrtc.org>
Reviewed-by: Steve Anton <steveanton@webrtc.org>
Reviewed-by: Seth Hampson <shampson@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#25978}
diff --git a/pc/sdpserializer_unittest.cc b/pc/sdpserializer_unittest.cc
index 8b10da1..c5e95d0 100644
--- a/pc/sdpserializer_unittest.cc
+++ b/pc/sdpserializer_unittest.cc
@@ -8,27 +8,97 @@
* be found in the AUTHORS file in the root of the source tree.
*/
+#include <map>
#include <string>
+#include <utility>
#include <vector>
#include "pc/sdpserializer.h"
#include "rtc_base/gunit.h"
using ::testing::ValuesIn;
+using ::testing::TestWithParam;
+using cricket::RidDescription;
+using cricket::RidDirection;
using cricket::SimulcastDescription;
using cricket::SimulcastLayer;
using cricket::SimulcastLayerList;
namespace webrtc {
-class SdpSerializerTest : public ::testing::TestWithParam<const char*> {
+namespace {
+// Checks that two vectors have the same objects in the same order.
+template <typename TElement>
+void ExpectEqual(const std::vector<TElement>& expected,
+ const std::vector<TElement>& actual) {
+ ASSERT_EQ(expected.size(), actual.size());
+ for (size_t i = 0; i < expected.size(); i++) {
+ EXPECT_EQ(expected[i], actual[i]) << "Vectors differ at element " << i;
+ }
+}
+
+// Template specialization for vectors of SimulcastLayer objects.
+template <>
+void ExpectEqual(const std::vector<SimulcastLayer>& expected,
+ const std::vector<SimulcastLayer>& actual) {
+ EXPECT_EQ(expected.size(), actual.size());
+ for (size_t i = 0; i < expected.size(); i++) {
+ EXPECT_EQ(expected[i].rid, actual[i].rid);
+ EXPECT_EQ(expected[i].is_paused, actual[i].is_paused);
+ }
+}
+
+// Checks that two maps have the same key-value pairs.
+// Even though a map is technically ordered, the order semantics are not
+// tested because having the same key-set in both maps implies that they
+// are ordered the same because the template enforces that they have the
+// same Key-Comparer type.
+template <typename TKey, typename TValue>
+void ExpectEqual(const std::map<TKey, TValue>& expected,
+ const std::map<TKey, TValue>& actual) {
+ typedef typename std::map<TKey, TValue>::const_iterator const_iterator;
+ ASSERT_EQ(expected.size(), actual.size());
+ // Maps have unique keys, so if size is equal, it is enough to check
+ // that all the keys (and values) from one map exist in the other.
+ for (const std::pair<TKey, TValue>& pair : expected) {
+ const_iterator iter = actual.find(pair.first);
+ EXPECT_NE(iter, actual.end()) << "Key: " << pair.first << " not found";
+ EXPECT_EQ(pair.second, iter->second);
+ }
+}
+
+// Checks that the two SimulcastLayerLists are equal.
+void ExpectEqual(const SimulcastLayerList& expected,
+ const SimulcastLayerList& actual) {
+ EXPECT_EQ(expected.size(), actual.size());
+ for (size_t i = 0; i < expected.size(); i++) {
+ ExpectEqual(expected[i], actual[i]);
+ }
+}
+
+// Checks that the two SimulcastDescriptions are equal.
+void ExpectEqual(const SimulcastDescription& expected,
+ const SimulcastDescription& actual) {
+ ExpectEqual(expected.send_layers(), actual.send_layers());
+ ExpectEqual(expected.receive_layers(), actual.receive_layers());
+}
+
+// Checks that the two RidDescriptions are equal.
+void ExpectEqual(const RidDescription& expected, const RidDescription& actual) {
+ EXPECT_EQ(expected.rid, actual.rid);
+ EXPECT_EQ(expected.direction, actual.direction);
+ ExpectEqual(expected.payload_types, actual.payload_types);
+ ExpectEqual(expected.restrictions, actual.restrictions);
+}
+} // namespace
+
+class SimulcastSdpSerializerTest : public TestWithParam<const char*> {
public:
// Runs a test for deserializing Simulcast.
// |str| - The serialized Simulcast to parse.
// |expected| - The expected output Simulcast to compare to.
- void TestSimulcastDeserialization(
- const std::string& str,
- const SimulcastDescription& expected) const {
+ void TestDeserialization(const std::string& str,
+ const SimulcastDescription& expected) const {
SdpSerializer deserializer;
auto result = deserializer.DeserializeSimulcastDescription(str);
EXPECT_TRUE(result.ok());
@@ -38,55 +108,29 @@
// Runs a test for serializing Simulcast.
// |simulcast| - The Simulcast to serialize.
// |expected| - The expected output string to compare to.
- void TestSimulcastSerialization(const SimulcastDescription& simulcast,
- const std::string& expected) const {
+ void TestSerialization(const SimulcastDescription& simulcast,
+ const std::string& expected) const {
SdpSerializer serializer;
auto result = serializer.SerializeSimulcastDescription(simulcast);
EXPECT_EQ(expected, result);
}
-
- // Checks that the two vectors of SimulcastLayer objects are equal.
- void ExpectEqual(const std::vector<SimulcastLayer>& expected,
- const std::vector<SimulcastLayer>& actual) const {
- EXPECT_EQ(expected.size(), actual.size());
- for (size_t i = 0; i < expected.size(); i++) {
- EXPECT_EQ(expected[i].rid, actual[i].rid);
- EXPECT_EQ(expected[i].is_paused, actual[i].is_paused);
- }
- }
-
- // Checks that the two SimulcastLayerLists are equal.
- void ExpectEqual(const SimulcastLayerList& expected,
- const SimulcastLayerList& actual) const {
- EXPECT_EQ(expected.size(), actual.size());
- for (size_t i = 0; i < expected.size(); i++) {
- ExpectEqual(expected[i], actual[i]);
- }
- }
-
- // Checks that the two SimulcastDescriptions are equal.
- void ExpectEqual(const SimulcastDescription& expected,
- const SimulcastDescription& actual) const {
- ExpectEqual(expected.send_layers(), actual.send_layers());
- ExpectEqual(expected.receive_layers(), actual.receive_layers());
- }
};
// Test Cases
// Test simple deserialization with no alternative streams.
-TEST_F(SdpSerializerTest, DeserializeSimulcast_SimpleCaseNoAlternatives) {
+TEST_F(SimulcastSdpSerializerTest, Deserialize_SimpleCaseNoAlternatives) {
std::string simulcast_str = "send 1;2 recv 3;4";
SimulcastDescription expected;
expected.send_layers().AddLayer(SimulcastLayer("1", false));
expected.send_layers().AddLayer(SimulcastLayer("2", false));
expected.receive_layers().AddLayer(SimulcastLayer("3", false));
expected.receive_layers().AddLayer(SimulcastLayer("4", false));
- TestSimulcastDeserialization(simulcast_str, expected);
+ TestDeserialization(simulcast_str, expected);
}
// Test simulcast deserialization with alternative streams.
-TEST_F(SdpSerializerTest, DeserializeSimulcast_SimpleCaseWithAlternatives) {
+TEST_F(SimulcastSdpSerializerTest, Deserialize_SimpleCaseWithAlternatives) {
std::string simulcast_str = "send 1,5;2,6 recv 3,7;4,8";
SimulcastDescription expected;
expected.send_layers().AddLayerWithAlternatives(
@@ -97,11 +141,11 @@
{SimulcastLayer("3", false), SimulcastLayer("7", false)});
expected.receive_layers().AddLayerWithAlternatives(
{SimulcastLayer("4", false), SimulcastLayer("8", false)});
- TestSimulcastDeserialization(simulcast_str, expected);
+ TestDeserialization(simulcast_str, expected);
}
// Test simulcast deserialization when only some streams have alternatives.
-TEST_F(SdpSerializerTest, DeserializeSimulcast_WithSomeAlternatives) {
+TEST_F(SimulcastSdpSerializerTest, Deserialize_WithSomeAlternatives) {
std::string simulcast_str = "send 1;2,6 recv 3,7;4";
SimulcastDescription expected;
expected.send_layers().AddLayer(SimulcastLayer("1", false));
@@ -110,11 +154,11 @@
expected.receive_layers().AddLayerWithAlternatives(
{SimulcastLayer("3", false), SimulcastLayer("7", false)});
expected.receive_layers().AddLayer(SimulcastLayer("4", false));
- TestSimulcastDeserialization(simulcast_str, expected);
+ TestDeserialization(simulcast_str, expected);
}
// Test simulcast deserialization when only send streams are specified.
-TEST_F(SdpSerializerTest, DeserializeSimulcast_OnlySendStreams) {
+TEST_F(SimulcastSdpSerializerTest, Deserialize_OnlySendStreams) {
std::string simulcast_str = "send 1;2,6;3,7;4";
SimulcastDescription expected;
expected.send_layers().AddLayer(SimulcastLayer("1", false));
@@ -123,11 +167,11 @@
expected.send_layers().AddLayerWithAlternatives(
{SimulcastLayer("3", false), SimulcastLayer("7", false)});
expected.send_layers().AddLayer(SimulcastLayer("4", false));
- TestSimulcastDeserialization(simulcast_str, expected);
+ TestDeserialization(simulcast_str, expected);
}
// Test simulcast deserialization when only receive streams are specified.
-TEST_F(SdpSerializerTest, DeserializeSimulcast_OnlyReceiveStreams) {
+TEST_F(SimulcastSdpSerializerTest, Deserialize_OnlyReceiveStreams) {
std::string simulcast_str = "recv 1;2,6;3,7;4";
SimulcastDescription expected;
expected.receive_layers().AddLayer(SimulcastLayer("1", false));
@@ -136,11 +180,11 @@
expected.receive_layers().AddLayerWithAlternatives(
{SimulcastLayer("3", false), SimulcastLayer("7", false)});
expected.receive_layers().AddLayer(SimulcastLayer("4", false));
- TestSimulcastDeserialization(simulcast_str, expected);
+ TestDeserialization(simulcast_str, expected);
}
// Test simulcast deserialization with receive streams before send streams.
-TEST_F(SdpSerializerTest, DeserializeSimulcast_SendReceiveReversed) {
+TEST_F(SimulcastSdpSerializerTest, Deserialize_SendReceiveReversed) {
std::string simulcast_str = "recv 1;2,6 send 3,7;4";
SimulcastDescription expected;
expected.receive_layers().AddLayer(SimulcastLayer("1", false));
@@ -149,11 +193,11 @@
expected.send_layers().AddLayerWithAlternatives(
{SimulcastLayer("3", false), SimulcastLayer("7", false)});
expected.send_layers().AddLayer(SimulcastLayer("4", false));
- TestSimulcastDeserialization(simulcast_str, expected);
+ TestDeserialization(simulcast_str, expected);
}
// Test simulcast deserialization with some streams set to paused state.
-TEST_F(SdpSerializerTest, DeserializeSimulcast_PausedStreams) {
+TEST_F(SimulcastSdpSerializerTest, Deserialize_PausedStreams) {
std::string simulcast_str = "recv 1;~2,6 send 3,7;~4";
SimulcastDescription expected;
expected.receive_layers().AddLayer(SimulcastLayer("1", false));
@@ -162,11 +206,11 @@
expected.send_layers().AddLayerWithAlternatives(
{SimulcastLayer("3", false), SimulcastLayer("7", false)});
expected.send_layers().AddLayer(SimulcastLayer("4", true));
- TestSimulcastDeserialization(simulcast_str, expected);
+ TestDeserialization(simulcast_str, expected);
}
// Parameterized negative test case for deserialization with invalid inputs.
-TEST_P(SdpSerializerTest, SimulcastDeserializationFailed) {
+TEST_P(SimulcastSdpSerializerTest, SimulcastDeserializationFailed) {
SdpSerializer deserializer;
auto result = deserializer.DeserializeSimulcastDescription(GetParam());
EXPECT_FALSE(result.ok());
@@ -188,35 +232,35 @@
};
INSTANTIATE_TEST_CASE_P(SimulcastDeserializationErrors,
- SdpSerializerTest,
+ SimulcastSdpSerializerTest,
ValuesIn(kSimulcastMalformedStrings));
// Test a simple serialization scenario.
-TEST_F(SdpSerializerTest, SerializeSimulcast_SimpleCase) {
+TEST_F(SimulcastSdpSerializerTest, Serialize_SimpleCase) {
SimulcastDescription simulcast;
simulcast.send_layers().AddLayer(SimulcastLayer("1", false));
simulcast.receive_layers().AddLayer(SimulcastLayer("2", false));
- TestSimulcastSerialization(simulcast, "send 1 recv 2");
+ TestSerialization(simulcast, "send 1 recv 2");
}
// Test serialization with only send streams.
-TEST_F(SdpSerializerTest, SerializeSimulcast_OnlySend) {
+TEST_F(SimulcastSdpSerializerTest, Serialize_OnlySend) {
SimulcastDescription simulcast;
simulcast.send_layers().AddLayer(SimulcastLayer("1", false));
simulcast.send_layers().AddLayer(SimulcastLayer("2", false));
- TestSimulcastSerialization(simulcast, "send 1;2");
+ TestSerialization(simulcast, "send 1;2");
}
// Test serialization with only receive streams
-TEST_F(SdpSerializerTest, SerializeSimulcast_OnlyReceive) {
+TEST_F(SimulcastSdpSerializerTest, Serialize_OnlyReceive) {
SimulcastDescription simulcast;
simulcast.receive_layers().AddLayer(SimulcastLayer("1", false));
simulcast.receive_layers().AddLayer(SimulcastLayer("2", false));
- TestSimulcastSerialization(simulcast, "recv 1;2");
+ TestSerialization(simulcast, "recv 1;2");
}
// Test a complex serialization with multiple streams, alternatives and states.
-TEST_F(SdpSerializerTest, SerializeSimulcast_ComplexSerialization) {
+TEST_F(SimulcastSdpSerializerTest, Serialize_ComplexSerialization) {
SimulcastDescription simulcast;
simulcast.send_layers().AddLayerWithAlternatives(
{SimulcastLayer("2", false), SimulcastLayer("1", true)});
@@ -229,7 +273,206 @@
simulcast.receive_layers().AddLayerWithAlternatives(
{SimulcastLayer("9", false), SimulcastLayer("10", true),
SimulcastLayer("11", false)});
- TestSimulcastSerialization(simulcast, "send 2,~1;4,3 recv 6,7;~8;9,~10,11");
+ TestSerialization(simulcast, "send 2,~1;4,3 recv 6,7;~8;9,~10,11");
}
+class RidDescriptionSdpSerializerTest : public TestWithParam<const char*> {
+ public:
+ // Runs a test for deserializing Rid Descriptions.
+ // |str| - The serialized Rid Description to parse.
+ // |expected| - The expected output RidDescription to compare to.
+ void TestDeserialization(const std::string& str,
+ const RidDescription& expected) const {
+ SdpSerializer deserializer;
+ auto result = deserializer.DeserializeRidDescription(str);
+ EXPECT_TRUE(result.ok());
+ ExpectEqual(expected, result.value());
+ }
+
+ // Runs a test for serializing RidDescriptions.
+ // |rid_description| - The RidDescription to serialize.
+ // |expected| - The expected output string to compare to.
+ void TestSerialization(const RidDescription& rid_description,
+ const std::string& expected) const {
+ SdpSerializer serializer;
+ auto result = serializer.SerializeRidDescription(rid_description);
+ EXPECT_EQ(expected, result);
+ }
+};
+
+// Test serialization for RidDescription that only specifies send.
+TEST_F(RidDescriptionSdpSerializerTest, Serialize_OnlyDirectionSend) {
+ RidDescription rid_description("1", RidDirection::kSend);
+ TestSerialization(rid_description, "1 send");
+}
+
+// Test serialization for RidDescription that only specifies receive.
+TEST_F(RidDescriptionSdpSerializerTest, Serialize_OnlyDirectionReceive) {
+ RidDescription rid_description("2", RidDirection::kReceive);
+ TestSerialization(rid_description, "2 recv");
+}
+
+// Test serialization for RidDescription with format list.
+TEST_F(RidDescriptionSdpSerializerTest, Serialize_FormatList) {
+ RidDescription rid_description("3", RidDirection::kSend);
+ rid_description.payload_types = {102, 101};
+ TestSerialization(rid_description, "3 send pt=102,101");
+}
+
+// Test serialization for RidDescription with format list.
+TEST_F(RidDescriptionSdpSerializerTest, Serialize_FormatListSingleFormat) {
+ RidDescription rid_description("4", RidDirection::kReceive);
+ rid_description.payload_types = {100};
+ TestSerialization(rid_description, "4 recv pt=100");
+}
+
+// Test serialization for RidDescription with restriction list.
+// Note: restriction list will be sorted because it is stored in a map.
+TEST_F(RidDescriptionSdpSerializerTest, Serialize_AttributeList) {
+ RidDescription rid_description("5", RidDirection::kSend);
+ rid_description.restrictions["max-width"] = "1280";
+ rid_description.restrictions["max-height"] = "720";
+ TestSerialization(rid_description, "5 send max-height=720;max-width=1280");
+}
+
+// Test serialization for RidDescription with format list and attribute list.
+// Note: restriction list will be sorted because it is stored in a map.
+TEST_F(RidDescriptionSdpSerializerTest, Serialize_FormatAndAttributeList) {
+ RidDescription rid_description("6", RidDirection::kSend);
+ rid_description.payload_types = {103, 104};
+ rid_description.restrictions["max-mbps"] = "108000";
+ rid_description.restrictions["max-br"] = "64000";
+ TestSerialization(rid_description,
+ "6 send pt=103,104;max-br=64000;max-mbps=108000");
+}
+
+// Test serialization for attribute list that has key with no value.
+// Note: restriction list will be sorted because it is stored in a map.
+TEST_F(RidDescriptionSdpSerializerTest, Serialize_RestrictionWithoutValue) {
+ RidDescription rid_description("7", RidDirection::kReceive);
+ rid_description.payload_types = {103};
+ rid_description.restrictions["max-width"] = "1280";
+ rid_description.restrictions["max-height"] = "720";
+ rid_description.restrictions["max-myval"] = "";
+ TestSerialization(rid_description,
+ "7 recv pt=103;max-height=720;max-myval;max-width=1280");
+}
+
+// Test simulcast deserialization with simple send stream.
+TEST_F(RidDescriptionSdpSerializerTest, Deserialize_SimpleSendCase) {
+ RidDescription rid_description("1", RidDirection::kSend);
+ TestDeserialization("1 send", rid_description);
+}
+
+// Test simulcast deserialization with simple receive stream.
+TEST_F(RidDescriptionSdpSerializerTest, Deserialize_SimpleReceiveCase) {
+ RidDescription rid_description("2", RidDirection::kReceive);
+ TestDeserialization("2 recv", rid_description);
+}
+
+// Test simulcast deserialization with single format.
+TEST_F(RidDescriptionSdpSerializerTest, Deserialize_WithFormat) {
+ RidDescription rid_description("3", RidDirection::kSend);
+ rid_description.payload_types = {101};
+ TestDeserialization("3 send pt=101", rid_description);
+}
+
+// Test simulcast deserialization with multiple formats.
+TEST_F(RidDescriptionSdpSerializerTest, Deserialize_WithMultipleFormats) {
+ RidDescription rid_description("4", RidDirection::kSend);
+ rid_description.payload_types = {103, 104, 101, 102};
+ TestDeserialization("4 send pt=103,104,101,102", rid_description);
+}
+
+// Test simulcast deserialization with restriction.
+TEST_F(RidDescriptionSdpSerializerTest, Deserialize_WithRestriction) {
+ RidDescription rid_description("5", RidDirection::kReceive);
+ rid_description.restrictions["max-height"] = "720";
+ TestDeserialization("5 recv max-height=720", rid_description);
+}
+
+// Test simulcast deserialization with multiple restrictions.
+TEST_F(RidDescriptionSdpSerializerTest, Deserialize_WithMultipleRestrictions) {
+ RidDescription rid_description("6", RidDirection::kReceive);
+ rid_description.restrictions["max-height"] = "720";
+ rid_description.restrictions["max-width"] = "1920";
+ rid_description.restrictions["max-fr"] = "60";
+ rid_description.restrictions["max-bps"] = "14000";
+ TestDeserialization(
+ "6 recv max-height=720;max-width=1920;max-bps=14000;max-fr=60",
+ rid_description);
+}
+
+// Test simulcast deserialization with custom (non-standard) restriction.
+TEST_F(RidDescriptionSdpSerializerTest, Deserialize_WithCustomRestrictions) {
+ RidDescription rid_description("7", RidDirection::kSend);
+ rid_description.restrictions["foo"] = "bar";
+ rid_description.restrictions["max-height"] = "720";
+ TestDeserialization("7 send max-height=720;foo=bar", rid_description);
+}
+
+// Test simulcast deserialization with multiple formats and restrictions.
+TEST_F(RidDescriptionSdpSerializerTest, Deserialize_WithFormatAndRestrictions) {
+ RidDescription rid_description("8", RidDirection::kSend);
+ rid_description.payload_types = {104, 103};
+ rid_description.restrictions["max-height"] = "720";
+ rid_description.restrictions["max-width"] = "1920";
+ TestDeserialization("8 send pt=104,103;max-height=720;max-width=1920",
+ rid_description);
+}
+
+// Test simulcast deserialization with restriction that has no value.
+TEST_F(RidDescriptionSdpSerializerTest, Deserialize_RestrictionHasNoValue) {
+ RidDescription rid_description("9", RidDirection::kReceive);
+ rid_description.payload_types = {104};
+ rid_description.restrictions["max-height"];
+ rid_description.restrictions["max-width"] = "1920";
+ TestDeserialization("9 recv pt=104;max-height;max-width=1920",
+ rid_description);
+}
+
+// Add this test to explicitly indicate that this is not an error.
+// The following string "1 send recv" looks malformed because it specifies
+// two directions, but in fact, the recv can be interpreted as a parameter
+// without a value. While such a use case is dubious, the input string is
+// not malformed.
+TEST_F(RidDescriptionSdpSerializerTest, Deserialize_AmbiguousCase) {
+ RidDescription rid_description("1", RidDirection::kSend);
+ rid_description.restrictions["recv"]; // No value.
+ TestDeserialization("1 send recv", rid_description);
+}
+
+// Parameterized negative test case for deserialization with invalid inputs.
+TEST_P(RidDescriptionSdpSerializerTest, RidDescriptionDeserializationFailed) {
+ SdpSerializer deserializer;
+ auto result = deserializer.DeserializeRidDescription(GetParam());
+ EXPECT_FALSE(result.ok());
+}
+
+// The malformed Rid Description inputs to use in the negative test case.
+const char* kRidDescriptionMalformedStrings[] = {
+ "1",
+ "recv",
+ "send",
+ "recv 1",
+ "send 1",
+ "1 receive",
+ "one direction",
+ "1 send pt=1 max-width=720", // The ' ' should be ';' in restriction list.
+ "1 recv ;",
+ "1 recv =",
+ "1 recv a=b=c",
+ "1 send max-width=720;pt=101", // pt= should appear first.
+ "1 send pt=101;pt=102",
+ "1 send pt=101,101",
+ "1 recv max-width=720;max-width=720",
+ "1 send pt=",
+ "1 send pt=abc",
+ "1 recv ;;",
+};
+
+INSTANTIATE_TEST_CASE_P(RidDescriptionDeserializationErrors,
+ RidDescriptionSdpSerializerTest,
+ ValuesIn(kRidDescriptionMalformedStrings));
+
} // namespace webrtc