summaryrefslogtreecommitdiffstats
path: root/dom/media/webrtc/sdp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/media/webrtc/sdp')
-rw-r--r--dom/media/webrtc/sdp/HybridSdpParser.cpp88
-rw-r--r--dom/media/webrtc/sdp/HybridSdpParser.h38
-rw-r--r--dom/media/webrtc/sdp/ParsingResultComparer.cpp331
-rw-r--r--dom/media/webrtc/sdp/ParsingResultComparer.h57
-rw-r--r--dom/media/webrtc/sdp/RsdparsaSdp.cpp126
-rw-r--r--dom/media/webrtc/sdp/RsdparsaSdp.h72
-rw-r--r--dom/media/webrtc/sdp/RsdparsaSdpAttributeList.cpp1301
-rw-r--r--dom/media/webrtc/sdp/RsdparsaSdpAttributeList.h157
-rw-r--r--dom/media/webrtc/sdp/RsdparsaSdpGlue.cpp106
-rw-r--r--dom/media/webrtc/sdp/RsdparsaSdpGlue.h36
-rw-r--r--dom/media/webrtc/sdp/RsdparsaSdpInc.h510
-rw-r--r--dom/media/webrtc/sdp/RsdparsaSdpMediaSection.cpp253
-rw-r--r--dom/media/webrtc/sdp/RsdparsaSdpMediaSection.h71
-rw-r--r--dom/media/webrtc/sdp/RsdparsaSdpParser.cpp73
-rw-r--r--dom/media/webrtc/sdp/RsdparsaSdpParser.h34
-rw-r--r--dom/media/webrtc/sdp/Sdp.h166
-rw-r--r--dom/media/webrtc/sdp/SdpAttribute.cpp1562
-rw-r--r--dom/media/webrtc/sdp/SdpAttribute.h1907
-rw-r--r--dom/media/webrtc/sdp/SdpAttributeList.h90
-rw-r--r--dom/media/webrtc/sdp/SdpEnum.h64
-rw-r--r--dom/media/webrtc/sdp/SdpHelper.cpp801
-rw-r--r--dom/media/webrtc/sdp/SdpHelper.h109
-rw-r--r--dom/media/webrtc/sdp/SdpLog.cpp68
-rw-r--r--dom/media/webrtc/sdp/SdpLog.h17
-rw-r--r--dom/media/webrtc/sdp/SdpMediaSection.cpp197
-rw-r--r--dom/media/webrtc/sdp/SdpMediaSection.h317
-rw-r--r--dom/media/webrtc/sdp/SdpParser.h81
-rw-r--r--dom/media/webrtc/sdp/SdpPref.cpp107
-rw-r--r--dom/media/webrtc/sdp/SdpPref.h82
-rw-r--r--dom/media/webrtc/sdp/SdpTelemetry.cpp63
-rw-r--r--dom/media/webrtc/sdp/SdpTelemetry.h43
-rw-r--r--dom/media/webrtc/sdp/SipccSdp.cpp173
-rw-r--r--dom/media/webrtc/sdp/SipccSdp.h82
-rw-r--r--dom/media/webrtc/sdp/SipccSdpAttributeList.cpp1386
-rw-r--r--dom/media/webrtc/sdp/SipccSdpAttributeList.h145
-rw-r--r--dom/media/webrtc/sdp/SipccSdpMediaSection.cpp401
-rw-r--r--dom/media/webrtc/sdp/SipccSdpMediaSection.h101
-rw-r--r--dom/media/webrtc/sdp/SipccSdpParser.cpp88
-rw-r--r--dom/media/webrtc/sdp/SipccSdpParser.h35
-rw-r--r--dom/media/webrtc/sdp/moz.build48
-rw-r--r--dom/media/webrtc/sdp/rsdparsa_capi/Cargo.toml12
-rw-r--r--dom/media/webrtc/sdp/rsdparsa_capi/src/attribute.rs1472
-rw-r--r--dom/media/webrtc/sdp/rsdparsa_capi/src/lib.rs298
-rw-r--r--dom/media/webrtc/sdp/rsdparsa_capi/src/media_section.rs233
-rw-r--r--dom/media/webrtc/sdp/rsdparsa_capi/src/network.rs266
-rw-r--r--dom/media/webrtc/sdp/rsdparsa_capi/src/types.rs199
46 files changed, 13866 insertions, 0 deletions
diff --git a/dom/media/webrtc/sdp/HybridSdpParser.cpp b/dom/media/webrtc/sdp/HybridSdpParser.cpp
new file mode 100644
index 0000000000..d1f2d5c058
--- /dev/null
+++ b/dom/media/webrtc/sdp/HybridSdpParser.cpp
@@ -0,0 +1,88 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+#include "sdp/HybridSdpParser.h"
+#include "sdp/SdpLog.h"
+#include "sdp/SdpPref.h"
+#include "sdp/SdpTelemetry.h"
+#include "sdp/SipccSdpParser.h"
+#include "sdp/RsdparsaSdpParser.h"
+#include "sdp/ParsingResultComparer.h"
+
+#include "mozilla/Logging.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/Telemetry.h"
+
+#include <unordered_map>
+
+namespace mozilla {
+
+using mozilla::LogLevel;
+
+const std::string& HybridSdpParser::ParserName() {
+ const static std::string PARSER_NAME = "hybrid";
+ return PARSER_NAME;
+}
+
+HybridSdpParser::HybridSdpParser()
+ : mStrictSuccess(SdpPref::StrictSuccess()),
+ mPrimary(SdpPref::Primary()),
+ mSecondary(SdpPref::Secondary()),
+ mFailover(SdpPref::Failover()) {
+ MOZ_ASSERT(!(mSecondary && mFailover),
+ "Can not have both a secondary and failover parser!");
+ MOZ_LOG(SdpLog, LogLevel::Info,
+ ("Primary SDP Parser: %s", mPrimary->Name().c_str()));
+ mSecondary.apply([](auto& parser) {
+ MOZ_LOG(SdpLog, LogLevel::Info,
+ ("Secondary SDP Logger: %s", parser->Name().c_str()));
+ });
+ mFailover.apply([](auto& parser) {
+ MOZ_LOG(SdpLog, LogLevel::Info,
+ ("Failover SDP Logger: %s", parser->Name().c_str()));
+ });
+}
+
+auto HybridSdpParser::Parse(const std::string& aText)
+ -> UniquePtr<SdpParser::Results> {
+ using Results = UniquePtr<SdpParser::Results>;
+ using Role = SdpTelemetry::Roles;
+ using Mode = SdpPref::AlternateParseModes;
+
+ Mode mode = Mode::Never;
+ auto results = mPrimary->Parse(aText);
+
+ auto successful = [&](Results& aRes) -> bool {
+ // In strict mode any reported error counts as failure
+ if (mStrictSuccess) {
+ return aRes->Ok();
+ }
+ return aRes->Sdp() != nullptr;
+ };
+ // Pass results on for comparison and return A if it was a success and B
+ // otherwise.
+ auto compare = [&](Results&& aResB) -> Results {
+ SdpTelemetry::RecordParse(aResB, mode, Role::Secondary);
+ ParsingResultComparer::Compare(results, aResB, aText, mode);
+ return std::move(successful(results) ? results : aResB);
+ };
+ // Run secondary parser, if there is one, and update selected results.
+ mSecondary.apply([&](auto& sec) {
+ mode = Mode::Parallel;
+ results = compare(std::move(sec->Parse(aText)));
+ });
+ // Run failover parser, if there is one, and update selected results.
+ mFailover.apply([&](auto& failover) { // Only run if primary parser failed
+ mode = Mode::Failover;
+ if (!successful(results)) {
+ results = compare(std::move(failover->Parse(aText)));
+ }
+ });
+
+ SdpTelemetry::RecordParse(results, mode, Role::Primary);
+ return results;
+}
+
+} // namespace mozilla
diff --git a/dom/media/webrtc/sdp/HybridSdpParser.h b/dom/media/webrtc/sdp/HybridSdpParser.h
new file mode 100644
index 0000000000..2b0ef399a5
--- /dev/null
+++ b/dom/media/webrtc/sdp/HybridSdpParser.h
@@ -0,0 +1,38 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _HYBRIDSDPPARSER_H_
+#define _HYBRIDSDPPARSER_H_
+
+#include "sdp/SdpParser.h"
+#include "sdp/SdpTelemetry.h"
+
+namespace mozilla {
+
+// This shim parser delegates parsing to WEbRTC-SDP and SIPCC, based on
+// preference flags. Additionally it handles collecting telemetry and fallback
+// behavior between the parsers.
+class HybridSdpParser : public SdpParser {
+ static const std::string& ParserName();
+
+ public:
+ HybridSdpParser();
+ virtual ~HybridSdpParser() = default;
+
+ auto Name() const -> const std::string& override { return ParserName(); }
+ auto Parse(const std::string& aText)
+ -> UniquePtr<SdpParser::Results> override;
+
+ private:
+ const bool mStrictSuccess;
+ const UniquePtr<SdpParser> mPrimary;
+ const Maybe<UniquePtr<SdpParser>> mSecondary;
+ const Maybe<UniquePtr<SdpParser>> mFailover;
+};
+
+} // namespace mozilla
+
+#endif
diff --git a/dom/media/webrtc/sdp/ParsingResultComparer.cpp b/dom/media/webrtc/sdp/ParsingResultComparer.cpp
new file mode 100644
index 0000000000..8592fa52b3
--- /dev/null
+++ b/dom/media/webrtc/sdp/ParsingResultComparer.cpp
@@ -0,0 +1,331 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "sdp/Sdp.h"
+#include "sdp/ParsingResultComparer.h"
+#include "sdp/SipccSdpParser.h"
+#include "sdp/RsdparsaSdpParser.h"
+#include "sdp/SdpTelemetry.h"
+
+#include <string>
+#include <ostream>
+#include <regex>
+
+#include "mozilla/Assertions.h"
+#include "mozilla/Telemetry.h"
+#include "mozilla/Logging.h"
+
+using mozilla::LogLevel;
+static mozilla::LazyLogModule sSdpDiffLogger("sdpdiff_logger");
+
+#define LOGD(msg) MOZ_LOG(sSdpDiffLogger, LogLevel::Debug, msg)
+#define LOGE(msg) MOZ_LOG(sSdpDiffLogger, LogLevel::Error, msg)
+
+#define LOG_EXPECT(result, expect, msg) \
+ { \
+ if (((expect) == SdpComparisonResult::Equal) == (result)) { \
+ LOGD(msg); \
+ } else { \
+ LOGE(("UNEXPECTED COMPARISON RESULT: vvvvvv")); \
+ LOGE(msg); \
+ } \
+ }
+
+namespace mozilla {
+
+using AttributeType = SdpAttribute::AttributeType;
+
+template <typename T>
+std::string ToString(const T& serializable) {
+ std::ostringstream os;
+
+ os << serializable;
+ return os.str();
+}
+bool ParsingResultComparer::Compare(const Results& aResA, const Results& aResB,
+ const std::string& aOriginalSdp,
+ const SdpPref::AlternateParseModes& aMode) {
+ MOZ_ASSERT(aResA, "aResA must not be a nullptr");
+ MOZ_ASSERT(aResB, "aResB must not be a nullptr");
+ MOZ_ASSERT(aResA->ParserName() != aResB->ParserName(),
+ "aResA and aResB must be from different parsers");
+ SdpTelemetry::RecordCompare(aResA, aResB, aMode);
+
+ ParsingResultComparer comparer;
+ if (!aResA->Sdp() || !aResB->Sdp()) {
+ return !aResA->Sdp() && !aResB->Sdp();
+ }
+ if (SipccSdpParser::IsNamed(aResA->ParserName())) {
+ MOZ_ASSERT(RsdparsaSdpParser::IsNamed(aResB->ParserName()));
+ return comparer.Compare(*aResB->Sdp(), *aResA->Sdp(), aOriginalSdp,
+ SdpComparisonResult::Equal);
+ }
+ MOZ_ASSERT(SipccSdpParser::IsNamed(aResB->ParserName()));
+ MOZ_ASSERT(RsdparsaSdpParser::IsNamed(aResA->ParserName()));
+ return comparer.Compare(*aResA->Sdp(), *aResB->Sdp(), aOriginalSdp,
+ SdpComparisonResult::Equal);
+}
+
+bool ParsingResultComparer::Compare(const Sdp& rsdparsaSdp, const Sdp& sipccSdp,
+ const std::string& originalSdp,
+ const SdpComparisonResult expect) {
+ mOriginalSdp = originalSdp;
+ const std::string sipccSdpStr = sipccSdp.ToString();
+ const std::string rsdparsaSdpStr = rsdparsaSdp.ToString();
+
+ bool result = rsdparsaSdpStr == sipccSdpStr;
+ LOG_EXPECT(result, expect, ("The original sdp: \n%s", mOriginalSdp.c_str()));
+ if (result) {
+ Telemetry::ScalarAdd(Telemetry::ScalarID::WEBRTC_SDP_PARSER_DIFF,
+ u"serialization_is_equal"_ns, 1);
+ LOG_EXPECT(result, expect, ("Serialization is equal"));
+ return result;
+ }
+ // Do a deep comparison
+ result = true;
+
+ Telemetry::ScalarAdd(Telemetry::ScalarID::WEBRTC_SDP_PARSER_DIFF,
+ u"serialization_is_not_equal"_ns, 1);
+ LOG_EXPECT(result, expect,
+ ("Serialization is not equal\n"
+ " --- Sipcc SDP ---\n"
+ "%s\n"
+ "--- Rsdparsa SDP ---\n"
+ "%s\n",
+ sipccSdpStr.c_str(), rsdparsaSdpStr.c_str()));
+
+ const std::string rsdparsaOriginStr = ToString(rsdparsaSdp.GetOrigin());
+ const std::string sipccOriginStr = ToString(sipccSdp.GetOrigin());
+
+ // Compare the session level
+ if (rsdparsaOriginStr != sipccOriginStr) {
+ Telemetry::ScalarAdd(Telemetry::ScalarID::WEBRTC_SDP_PARSER_DIFF, u"o="_ns,
+ 1);
+ result = false;
+ LOG_EXPECT(result, expect,
+ ("origin is not equal\nrust origin: %s\nsipcc origin: %s",
+ rsdparsaOriginStr.c_str(), sipccOriginStr.c_str()));
+ }
+
+ if (MOZ_LOG_TEST(sSdpDiffLogger, LogLevel::Debug)) {
+ const auto rust_sess_attr_count = rsdparsaSdp.GetAttributeList().Count();
+ const auto sipcc_sess_attr_count = sipccSdp.GetAttributeList().Count();
+
+ if (rust_sess_attr_count != sipcc_sess_attr_count) {
+ LOG_EXPECT(false, expect,
+ ("Session level attribute count is NOT equal, rsdparsa: %u, "
+ "sipcc: %u\n",
+ rust_sess_attr_count, sipcc_sess_attr_count));
+ }
+ }
+
+ result &= CompareAttrLists(rsdparsaSdp.GetAttributeList(),
+ sipccSdp.GetAttributeList(), -1);
+
+ const uint32_t sipccMediaSecCount =
+ static_cast<uint32_t>(sipccSdp.GetMediaSectionCount());
+ const uint32_t rsdparsaMediaSecCount =
+ static_cast<uint32_t>(rsdparsaSdp.GetMediaSectionCount());
+
+ if (sipccMediaSecCount != rsdparsaMediaSecCount) {
+ result = false;
+ Telemetry::ScalarAdd(Telemetry::ScalarID::WEBRTC_SDP_PARSER_DIFF,
+ u"inequal_msec_count"_ns, 1);
+ LOG_EXPECT(result, expect,
+ ("Media section count is NOT equal, rsdparsa: %d, sipcc: %d \n",
+ rsdparsaMediaSecCount, sipccMediaSecCount));
+ }
+
+ for (size_t i = 0; i < std::min(sipccMediaSecCount, rsdparsaMediaSecCount);
+ i++) {
+ result &= CompareMediaSections(rsdparsaSdp.GetMediaSection(i),
+ sipccSdp.GetMediaSection(i));
+ }
+
+ return result;
+}
+
+bool ParsingResultComparer::CompareMediaSections(
+ const SdpMediaSection& rustMediaSection,
+ const SdpMediaSection& sipccMediaSection,
+ const SdpComparisonResult expect) const {
+ bool result = true;
+ auto trackMediaLineMismatch = [&result, &expect](
+ auto rustValue, auto sipccValue,
+ const nsString& valueDescription) {
+ result = false;
+ nsString typeStr = u"m="_ns;
+ typeStr += valueDescription;
+ Telemetry::ScalarAdd(Telemetry::ScalarID::WEBRTC_SDP_PARSER_DIFF, typeStr,
+ 1);
+ LOG_EXPECT(result, expect,
+ ("The media line values %s are not equal\n"
+ "rsdparsa value: %s\n"
+ "sipcc value: %s\n",
+ NS_LossyConvertUTF16toASCII(valueDescription).get(),
+ ToString(rustValue).c_str(), ToString(sipccValue).c_str()));
+ };
+
+ auto compareMediaLineValue = [trackMediaLineMismatch](
+ auto rustValue, auto sipccValue,
+ const nsString& valueDescription) {
+ if (rustValue != sipccValue) {
+ trackMediaLineMismatch(rustValue, sipccValue, valueDescription);
+ }
+ };
+
+ auto compareSimpleMediaLineValue =
+ [&rustMediaSection, &sipccMediaSection, compareMediaLineValue](
+ auto valGetFuncPtr, const nsString& valueDescription) {
+ compareMediaLineValue((rustMediaSection.*valGetFuncPtr)(),
+ (sipccMediaSection.*valGetFuncPtr)(),
+ valueDescription);
+ };
+
+ compareSimpleMediaLineValue(&SdpMediaSection::GetMediaType, u"media_type"_ns);
+ compareSimpleMediaLineValue(&SdpMediaSection::GetPort, u"port"_ns);
+ compareSimpleMediaLineValue(&SdpMediaSection::GetPortCount, u"port_count"_ns);
+ compareSimpleMediaLineValue(&SdpMediaSection::GetProtocol, u"protocol"_ns);
+ compareSimpleMediaLineValue(&SdpMediaSection::IsReceiving,
+ u"is_receiving"_ns);
+ compareSimpleMediaLineValue(&SdpMediaSection::IsSending, u"is_sending"_ns);
+ compareSimpleMediaLineValue(&SdpMediaSection::GetDirection, u"direction"_ns);
+ compareSimpleMediaLineValue(&SdpMediaSection::GetLevel, u"level"_ns);
+
+ compareMediaLineValue(ToString(rustMediaSection.GetConnection()),
+ ToString(sipccMediaSection.GetConnection()),
+ u"connection"_ns);
+
+ result &= CompareAttrLists(rustMediaSection.GetAttributeList(),
+ sipccMediaSection.GetAttributeList(),
+ static_cast<int>(rustMediaSection.GetLevel()));
+ return result;
+}
+
+bool ParsingResultComparer::CompareAttrLists(
+ const SdpAttributeList& rustAttrlist, const SdpAttributeList& sipccAttrlist,
+ int level, const SdpComparisonResult expect) const {
+ bool result = true;
+
+ for (size_t i = AttributeType::kFirstAttribute;
+ i <= static_cast<size_t>(AttributeType::kLastAttribute); i++) {
+ const AttributeType type = static_cast<AttributeType>(i);
+ std::string attrStr;
+ if (type != AttributeType::kDirectionAttribute) {
+ attrStr = "a=" + SdpAttribute::GetAttributeTypeString(type);
+ } else {
+ attrStr = "a=_direction_attribute_";
+ }
+
+ if (sipccAttrlist.HasAttribute(type, false)) {
+ auto sipccAttrStr = ToString(*sipccAttrlist.GetAttribute(type, false));
+
+ if (!rustAttrlist.HasAttribute(type, false)) {
+ result = false;
+ nsString typeStr;
+ typeStr.AssignASCII(attrStr.c_str());
+ typeStr += u"_missing"_ns;
+ Telemetry::ScalarAdd(Telemetry::ScalarID::WEBRTC_SDP_PARSER_DIFF,
+ typeStr, 1);
+ LOG_EXPECT(result, expect,
+ ("Rust is missing the attribute: %s\n", attrStr.c_str()));
+ LOG_EXPECT(result, expect,
+ ("Rust is missing: %s\n", sipccAttrStr.c_str()));
+
+ continue;
+ }
+
+ auto rustAttrStr = ToString(*rustAttrlist.GetAttribute(type, false));
+
+ if (rustAttrStr != sipccAttrStr) {
+ if (type == AttributeType::kFmtpAttribute) {
+ if (rustAttrlist.GetFmtp() == sipccAttrlist.GetFmtp()) {
+ continue;
+ }
+ }
+
+ std::string originalAttrStr = GetAttributeLines(attrStr, level);
+ if (rustAttrStr != originalAttrStr) {
+ result = false;
+ nsString typeStr;
+ typeStr.AssignASCII(attrStr.c_str());
+ typeStr += u"_inequal"_ns;
+ Telemetry::ScalarAdd(Telemetry::ScalarID::WEBRTC_SDP_PARSER_DIFF,
+ typeStr, 1);
+ LOG_EXPECT(result, expect,
+ ("%s is neither equal to sipcc nor to the orginal sdp\n"
+ "--------------rsdparsa attribute---------------\n"
+ "%s"
+ "--------------sipcc attribute---------------\n"
+ "%s"
+ "--------------original attribute---------------\n"
+ "%s\n",
+ attrStr.c_str(), rustAttrStr.c_str(),
+ sipccAttrStr.c_str(), originalAttrStr.c_str()));
+ } else {
+ LOG_EXPECT(
+ result, expect,
+ ("But the rust serialization is equal to the orignal sdp\n"));
+ }
+ }
+ } else {
+ if (rustAttrlist.HasAttribute(type, false)) {
+ nsString typeStr;
+ typeStr.AssignASCII(attrStr.c_str());
+ typeStr += u"_unexpected"_ns;
+ Telemetry::ScalarAdd(Telemetry::ScalarID::WEBRTC_SDP_PARSER_DIFF,
+ typeStr, 1);
+ }
+ }
+ }
+
+ return result;
+}
+
+std::vector<std::string> SplitLines(const std::string& sdp) {
+ std::stringstream ss(sdp);
+ std::string to;
+ std::vector<std::string> lines;
+
+ while (std::getline(ss, to, '\n')) {
+ lines.push_back(to);
+ }
+
+ return lines;
+}
+
+std::string ParsingResultComparer::GetAttributeLines(
+ const std::string& attrType, int level) const {
+ std::vector<std::string> lines = SplitLines(mOriginalSdp);
+ std::string attrToFind = attrType + ":";
+ std::string attrLines;
+ int currentLevel = -1;
+ // Filters rtcp-fb lines that contain "x-..." types
+ // This is because every SDP from Edge contains these rtcp-fb x- types
+ // for example: a=rtcp-fb:121 x-foo
+ std::regex customRtcpFbLines(R"(a\=rtcp\-fb\:(\d+|\*).* x\-.*)");
+
+ for (auto& line : lines) {
+ if (line.find("m=") == 0) {
+ if (level > currentLevel) {
+ attrLines.clear();
+ currentLevel++;
+ } else {
+ break;
+ }
+ } else if (line.find(attrToFind) == 0) {
+ if (std::regex_match(line, customRtcpFbLines)) {
+ continue;
+ }
+
+ attrLines += (line + '\n');
+ }
+ }
+
+ return attrLines;
+}
+
+} // namespace mozilla
diff --git a/dom/media/webrtc/sdp/ParsingResultComparer.h b/dom/media/webrtc/sdp/ParsingResultComparer.h
new file mode 100644
index 0000000000..5a1c2ac635
--- /dev/null
+++ b/dom/media/webrtc/sdp/ParsingResultComparer.h
@@ -0,0 +1,57 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _PARSINGRESULTCOMPARER_H_
+#define _PARSINGRESULTCOMPARER_H_
+
+#include "sdp/SdpParser.h"
+#include "sdp/SdpPref.h"
+
+#include <string>
+
+namespace mozilla {
+
+class Sdp;
+class SdpMediaSection;
+class SdpAttributeList;
+
+enum class SdpComparisonResult {
+ Inequal = false,
+ Equal = true,
+};
+
+class ParsingResultComparer {
+ public:
+ using Results = UniquePtr<SdpParser::Results>;
+
+ ParsingResultComparer() = default;
+
+ static bool Compare(const Results& aResA, const Results& aResB,
+ const std::string& aOrignalSdp,
+ const SdpPref::AlternateParseModes& aMode);
+ bool Compare(const Sdp& rsdparsaSdp, const Sdp& sipccSdp,
+ const std::string& aOriginalSdp,
+ const SdpComparisonResult expect = SdpComparisonResult::Equal);
+ bool CompareMediaSections(
+ const SdpMediaSection& rustMediaSection,
+ const SdpMediaSection& sipccMediaSection,
+ const SdpComparisonResult expect = SdpComparisonResult::Equal) const;
+ bool CompareAttrLists(
+ const SdpAttributeList& rustAttrlist,
+ const SdpAttributeList& sipccAttrlist, int level,
+ const SdpComparisonResult expect = SdpComparisonResult::Equal) const;
+ void TrackRustParsingFailed(size_t sipccErrorCount) const;
+ void TrackSipccParsingFailed(size_t rustErrorCount) const;
+
+ private:
+ std::string mOriginalSdp;
+
+ std::string GetAttributeLines(const std::string& attrType, int level) const;
+};
+
+} // namespace mozilla
+
+#endif // _PARSINGRESULTCOMPARER_H_
diff --git a/dom/media/webrtc/sdp/RsdparsaSdp.cpp b/dom/media/webrtc/sdp/RsdparsaSdp.cpp
new file mode 100644
index 0000000000..0e653dc17e
--- /dev/null
+++ b/dom/media/webrtc/sdp/RsdparsaSdp.cpp
@@ -0,0 +1,126 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "sdp/RsdparsaSdp.h"
+
+#include <cstdlib>
+#include "mozilla/UniquePtr.h"
+#include "mozilla/Assertions.h"
+#include "nsError.h"
+#include <iostream>
+
+#include "sdp/RsdparsaSdpInc.h"
+#include "sdp/RsdparsaSdpMediaSection.h"
+
+#ifdef CRLF
+# undef CRLF
+#endif
+#define CRLF "\r\n"
+
+namespace mozilla {
+
+RsdparsaSdp::RsdparsaSdp(RsdparsaSessionHandle session, const SdpOrigin& origin)
+ : mSession(std::move(session)), mOrigin(origin) {
+ RsdparsaSessionHandle attributeSession(sdp_new_reference(mSession.get()));
+ mAttributeList.reset(
+ new RsdparsaSdpAttributeList(std::move(attributeSession)));
+
+ size_t section_count = sdp_media_section_count(mSession.get());
+ for (size_t level = 0; level < section_count; level++) {
+ RustMediaSection* mediaSection =
+ sdp_get_media_section(mSession.get(), level);
+ if (!mediaSection) {
+ MOZ_ASSERT(false,
+ "sdp_get_media_section failed because level was out of"
+ " bounds, but we did a bounds check!");
+ break;
+ }
+ RsdparsaSessionHandle newSession(sdp_new_reference(mSession.get()));
+ RsdparsaSdpMediaSection* sdpMediaSection;
+ sdpMediaSection = new RsdparsaSdpMediaSection(
+ level, std::move(newSession), mediaSection, mAttributeList.get());
+ mMediaSections.emplace_back(sdpMediaSection);
+ }
+}
+
+RsdparsaSdp::RsdparsaSdp(const RsdparsaSdp& aOrig)
+ : RsdparsaSdp(RsdparsaSessionHandle(create_sdp_clone(aOrig.mSession.get())),
+ aOrig.mOrigin) {}
+
+Sdp* RsdparsaSdp::Clone() const { return new RsdparsaSdp(*this); }
+
+const SdpOrigin& RsdparsaSdp::GetOrigin() const { return mOrigin; }
+
+uint32_t RsdparsaSdp::GetBandwidth(const std::string& type) const {
+ return get_sdp_bandwidth(mSession.get(), type.c_str());
+}
+
+const SdpMediaSection& RsdparsaSdp::GetMediaSection(size_t level) const {
+ if (level > mMediaSections.size()) {
+ MOZ_CRASH();
+ }
+ return *mMediaSections[level];
+}
+
+SdpMediaSection& RsdparsaSdp::GetMediaSection(size_t level) {
+ if (level > mMediaSections.size()) {
+ MOZ_CRASH();
+ }
+ return *mMediaSections[level];
+}
+
+SdpMediaSection& RsdparsaSdp::AddMediaSection(
+ SdpMediaSection::MediaType mediaType, SdpDirectionAttribute::Direction dir,
+ uint16_t port, SdpMediaSection::Protocol protocol, sdp::AddrType addrType,
+ const std::string& addr) {
+ StringView rustAddr{addr.c_str(), addr.size()};
+ auto nr = sdp_add_media_section(mSession.get(), mediaType, dir, port,
+ protocol, addrType, rustAddr);
+
+ if (NS_SUCCEEDED(nr)) {
+ size_t level = mMediaSections.size();
+ RsdparsaSessionHandle newSessHandle(sdp_new_reference(mSession.get()));
+
+ auto rustMediaSection = sdp_get_media_section(mSession.get(), level);
+ auto mediaSection =
+ new RsdparsaSdpMediaSection(level, std::move(newSessHandle),
+ rustMediaSection, mAttributeList.get());
+ mMediaSections.emplace_back(mediaSection);
+
+ return *mediaSection;
+ } else {
+ // Return the last media section if the construction of this one fails
+ return GetMediaSection(mMediaSections.size() - 1);
+ }
+}
+
+void RsdparsaSdp::Serialize(std::ostream& os) const {
+ os << "v=0" << CRLF << mOrigin << "s=-" << CRLF;
+
+ // We don't support creating i=, u=, e=, p=
+ // We don't generate c= at the session level (only in media)
+
+ BandwidthVec* bwVec = sdp_get_session_bandwidth_vec(mSession.get());
+ char* bwString = sdp_serialize_bandwidth(bwVec);
+ if (bwString) {
+ os << bwString;
+ sdp_free_string(bwString);
+ }
+
+ os << "t=0 0" << CRLF;
+
+ // We don't support r= or z=
+
+ // attributes
+ os << *mAttributeList;
+
+ // media sections
+ for (const auto& msection : mMediaSections) {
+ os << *msection;
+ }
+}
+
+} // namespace mozilla
diff --git a/dom/media/webrtc/sdp/RsdparsaSdp.h b/dom/media/webrtc/sdp/RsdparsaSdp.h
new file mode 100644
index 0000000000..4942abb574
--- /dev/null
+++ b/dom/media/webrtc/sdp/RsdparsaSdp.h
@@ -0,0 +1,72 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _RSDPARSA_SDP_H_
+#define _RSDPARSA_SDP_H_
+
+#include "mozilla/UniquePtr.h"
+
+#include "sdp/Sdp.h"
+
+#include "sdp/RsdparsaSdpMediaSection.h"
+#include "sdp/RsdparsaSdpAttributeList.h"
+#include "sdp/RsdparsaSdpInc.h"
+#include "sdp/RsdparsaSdpGlue.h"
+
+namespace mozilla {
+
+class RsdparsaSdpParser;
+class SdpParser;
+
+class RsdparsaSdp final : public Sdp {
+ friend class RsdparsaSdpParser;
+
+ public:
+ explicit RsdparsaSdp(RsdparsaSessionHandle session, const SdpOrigin& origin);
+
+ Sdp* Clone() const override;
+
+ const SdpOrigin& GetOrigin() const override;
+
+ // Note: connection information is always retrieved from media sections
+ uint32_t GetBandwidth(const std::string& type) const override;
+
+ size_t GetMediaSectionCount() const override {
+ return sdp_media_section_count(mSession.get());
+ }
+
+ const SdpAttributeList& GetAttributeList() const override {
+ return *mAttributeList;
+ }
+
+ SdpAttributeList& GetAttributeList() override { return *mAttributeList; }
+
+ const SdpMediaSection& GetMediaSection(size_t level) const override;
+
+ SdpMediaSection& GetMediaSection(size_t level) override;
+
+ SdpMediaSection& AddMediaSection(SdpMediaSection::MediaType media,
+ SdpDirectionAttribute::Direction dir,
+ uint16_t port,
+ SdpMediaSection::Protocol proto,
+ sdp::AddrType addrType,
+ const std::string& addr) override;
+
+ void Serialize(std::ostream&) const override;
+
+ private:
+ RsdparsaSdp() : mOrigin("", 0, 0, sdp::kIPv4, "") {}
+ RsdparsaSdp(const RsdparsaSdp& aOrig);
+
+ RsdparsaSessionHandle mSession;
+ SdpOrigin mOrigin;
+ UniquePtr<RsdparsaSdpAttributeList> mAttributeList;
+ std::vector<UniquePtr<RsdparsaSdpMediaSection>> mMediaSections;
+};
+
+} // namespace mozilla
+
+#endif
diff --git a/dom/media/webrtc/sdp/RsdparsaSdpAttributeList.cpp b/dom/media/webrtc/sdp/RsdparsaSdpAttributeList.cpp
new file mode 100644
index 0000000000..0b76757c44
--- /dev/null
+++ b/dom/media/webrtc/sdp/RsdparsaSdpAttributeList.cpp
@@ -0,0 +1,1301 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsCRT.h"
+
+#include "sdp/RsdparsaSdpAttributeList.h"
+#include "sdp/RsdparsaSdpInc.h"
+#include "sdp/RsdparsaSdpGlue.h"
+
+#include <ostream>
+#include "mozilla/Assertions.h"
+
+#include <limits>
+
+namespace mozilla {
+
+const std::string RsdparsaSdpAttributeList::kEmptyString = "";
+
+RsdparsaSdpAttributeList::~RsdparsaSdpAttributeList() {
+ for (size_t i = 0; i < kNumAttributeTypes; ++i) {
+ delete mAttributes[i];
+ }
+}
+
+bool RsdparsaSdpAttributeList::HasAttribute(AttributeType type,
+ bool sessionFallback) const {
+ return !!GetAttribute(type, sessionFallback);
+}
+
+const SdpAttribute* RsdparsaSdpAttributeList::GetAttribute(
+ AttributeType type, bool sessionFallback) const {
+ const SdpAttribute* value = mAttributes[static_cast<size_t>(type)];
+ // Only do fallback when the attribute can appear at both the media and
+ // session level
+ if (!value && !AtSessionLevel() && sessionFallback &&
+ SdpAttribute::IsAllowedAtSessionLevel(type) &&
+ SdpAttribute::IsAllowedAtMediaLevel(type)) {
+ return mSessionAttributes->GetAttribute(type, false);
+ }
+ return value;
+}
+
+void RsdparsaSdpAttributeList::RemoveAttribute(AttributeType type) {
+ delete mAttributes[static_cast<size_t>(type)];
+ mAttributes[static_cast<size_t>(type)] = nullptr;
+}
+
+void RsdparsaSdpAttributeList::Clear() {
+ for (size_t i = 0; i < kNumAttributeTypes; ++i) {
+ RemoveAttribute(static_cast<AttributeType>(i));
+ }
+}
+
+uint32_t RsdparsaSdpAttributeList::Count() const {
+ uint32_t count = 0;
+ for (size_t i = 0; i < kNumAttributeTypes; ++i) {
+ if (mAttributes[i]) {
+ count++;
+ }
+ }
+ return count;
+}
+
+void RsdparsaSdpAttributeList::SetAttribute(SdpAttribute* attr) {
+ if (!IsAllowedHere(attr->GetType())) {
+ MOZ_ASSERT(false, "This type of attribute is not allowed here");
+ delete attr;
+ return;
+ }
+ RemoveAttribute(attr->GetType());
+ mAttributes[attr->GetType()] = attr;
+}
+
+const std::vector<std::string>& RsdparsaSdpAttributeList::GetCandidate() const {
+ if (!HasAttribute(SdpAttribute::kCandidateAttribute)) {
+ MOZ_CRASH();
+ }
+
+ return static_cast<const SdpMultiStringAttribute*>(
+ GetAttribute(SdpAttribute::kCandidateAttribute))
+ ->mValues;
+}
+
+const SdpConnectionAttribute& RsdparsaSdpAttributeList::GetConnection() const {
+ if (!HasAttribute(SdpAttribute::kConnectionAttribute)) {
+ MOZ_CRASH();
+ }
+
+ return *static_cast<const SdpConnectionAttribute*>(
+ GetAttribute(SdpAttribute::kConnectionAttribute));
+}
+
+SdpDirectionAttribute::Direction RsdparsaSdpAttributeList::GetDirection()
+ const {
+ if (!HasAttribute(SdpAttribute::kDirectionAttribute)) {
+ MOZ_CRASH();
+ }
+
+ const SdpAttribute* attr = GetAttribute(SdpAttribute::kDirectionAttribute);
+ return static_cast<const SdpDirectionAttribute*>(attr)->mValue;
+}
+
+const SdpDtlsMessageAttribute& RsdparsaSdpAttributeList::GetDtlsMessage()
+ const {
+ if (!HasAttribute(SdpAttribute::kDtlsMessageAttribute)) {
+ MOZ_CRASH();
+ }
+ const SdpAttribute* attr = GetAttribute(SdpAttribute::kDtlsMessageAttribute);
+ return *static_cast<const SdpDtlsMessageAttribute*>(attr);
+}
+
+const SdpExtmapAttributeList& RsdparsaSdpAttributeList::GetExtmap() const {
+ if (!HasAttribute(SdpAttribute::kExtmapAttribute)) {
+ MOZ_CRASH();
+ }
+
+ return *static_cast<const SdpExtmapAttributeList*>(
+ GetAttribute(SdpAttribute::kExtmapAttribute));
+}
+
+const SdpFingerprintAttributeList& RsdparsaSdpAttributeList::GetFingerprint()
+ const {
+ if (!HasAttribute(SdpAttribute::kFingerprintAttribute)) {
+ MOZ_CRASH();
+ }
+ const SdpAttribute* attr = GetAttribute(SdpAttribute::kFingerprintAttribute);
+ return *static_cast<const SdpFingerprintAttributeList*>(attr);
+}
+
+const SdpFmtpAttributeList& RsdparsaSdpAttributeList::GetFmtp() const {
+ if (!HasAttribute(SdpAttribute::kFmtpAttribute)) {
+ MOZ_CRASH();
+ }
+
+ return *static_cast<const SdpFmtpAttributeList*>(
+ GetAttribute(SdpAttribute::kFmtpAttribute));
+}
+
+const SdpGroupAttributeList& RsdparsaSdpAttributeList::GetGroup() const {
+ if (!HasAttribute(SdpAttribute::kGroupAttribute)) {
+ MOZ_CRASH();
+ }
+
+ return *static_cast<const SdpGroupAttributeList*>(
+ GetAttribute(SdpAttribute::kGroupAttribute));
+}
+
+const SdpOptionsAttribute& RsdparsaSdpAttributeList::GetIceOptions() const {
+ if (!HasAttribute(SdpAttribute::kIceOptionsAttribute)) {
+ MOZ_CRASH();
+ }
+
+ const SdpAttribute* attr = GetAttribute(SdpAttribute::kIceOptionsAttribute);
+ return *static_cast<const SdpOptionsAttribute*>(attr);
+}
+
+const std::string& RsdparsaSdpAttributeList::GetIcePwd() const {
+ if (!HasAttribute(SdpAttribute::kIcePwdAttribute)) {
+ return kEmptyString;
+ }
+ const SdpAttribute* attr = GetAttribute(SdpAttribute::kIcePwdAttribute);
+ return static_cast<const SdpStringAttribute*>(attr)->mValue;
+}
+
+const std::string& RsdparsaSdpAttributeList::GetIceUfrag() const {
+ if (!HasAttribute(SdpAttribute::kIceUfragAttribute)) {
+ return kEmptyString;
+ }
+ const SdpAttribute* attr = GetAttribute(SdpAttribute::kIceUfragAttribute);
+ return static_cast<const SdpStringAttribute*>(attr)->mValue;
+}
+
+const std::string& RsdparsaSdpAttributeList::GetIdentity() const {
+ if (!HasAttribute(SdpAttribute::kIdentityAttribute)) {
+ return kEmptyString;
+ }
+ const SdpAttribute* attr = GetAttribute(SdpAttribute::kIdentityAttribute);
+ return static_cast<const SdpStringAttribute*>(attr)->mValue;
+}
+
+const SdpImageattrAttributeList& RsdparsaSdpAttributeList::GetImageattr()
+ const {
+ if (!HasAttribute(SdpAttribute::kImageattrAttribute)) {
+ MOZ_CRASH();
+ }
+ const SdpAttribute* attr = GetAttribute(SdpAttribute::kImageattrAttribute);
+ return *static_cast<const SdpImageattrAttributeList*>(attr);
+}
+
+const SdpSimulcastAttribute& RsdparsaSdpAttributeList::GetSimulcast() const {
+ if (!HasAttribute(SdpAttribute::kSimulcastAttribute)) {
+ MOZ_CRASH();
+ }
+ const SdpAttribute* attr = GetAttribute(SdpAttribute::kSimulcastAttribute);
+ return *static_cast<const SdpSimulcastAttribute*>(attr);
+}
+
+const std::string& RsdparsaSdpAttributeList::GetLabel() const {
+ if (!HasAttribute(SdpAttribute::kLabelAttribute)) {
+ return kEmptyString;
+ }
+ const SdpAttribute* attr = GetAttribute(SdpAttribute::kLabelAttribute);
+ return static_cast<const SdpStringAttribute*>(attr)->mValue;
+}
+
+uint32_t RsdparsaSdpAttributeList::GetMaxptime() const {
+ if (!HasAttribute(SdpAttribute::kMaxptimeAttribute)) {
+ MOZ_CRASH();
+ }
+ const SdpAttribute* attr = GetAttribute(SdpAttribute::kMaxptimeAttribute);
+ return static_cast<const SdpNumberAttribute*>(attr)->mValue;
+}
+
+const std::string& RsdparsaSdpAttributeList::GetMid() const {
+ if (!HasAttribute(SdpAttribute::kMidAttribute)) {
+ return kEmptyString;
+ }
+ const SdpAttribute* attr = GetAttribute(SdpAttribute::kMidAttribute);
+ return static_cast<const SdpStringAttribute*>(attr)->mValue;
+}
+
+const SdpMsidAttributeList& RsdparsaSdpAttributeList::GetMsid() const {
+ if (!HasAttribute(SdpAttribute::kMsidAttribute)) {
+ MOZ_CRASH();
+ }
+ const SdpAttribute* attr = GetAttribute(SdpAttribute::kMsidAttribute);
+ return *static_cast<const SdpMsidAttributeList*>(attr);
+}
+
+const SdpMsidSemanticAttributeList& RsdparsaSdpAttributeList::GetMsidSemantic()
+ const {
+ if (!HasAttribute(SdpAttribute::kMsidSemanticAttribute)) {
+ MOZ_CRASH();
+ }
+ const SdpAttribute* attr = GetAttribute(SdpAttribute::kMsidSemanticAttribute);
+ return *static_cast<const SdpMsidSemanticAttributeList*>(attr);
+}
+
+const SdpRidAttributeList& RsdparsaSdpAttributeList::GetRid() const {
+ if (!HasAttribute(SdpAttribute::kRidAttribute)) {
+ MOZ_CRASH();
+ }
+ const SdpAttribute* attr = GetAttribute(SdpAttribute::kRidAttribute);
+ return *static_cast<const SdpRidAttributeList*>(attr);
+}
+
+uint32_t RsdparsaSdpAttributeList::GetPtime() const {
+ if (!HasAttribute(SdpAttribute::kPtimeAttribute)) {
+ MOZ_CRASH();
+ }
+ const SdpAttribute* attr = GetAttribute(SdpAttribute::kPtimeAttribute);
+ return static_cast<const SdpNumberAttribute*>(attr)->mValue;
+}
+
+const SdpRtcpAttribute& RsdparsaSdpAttributeList::GetRtcp() const {
+ if (!HasAttribute(SdpAttribute::kRtcpAttribute)) {
+ MOZ_CRASH();
+ }
+ const SdpAttribute* attr = GetAttribute(SdpAttribute::kRtcpAttribute);
+ return *static_cast<const SdpRtcpAttribute*>(attr);
+}
+
+const SdpRtcpFbAttributeList& RsdparsaSdpAttributeList::GetRtcpFb() const {
+ if (!HasAttribute(SdpAttribute::kRtcpFbAttribute)) {
+ MOZ_CRASH();
+ }
+ const SdpAttribute* attr = GetAttribute(SdpAttribute::kRtcpFbAttribute);
+ return *static_cast<const SdpRtcpFbAttributeList*>(attr);
+}
+
+const SdpRemoteCandidatesAttribute&
+RsdparsaSdpAttributeList::GetRemoteCandidates() const {
+ MOZ_CRASH("Not yet implemented");
+}
+
+const SdpRtpmapAttributeList& RsdparsaSdpAttributeList::GetRtpmap() const {
+ if (!HasAttribute(SdpAttribute::kRtpmapAttribute)) {
+ MOZ_CRASH();
+ }
+ const SdpAttribute* attr = GetAttribute(SdpAttribute::kRtpmapAttribute);
+ return *static_cast<const SdpRtpmapAttributeList*>(attr);
+}
+
+const SdpSctpmapAttributeList& RsdparsaSdpAttributeList::GetSctpmap() const {
+ if (!HasAttribute(SdpAttribute::kSctpmapAttribute)) {
+ MOZ_CRASH();
+ }
+ const SdpAttribute* attr = GetAttribute(SdpAttribute::kSctpmapAttribute);
+ return *static_cast<const SdpSctpmapAttributeList*>(attr);
+}
+
+uint32_t RsdparsaSdpAttributeList::GetSctpPort() const {
+ if (!HasAttribute(SdpAttribute::kSctpPortAttribute)) {
+ MOZ_CRASH();
+ }
+
+ const SdpAttribute* attr = GetAttribute(SdpAttribute::kSctpPortAttribute);
+ return static_cast<const SdpNumberAttribute*>(attr)->mValue;
+}
+
+uint32_t RsdparsaSdpAttributeList::GetMaxMessageSize() const {
+ if (!HasAttribute(SdpAttribute::kMaxMessageSizeAttribute)) {
+ MOZ_CRASH();
+ }
+
+ const SdpAttribute* attr =
+ GetAttribute(SdpAttribute::kMaxMessageSizeAttribute);
+ return static_cast<const SdpNumberAttribute*>(attr)->mValue;
+}
+
+const SdpSetupAttribute& RsdparsaSdpAttributeList::GetSetup() const {
+ if (!HasAttribute(SdpAttribute::kSetupAttribute)) {
+ MOZ_CRASH();
+ }
+ const SdpAttribute* attr = GetAttribute(SdpAttribute::kSetupAttribute);
+ return *static_cast<const SdpSetupAttribute*>(attr);
+}
+
+const SdpSsrcAttributeList& RsdparsaSdpAttributeList::GetSsrc() const {
+ if (!HasAttribute(SdpAttribute::kSsrcAttribute)) {
+ MOZ_CRASH();
+ }
+ const SdpAttribute* attr = GetAttribute(SdpAttribute::kSsrcAttribute);
+ return *static_cast<const SdpSsrcAttributeList*>(attr);
+}
+
+const SdpSsrcGroupAttributeList& RsdparsaSdpAttributeList::GetSsrcGroup()
+ const {
+ if (!HasAttribute(SdpAttribute::kSsrcGroupAttribute)) {
+ MOZ_CRASH();
+ }
+ const SdpAttribute* attr = GetAttribute(SdpAttribute::kSsrcGroupAttribute);
+ return *static_cast<const SdpSsrcGroupAttributeList*>(attr);
+}
+
+void RsdparsaSdpAttributeList::LoadAttribute(RustAttributeList* attributeList,
+ AttributeType type) {
+ if (!mAttributes[type]) {
+ switch (type) {
+ case SdpAttribute::kIceUfragAttribute:
+ LoadIceUfrag(attributeList);
+ return;
+ case SdpAttribute::kIcePwdAttribute:
+ LoadIcePwd(attributeList);
+ return;
+ case SdpAttribute::kIceOptionsAttribute:
+ LoadIceOptions(attributeList);
+ return;
+ case SdpAttribute::kDtlsMessageAttribute:
+ LoadDtlsMessage(attributeList);
+ return;
+ case SdpAttribute::kFingerprintAttribute:
+ LoadFingerprint(attributeList);
+ return;
+ case SdpAttribute::kIdentityAttribute:
+ LoadIdentity(attributeList);
+ return;
+ case SdpAttribute::kSetupAttribute:
+ LoadSetup(attributeList);
+ return;
+ case SdpAttribute::kSsrcAttribute:
+ LoadSsrc(attributeList);
+ return;
+ case SdpAttribute::kRtpmapAttribute:
+ LoadRtpmap(attributeList);
+ return;
+ case SdpAttribute::kFmtpAttribute:
+ LoadFmtp(attributeList);
+ return;
+ case SdpAttribute::kPtimeAttribute:
+ LoadPtime(attributeList);
+ return;
+ case SdpAttribute::kIceLiteAttribute:
+ case SdpAttribute::kRtcpMuxAttribute:
+ case SdpAttribute::kRtcpRsizeAttribute:
+ case SdpAttribute::kBundleOnlyAttribute:
+ case SdpAttribute::kEndOfCandidatesAttribute:
+ LoadFlags(attributeList);
+ return;
+ case SdpAttribute::kMaxMessageSizeAttribute:
+ LoadMaxMessageSize(attributeList);
+ return;
+ case SdpAttribute::kMidAttribute:
+ LoadMid(attributeList);
+ return;
+ case SdpAttribute::kMsidAttribute:
+ LoadMsid(attributeList);
+ return;
+ case SdpAttribute::kMsidSemanticAttribute:
+ LoadMsidSemantics(attributeList);
+ return;
+ case SdpAttribute::kGroupAttribute:
+ LoadGroup(attributeList);
+ return;
+ case SdpAttribute::kRtcpAttribute:
+ LoadRtcp(attributeList);
+ return;
+ case SdpAttribute::kRtcpFbAttribute:
+ LoadRtcpFb(attributeList);
+ return;
+ case SdpAttribute::kImageattrAttribute:
+ LoadImageattr(attributeList);
+ return;
+ case SdpAttribute::kSctpmapAttribute:
+ LoadSctpmaps(attributeList);
+ return;
+ case SdpAttribute::kDirectionAttribute:
+ LoadDirection(attributeList);
+ return;
+ case SdpAttribute::kRemoteCandidatesAttribute:
+ LoadRemoteCandidates(attributeList);
+ return;
+ case SdpAttribute::kRidAttribute:
+ LoadRids(attributeList);
+ return;
+ case SdpAttribute::kSctpPortAttribute:
+ LoadSctpPort(attributeList);
+ return;
+ case SdpAttribute::kExtmapAttribute:
+ LoadExtmap(attributeList);
+ return;
+ case SdpAttribute::kSimulcastAttribute:
+ LoadSimulcast(attributeList);
+ return;
+ case SdpAttribute::kMaxptimeAttribute:
+ LoadMaxPtime(attributeList);
+ return;
+ case SdpAttribute::kCandidateAttribute:
+ LoadCandidate(attributeList);
+ return;
+ case SdpAttribute::kSsrcGroupAttribute:
+ LoadSsrcGroup(attributeList);
+ return;
+ case SdpAttribute::kConnectionAttribute:
+ case SdpAttribute::kIceMismatchAttribute:
+ case SdpAttribute::kLabelAttribute:
+ // These attributes are unused
+ return;
+ }
+ }
+}
+
+void RsdparsaSdpAttributeList::LoadAll(RustAttributeList* attributeList) {
+ for (int i = SdpAttribute::kFirstAttribute; i <= SdpAttribute::kLastAttribute;
+ i++) {
+ LoadAttribute(attributeList, static_cast<SdpAttribute::AttributeType>(i));
+ }
+}
+
+void RsdparsaSdpAttributeList::LoadIceUfrag(RustAttributeList* attributeList) {
+ StringView ufragStr;
+ nsresult nr = sdp_get_iceufrag(attributeList, &ufragStr);
+ if (NS_SUCCEEDED(nr)) {
+ std::string iceufrag = convertStringView(ufragStr);
+ SetAttribute(
+ new SdpStringAttribute(SdpAttribute::kIceUfragAttribute, iceufrag));
+ }
+}
+
+void RsdparsaSdpAttributeList::LoadIcePwd(RustAttributeList* attributeList) {
+ StringView pwdStr;
+ nsresult nr = sdp_get_icepwd(attributeList, &pwdStr);
+ if (NS_SUCCEEDED(nr)) {
+ std::string icePwd = convertStringView(pwdStr);
+ SetAttribute(
+ new SdpStringAttribute(SdpAttribute::kIcePwdAttribute, icePwd));
+ }
+}
+
+void RsdparsaSdpAttributeList::LoadIdentity(RustAttributeList* attributeList) {
+ StringView identityStr;
+ nsresult nr = sdp_get_identity(attributeList, &identityStr);
+ if (NS_SUCCEEDED(nr)) {
+ std::string identity = convertStringView(identityStr);
+ SetAttribute(
+ new SdpStringAttribute(SdpAttribute::kIdentityAttribute, identity));
+ }
+}
+
+void RsdparsaSdpAttributeList::LoadIceOptions(
+ RustAttributeList* attributeList) {
+ StringVec* options;
+ nsresult nr = sdp_get_iceoptions(attributeList, &options);
+ if (NS_SUCCEEDED(nr)) {
+ std::vector<std::string> optionsVec;
+ auto optionsAttr =
+ MakeUnique<SdpOptionsAttribute>(SdpAttribute::kIceOptionsAttribute);
+ for (size_t i = 0; i < string_vec_len(options); i++) {
+ StringView optionStr;
+ string_vec_get_view(options, i, &optionStr);
+ optionsAttr->PushEntry(convertStringView(optionStr));
+ }
+ SetAttribute(optionsAttr.release());
+ }
+}
+
+void RsdparsaSdpAttributeList::LoadFingerprint(
+ RustAttributeList* attributeList) {
+ size_t nFp = sdp_get_fingerprint_count(attributeList);
+ if (nFp == 0) {
+ return;
+ }
+ auto rustFingerprints = MakeUnique<RustSdpAttributeFingerprint[]>(nFp);
+ sdp_get_fingerprints(attributeList, nFp, rustFingerprints.get());
+ auto fingerprints = MakeUnique<SdpFingerprintAttributeList>();
+ for (size_t i = 0; i < nFp; i++) {
+ const RustSdpAttributeFingerprint& fingerprint = rustFingerprints[i];
+ std::string algorithm;
+ switch (fingerprint.hashAlgorithm) {
+ case RustSdpAttributeFingerprintHashAlgorithm::kSha1:
+ algorithm = "sha-1";
+ break;
+ case RustSdpAttributeFingerprintHashAlgorithm::kSha224:
+ algorithm = "sha-224";
+ break;
+ case RustSdpAttributeFingerprintHashAlgorithm::kSha256:
+ algorithm = "sha-256";
+ break;
+ case RustSdpAttributeFingerprintHashAlgorithm::kSha384:
+ algorithm = "sha-384";
+ break;
+ case RustSdpAttributeFingerprintHashAlgorithm::kSha512:
+ algorithm = "sha-512";
+ break;
+ }
+
+ std::vector<uint8_t> fingerprintBytes =
+ convertU8Vec(fingerprint.fingerprint);
+
+ fingerprints->PushEntry(algorithm, fingerprintBytes);
+ }
+ SetAttribute(fingerprints.release());
+}
+
+void RsdparsaSdpAttributeList::LoadDtlsMessage(
+ RustAttributeList* attributeList) {
+ RustSdpAttributeDtlsMessage rustDtlsMessage;
+ nsresult nr = sdp_get_dtls_message(attributeList, &rustDtlsMessage);
+ if (NS_SUCCEEDED(nr)) {
+ SdpDtlsMessageAttribute::Role role;
+ if (rustDtlsMessage.role == RustSdpAttributeDtlsMessageType::kClient) {
+ role = SdpDtlsMessageAttribute::kClient;
+ } else {
+ role = SdpDtlsMessageAttribute::kServer;
+ }
+
+ std::string value = convertStringView(rustDtlsMessage.value);
+
+ SetAttribute(new SdpDtlsMessageAttribute(role, value));
+ }
+}
+
+void RsdparsaSdpAttributeList::LoadSetup(RustAttributeList* attributeList) {
+ RustSdpSetup rustSetup;
+ nsresult nr = sdp_get_setup(attributeList, &rustSetup);
+ if (NS_SUCCEEDED(nr)) {
+ SdpSetupAttribute::Role setupEnum;
+ switch (rustSetup) {
+ case RustSdpSetup::kRustActive:
+ setupEnum = SdpSetupAttribute::kActive;
+ break;
+ case RustSdpSetup::kRustActpass:
+ setupEnum = SdpSetupAttribute::kActpass;
+ break;
+ case RustSdpSetup::kRustHoldconn:
+ setupEnum = SdpSetupAttribute::kHoldconn;
+ break;
+ case RustSdpSetup::kRustPassive:
+ setupEnum = SdpSetupAttribute::kPassive;
+ break;
+ }
+ SetAttribute(new SdpSetupAttribute(setupEnum));
+ }
+}
+
+void RsdparsaSdpAttributeList::LoadSsrc(RustAttributeList* attributeList) {
+ size_t numSsrc = sdp_get_ssrc_count(attributeList);
+ if (numSsrc == 0) {
+ return;
+ }
+ auto rustSsrcs = MakeUnique<RustSdpAttributeSsrc[]>(numSsrc);
+ sdp_get_ssrcs(attributeList, numSsrc, rustSsrcs.get());
+ auto ssrcs = MakeUnique<SdpSsrcAttributeList>();
+ for (size_t i = 0; i < numSsrc; i++) {
+ RustSdpAttributeSsrc& ssrc = rustSsrcs[i];
+ std::string attribute = convertStringView(ssrc.attribute);
+ std::string value = convertStringView(ssrc.value);
+ if (value.length() == 0) {
+ ssrcs->PushEntry(ssrc.id, attribute);
+ } else {
+ ssrcs->PushEntry(ssrc.id, attribute + ":" + value);
+ }
+ }
+ SetAttribute(ssrcs.release());
+}
+
+void RsdparsaSdpAttributeList::LoadSsrcGroup(RustAttributeList* attributeList) {
+ size_t numSsrcGroups = sdp_get_ssrc_group_count(attributeList);
+ if (numSsrcGroups == 0) {
+ return;
+ }
+ auto rustSsrcGroups = MakeUnique<RustSdpAttributeSsrcGroup[]>(numSsrcGroups);
+ sdp_get_ssrc_groups(attributeList, numSsrcGroups, rustSsrcGroups.get());
+ auto ssrcGroups = MakeUnique<SdpSsrcGroupAttributeList>();
+ for (size_t i = 0; i < numSsrcGroups; i++) {
+ RustSdpAttributeSsrcGroup& ssrcGroup = rustSsrcGroups[i];
+ SdpSsrcGroupAttributeList::Semantics semantic;
+ switch (ssrcGroup.semantic) {
+ case RustSdpAttributeSsrcGroupSemantic ::kRustDup:
+ semantic = SdpSsrcGroupAttributeList::kDup;
+ break;
+ case RustSdpAttributeSsrcGroupSemantic ::kRustFec:
+ semantic = SdpSsrcGroupAttributeList::kFec;
+ break;
+ case RustSdpAttributeSsrcGroupSemantic ::kRustFecFr:
+ semantic = SdpSsrcGroupAttributeList::kFecFr;
+ break;
+ case RustSdpAttributeSsrcGroupSemantic ::kRustFid:
+ semantic = SdpSsrcGroupAttributeList::kFid;
+ break;
+ case RustSdpAttributeSsrcGroupSemantic ::kRustSim:
+ semantic = SdpSsrcGroupAttributeList::kSim;
+ break;
+ }
+ std::vector<uint32_t> ssrcs;
+ for (size_t i = 0; i < ssrc_vec_len(ssrcGroup.ssrcs); ++i) {
+ uint32_t ssrc;
+ ssrc_vec_get_id(ssrcGroup.ssrcs, i, &ssrc);
+ ssrcs.push_back(ssrc);
+ }
+ ssrcGroups->PushEntry(semantic, ssrcs);
+ }
+ SetAttribute(ssrcGroups.release());
+}
+
+struct FmtDefaults {
+ uint32_t minimumChannels = 0;
+};
+
+std::tuple<SdpRtpmapAttributeList::CodecType, FmtDefaults> strToCodecType(
+ const std::string& name) {
+ auto codec = SdpRtpmapAttributeList::kOtherCodec;
+ FmtDefaults defaults = {0}; // This is tracked to match SIPCC behavior only
+ if (!nsCRT::strcasecmp(name.c_str(), "opus")) {
+ codec = SdpRtpmapAttributeList::kOpus;
+ defaults = {0};
+ } else if (!nsCRT::strcasecmp(name.c_str(), "G722")) {
+ codec = SdpRtpmapAttributeList::kG722;
+ defaults = {1};
+ } else if (!nsCRT::strcasecmp(name.c_str(), "PCMU")) {
+ codec = SdpRtpmapAttributeList::kPCMU;
+ defaults = {1};
+ } else if (!nsCRT::strcasecmp(name.c_str(), "PCMA")) {
+ codec = SdpRtpmapAttributeList::kPCMA;
+ defaults = {1};
+ } else if (!nsCRT::strcasecmp(name.c_str(), "VP8")) {
+ codec = SdpRtpmapAttributeList::kVP8;
+ defaults = {0};
+ } else if (!nsCRT::strcasecmp(name.c_str(), "VP9")) {
+ codec = SdpRtpmapAttributeList::kVP9;
+ defaults = {0};
+ } else if (!nsCRT::strcasecmp(name.c_str(), "iLBC")) {
+ codec = SdpRtpmapAttributeList::kiLBC;
+ defaults = {1};
+ } else if (!nsCRT::strcasecmp(name.c_str(), "iSAC")) {
+ codec = SdpRtpmapAttributeList::kiSAC;
+ defaults = {1};
+ } else if (!nsCRT::strcasecmp(name.c_str(), "H264")) {
+ codec = SdpRtpmapAttributeList::kH264;
+ defaults = {0};
+ } else if (!nsCRT::strcasecmp(name.c_str(), "red")) {
+ codec = SdpRtpmapAttributeList::kRed;
+ defaults = {0};
+ } else if (!nsCRT::strcasecmp(name.c_str(), "ulpfec")) {
+ codec = SdpRtpmapAttributeList::kUlpfec;
+ defaults = {0};
+ } else if (!nsCRT::strcasecmp(name.c_str(), "telephone-event")) {
+ codec = SdpRtpmapAttributeList::kTelephoneEvent;
+ defaults = {1};
+ } else if (!nsCRT::strcasecmp(name.c_str(), "rtx")) {
+ codec = SdpRtpmapAttributeList::kRtx;
+ defaults = {0};
+ }
+ return std::make_tuple(codec, defaults);
+}
+
+void RsdparsaSdpAttributeList::LoadRtpmap(RustAttributeList* attributeList) {
+ size_t numRtpmap = sdp_get_rtpmap_count(attributeList);
+ if (numRtpmap == 0) {
+ return;
+ }
+ auto rustRtpmaps = MakeUnique<RustSdpAttributeRtpmap[]>(numRtpmap);
+ sdp_get_rtpmaps(attributeList, numRtpmap, rustRtpmaps.get());
+ auto rtpmapList = MakeUnique<SdpRtpmapAttributeList>();
+ for (size_t i = 0; i < numRtpmap; i++) {
+ RustSdpAttributeRtpmap& rtpmap = rustRtpmaps[i];
+ std::string payloadType = std::to_string(rtpmap.payloadType);
+ std::string name = convertStringView(rtpmap.codecName);
+ auto [codec, defaults] = strToCodecType(name);
+ uint32_t channels = rtpmap.channels;
+ if (channels == 0) {
+ channels = defaults.minimumChannels;
+ }
+ rtpmapList->PushEntry(payloadType, codec, name, rtpmap.frequency, channels);
+ }
+ SetAttribute(rtpmapList.release());
+}
+
+void RsdparsaSdpAttributeList::LoadFmtp(RustAttributeList* attributeList) {
+ size_t numFmtp = sdp_get_fmtp_count(attributeList);
+ if (numFmtp == 0) {
+ return;
+ }
+ auto rustFmtps = MakeUnique<RustSdpAttributeFmtp[]>(numFmtp);
+ size_t numValidFmtp = sdp_get_fmtp(attributeList, numFmtp, rustFmtps.get());
+ auto fmtpList = MakeUnique<SdpFmtpAttributeList>();
+ for (size_t i = 0; i < numValidFmtp; i++) {
+ const RustSdpAttributeFmtp& fmtp = rustFmtps[i];
+ uint8_t payloadType = fmtp.payloadType;
+ std::string codecName = convertStringView(fmtp.codecName);
+ const RustSdpAttributeFmtpParameters& rustFmtpParameters = fmtp.parameters;
+
+ UniquePtr<SdpFmtpAttributeList::Parameters> fmtpParameters;
+
+ // use the upper case version of the codec name
+ std::transform(codecName.begin(), codecName.end(), codecName.begin(),
+ ::toupper);
+
+ if (codecName == "H264") {
+ SdpFmtpAttributeList::H264Parameters h264Parameters;
+
+ h264Parameters.packetization_mode = rustFmtpParameters.packetization_mode;
+ h264Parameters.level_asymmetry_allowed =
+ rustFmtpParameters.level_asymmetry_allowed;
+ h264Parameters.profile_level_id = rustFmtpParameters.profile_level_id;
+ h264Parameters.max_mbps = rustFmtpParameters.max_mbps;
+ h264Parameters.max_fs = rustFmtpParameters.max_fs;
+ h264Parameters.max_cpb = rustFmtpParameters.max_cpb;
+ h264Parameters.max_dpb = rustFmtpParameters.max_dpb;
+ h264Parameters.max_br = rustFmtpParameters.max_br;
+
+ // TODO(bug 1466859): Support sprop-parameter-sets
+
+ fmtpParameters.reset(
+ new SdpFmtpAttributeList::H264Parameters(std::move(h264Parameters)));
+ } else if (codecName == "OPUS") {
+ SdpFmtpAttributeList::OpusParameters opusParameters;
+
+ opusParameters.maxplaybackrate = rustFmtpParameters.maxplaybackrate;
+ opusParameters.maxAverageBitrate = rustFmtpParameters.maxaveragebitrate;
+ opusParameters.useDTX = rustFmtpParameters.usedtx;
+ opusParameters.stereo = rustFmtpParameters.stereo;
+ opusParameters.useInBandFec = rustFmtpParameters.useinbandfec;
+ opusParameters.frameSizeMs = rustFmtpParameters.ptime;
+ opusParameters.minFrameSizeMs = rustFmtpParameters.minptime;
+ opusParameters.maxFrameSizeMs = rustFmtpParameters.maxptime;
+ opusParameters.useCbr = rustFmtpParameters.cbr;
+
+ fmtpParameters.reset(
+ new SdpFmtpAttributeList::OpusParameters(std::move(opusParameters)));
+ } else if ((codecName == "VP8") || (codecName == "VP9")) {
+ SdpFmtpAttributeList::VP8Parameters vp8Parameters(
+ codecName == "VP8" ? SdpRtpmapAttributeList::kVP8
+ : SdpRtpmapAttributeList::kVP9);
+
+ vp8Parameters.max_fs = rustFmtpParameters.max_fs;
+ vp8Parameters.max_fr = rustFmtpParameters.max_fr;
+
+ fmtpParameters.reset(
+ new SdpFmtpAttributeList::VP8Parameters(std::move(vp8Parameters)));
+ } else if (codecName == "TELEPHONE-EVENT") {
+ SdpFmtpAttributeList::TelephoneEventParameters telephoneEventParameters;
+
+ telephoneEventParameters.dtmfTones =
+ convertStringView(rustFmtpParameters.dtmf_tones);
+
+ fmtpParameters.reset(new SdpFmtpAttributeList::TelephoneEventParameters(
+ std::move(telephoneEventParameters)));
+ } else if (codecName == "RED") {
+ SdpFmtpAttributeList::RedParameters redParameters;
+
+ redParameters.encodings = convertU8Vec(rustFmtpParameters.encodings);
+
+ fmtpParameters.reset(
+ new SdpFmtpAttributeList::RedParameters(std::move(redParameters)));
+ } else if (codecName == "RTX") {
+ SdpFmtpAttributeList::RtxParameters rtxParameters;
+
+ rtxParameters.apt = rustFmtpParameters.rtx.apt;
+ if (rustFmtpParameters.rtx.has_rtx_time) {
+ rtxParameters.rtx_time = Some(rustFmtpParameters.rtx.rtx_time);
+ }
+
+ fmtpParameters.reset(
+ new SdpFmtpAttributeList::RtxParameters(rtxParameters));
+ } else {
+ // The parameter set is unknown so skip it
+ continue;
+ }
+ fmtpList->PushEntry(std::to_string(payloadType), *fmtpParameters);
+ }
+ SetAttribute(fmtpList.release());
+}
+
+void RsdparsaSdpAttributeList::LoadPtime(RustAttributeList* attributeList) {
+ int64_t ptime = sdp_get_ptime(attributeList);
+ if (ptime >= 0) {
+ SetAttribute(new SdpNumberAttribute(SdpAttribute::kPtimeAttribute,
+ static_cast<uint32_t>(ptime)));
+ }
+}
+
+void RsdparsaSdpAttributeList::LoadFlags(RustAttributeList* attributeList) {
+ RustSdpAttributeFlags flags = sdp_get_attribute_flags(attributeList);
+ if (flags.iceLite) {
+ SetAttribute(new SdpFlagAttribute(SdpAttribute::kIceLiteAttribute));
+ }
+ if (flags.rtcpMux) {
+ SetAttribute(new SdpFlagAttribute(SdpAttribute::kRtcpMuxAttribute));
+ }
+ if (flags.rtcpRsize) {
+ SetAttribute(new SdpFlagAttribute(SdpAttribute::kRtcpRsizeAttribute));
+ }
+ if (flags.bundleOnly) {
+ SetAttribute(new SdpFlagAttribute(SdpAttribute::kBundleOnlyAttribute));
+ }
+ if (flags.endOfCandidates) {
+ SetAttribute(new SdpFlagAttribute(SdpAttribute::kEndOfCandidatesAttribute));
+ }
+}
+
+void RsdparsaSdpAttributeList::LoadMaxMessageSize(
+ RustAttributeList* attributeList) {
+ int64_t max_msg_size = sdp_get_max_msg_size(attributeList);
+ if (max_msg_size >= 0) {
+ SetAttribute(new SdpNumberAttribute(SdpAttribute::kMaxMessageSizeAttribute,
+ static_cast<uint32_t>(max_msg_size)));
+ }
+}
+
+void RsdparsaSdpAttributeList::LoadMid(RustAttributeList* attributeList) {
+ StringView rustMid;
+ if (NS_SUCCEEDED(sdp_get_mid(attributeList, &rustMid))) {
+ std::string mid = convertStringView(rustMid);
+ SetAttribute(new SdpStringAttribute(SdpAttribute::kMidAttribute, mid));
+ }
+}
+
+void RsdparsaSdpAttributeList::LoadMsid(RustAttributeList* attributeList) {
+ size_t numMsid = sdp_get_msid_count(attributeList);
+ if (numMsid == 0) {
+ return;
+ }
+ auto rustMsid = MakeUnique<RustSdpAttributeMsid[]>(numMsid);
+ sdp_get_msids(attributeList, numMsid, rustMsid.get());
+ auto msids = MakeUnique<SdpMsidAttributeList>();
+ for (size_t i = 0; i < numMsid; i++) {
+ RustSdpAttributeMsid& msid = rustMsid[i];
+ std::string id = convertStringView(msid.id);
+ std::string appdata = convertStringView(msid.appdata);
+ msids->PushEntry(id, appdata);
+ }
+ SetAttribute(msids.release());
+}
+
+void RsdparsaSdpAttributeList::LoadMsidSemantics(
+ RustAttributeList* attributeList) {
+ size_t numMsidSemantic = sdp_get_msid_semantic_count(attributeList);
+ if (numMsidSemantic == 0) {
+ return;
+ }
+ auto rustMsidSemantics =
+ MakeUnique<RustSdpAttributeMsidSemantic[]>(numMsidSemantic);
+ sdp_get_msid_semantics(attributeList, numMsidSemantic,
+ rustMsidSemantics.get());
+ auto msidSemantics = MakeUnique<SdpMsidSemanticAttributeList>();
+ for (size_t i = 0; i < numMsidSemantic; i++) {
+ RustSdpAttributeMsidSemantic& rustMsidSemantic = rustMsidSemantics[i];
+ std::string semantic = convertStringView(rustMsidSemantic.semantic);
+ std::vector<std::string> msids = convertStringVec(rustMsidSemantic.msids);
+ msidSemantics->PushEntry(semantic, msids);
+ }
+ SetAttribute(msidSemantics.release());
+}
+
+void RsdparsaSdpAttributeList::LoadGroup(RustAttributeList* attributeList) {
+ size_t numGroup = sdp_get_group_count(attributeList);
+ if (numGroup == 0) {
+ return;
+ }
+ auto rustGroups = MakeUnique<RustSdpAttributeGroup[]>(numGroup);
+ sdp_get_groups(attributeList, numGroup, rustGroups.get());
+ auto groups = MakeUnique<SdpGroupAttributeList>();
+ for (size_t i = 0; i < numGroup; i++) {
+ RustSdpAttributeGroup& group = rustGroups[i];
+ SdpGroupAttributeList::Semantics semantic;
+ switch (group.semantic) {
+ case RustSdpAttributeGroupSemantic ::kRustLipSynchronization:
+ semantic = SdpGroupAttributeList::kLs;
+ break;
+ case RustSdpAttributeGroupSemantic ::kRustFlowIdentification:
+ semantic = SdpGroupAttributeList::kFid;
+ break;
+ case RustSdpAttributeGroupSemantic ::kRustSingleReservationFlow:
+ semantic = SdpGroupAttributeList::kSrf;
+ break;
+ case RustSdpAttributeGroupSemantic ::kRustAlternateNetworkAddressType:
+ semantic = SdpGroupAttributeList::kAnat;
+ break;
+ case RustSdpAttributeGroupSemantic ::kRustForwardErrorCorrection:
+ semantic = SdpGroupAttributeList::kFec;
+ break;
+ case RustSdpAttributeGroupSemantic ::kRustDecodingDependency:
+ semantic = SdpGroupAttributeList::kDdp;
+ break;
+ case RustSdpAttributeGroupSemantic ::kRustBundle:
+ semantic = SdpGroupAttributeList::kBundle;
+ break;
+ }
+ std::vector<std::string> tags = convertStringVec(group.tags);
+ groups->PushEntry(semantic, tags);
+ }
+ SetAttribute(groups.release());
+}
+
+void RsdparsaSdpAttributeList::LoadRtcp(RustAttributeList* attributeList) {
+ RustSdpAttributeRtcp rtcp;
+ if (NS_SUCCEEDED(sdp_get_rtcp(attributeList, &rtcp))) {
+ if (rtcp.has_address) {
+ auto address = convertExplicitlyTypedAddress(&rtcp.unicastAddr);
+ SetAttribute(new SdpRtcpAttribute(rtcp.port, sdp::kInternet,
+ address.first, address.second));
+ } else {
+ SetAttribute(new SdpRtcpAttribute(rtcp.port));
+ }
+ }
+}
+
+void RsdparsaSdpAttributeList::LoadRtcpFb(RustAttributeList* attributeList) {
+ auto rtcpfbsCount = sdp_get_rtcpfb_count(attributeList);
+ if (!rtcpfbsCount) {
+ return;
+ }
+
+ auto rustRtcpfbs = MakeUnique<RustSdpAttributeRtcpFb[]>(rtcpfbsCount);
+ sdp_get_rtcpfbs(attributeList, rtcpfbsCount, rustRtcpfbs.get());
+
+ auto rtcpfbList = MakeUnique<SdpRtcpFbAttributeList>();
+ for (size_t i = 0; i < rtcpfbsCount; i++) {
+ RustSdpAttributeRtcpFb& rtcpfb = rustRtcpfbs[i];
+ uint32_t payloadTypeU32 = rtcpfb.payloadType;
+
+ std::stringstream ss;
+ if (payloadTypeU32 == std::numeric_limits<uint32_t>::max()) {
+ ss << "*";
+ } else {
+ ss << payloadTypeU32;
+ }
+
+ uint32_t feedbackType = rtcpfb.feedbackType;
+ std::string parameter = convertStringView(rtcpfb.parameter);
+ std::string extra = convertStringView(rtcpfb.extra);
+
+ rtcpfbList->PushEntry(
+ ss.str(), static_cast<SdpRtcpFbAttributeList::Type>(feedbackType),
+ parameter, extra);
+ }
+
+ SetAttribute(rtcpfbList.release());
+}
+
+SdpSimulcastAttribute::Versions LoadSimulcastVersions(
+ const RustSdpAttributeSimulcastVersionVec* rustVersionList) {
+ size_t rustVersionCount = sdp_simulcast_get_version_count(rustVersionList);
+ auto rustVersionArray =
+ MakeUnique<RustSdpAttributeSimulcastVersion[]>(rustVersionCount);
+ sdp_simulcast_get_versions(rustVersionList, rustVersionCount,
+ rustVersionArray.get());
+
+ SdpSimulcastAttribute::Versions versions;
+
+ for (size_t i = 0; i < rustVersionCount; i++) {
+ const RustSdpAttributeSimulcastVersion& rustVersion = rustVersionArray[i];
+ size_t rustIdCount = sdp_simulcast_get_ids_count(rustVersion.ids);
+ if (!rustIdCount) {
+ continue;
+ }
+
+ SdpSimulcastAttribute::Version version;
+ auto rustIdArray = MakeUnique<RustSdpAttributeSimulcastId[]>(rustIdCount);
+ sdp_simulcast_get_ids(rustVersion.ids, rustIdCount, rustIdArray.get());
+
+ for (size_t j = 0; j < rustIdCount; j++) {
+ const RustSdpAttributeSimulcastId& rustId = rustIdArray[j];
+ std::string id = convertStringView(rustId.id);
+ // TODO: Bug 1225877. Added support for 'paused'-state
+ version.choices.push_back(
+ SdpSimulcastAttribute::Encoding(id, rustId.paused));
+ }
+
+ versions.push_back(version);
+ }
+
+ return versions;
+}
+
+void RsdparsaSdpAttributeList::LoadSimulcast(RustAttributeList* attributeList) {
+ RustSdpAttributeSimulcast rustSimulcast;
+ if (NS_SUCCEEDED(sdp_get_simulcast(attributeList, &rustSimulcast))) {
+ auto simulcast = MakeUnique<SdpSimulcastAttribute>();
+
+ simulcast->sendVersions = LoadSimulcastVersions(rustSimulcast.send);
+ simulcast->recvVersions = LoadSimulcastVersions(rustSimulcast.recv);
+
+ SetAttribute(simulcast.release());
+ }
+}
+
+SdpImageattrAttributeList::XYRange LoadImageattrXYRange(
+ const RustSdpAttributeImageAttrXYRange& rustXYRange) {
+ SdpImageattrAttributeList::XYRange xyRange;
+
+ if (!rustXYRange.discrete_values) {
+ xyRange.min = rustXYRange.min;
+ xyRange.max = rustXYRange.max;
+ xyRange.step = rustXYRange.step;
+
+ } else {
+ xyRange.discreteValues = convertU32Vec(rustXYRange.discrete_values);
+ }
+
+ return xyRange;
+}
+
+std::vector<SdpImageattrAttributeList::Set> LoadImageattrSets(
+ const RustSdpAttributeImageAttrSetVec* rustSets) {
+ std::vector<SdpImageattrAttributeList::Set> sets;
+
+ size_t rustSetCount = sdp_imageattr_get_set_count(rustSets);
+ if (!rustSetCount) {
+ return sets;
+ }
+
+ auto rustSetArray = MakeUnique<RustSdpAttributeImageAttrSet[]>(rustSetCount);
+ sdp_imageattr_get_sets(rustSets, rustSetCount, rustSetArray.get());
+
+ for (size_t i = 0; i < rustSetCount; i++) {
+ const RustSdpAttributeImageAttrSet& rustSet = rustSetArray[i];
+ SdpImageattrAttributeList::Set set;
+
+ set.xRange = LoadImageattrXYRange(rustSet.x);
+ set.yRange = LoadImageattrXYRange(rustSet.y);
+
+ if (rustSet.has_sar) {
+ if (!rustSet.sar.discrete_values) {
+ set.sRange.min = rustSet.sar.min;
+ set.sRange.max = rustSet.sar.max;
+ } else {
+ set.sRange.discreteValues = convertF32Vec(rustSet.sar.discrete_values);
+ }
+ }
+
+ if (rustSet.has_par) {
+ set.pRange.min = rustSet.par.min;
+ set.pRange.max = rustSet.par.max;
+ }
+
+ set.qValue = rustSet.q;
+
+ sets.push_back(set);
+ }
+
+ return sets;
+}
+
+void RsdparsaSdpAttributeList::LoadImageattr(RustAttributeList* attributeList) {
+ size_t numImageattrs = sdp_get_imageattr_count(attributeList);
+ if (numImageattrs == 0) {
+ return;
+ }
+ auto rustImageattrs = MakeUnique<RustSdpAttributeImageAttr[]>(numImageattrs);
+ sdp_get_imageattrs(attributeList, numImageattrs, rustImageattrs.get());
+ auto imageattrList = MakeUnique<SdpImageattrAttributeList>();
+ for (size_t i = 0; i < numImageattrs; i++) {
+ const RustSdpAttributeImageAttr& rustImageAttr = rustImageattrs[i];
+
+ SdpImageattrAttributeList::Imageattr imageAttr;
+
+ if (rustImageAttr.payloadType != std::numeric_limits<uint32_t>::max()) {
+ imageAttr.pt = Some(rustImageAttr.payloadType);
+ }
+
+ if (rustImageAttr.send.sets) {
+ imageAttr.sendSets = LoadImageattrSets(rustImageAttr.send.sets);
+ } else {
+ imageAttr.sendAll = true;
+ }
+
+ if (rustImageAttr.recv.sets) {
+ imageAttr.recvSets = LoadImageattrSets(rustImageAttr.recv.sets);
+ } else {
+ imageAttr.recvAll = true;
+ }
+
+ imageattrList->mImageattrs.push_back(imageAttr);
+ }
+ SetAttribute(imageattrList.release());
+}
+
+void RsdparsaSdpAttributeList::LoadSctpmaps(RustAttributeList* attributeList) {
+ size_t numSctpmaps = sdp_get_sctpmap_count(attributeList);
+ if (numSctpmaps == 0) {
+ return;
+ }
+ auto rustSctpmaps = MakeUnique<RustSdpAttributeSctpmap[]>(numSctpmaps);
+ sdp_get_sctpmaps(attributeList, numSctpmaps, rustSctpmaps.get());
+ auto sctpmapList = MakeUnique<SdpSctpmapAttributeList>();
+ for (size_t i = 0; i < numSctpmaps; i++) {
+ RustSdpAttributeSctpmap& sctpmap = rustSctpmaps[i];
+ sctpmapList->PushEntry(std::to_string(sctpmap.port), "webrtc-datachannel",
+ sctpmap.channels);
+ }
+ SetAttribute(sctpmapList.release());
+}
+
+void RsdparsaSdpAttributeList::LoadDirection(RustAttributeList* attributeList) {
+ SdpDirectionAttribute::Direction dir;
+ RustDirection rustDir = sdp_get_direction(attributeList);
+ switch (rustDir) {
+ case RustDirection::kRustRecvonly:
+ dir = SdpDirectionAttribute::kRecvonly;
+ break;
+ case RustDirection::kRustSendonly:
+ dir = SdpDirectionAttribute::kSendonly;
+ break;
+ case RustDirection::kRustSendrecv:
+ dir = SdpDirectionAttribute::kSendrecv;
+ break;
+ case RustDirection::kRustInactive:
+ dir = SdpDirectionAttribute::kInactive;
+ break;
+ }
+ SetAttribute(new SdpDirectionAttribute(dir));
+}
+
+void RsdparsaSdpAttributeList::LoadRemoteCandidates(
+ RustAttributeList* attributeList) {
+ size_t nC = sdp_get_remote_candidate_count(attributeList);
+ if (nC == 0) {
+ return;
+ }
+ auto rustCandidates = MakeUnique<RustSdpAttributeRemoteCandidate[]>(nC);
+ sdp_get_remote_candidates(attributeList, nC, rustCandidates.get());
+ std::vector<SdpRemoteCandidatesAttribute::Candidate> candidates;
+ for (size_t i = 0; i < nC; i++) {
+ RustSdpAttributeRemoteCandidate& rustCandidate = rustCandidates[i];
+ SdpRemoteCandidatesAttribute::Candidate candidate;
+ candidate.port = rustCandidate.port;
+ candidate.id = std::to_string(rustCandidate.component);
+ candidate.address = convertAddress(&rustCandidate.address);
+ candidates.push_back(candidate);
+ }
+ SdpRemoteCandidatesAttribute* candidatesList;
+ candidatesList = new SdpRemoteCandidatesAttribute(candidates);
+ SetAttribute(candidatesList);
+}
+
+void RsdparsaSdpAttributeList::LoadRids(RustAttributeList* attributeList) {
+ size_t numRids = sdp_get_rid_count(attributeList);
+ if (numRids == 0) {
+ return;
+ }
+
+ auto rustRids = MakeUnique<RustSdpAttributeRid[]>(numRids);
+ sdp_get_rids(attributeList, numRids, rustRids.get());
+
+ auto ridList = MakeUnique<SdpRidAttributeList>();
+ for (size_t i = 0; i < numRids; i++) {
+ const RustSdpAttributeRid& rid = rustRids[i];
+
+ std::string id = convertStringView(rid.id);
+ auto direction = static_cast<sdp::Direction>(rid.direction);
+ std::vector<uint16_t> formats = convertU16Vec(rid.formats);
+
+ EncodingConstraints parameters;
+ parameters.maxWidth = rid.params.max_width;
+ parameters.maxHeight = rid.params.max_height;
+ // Right now, we treat max-fps=0 and the absence of max-fps as no limit.
+ // We will eventually want to treat max-fps=0 as 0 frames per second, and
+ // the absence of max-fps as no limit (bug 1762632).
+ if (rid.params.max_fps) {
+ parameters.maxFps = Some(rid.params.max_fps);
+ }
+ parameters.maxFs = rid.params.max_fs;
+ parameters.maxBr = rid.params.max_br;
+ parameters.maxPps = rid.params.max_pps;
+
+ std::vector<std::string> depends = convertStringVec(rid.depends);
+
+ ridList->PushEntry(id, direction, formats, parameters, depends);
+ }
+
+ SetAttribute(ridList.release());
+}
+
+void RsdparsaSdpAttributeList::LoadSctpPort(RustAttributeList* attributeList) {
+ int64_t port = sdp_get_sctp_port(attributeList);
+ if (port >= 0) {
+ SetAttribute(new SdpNumberAttribute(SdpAttribute::kSctpPortAttribute,
+ static_cast<uint32_t>(port)));
+ }
+}
+
+void RsdparsaSdpAttributeList::LoadExtmap(RustAttributeList* attributeList) {
+ size_t numExtmap = sdp_get_extmap_count(attributeList);
+ if (numExtmap == 0) {
+ return;
+ }
+ auto rustExtmaps = MakeUnique<RustSdpAttributeExtmap[]>(numExtmap);
+ sdp_get_extmaps(attributeList, numExtmap, rustExtmaps.get());
+ auto extmaps = MakeUnique<SdpExtmapAttributeList>();
+ for (size_t i = 0; i < numExtmap; i++) {
+ RustSdpAttributeExtmap& rustExtmap = rustExtmaps[i];
+ std::string name = convertStringView(rustExtmap.url);
+ SdpDirectionAttribute::Direction direction;
+ bool directionSpecified = rustExtmap.direction_specified;
+ switch (rustExtmap.direction) {
+ case RustDirection::kRustRecvonly:
+ direction = SdpDirectionAttribute::kRecvonly;
+ break;
+ case RustDirection::kRustSendonly:
+ direction = SdpDirectionAttribute::kSendonly;
+ break;
+ case RustDirection::kRustSendrecv:
+ direction = SdpDirectionAttribute::kSendrecv;
+ break;
+ case RustDirection::kRustInactive:
+ direction = SdpDirectionAttribute::kInactive;
+ break;
+ }
+ std::string extensionAttributes;
+ extensionAttributes = convertStringView(rustExtmap.extensionAttributes);
+ extmaps->PushEntry((uint16_t)rustExtmap.id, direction, directionSpecified,
+ name, extensionAttributes);
+ }
+ SetAttribute(extmaps.release());
+}
+
+void RsdparsaSdpAttributeList::LoadMaxPtime(RustAttributeList* attributeList) {
+ uint64_t maxPtime = 0;
+ nsresult nr = sdp_get_maxptime(attributeList, &maxPtime);
+ if (NS_SUCCEEDED(nr)) {
+ SetAttribute(
+ new SdpNumberAttribute(SdpAttribute::kMaxptimeAttribute, maxPtime));
+ }
+}
+
+void RsdparsaSdpAttributeList::LoadCandidate(RustAttributeList* attributeList) {
+ size_t candidatesCount = sdp_get_candidate_count(attributeList);
+ if (!candidatesCount) {
+ return;
+ }
+
+ StringVec* rustCandidatesStrings;
+ sdp_get_candidates(attributeList, candidatesCount, &rustCandidatesStrings);
+
+ std::vector<std::string> candidatesStrings =
+ convertStringVec(rustCandidatesStrings);
+ free_boxed_string_vec(rustCandidatesStrings);
+
+ auto candidates =
+ MakeUnique<SdpMultiStringAttribute>(SdpAttribute::kCandidateAttribute);
+ candidates->mValues = candidatesStrings;
+
+ SetAttribute(candidates.release());
+}
+
+bool RsdparsaSdpAttributeList::IsAllowedHere(SdpAttribute::AttributeType type) {
+ if (AtSessionLevel() && !SdpAttribute::IsAllowedAtSessionLevel(type)) {
+ return false;
+ }
+
+ if (!AtSessionLevel() && !SdpAttribute::IsAllowedAtMediaLevel(type)) {
+ return false;
+ }
+
+ return true;
+}
+
+void RsdparsaSdpAttributeList::Serialize(std::ostream& os) const {
+ for (size_t i = 0; i < kNumAttributeTypes; ++i) {
+ if (mAttributes[i]) {
+ os << *mAttributes[i];
+ }
+ }
+}
+
+} // namespace mozilla
diff --git a/dom/media/webrtc/sdp/RsdparsaSdpAttributeList.h b/dom/media/webrtc/sdp/RsdparsaSdpAttributeList.h
new file mode 100644
index 0000000000..812f15e112
--- /dev/null
+++ b/dom/media/webrtc/sdp/RsdparsaSdpAttributeList.h
@@ -0,0 +1,157 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _RSDPARSA_SDP_ATTRIBUTE_LIST_H_
+#define _RSDPARSA_SDP_ATTRIBUTE_LIST_H_
+
+#include "sdp/RsdparsaSdpGlue.h"
+#include "sdp/RsdparsaSdpInc.h"
+#include "sdp/SdpAttributeList.h"
+
+namespace mozilla {
+
+class RsdparsaSdp;
+class RsdparsaSdpMediaSection;
+class SdpParser;
+
+class RsdparsaSdpAttributeList : public SdpAttributeList {
+ friend class RsdparsaSdpMediaSection;
+ friend class RsdparsaSdp;
+
+ public:
+ // Make sure we don't hide the default arg thunks
+ using SdpAttributeList::GetAttribute;
+ using SdpAttributeList::HasAttribute;
+
+ bool HasAttribute(AttributeType type, bool sessionFallback) const override;
+ const SdpAttribute* GetAttribute(AttributeType type,
+ bool sessionFallback) const override;
+ void SetAttribute(SdpAttribute* attr) override;
+ void RemoveAttribute(AttributeType type) override;
+ void Clear() override;
+ uint32_t Count() const override;
+
+ const SdpConnectionAttribute& GetConnection() const override;
+ const SdpFingerprintAttributeList& GetFingerprint() const override;
+ const SdpGroupAttributeList& GetGroup() const override;
+ const SdpOptionsAttribute& GetIceOptions() const override;
+ const SdpRtcpAttribute& GetRtcp() const override;
+ const SdpRemoteCandidatesAttribute& GetRemoteCandidates() const override;
+ const SdpSetupAttribute& GetSetup() const override;
+ const SdpSsrcAttributeList& GetSsrc() const override;
+ const SdpSsrcGroupAttributeList& GetSsrcGroup() const override;
+ const SdpDtlsMessageAttribute& GetDtlsMessage() const override;
+
+ // These attributes can appear multiple times, so the returned
+ // classes actually represent a collection of values.
+ const std::vector<std::string>& GetCandidate() const override;
+ const SdpExtmapAttributeList& GetExtmap() const override;
+ const SdpFmtpAttributeList& GetFmtp() const override;
+ const SdpImageattrAttributeList& GetImageattr() const override;
+ const SdpSimulcastAttribute& GetSimulcast() const override;
+ const SdpMsidAttributeList& GetMsid() const override;
+ const SdpMsidSemanticAttributeList& GetMsidSemantic() const override;
+ const SdpRidAttributeList& GetRid() const override;
+ const SdpRtcpFbAttributeList& GetRtcpFb() const override;
+ const SdpRtpmapAttributeList& GetRtpmap() const override;
+ const SdpSctpmapAttributeList& GetSctpmap() const override;
+
+ // These attributes are effectively simple types, so we'll make life
+ // easy by just returning their value.
+ uint32_t GetSctpPort() const override;
+ uint32_t GetMaxMessageSize() const override;
+ const std::string& GetIcePwd() const override;
+ const std::string& GetIceUfrag() const override;
+ const std::string& GetIdentity() const override;
+ const std::string& GetLabel() const override;
+ unsigned int GetMaxptime() const override;
+ const std::string& GetMid() const override;
+ unsigned int GetPtime() const override;
+
+ SdpDirectionAttribute::Direction GetDirection() const override;
+
+ void Serialize(std::ostream&) const override;
+
+ virtual ~RsdparsaSdpAttributeList();
+
+ private:
+ explicit RsdparsaSdpAttributeList(RsdparsaSessionHandle session)
+ : mSession(std::move(session)),
+ mSessionAttributes(nullptr),
+ mIsVideo(false),
+ mAttributes() {
+ RustAttributeList* attributes = get_sdp_session_attributes(mSession.get());
+ LoadAll(attributes);
+ }
+
+ RsdparsaSdpAttributeList(RsdparsaSessionHandle session,
+ const RustMediaSection* const msection,
+ const RsdparsaSdpAttributeList* sessionAttributes)
+ : mSession(std::move(session)),
+ mSessionAttributes(sessionAttributes),
+ mAttributes() {
+ mIsVideo =
+ sdp_rust_get_media_type(msection) == RustSdpMediaValue::kRustVideo;
+ RustAttributeList* attributes = sdp_get_media_attribute_list(msection);
+ LoadAll(attributes);
+ }
+
+ static const std::string kEmptyString;
+ static const size_t kNumAttributeTypes = SdpAttribute::kLastAttribute + 1;
+
+ const RsdparsaSessionHandle mSession;
+ const RsdparsaSdpAttributeList* mSessionAttributes;
+ bool mIsVideo;
+
+ bool AtSessionLevel() const { return !mSessionAttributes; }
+
+ bool IsAllowedHere(SdpAttribute::AttributeType type);
+ void LoadAll(RustAttributeList* attributeList);
+ void LoadAttribute(RustAttributeList* attributeList, AttributeType type);
+ void LoadIceUfrag(RustAttributeList* attributeList);
+ void LoadIcePwd(RustAttributeList* attributeList);
+ void LoadIdentity(RustAttributeList* attributeList);
+ void LoadIceOptions(RustAttributeList* attributeList);
+ void LoadFingerprint(RustAttributeList* attributeList);
+ void LoadDtlsMessage(RustAttributeList* attributeList);
+ void LoadSetup(RustAttributeList* attributeList);
+ void LoadSsrc(RustAttributeList* attributeList);
+ void LoadSsrcGroup(RustAttributeList* attributeList);
+ void LoadRtpmap(RustAttributeList* attributeList);
+ void LoadFmtp(RustAttributeList* attributeList);
+ void LoadPtime(RustAttributeList* attributeList);
+ void LoadFlags(RustAttributeList* attributeList);
+ void LoadMaxMessageSize(RustAttributeList* attributeList);
+ void LoadMid(RustAttributeList* attributeList);
+ void LoadMsid(RustAttributeList* attributeList);
+ void LoadMsidSemantics(RustAttributeList* attributeList);
+ void LoadGroup(RustAttributeList* attributeList);
+ void LoadRtcp(RustAttributeList* attributeList);
+ void LoadRtcpFb(RustAttributeList* attributeList);
+ void LoadSctpPort(RustAttributeList* attributeList);
+ void LoadSimulcast(RustAttributeList* attributeList);
+ void LoadImageattr(RustAttributeList* attributeList);
+ void LoadSctpmaps(RustAttributeList* attributeList);
+ void LoadDirection(RustAttributeList* attributeList);
+ void LoadRemoteCandidates(RustAttributeList* attributeList);
+ void LoadRids(RustAttributeList* attributeList);
+ void LoadExtmap(RustAttributeList* attributeList);
+ void LoadMaxPtime(RustAttributeList* attributeList);
+ void LoadCandidate(RustAttributeList* attributeList);
+
+ void WarnAboutMisplacedAttribute(SdpAttribute::AttributeType type,
+ uint32_t lineNumber, SdpParser& errorHolder);
+
+ SdpAttribute* mAttributes[kNumAttributeTypes];
+
+ RsdparsaSdpAttributeList(const RsdparsaSdpAttributeList& orig) = delete;
+ RsdparsaSdpAttributeList& operator=(const RsdparsaSdpAttributeList& rhs) =
+ delete;
+};
+
+} // namespace mozilla
+
+#endif
diff --git a/dom/media/webrtc/sdp/RsdparsaSdpGlue.cpp b/dom/media/webrtc/sdp/RsdparsaSdpGlue.cpp
new file mode 100644
index 0000000000..01a1a1d817
--- /dev/null
+++ b/dom/media/webrtc/sdp/RsdparsaSdpGlue.cpp
@@ -0,0 +1,106 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+#include <string>
+#include <cstdint>
+
+#include "sdp/RsdparsaSdpInc.h"
+#include "sdp/RsdparsaSdpGlue.h"
+namespace mozilla {
+
+std::string convertStringView(StringView str) {
+ if (nullptr == str.buf) {
+ return std::string();
+ } else {
+ return std::string(str.buf, str.len);
+ }
+}
+
+std::vector<std::string> convertStringVec(StringVec* vec) {
+ std::vector<std::string> ret;
+ size_t len = string_vec_len(vec);
+ for (size_t i = 0; i < len; i++) {
+ StringView view;
+ string_vec_get_view(vec, i, &view);
+ ret.push_back(convertStringView(view));
+ }
+ return ret;
+}
+
+sdp::AddrType convertAddressType(RustSdpAddressType addrType) {
+ switch (addrType) {
+ case RustSdpAddressType::kRustAddrIp4:
+ return sdp::kIPv4;
+ case RustSdpAddressType::kRustAddrIp6:
+ return sdp::kIPv6;
+ }
+
+ MOZ_CRASH("unknown address type");
+}
+
+std::string convertAddress(RustAddress* address) {
+ return address->isFqdn ? convertStringView(address->fqdn)
+ : std::string(address->ipAddress);
+}
+
+std::pair<sdp::AddrType, std::string> convertExplicitlyTypedAddress(
+ RustExplicitlyTypedAddress* address) {
+ return std::make_pair(convertAddressType(address->addressType),
+ convertAddress(&address->address));
+}
+
+std::vector<uint8_t> convertU8Vec(U8Vec* vec) {
+ std::vector<std::uint8_t> ret;
+
+ size_t len = u8_vec_len(vec);
+ for (size_t i = 0; i < len; i++) {
+ uint8_t byte;
+ u8_vec_get(vec, i, &byte);
+ ret.push_back(byte);
+ }
+
+ return ret;
+}
+
+std::vector<uint16_t> convertU16Vec(U16Vec* vec) {
+ std::vector<std::uint16_t> ret;
+
+ size_t len = u16_vec_len(vec);
+ for (size_t i = 0; i < len; i++) {
+ uint16_t word;
+ u16_vec_get(vec, i, &word);
+ ret.push_back(word);
+ }
+
+ return ret;
+}
+
+std::vector<uint32_t> convertU32Vec(U32Vec* vec) {
+ std::vector<std::uint32_t> ret;
+
+ size_t len = u32_vec_len(vec);
+ for (size_t i = 0; i < len; i++) {
+ uint32_t num;
+ u32_vec_get(vec, i, &num);
+ ret.push_back(num);
+ }
+
+ return ret;
+}
+
+std::vector<float> convertF32Vec(F32Vec* vec) {
+ std::vector<float> ret;
+
+ size_t len = f32_vec_len(vec);
+ for (size_t i = 0; i < len; i++) {
+ float flt;
+ f32_vec_get(vec, i, &flt);
+ ret.push_back(flt);
+ }
+
+ return ret;
+}
+
+} // namespace mozilla
diff --git a/dom/media/webrtc/sdp/RsdparsaSdpGlue.h b/dom/media/webrtc/sdp/RsdparsaSdpGlue.h
new file mode 100644
index 0000000000..8ed275be75
--- /dev/null
+++ b/dom/media/webrtc/sdp/RsdparsaSdpGlue.h
@@ -0,0 +1,36 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef _RUSTSDPGLUE_H_
+#define _RUSTSDPGLUE_H_
+
+#include <string>
+#include <vector>
+#include <utility>
+#include "SdpEnum.h"
+#include "mozilla/UniquePtr.h"
+#include "sdp/RsdparsaSdpInc.h"
+
+namespace mozilla {
+
+struct FreeRustSdpSession {
+ void operator()(RustSdpSession* aSess) { sdp_free_session(aSess); }
+};
+
+typedef UniquePtr<RustSdpSession, FreeRustSdpSession> RsdparsaSessionHandle;
+
+std::string convertStringView(StringView str);
+std::vector<std::string> convertStringVec(StringVec* vec);
+std::string convertAddress(RustAddress* address);
+std::pair<sdp::AddrType, std::string> convertExplicitlyTypedAddress(
+ RustExplicitlyTypedAddress* addr);
+std::vector<uint8_t> convertU8Vec(U8Vec* vec);
+std::vector<uint16_t> convertU16Vec(U16Vec* vec);
+std::vector<uint32_t> convertU32Vec(U32Vec* vec);
+std::vector<float> convertF32Vec(F32Vec* vec);
+
+} // namespace mozilla
+
+#endif
diff --git a/dom/media/webrtc/sdp/RsdparsaSdpInc.h b/dom/media/webrtc/sdp/RsdparsaSdpInc.h
new file mode 100644
index 0000000000..1237d2fdad
--- /dev/null
+++ b/dom/media/webrtc/sdp/RsdparsaSdpInc.h
@@ -0,0 +1,510 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef _RUSTSDPINC_H_
+#define _RUSTSDPINC_H_
+
+#include "nsError.h"
+#include "mozilla/Maybe.h"
+
+#include <stdint.h>
+#include <stdbool.h>
+
+struct BandwidthVec;
+struct RustSdpSession;
+struct RustSdpError;
+struct RustMediaSection;
+struct RustAttributeList;
+struct StringVec;
+struct U8Vec;
+struct U32Vec;
+struct U16Vec;
+struct F32Vec;
+struct SsrcVec;
+struct RustHeapString;
+
+enum class RustSdpAddressType { kRustAddrIp4, kRustAddrIp6 };
+
+struct StringView {
+ const char* buf;
+ size_t len;
+};
+
+struct RustAddress {
+ char ipAddress[50];
+ StringView fqdn;
+ bool isFqdn;
+};
+
+struct RustExplicitlyTypedAddress {
+ RustSdpAddressType addressType;
+ RustAddress address;
+};
+
+struct RustSdpConnection {
+ RustExplicitlyTypedAddress addr;
+ uint8_t ttl;
+ uint64_t amount;
+};
+
+struct RustSdpOrigin {
+ StringView username;
+ uint64_t sessionId;
+ uint64_t sessionVersion;
+ RustExplicitlyTypedAddress addr; // TODO address
+};
+
+enum class RustSdpMediaValue { kRustAudio, kRustVideo, kRustApplication };
+
+enum class RustSdpProtocolValue {
+ kRustRtpSavpf,
+ kRustUdpTlsRtpSavp,
+ kRustTcpDtlsRtpSavp,
+ kRustUdpTlsRtpSavpf,
+ kRustTcpDtlsRtpSavpf,
+ kRustDtlsSctp,
+ kRustUdpDtlsSctp,
+ kRustTcpDtlsSctp,
+ kRustRtpAvp,
+ kRustRtpAvpf,
+ kRustRtpSavp,
+};
+
+enum class RustSdpFormatType { kRustIntegers, kRustStrings };
+
+enum class RustSdpAttributeFingerprintHashAlgorithm : uint16_t {
+ kSha1,
+ kSha224,
+ kSha256,
+ kSha384,
+ kSha512,
+};
+
+struct RustSdpAttributeFingerprint {
+ RustSdpAttributeFingerprintHashAlgorithm hashAlgorithm;
+ U8Vec* fingerprint;
+};
+
+enum class RustSdpSetup {
+ kRustActive,
+ kRustActpass,
+ kRustHoldconn,
+ kRustPassive
+};
+
+enum class RustSdpAttributeDtlsMessageType : uint8_t {
+ kClient,
+ kServer,
+};
+
+struct RustSdpAttributeDtlsMessage {
+ RustSdpAttributeDtlsMessageType role;
+ StringView value;
+};
+
+struct RustSdpAttributeSsrc {
+ uint32_t id;
+ StringView attribute;
+ StringView value;
+};
+
+enum class RustSdpAttributeSsrcGroupSemantic {
+ kRustDup,
+ kRustFid,
+ kRustFec,
+ kRustFecFr,
+ kRustSim,
+};
+
+struct RustSdpAttributeSsrcGroup {
+ RustSdpAttributeSsrcGroupSemantic semantic;
+ SsrcVec* ssrcs;
+};
+
+struct RustSdpAttributeRtpmap {
+ uint8_t payloadType;
+ StringView codecName;
+ uint32_t frequency;
+ uint32_t channels;
+};
+
+struct RustSdpAttributeRtcpFb {
+ uint32_t payloadType;
+ uint32_t feedbackType;
+ StringView parameter;
+ StringView extra;
+};
+
+struct RustSdpAttributeRidParameters {
+ uint32_t max_width;
+ uint32_t max_height;
+ uint32_t max_fps;
+ uint32_t max_fs;
+ uint32_t max_br;
+ uint32_t max_pps;
+ StringVec* unknown;
+};
+
+struct RustSdpAttributeRid {
+ StringView id;
+ uint32_t direction;
+ U16Vec* formats;
+ RustSdpAttributeRidParameters params;
+ StringVec* depends;
+};
+
+struct RustSdpAttributeImageAttrXYRange {
+ uint32_t min;
+ uint32_t max;
+ uint32_t step;
+ U32Vec* discrete_values;
+};
+
+struct RustSdpAttributeImageAttrSRange {
+ float min;
+ float max;
+ F32Vec* discrete_values;
+};
+
+struct RustSdpAttributeImageAttrPRange {
+ float min;
+ float max;
+};
+
+struct RustSdpAttributeImageAttrSet {
+ RustSdpAttributeImageAttrXYRange x;
+ RustSdpAttributeImageAttrXYRange y;
+ bool has_sar;
+ RustSdpAttributeImageAttrSRange sar;
+ bool has_par;
+ RustSdpAttributeImageAttrPRange par;
+ float q;
+};
+
+struct RustSdpAttributeImageAttrSetVec;
+struct RustSdpAttributeImageAttrSetList {
+ RustSdpAttributeImageAttrSetVec* sets;
+};
+
+struct RustSdpAttributeImageAttr {
+ uint32_t payloadType;
+ RustSdpAttributeImageAttrSetList send;
+ RustSdpAttributeImageAttrSetList recv;
+};
+
+struct RustRtxFmtpParameters {
+ uint8_t apt;
+ bool has_rtx_time;
+ uint32_t rtx_time;
+};
+
+struct RustSdpAttributeFmtpParameters {
+ // H264
+ uint32_t packetization_mode;
+ bool level_asymmetry_allowed;
+ uint32_t profile_level_id;
+ uint32_t max_fs;
+ uint32_t max_cpb;
+ uint32_t max_dpb;
+ uint32_t max_br;
+ uint32_t max_mbps;
+
+ // VP8 and VP9
+ // max_fs, already defined in H264
+ uint32_t max_fr;
+
+ // Opus
+ uint32_t maxplaybackrate;
+ uint32_t maxaveragebitrate;
+ bool usedtx;
+ bool stereo;
+ bool useinbandfec;
+ bool cbr;
+ uint32_t ptime;
+ uint32_t minptime;
+ uint32_t maxptime;
+
+ // telephone-event
+ StringView dtmf_tones;
+
+ // RTX
+ RustRtxFmtpParameters rtx;
+
+ // Red codecs
+ U8Vec* encodings;
+
+ // Unknown
+ StringVec* unknown_tokens;
+};
+
+struct RustSdpAttributeFmtp {
+ uint8_t payloadType;
+ StringView codecName;
+ RustSdpAttributeFmtpParameters parameters;
+};
+
+struct RustSdpAttributeFlags {
+ bool iceLite;
+ bool rtcpMux;
+ bool rtcpRsize;
+ bool bundleOnly;
+ bool endOfCandidates;
+};
+
+struct RustSdpAttributeMsid {
+ StringView id;
+ StringView appdata;
+};
+
+struct RustSdpAttributeMsidSemantic {
+ StringView semantic;
+ StringVec* msids;
+};
+
+enum class RustSdpAttributeGroupSemantic {
+ kRustLipSynchronization,
+ kRustFlowIdentification,
+ kRustSingleReservationFlow,
+ kRustAlternateNetworkAddressType,
+ kRustForwardErrorCorrection,
+ kRustDecodingDependency,
+ kRustBundle,
+};
+
+struct RustSdpAttributeGroup {
+ RustSdpAttributeGroupSemantic semantic;
+ StringVec* tags;
+};
+
+struct RustSdpAttributeRtcp {
+ uint32_t port;
+ RustExplicitlyTypedAddress unicastAddr;
+ bool has_address;
+};
+
+struct RustSdpAttributeSctpmap {
+ uint32_t port;
+ uint32_t channels;
+};
+
+struct RustSdpAttributeSimulcastId {
+ StringView id;
+ bool paused;
+};
+
+struct RustSdpAttributeSimulcastIdVec;
+struct RustSdpAttributeSimulcastVersion {
+ RustSdpAttributeSimulcastIdVec* ids;
+};
+
+struct RustSdpAttributeSimulcastVersionVec;
+struct RustSdpAttributeSimulcast {
+ RustSdpAttributeSimulcastVersionVec* send;
+ RustSdpAttributeSimulcastVersionVec* recv;
+};
+
+enum class RustDirection {
+ kRustRecvonly,
+ kRustSendonly,
+ kRustSendrecv,
+ kRustInactive
+};
+
+struct RustSdpAttributeRemoteCandidate {
+ uint32_t component;
+ RustAddress address;
+ uint32_t port;
+};
+
+struct RustSdpAttributeExtmap {
+ uint16_t id;
+ bool direction_specified;
+ RustDirection direction;
+ StringView url;
+ StringView extensionAttributes;
+};
+
+extern "C" {
+
+size_t string_vec_len(const StringVec* vec);
+nsresult string_vec_get_view(const StringVec* vec, size_t index,
+ StringView* str);
+nsresult free_boxed_string_vec(StringVec* vec);
+
+size_t f32_vec_len(const F32Vec* vec);
+nsresult f32_vec_get(const F32Vec* vec, size_t index, float* ret);
+
+size_t u32_vec_len(const U32Vec* vec);
+nsresult u32_vec_get(const U32Vec* vec, size_t index, uint32_t* ret);
+
+size_t u16_vec_len(const U16Vec* vec);
+nsresult u16_vec_get(const U16Vec* vec, size_t index, uint16_t* ret);
+
+size_t u8_vec_len(const U8Vec* vec);
+nsresult u8_vec_get(const U8Vec* vec, size_t index, uint8_t* ret);
+
+size_t ssrc_vec_len(const SsrcVec* vec);
+nsresult ssrc_vec_get_id(const SsrcVec* vec, size_t index, uint32_t* ret);
+
+void sdp_free_string(char* string);
+
+nsresult parse_sdp(StringView sdp, bool fail_on_warning, RustSdpSession** ret,
+ RustSdpError** err);
+RustSdpSession* sdp_new_reference(RustSdpSession* aSess);
+RustSdpSession* create_sdp_clone(const RustSdpSession* aSess);
+void sdp_free_session(RustSdpSession* ret);
+size_t sdp_get_error_line_num(const RustSdpError* err);
+char* sdp_get_error_message(const RustSdpError* err);
+void sdp_free_error_message(char* message);
+void sdp_free_error(RustSdpError* err);
+
+RustSdpOrigin sdp_get_origin(const RustSdpSession* aSess);
+
+uint32_t get_sdp_bandwidth(const RustSdpSession* aSess,
+ const char* aBandwidthType);
+BandwidthVec* sdp_get_session_bandwidth_vec(const RustSdpSession* aSess);
+BandwidthVec* sdp_get_media_bandwidth_vec(const RustMediaSection* aMediaSec);
+char* sdp_serialize_bandwidth(const BandwidthVec* bandwidths);
+bool sdp_session_has_connection(const RustSdpSession* aSess);
+nsresult sdp_get_session_connection(const RustSdpSession* aSess,
+ RustSdpConnection* ret);
+RustAttributeList* get_sdp_session_attributes(const RustSdpSession* aSess);
+
+size_t sdp_media_section_count(const RustSdpSession* aSess);
+RustMediaSection* sdp_get_media_section(const RustSdpSession* aSess,
+ size_t aLevel);
+nsresult sdp_add_media_section(RustSdpSession* aSess, uint32_t aMediaType,
+ uint32_t aDirection, uint16_t aPort,
+ uint32_t aProtocol, uint32_t aAddrType,
+ StringView aAddr);
+RustSdpMediaValue sdp_rust_get_media_type(const RustMediaSection* aMediaSec);
+RustSdpProtocolValue sdp_get_media_protocol(const RustMediaSection* aMediaSec);
+RustSdpFormatType sdp_get_format_type(const RustMediaSection* aMediaSec);
+StringVec* sdp_get_format_string_vec(const RustMediaSection* aMediaSec);
+U32Vec* sdp_get_format_u32_vec(const RustMediaSection* aMediaSec);
+void sdp_set_media_port(const RustMediaSection* aMediaSec, uint32_t aPort);
+uint32_t sdp_get_media_port(const RustMediaSection* aMediaSec);
+uint32_t sdp_get_media_port_count(const RustMediaSection* aMediaSec);
+uint32_t sdp_get_media_bandwidth(const RustMediaSection* aMediaSec,
+ const char* aBandwidthType);
+bool sdp_media_has_connection(const RustMediaSection* aMediaSec);
+nsresult sdp_get_media_connection(const RustMediaSection* aMediaSec,
+ RustSdpConnection* ret);
+
+RustAttributeList* sdp_get_media_attribute_list(
+ const RustMediaSection* aMediaSec);
+
+nsresult sdp_media_add_codec(const RustMediaSection* aMediaSec, uint8_t aPT,
+ StringView aCodecName, uint32_t aClockrate,
+ uint16_t channels);
+void sdp_media_clear_codecs(const RustMediaSection* aMediaSec);
+nsresult sdp_media_add_datachannel(const RustMediaSection* aMediaSec,
+ StringView aName, uint16_t aPort,
+ uint16_t streams, uint32_t aMessageSize);
+
+nsresult sdp_get_iceufrag(const RustAttributeList* aList, StringView* ret);
+nsresult sdp_get_icepwd(const RustAttributeList* aList, StringView* ret);
+nsresult sdp_get_identity(const RustAttributeList* aList, StringView* ret);
+nsresult sdp_get_iceoptions(const RustAttributeList* aList, StringVec** ret);
+
+nsresult sdp_get_dtls_message(const RustAttributeList* aList,
+ RustSdpAttributeDtlsMessage* ret);
+
+size_t sdp_get_fingerprint_count(const RustAttributeList* aList);
+void sdp_get_fingerprints(const RustAttributeList* aList, size_t listSize,
+ RustSdpAttributeFingerprint* ret);
+
+nsresult sdp_get_setup(const RustAttributeList* aList, RustSdpSetup* ret);
+
+size_t sdp_get_ssrc_count(const RustAttributeList* aList);
+void sdp_get_ssrcs(const RustAttributeList* aList, size_t listSize,
+ RustSdpAttributeSsrc* ret);
+
+size_t sdp_get_ssrc_group_count(const RustAttributeList* aList);
+void sdp_get_ssrc_groups(const RustAttributeList* aList, size_t listSize,
+ RustSdpAttributeSsrcGroup* ret);
+
+size_t sdp_get_rtpmap_count(const RustAttributeList* aList);
+void sdp_get_rtpmaps(const RustAttributeList* aList, size_t listSize,
+ RustSdpAttributeRtpmap* ret);
+
+size_t sdp_get_fmtp_count(const RustAttributeList* aList);
+size_t sdp_get_fmtp(const RustAttributeList* aList, size_t listSize,
+ RustSdpAttributeFmtp* ret);
+
+int64_t sdp_get_ptime(const RustAttributeList* aList);
+int64_t sdp_get_max_msg_size(const RustAttributeList* aList);
+int64_t sdp_get_sctp_port(const RustAttributeList* aList);
+nsresult sdp_get_maxptime(const RustAttributeList* aList, uint64_t* aMaxPtime);
+
+RustSdpAttributeFlags sdp_get_attribute_flags(const RustAttributeList* aList);
+
+nsresult sdp_get_mid(const RustAttributeList* aList, StringView* ret);
+
+size_t sdp_get_msid_count(const RustAttributeList* aList);
+void sdp_get_msids(const RustAttributeList* aList, size_t listSize,
+ RustSdpAttributeMsid* ret);
+
+size_t sdp_get_msid_semantic_count(RustAttributeList* aList);
+void sdp_get_msid_semantics(const RustAttributeList* aList, size_t listSize,
+ RustSdpAttributeMsidSemantic* ret);
+
+size_t sdp_get_group_count(const RustAttributeList* aList);
+void sdp_get_groups(const RustAttributeList* aList, size_t listSize,
+ RustSdpAttributeGroup* ret);
+
+nsresult sdp_get_rtcp(const RustAttributeList* aList,
+ RustSdpAttributeRtcp* ret);
+
+size_t sdp_get_rtcpfb_count(const RustAttributeList* aList);
+void sdp_get_rtcpfbs(const RustAttributeList* aList, size_t listSize,
+ RustSdpAttributeRtcpFb* ret);
+
+size_t sdp_get_imageattr_count(const RustAttributeList* aList);
+void sdp_get_imageattrs(const RustAttributeList* aList, size_t listSize,
+ RustSdpAttributeImageAttr* ret);
+
+size_t sdp_imageattr_get_set_count(const RustSdpAttributeImageAttrSetVec* sets);
+void sdp_imageattr_get_sets(const RustSdpAttributeImageAttrSetVec* sets,
+ size_t listSize, RustSdpAttributeImageAttrSet* ret);
+
+size_t sdp_get_sctpmap_count(const RustAttributeList* aList);
+void sdp_get_sctpmaps(const RustAttributeList* aList, size_t listSize,
+ RustSdpAttributeSctpmap* ret);
+
+nsresult sdp_get_simulcast(const RustAttributeList* aList,
+ RustSdpAttributeSimulcast* ret);
+
+size_t sdp_simulcast_get_version_count(
+ const RustSdpAttributeSimulcastVersionVec* aVersionList);
+void sdp_simulcast_get_versions(
+ const RustSdpAttributeSimulcastVersionVec* aversionList, size_t listSize,
+ RustSdpAttributeSimulcastVersion* ret);
+
+size_t sdp_simulcast_get_ids_count(const RustSdpAttributeSimulcastIdVec* aAlts);
+void sdp_simulcast_get_ids(const RustSdpAttributeSimulcastIdVec* aAlts,
+ size_t listSize, RustSdpAttributeSimulcastId* ret);
+
+RustDirection sdp_get_direction(const RustAttributeList* aList);
+
+size_t sdp_get_remote_candidate_count(const RustAttributeList* aList);
+void sdp_get_remote_candidates(const RustAttributeList* aList, size_t listSize,
+ RustSdpAttributeRemoteCandidate* ret);
+
+size_t sdp_get_candidate_count(const RustAttributeList* aList);
+void sdp_get_candidates(const RustAttributeList* aLisst, size_t listSize,
+ StringVec** ret);
+
+size_t sdp_get_rid_count(const RustAttributeList* aList);
+void sdp_get_rids(const RustAttributeList* aList, size_t listSize,
+ RustSdpAttributeRid* ret);
+
+size_t sdp_get_extmap_count(const RustAttributeList* aList);
+void sdp_get_extmaps(const RustAttributeList* aList, size_t listSize,
+ RustSdpAttributeExtmap* ret);
+
+} // extern "C"
+
+#endif
diff --git a/dom/media/webrtc/sdp/RsdparsaSdpMediaSection.cpp b/dom/media/webrtc/sdp/RsdparsaSdpMediaSection.cpp
new file mode 100644
index 0000000000..25084db2ad
--- /dev/null
+++ b/dom/media/webrtc/sdp/RsdparsaSdpMediaSection.cpp
@@ -0,0 +1,253 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "sdp/SdpMediaSection.h"
+#include "sdp/RsdparsaSdpMediaSection.h"
+
+#include "sdp/RsdparsaSdpGlue.h"
+#include "sdp/RsdparsaSdpInc.h"
+
+#include <ostream>
+
+#ifdef CRLF
+# undef CRLF
+#endif
+#define CRLF "\r\n"
+
+namespace mozilla {
+
+RsdparsaSdpMediaSection::RsdparsaSdpMediaSection(
+ size_t level, RsdparsaSessionHandle session,
+ const RustMediaSection* const section,
+ const RsdparsaSdpAttributeList* sessionLevel)
+ : SdpMediaSection(level), mSession(std::move(session)), mSection(section) {
+ switch (sdp_rust_get_media_type(section)) {
+ case RustSdpMediaValue::kRustAudio:
+ mMediaType = kAudio;
+ break;
+ case RustSdpMediaValue::kRustVideo:
+ mMediaType = kVideo;
+ break;
+ case RustSdpMediaValue::kRustApplication:
+ mMediaType = kApplication;
+ break;
+ }
+
+ RsdparsaSessionHandle attributeSession(sdp_new_reference(mSession.get()));
+ mAttributeList.reset(new RsdparsaSdpAttributeList(std::move(attributeSession),
+ section, sessionLevel));
+
+ LoadFormats();
+ LoadConnection();
+}
+
+unsigned int RsdparsaSdpMediaSection::GetPort() const {
+ return sdp_get_media_port(mSection);
+}
+
+void RsdparsaSdpMediaSection::SetPort(unsigned int port) {
+ sdp_set_media_port(mSection, port);
+}
+
+unsigned int RsdparsaSdpMediaSection::GetPortCount() const {
+ return sdp_get_media_port_count(mSection);
+}
+
+SdpMediaSection::Protocol RsdparsaSdpMediaSection::GetProtocol() const {
+ switch (sdp_get_media_protocol(mSection)) {
+ case RustSdpProtocolValue::kRustRtpSavpf:
+ return kRtpSavpf;
+ case RustSdpProtocolValue::kRustUdpTlsRtpSavp:
+ return kUdpTlsRtpSavp;
+ case RustSdpProtocolValue::kRustTcpDtlsRtpSavp:
+ return kTcpDtlsRtpSavp;
+ case RustSdpProtocolValue::kRustUdpTlsRtpSavpf:
+ return kUdpTlsRtpSavpf;
+ case RustSdpProtocolValue::kRustTcpDtlsRtpSavpf:
+ return kTcpDtlsRtpSavpf;
+ case RustSdpProtocolValue::kRustDtlsSctp:
+ return kDtlsSctp;
+ case RustSdpProtocolValue::kRustUdpDtlsSctp:
+ return kUdpDtlsSctp;
+ case RustSdpProtocolValue::kRustTcpDtlsSctp:
+ return kTcpDtlsSctp;
+ case RustSdpProtocolValue::kRustRtpAvp:
+ return kRtpAvp;
+ case RustSdpProtocolValue::kRustRtpAvpf:
+ return kRtpAvpf;
+ case RustSdpProtocolValue::kRustRtpSavp:
+ return kRtpSavp;
+ }
+ MOZ_CRASH("invalid media protocol");
+}
+
+const SdpConnection& RsdparsaSdpMediaSection::GetConnection() const {
+ MOZ_ASSERT(mConnection);
+ return *mConnection;
+}
+
+SdpConnection& RsdparsaSdpMediaSection::GetConnection() {
+ MOZ_ASSERT(mConnection);
+ return *mConnection;
+}
+
+uint32_t RsdparsaSdpMediaSection::GetBandwidth(const std::string& type) const {
+ return sdp_get_media_bandwidth(mSection, type.c_str());
+}
+
+const std::vector<std::string>& RsdparsaSdpMediaSection::GetFormats() const {
+ return mFormats;
+}
+
+const SdpAttributeList& RsdparsaSdpMediaSection::GetAttributeList() const {
+ return *mAttributeList;
+}
+
+SdpAttributeList& RsdparsaSdpMediaSection::GetAttributeList() {
+ return *mAttributeList;
+}
+
+SdpDirectionAttribute RsdparsaSdpMediaSection::GetDirectionAttribute() const {
+ return SdpDirectionAttribute(mAttributeList->GetDirection());
+}
+
+void RsdparsaSdpMediaSection::AddCodec(const std::string& pt,
+ const std::string& name,
+ uint32_t clockrate, uint16_t channels) {
+ StringView rustName{name.c_str(), name.size()};
+
+ // call the rust interface
+ auto nr = sdp_media_add_codec(mSection, std::stoul(pt), rustName, clockrate,
+ channels);
+
+ if (NS_SUCCEEDED(nr)) {
+ // If the rust call was successful, adjust the shadow C++ structures
+ mFormats.push_back(pt);
+
+ // Add a rtpmap in mAttributeList
+ SdpRtpmapAttributeList* rtpmap = new SdpRtpmapAttributeList();
+ if (mAttributeList->HasAttribute(SdpAttribute::kRtpmapAttribute)) {
+ const SdpRtpmapAttributeList& old = mAttributeList->GetRtpmap();
+ for (auto it = old.mRtpmaps.begin(); it != old.mRtpmaps.end(); ++it) {
+ rtpmap->mRtpmaps.push_back(*it);
+ }
+ }
+
+ SdpRtpmapAttributeList::CodecType codec =
+ SdpRtpmapAttributeList::kOtherCodec;
+ if (name == "opus") {
+ codec = SdpRtpmapAttributeList::kOpus;
+ } else if (name == "VP8") {
+ codec = SdpRtpmapAttributeList::kVP8;
+ } else if (name == "VP9") {
+ codec = SdpRtpmapAttributeList::kVP9;
+ } else if (name == "H264") {
+ codec = SdpRtpmapAttributeList::kH264;
+ }
+
+ rtpmap->PushEntry(pt, codec, name, clockrate, channels);
+ mAttributeList->SetAttribute(rtpmap);
+ }
+}
+
+void RsdparsaSdpMediaSection::ClearCodecs() {
+ // Clear the codecs in rust
+ sdp_media_clear_codecs(mSection);
+
+ mFormats.clear();
+ mAttributeList->RemoveAttribute(SdpAttribute::kRtpmapAttribute);
+ mAttributeList->RemoveAttribute(SdpAttribute::kFmtpAttribute);
+ mAttributeList->RemoveAttribute(SdpAttribute::kSctpmapAttribute);
+ mAttributeList->RemoveAttribute(SdpAttribute::kRtcpFbAttribute);
+}
+
+void RsdparsaSdpMediaSection::AddDataChannel(const std::string& name,
+ uint16_t port, uint16_t streams,
+ uint32_t message_size) {
+ StringView rustName{name.c_str(), name.size()};
+ auto nr = sdp_media_add_datachannel(mSection, rustName, port, streams,
+ message_size);
+ if (NS_SUCCEEDED(nr)) {
+ // Update the formats
+ mFormats.clear();
+ LoadFormats();
+
+ // Update the attribute list
+ RsdparsaSessionHandle sessHandle(sdp_new_reference(mSession.get()));
+ auto sessAttributes = mAttributeList->mSessionAttributes;
+ mAttributeList.reset(new RsdparsaSdpAttributeList(
+ std::move(sessHandle), mSection, sessAttributes));
+ }
+}
+
+void RsdparsaSdpMediaSection::Serialize(std::ostream& os) const {
+ os << "m=" << mMediaType << " " << GetPort();
+ if (GetPortCount()) {
+ os << "/" << GetPortCount();
+ }
+ os << " " << GetProtocol();
+ for (auto i = mFormats.begin(); i != mFormats.end(); ++i) {
+ os << " " << (*i);
+ }
+ os << CRLF;
+
+ // We dont do i=
+
+ if (mConnection) {
+ os << *mConnection;
+ }
+
+ BandwidthVec* bwVec = sdp_get_media_bandwidth_vec(mSection);
+ char* bwString = sdp_serialize_bandwidth(bwVec);
+ if (bwString) {
+ os << bwString;
+ sdp_free_string(bwString);
+ }
+
+ // We dont do k= because they're evil
+
+ os << *mAttributeList;
+}
+
+void RsdparsaSdpMediaSection::LoadFormats() {
+ RustSdpFormatType formatType = sdp_get_format_type(mSection);
+ if (formatType == RustSdpFormatType::kRustIntegers) {
+ U32Vec* vec = sdp_get_format_u32_vec(mSection);
+ size_t len = u32_vec_len(vec);
+ for (size_t i = 0; i < len; i++) {
+ uint32_t val;
+ u32_vec_get(vec, i, &val);
+ mFormats.push_back(std::to_string(val));
+ }
+ } else {
+ StringVec* vec = sdp_get_format_string_vec(mSection);
+ mFormats = convertStringVec(vec);
+ }
+}
+
+UniquePtr<SdpConnection> convertRustConnection(RustSdpConnection conn) {
+ auto address = convertExplicitlyTypedAddress(&conn.addr);
+ return MakeUnique<SdpConnection>(address.first, address.second, conn.ttl,
+ conn.amount);
+}
+
+void RsdparsaSdpMediaSection::LoadConnection() {
+ RustSdpConnection conn;
+ nsresult nr;
+ if (sdp_media_has_connection(mSection)) {
+ nr = sdp_get_media_connection(mSection, &conn);
+ if (NS_SUCCEEDED(nr)) {
+ mConnection = convertRustConnection(conn);
+ }
+ } else if (sdp_session_has_connection(mSession.get())) {
+ nr = sdp_get_session_connection(mSession.get(), &conn);
+ if (NS_SUCCEEDED(nr)) {
+ mConnection = convertRustConnection(conn);
+ }
+ }
+}
+
+} // namespace mozilla
diff --git a/dom/media/webrtc/sdp/RsdparsaSdpMediaSection.h b/dom/media/webrtc/sdp/RsdparsaSdpMediaSection.h
new file mode 100644
index 0000000000..3c193bd99c
--- /dev/null
+++ b/dom/media/webrtc/sdp/RsdparsaSdpMediaSection.h
@@ -0,0 +1,71 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _RUSTSDPMEDIASECTION_H_
+#define _RUSTSDPMEDIASECTION_H_
+
+#include "mozilla/UniquePtr.h"
+#include "sdp/RsdparsaSdpInc.h"
+#include "sdp/RsdparsaSdpGlue.h"
+#include "sdp/SdpMediaSection.h"
+#include "sdp/RsdparsaSdpAttributeList.h"
+
+namespace mozilla {
+
+class RsdparsaSdp;
+class SdpParser;
+
+class RsdparsaSdpMediaSection final : public SdpMediaSection {
+ friend class RsdparsaSdp;
+
+ public:
+ ~RsdparsaSdpMediaSection() {}
+
+ MediaType GetMediaType() const override { return mMediaType; }
+
+ unsigned int GetPort() const override;
+ void SetPort(unsigned int port) override;
+ unsigned int GetPortCount() const override;
+ Protocol GetProtocol() const override;
+ const SdpConnection& GetConnection() const override;
+ SdpConnection& GetConnection() override;
+ uint32_t GetBandwidth(const std::string& type) const override;
+ const std::vector<std::string>& GetFormats() const override;
+
+ const SdpAttributeList& GetAttributeList() const override;
+ SdpAttributeList& GetAttributeList() override;
+ SdpDirectionAttribute GetDirectionAttribute() const override;
+
+ void AddCodec(const std::string& pt, const std::string& name,
+ uint32_t clockrate, uint16_t channels) override;
+ void ClearCodecs() override;
+
+ void AddDataChannel(const std::string& name, uint16_t port, uint16_t streams,
+ uint32_t message_size) override;
+
+ void Serialize(std::ostream&) const override;
+
+ private:
+ RsdparsaSdpMediaSection(size_t level, RsdparsaSessionHandle session,
+ const RustMediaSection* const section,
+ const RsdparsaSdpAttributeList* sessionLevel);
+
+ void LoadFormats();
+ void LoadConnection();
+
+ RsdparsaSessionHandle mSession;
+ const RustMediaSection* mSection;
+
+ MediaType mMediaType;
+ std::vector<std::string> mFormats;
+
+ UniquePtr<SdpConnection> mConnection;
+
+ UniquePtr<RsdparsaSdpAttributeList> mAttributeList;
+};
+} // namespace mozilla
+
+#endif
diff --git a/dom/media/webrtc/sdp/RsdparsaSdpParser.cpp b/dom/media/webrtc/sdp/RsdparsaSdpParser.cpp
new file mode 100644
index 0000000000..b955bcd46b
--- /dev/null
+++ b/dom/media/webrtc/sdp/RsdparsaSdpParser.cpp
@@ -0,0 +1,73 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsError.h"
+
+#include "mozilla/UniquePtr.h"
+
+#include "sdp/Sdp.h"
+#include "sdp/RsdparsaSdp.h"
+#include "sdp/RsdparsaSdpParser.h"
+#include "sdp/RsdparsaSdpInc.h"
+#include "sdp/RsdparsaSdpGlue.h"
+
+namespace mozilla {
+
+const std::string& RsdparsaSdpParser::ParserName() {
+ static const std::string& WEBRTC_SDP_NAME = "WEBRTCSDP";
+ return WEBRTC_SDP_NAME;
+}
+
+UniquePtr<SdpParser::Results> RsdparsaSdpParser::Parse(
+ const std::string& aText) {
+ UniquePtr<SdpParser::InternalResults> results(
+ new SdpParser::InternalResults(Name()));
+ RustSdpSession* result = nullptr;
+ RustSdpError* err = nullptr;
+ StringView sdpTextView{aText.c_str(), aText.length()};
+ nsresult rv = parse_sdp(sdpTextView, false, &result, &err);
+ if (rv != NS_OK) {
+ size_t line = sdp_get_error_line_num(err);
+ char* cString = sdp_get_error_message(err);
+ if (cString) {
+ std::string errMsg(cString);
+ sdp_free_error_message(cString);
+ sdp_free_error(err);
+ results->AddParseError(line, errMsg);
+ } else {
+ results->AddParseError(line, "Unable to retreive parse error.");
+ }
+ return results;
+ }
+
+ if (err) {
+ size_t line = sdp_get_error_line_num(err);
+ char* cString = sdp_get_error_message(err);
+ if (cString) {
+ std::string warningMsg(cString);
+ results->AddParseWarning(line, warningMsg);
+ sdp_free_error_message(cString);
+ sdp_free_error(err);
+ } else {
+ results->AddParseWarning(line, "Unable to retreive parse warning.");
+ }
+ }
+
+ RsdparsaSessionHandle uniqueResult(result);
+ RustSdpOrigin rustOrigin = sdp_get_origin(uniqueResult.get());
+ auto address = convertExplicitlyTypedAddress(&rustOrigin.addr);
+ SdpOrigin origin(convertStringView(rustOrigin.username), rustOrigin.sessionId,
+ rustOrigin.sessionVersion, address.first, address.second);
+
+ results->SetSdp(MakeUnique<RsdparsaSdp>(std::move(uniqueResult), origin));
+ return results;
+}
+
+bool RsdparsaSdpParser::IsNamed(const std::string& aName) {
+ return aName == ParserName();
+}
+
+} // namespace mozilla
diff --git a/dom/media/webrtc/sdp/RsdparsaSdpParser.h b/dom/media/webrtc/sdp/RsdparsaSdpParser.h
new file mode 100644
index 0000000000..443aa46557
--- /dev/null
+++ b/dom/media/webrtc/sdp/RsdparsaSdpParser.h
@@ -0,0 +1,34 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _RUSTSDPPARSER_H_
+#define _RUSTSDPPARSER_H_
+
+#include <string>
+
+#include "mozilla/UniquePtr.h"
+
+#include "sdp/SdpParser.h"
+
+namespace mozilla {
+
+class RsdparsaSdpParser final : public SdpParser {
+ static const std::string& ParserName();
+
+ public:
+ RsdparsaSdpParser() = default;
+ virtual ~RsdparsaSdpParser() = default;
+
+ const std::string& Name() const override { return ParserName(); }
+
+ UniquePtr<SdpParser::Results> Parse(const std::string& text) override;
+
+ static bool IsNamed(const std::string& aName);
+};
+
+} // namespace mozilla
+
+#endif
diff --git a/dom/media/webrtc/sdp/Sdp.h b/dom/media/webrtc/sdp/Sdp.h
new file mode 100644
index 0000000000..7576cac46f
--- /dev/null
+++ b/dom/media/webrtc/sdp/Sdp.h
@@ -0,0 +1,166 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+
+ ,-----. ,--. ,--.
+ ' .--./ ,--,--.,--.,--.,-' '-.`--' ,---. ,--,--,
+ | | ' ,-. || || |'-. .-',--.| .-. || `
+ ' '--'\\ '-' |' '' ' | | | |' '-' '| || |
+ `-----' `--`--' `----' `--' `--' `---' `--''--'
+
+ :+o+-
+ -dNNNNNd.
+ yNNNNNNNs
+ :mNNNNNm-
+ `/sso/``-://-
+ .:+sydNNNNNNms: `://`
+ `-/+shmNNNNNNNNNNNNNNNms- :mNNNm/
+ `-/oydmNNNNNNNNNNNNNNNNNNNNNNNNdo- +NNNNNN+
+ .shmNNNNNNNNNNNmdyo/:dNNNNNNNNNNNNNNNNdo. `sNNNNNm+
+ hNNNNNNNNmhs+:-` .dNNNNNNNNNNNNNNNNNNNNh+-` `hNNNNNm:
+ -yddyo/:. -dNNNNm::ymNNNNNNNNNNNNNNNmdy+/dNNNNNd.
+ :mNNNNd. `/ymNNNNNNNNNNNNNNNNNNNNNNh`
+ +NNNNNh` `+hNNNNNNNNNNNNNNNNNNNs
+ sNNNNNy` .yNNNNNm`-/oymNNNm+
+ `yNNNNNo oNNNNNm` `-.
+ .dNNNNm/ oNNNNNm`
+ oNNNNm: +NNNNNm`
+ `+yho. +NNNNNm`
+ +NNNNNNs.
+ `yNNNNNNmy-
+ -smNNNNNNh:
+ .smNNNNNNh/
+ `omNNNNNNd:
+ `+dNNNNNd
+ ````......```` /hmdy-
+ `.:/+osyhddmNNMMMMMMMMMMMMMMMMMMMMNNmddhyso+/:.`
+ `-+shmNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMNmhs+-`
+ -smMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMds-
+ hMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMh
+ yMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMs
+ .ohNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMNh+.
+ ./oydmMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMmhyo:.
+ `.:/+osyyhddmmNNMMMMMMMMMMMMMMNNmmddhyyso+/:.`
+
+ ,--------.,--. ,--. ,--.
+ '--. .--'| ,---. `--' ,---. | | ,---.
+ | | | .-. |,--.( .-' | |( .-'
+ | | | | | || |.-' `) | |.-' `)
+ `--' `--' `--'`--'`----' `--'`----'
+ ,--.
+ ,---. ,------. ,------. ,--. | |
+ ' .-' | .-. \ | .--. ' ,--,--.,--.--.,-' '-. ,--,--.| |
+ `. `-. | | \ :| '--' |' ,-. || .--''-. .-'' ,-. || |
+ .-' || '--' /| | --' \ '-' || | | | \ '-' |`--'
+ `-----' `-------' `--' `--`--'`--' `--' `--`--'.--.
+ '__'
+*/
+
+#ifndef _SDP_H_
+#define _SDP_H_
+
+#include <ostream>
+#include <vector>
+#include <sstream>
+#include "mozilla/UniquePtr.h"
+#include "mozilla/Maybe.h"
+#include "sdp/SdpMediaSection.h"
+#include "sdp/SdpAttributeList.h"
+#include "sdp/SdpEnum.h"
+
+namespace mozilla {
+
+class SdpOrigin;
+class SdpEncryptionKey;
+class SdpMediaSection;
+
+/**
+ * Base class for an SDP
+ */
+class Sdp {
+ public:
+ Sdp() = default;
+ virtual ~Sdp() = default;
+
+ virtual Sdp* Clone() const = 0;
+
+ virtual const SdpOrigin& GetOrigin() const = 0;
+ // Note: connection information is always retrieved from media sections
+ virtual uint32_t GetBandwidth(const std::string& type) const = 0;
+
+ virtual const SdpAttributeList& GetAttributeList() const = 0;
+ virtual SdpAttributeList& GetAttributeList() = 0;
+
+ virtual size_t GetMediaSectionCount() const = 0;
+ virtual const SdpMediaSection& GetMediaSection(size_t level) const = 0;
+ virtual SdpMediaSection& GetMediaSection(size_t level) = 0;
+
+ virtual SdpMediaSection& AddMediaSection(SdpMediaSection::MediaType media,
+ SdpDirectionAttribute::Direction dir,
+ uint16_t port,
+ SdpMediaSection::Protocol proto,
+ sdp::AddrType addrType,
+ const std::string& addr) = 0;
+
+ virtual void Serialize(std::ostream&) const = 0;
+
+ std::string ToString() const;
+};
+
+inline std::ostream& operator<<(std::ostream& os, const Sdp& sdp) {
+ sdp.Serialize(os);
+ return os;
+}
+
+inline std::string Sdp::ToString() const {
+ std::stringstream s;
+ s << *this;
+ return s.str();
+}
+
+class SdpOrigin {
+ public:
+ SdpOrigin(const std::string& username, uint64_t sessId, uint64_t sessVer,
+ sdp::AddrType addrType, const std::string& addr)
+ : mUsername(username),
+ mSessionId(sessId),
+ mSessionVersion(sessVer),
+ mAddrType(addrType),
+ mAddress(addr) {}
+
+ const std::string& GetUsername() const { return mUsername; }
+
+ uint64_t GetSessionId() const { return mSessionId; }
+
+ uint64_t GetSessionVersion() const { return mSessionVersion; }
+
+ sdp::AddrType GetAddrType() const { return mAddrType; }
+
+ const std::string& GetAddress() const { return mAddress; }
+
+ void Serialize(std::ostream& os) const {
+ sdp::NetType netType = sdp::kInternet;
+ os << "o=" << mUsername << " " << mSessionId << " " << mSessionVersion
+ << " " << netType << " " << mAddrType << " " << mAddress << "\r\n";
+ }
+
+ private:
+ std::string mUsername;
+ uint64_t mSessionId;
+ uint64_t mSessionVersion;
+ sdp::AddrType mAddrType;
+ std::string mAddress;
+};
+
+inline std::ostream& operator<<(std::ostream& os, const SdpOrigin& origin) {
+ origin.Serialize(os);
+ return os;
+}
+
+} // namespace mozilla
+
+#endif
diff --git a/dom/media/webrtc/sdp/SdpAttribute.cpp b/dom/media/webrtc/sdp/SdpAttribute.cpp
new file mode 100644
index 0000000000..cacb25e6b1
--- /dev/null
+++ b/dom/media/webrtc/sdp/SdpAttribute.cpp
@@ -0,0 +1,1562 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "sdp/SdpAttribute.h"
+#include "sdp/SdpHelper.h"
+#include <iomanip>
+#include <bitset>
+
+#ifdef CRLF
+# undef CRLF
+#endif
+#define CRLF "\r\n"
+
+namespace mozilla {
+
+static unsigned char PeekChar(std::istream& is, std::string* error) {
+ int next = is.peek();
+ if (next == EOF) {
+ *error = "Truncated";
+ return 0;
+ }
+
+ return next;
+}
+
+static std::string ParseToken(std::istream& is, const std::string& delims,
+ std::string* error) {
+ std::string token;
+ while (is) {
+ unsigned char c = PeekChar(is, error);
+ if (!c || (delims.find(c) != std::string::npos)) {
+ break;
+ }
+ token.push_back(std::tolower(is.get()));
+ }
+ return token;
+}
+
+static bool SkipChar(std::istream& is, unsigned char c, std::string* error) {
+ if (PeekChar(is, error) != c) {
+ *error = "Expected \'";
+ error->push_back(c);
+ error->push_back('\'');
+ return false;
+ }
+
+ is.get();
+ return true;
+}
+
+void SdpConnectionAttribute::Serialize(std::ostream& os) const {
+ os << "a=" << mType << ":" << mValue << CRLF;
+}
+
+void SdpDirectionAttribute::Serialize(std::ostream& os) const {
+ os << "a=" << mValue << CRLF;
+}
+
+void SdpDtlsMessageAttribute::Serialize(std::ostream& os) const {
+ os << "a=" << mType << ":" << mRole << " " << mValue << CRLF;
+}
+
+bool SdpDtlsMessageAttribute::Parse(std::istream& is, std::string* error) {
+ std::string roleToken = ParseToken(is, " ", error);
+ if (roleToken == "server") {
+ mRole = kServer;
+ } else if (roleToken == "client") {
+ mRole = kClient;
+ } else {
+ *error = "Invalid dtls-message role; must be either client or server";
+ return false;
+ }
+
+ is >> std::ws;
+
+ std::string s(std::istreambuf_iterator<char>(is), {});
+ mValue = s;
+
+ return true;
+}
+
+void SdpExtmapAttributeList::Serialize(std::ostream& os) const {
+ for (auto i = mExtmaps.begin(); i != mExtmaps.end(); ++i) {
+ os << "a=" << mType << ":" << i->entry;
+ if (i->direction_specified) {
+ os << "/" << i->direction;
+ }
+ os << " " << i->extensionname;
+ if (i->extensionattributes.length()) {
+ os << " " << i->extensionattributes;
+ }
+ os << CRLF;
+ }
+}
+
+void SdpFingerprintAttributeList::Serialize(std::ostream& os) const {
+ for (auto i = mFingerprints.begin(); i != mFingerprints.end(); ++i) {
+ os << "a=" << mType << ":" << i->hashFunc << " "
+ << FormatFingerprint(i->fingerprint) << CRLF;
+ }
+}
+
+// Format the fingerprint in RFC 4572 Section 5 attribute format
+std::string SdpFingerprintAttributeList::FormatFingerprint(
+ const std::vector<uint8_t>& fp) {
+ if (fp.empty()) {
+ MOZ_ASSERT(false, "Cannot format an empty fingerprint.");
+ return "";
+ }
+
+ std::ostringstream os;
+ for (auto i = fp.begin(); i != fp.end(); ++i) {
+ os << ":" << std::hex << std::uppercase << std::setw(2) << std::setfill('0')
+ << static_cast<uint32_t>(*i);
+ }
+ return os.str().substr(1);
+}
+
+static uint8_t FromUppercaseHex(char ch) {
+ if ((ch >= '0') && (ch <= '9')) {
+ return ch - '0';
+ }
+ if ((ch >= 'A') && (ch <= 'F')) {
+ return ch - 'A' + 10;
+ }
+ return 16; // invalid
+}
+
+// Parse the fingerprint from RFC 4572 Section 5 attribute format
+std::vector<uint8_t> SdpFingerprintAttributeList::ParseFingerprint(
+ const std::string& str) {
+ size_t targetSize = (str.length() + 1) / 3;
+ std::vector<uint8_t> fp(targetSize);
+ size_t fpIndex = 0;
+
+ if (str.length() % 3 != 2) {
+ fp.clear();
+ return fp;
+ }
+
+ for (size_t i = 0; i < str.length(); i += 3) {
+ uint8_t high = FromUppercaseHex(str[i]);
+ uint8_t low = FromUppercaseHex(str[i + 1]);
+ if (high > 0xf || low > 0xf ||
+ (i + 2 < str.length() && str[i + 2] != ':')) {
+ fp.clear(); // error
+ return fp;
+ }
+ fp[fpIndex++] = high << 4 | low;
+ }
+ return fp;
+}
+
+bool SdpFmtpAttributeList::operator==(const SdpFmtpAttributeList& other) const {
+ return mFmtps == other.mFmtps;
+}
+
+void SdpFmtpAttributeList::Serialize(std::ostream& os) const {
+ for (auto i = mFmtps.begin(); i != mFmtps.end(); ++i) {
+ if (i->parameters) {
+ os << "a=" << mType << ":" << i->format << " ";
+ i->parameters->Serialize(os);
+ os << CRLF;
+ }
+ }
+}
+
+void SdpGroupAttributeList::Serialize(std::ostream& os) const {
+ for (auto i = mGroups.begin(); i != mGroups.end(); ++i) {
+ os << "a=" << mType << ":" << i->semantics;
+ for (auto j = i->tags.begin(); j != i->tags.end(); ++j) {
+ os << " " << (*j);
+ }
+ os << CRLF;
+ }
+}
+
+// We're just using an SdpStringAttribute for this right now
+#if 0
+void SdpIdentityAttribute::Serialize(std::ostream& os) const
+{
+ os << "a=" << mType << ":" << mAssertion;
+ for (auto i = mExtensions.begin(); i != mExtensions.end(); i++) {
+ os << (i == mExtensions.begin() ? " " : ";") << (*i);
+ }
+ os << CRLF;
+}
+#endif
+
+// Class to help with omitting a leading delimiter for the first item in a list
+class SkipFirstDelimiter {
+ public:
+ explicit SkipFirstDelimiter(const std::string& delim)
+ : mDelim(delim), mFirst(true) {}
+
+ std::ostream& print(std::ostream& os) {
+ if (!mFirst) {
+ os << mDelim;
+ }
+ mFirst = false;
+ return os;
+ }
+
+ private:
+ std::string mDelim;
+ bool mFirst;
+};
+
+static std::ostream& operator<<(std::ostream& os, SkipFirstDelimiter& delim) {
+ return delim.print(os);
+}
+
+void SdpImageattrAttributeList::XYRange::Serialize(std::ostream& os) const {
+ if (discreteValues.empty()) {
+ os << "[" << min << ":";
+ if (step != 1) {
+ os << step << ":";
+ }
+ os << max << "]";
+ } else if (discreteValues.size() == 1) {
+ os << discreteValues.front();
+ } else {
+ os << "[";
+ SkipFirstDelimiter comma(",");
+ for (auto value : discreteValues) {
+ os << comma << value;
+ }
+ os << "]";
+ }
+}
+
+template <typename T>
+bool GetUnsigned(std::istream& is, T min, T max, T* value, std::string* error) {
+ if (PeekChar(is, error) == '-') {
+ *error = "Value is less than 0";
+ return false;
+ }
+
+ is >> std::noskipws >> *value;
+
+ if (is.fail()) {
+ *error = "Malformed";
+ return false;
+ }
+
+ if (*value < min) {
+ *error = "Value too small";
+ return false;
+ }
+
+ if (*value > max) {
+ *error = "Value too large";
+ return false;
+ }
+
+ return true;
+}
+
+static bool GetXYValue(std::istream& is, uint32_t* value, std::string* error) {
+ return GetUnsigned<uint32_t>(is, 1, 999999, value, error);
+}
+
+bool SdpImageattrAttributeList::XYRange::ParseDiscreteValues(
+ std::istream& is, std::string* error) {
+ do {
+ uint32_t value;
+ if (!GetXYValue(is, &value, error)) {
+ return false;
+ }
+ discreteValues.push_back(value);
+ } while (SkipChar(is, ',', error));
+
+ return SkipChar(is, ']', error);
+}
+
+bool SdpImageattrAttributeList::XYRange::ParseAfterMin(std::istream& is,
+ std::string* error) {
+ // We have already parsed "[320:", and now expect another uint
+ uint32_t value;
+ if (!GetXYValue(is, &value, error)) {
+ return false;
+ }
+
+ if (SkipChar(is, ':', error)) {
+ // Range with step eg [320:16:640]
+ step = value;
+ // Now |value| should be the max
+ if (!GetXYValue(is, &value, error)) {
+ return false;
+ }
+ }
+
+ max = value;
+ if (min >= max) {
+ *error = "Min is not smaller than max";
+ return false;
+ }
+
+ return SkipChar(is, ']', error);
+}
+
+bool SdpImageattrAttributeList::XYRange::ParseAfterBracket(std::istream& is,
+ std::string* error) {
+ // Either a range, or a list of discrete values
+ // [320:640], [320:16:640], or [320,640]
+ uint32_t value;
+ if (!GetXYValue(is, &value, error)) {
+ return false;
+ }
+
+ if (SkipChar(is, ':', error)) {
+ // Range - [640:480] or [640:16:480]
+ min = value;
+ return ParseAfterMin(is, error);
+ }
+
+ if (SkipChar(is, ',', error)) {
+ discreteValues.push_back(value);
+ return ParseDiscreteValues(is, error);
+ }
+
+ *error = "Expected \':\' or \',\'";
+ return false;
+}
+
+bool SdpImageattrAttributeList::XYRange::Parse(std::istream& is,
+ std::string* error) {
+ if (SkipChar(is, '[', error)) {
+ return ParseAfterBracket(is, error);
+ }
+
+ // Single discrete value
+ uint32_t value;
+ if (!GetXYValue(is, &value, error)) {
+ return false;
+ }
+ discreteValues.push_back(value);
+
+ return true;
+}
+
+static bool GetSPValue(std::istream& is, float* value, std::string* error) {
+ return GetUnsigned<float>(is, 0.1f, 9.9999f, value, error);
+}
+
+static bool GetQValue(std::istream& is, float* value, std::string* error) {
+ return GetUnsigned<float>(is, 0.0f, 1.0f, value, error);
+}
+
+bool SdpImageattrAttributeList::SRange::ParseDiscreteValues(
+ std::istream& is, std::string* error) {
+ do {
+ float value;
+ if (!GetSPValue(is, &value, error)) {
+ return false;
+ }
+ discreteValues.push_back(value);
+ } while (SkipChar(is, ',', error));
+
+ return SkipChar(is, ']', error);
+}
+
+bool SdpImageattrAttributeList::SRange::ParseAfterMin(std::istream& is,
+ std::string* error) {
+ if (!GetSPValue(is, &max, error)) {
+ return false;
+ }
+
+ if (min >= max) {
+ *error = "Min is not smaller than max";
+ return false;
+ }
+
+ return SkipChar(is, ']', error);
+}
+
+bool SdpImageattrAttributeList::SRange::ParseAfterBracket(std::istream& is,
+ std::string* error) {
+ // Either a range, or a list of discrete values
+ float value;
+ if (!GetSPValue(is, &value, error)) {
+ return false;
+ }
+
+ if (SkipChar(is, '-', error)) {
+ min = value;
+ return ParseAfterMin(is, error);
+ }
+
+ if (SkipChar(is, ',', error)) {
+ discreteValues.push_back(value);
+ return ParseDiscreteValues(is, error);
+ }
+
+ *error = "Expected either \'-\' or \',\'";
+ return false;
+}
+
+bool SdpImageattrAttributeList::SRange::Parse(std::istream& is,
+ std::string* error) {
+ if (SkipChar(is, '[', error)) {
+ return ParseAfterBracket(is, error);
+ }
+
+ // Single discrete value
+ float value;
+ if (!GetSPValue(is, &value, error)) {
+ return false;
+ }
+ discreteValues.push_back(value);
+ return true;
+}
+
+bool SdpImageattrAttributeList::PRange::Parse(std::istream& is,
+ std::string* error) {
+ if (!SkipChar(is, '[', error)) {
+ return false;
+ }
+
+ if (!GetSPValue(is, &min, error)) {
+ return false;
+ }
+
+ if (!SkipChar(is, '-', error)) {
+ return false;
+ }
+
+ if (!GetSPValue(is, &max, error)) {
+ return false;
+ }
+
+ if (min >= max) {
+ *error = "min must be smaller than max";
+ return false;
+ }
+
+ if (!SkipChar(is, ']', error)) {
+ return false;
+ }
+ return true;
+}
+
+void SdpImageattrAttributeList::SRange::Serialize(std::ostream& os) const {
+ os << std::setprecision(4) << std::fixed;
+ if (discreteValues.empty()) {
+ os << "[" << min << "-" << max << "]";
+ } else if (discreteValues.size() == 1) {
+ os << discreteValues.front();
+ } else {
+ os << "[";
+ SkipFirstDelimiter comma(",");
+ for (auto value : discreteValues) {
+ os << comma << value;
+ }
+ os << "]";
+ }
+}
+
+void SdpImageattrAttributeList::PRange::Serialize(std::ostream& os) const {
+ os << std::setprecision(4) << std::fixed;
+ os << "[" << min << "-" << max << "]";
+}
+
+static std::string ParseKey(std::istream& is, std::string* error) {
+ std::string token = ParseToken(is, "=", error);
+ if (!SkipChar(is, '=', error)) {
+ return "";
+ }
+ return token;
+}
+
+static bool SkipBraces(std::istream& is, std::string* error) {
+ if (PeekChar(is, error) != '[') {
+ *error = "Expected \'[\'";
+ return false;
+ }
+
+ size_t braceCount = 0;
+ do {
+ switch (PeekChar(is, error)) {
+ case '[':
+ ++braceCount;
+ break;
+ case ']':
+ --braceCount;
+ break;
+ default:
+ break;
+ }
+ is.get();
+ } while (braceCount && is);
+
+ if (!is) {
+ *error = "Expected closing brace";
+ return false;
+ }
+
+ return true;
+}
+
+// Assumptions:
+// 1. If the value contains '[' or ']', they are balanced.
+// 2. The value contains no ',' outside of brackets.
+static bool SkipValue(std::istream& is, std::string* error) {
+ while (is) {
+ switch (PeekChar(is, error)) {
+ case ',':
+ case ']':
+ return true;
+ case '[':
+ if (!SkipBraces(is, error)) {
+ return false;
+ }
+ break;
+ default:
+ is.get();
+ }
+ }
+
+ *error = "No closing \']\' on set";
+ return false;
+}
+
+bool SdpImageattrAttributeList::Set::Parse(std::istream& is,
+ std::string* error) {
+ if (!SkipChar(is, '[', error)) {
+ return false;
+ }
+
+ if (ParseKey(is, error) != "x") {
+ *error = "Expected x=";
+ return false;
+ }
+
+ if (!xRange.Parse(is, error)) {
+ return false;
+ }
+
+ if (!SkipChar(is, ',', error)) {
+ return false;
+ }
+
+ if (ParseKey(is, error) != "y") {
+ *error = "Expected y=";
+ return false;
+ }
+
+ if (!yRange.Parse(is, error)) {
+ return false;
+ }
+
+ qValue = 0.5f; // default
+
+ bool gotSar = false;
+ bool gotPar = false;
+ bool gotQ = false;
+
+ while (SkipChar(is, ',', error)) {
+ std::string key = ParseKey(is, error);
+ if (key.empty()) {
+ *error = "Expected key-value";
+ return false;
+ }
+
+ if (key == "sar") {
+ if (gotSar) {
+ *error = "Extra sar parameter";
+ return false;
+ }
+ gotSar = true;
+ if (!sRange.Parse(is, error)) {
+ return false;
+ }
+ } else if (key == "par") {
+ if (gotPar) {
+ *error = "Extra par parameter";
+ return false;
+ }
+ gotPar = true;
+ if (!pRange.Parse(is, error)) {
+ return false;
+ }
+ } else if (key == "q") {
+ if (gotQ) {
+ *error = "Extra q parameter";
+ return false;
+ }
+ gotQ = true;
+ if (!GetQValue(is, &qValue, error)) {
+ return false;
+ }
+ } else {
+ if (!SkipValue(is, error)) {
+ return false;
+ }
+ }
+ }
+
+ return SkipChar(is, ']', error);
+}
+
+void SdpImageattrAttributeList::Set::Serialize(std::ostream& os) const {
+ os << "[x=";
+ xRange.Serialize(os);
+ os << ",y=";
+ yRange.Serialize(os);
+ if (sRange.IsSet()) {
+ os << ",sar=";
+ sRange.Serialize(os);
+ }
+ if (pRange.IsSet()) {
+ os << ",par=";
+ pRange.Serialize(os);
+ }
+ if (qValue >= 0) {
+ os << std::setprecision(2) << std::fixed << ",q=" << qValue;
+ }
+ os << "]";
+}
+
+bool SdpImageattrAttributeList::Imageattr::ParseSets(std::istream& is,
+ std::string* error) {
+ std::string type = ParseToken(is, " \t", error);
+
+ bool* isAll = nullptr;
+ std::vector<Set>* sets = nullptr;
+
+ if (type == "send") {
+ isAll = &sendAll;
+ sets = &sendSets;
+ } else if (type == "recv") {
+ isAll = &recvAll;
+ sets = &recvSets;
+ } else {
+ *error = "Unknown type, must be either send or recv";
+ return false;
+ }
+
+ if (*isAll || !sets->empty()) {
+ *error = "Multiple send or recv set lists";
+ return false;
+ }
+
+ is >> std::ws;
+ if (SkipChar(is, '*', error)) {
+ *isAll = true;
+ return true;
+ }
+
+ do {
+ Set set;
+ if (!set.Parse(is, error)) {
+ return false;
+ }
+
+ sets->push_back(set);
+ is >> std::ws;
+ } while (PeekChar(is, error) == '[');
+
+ return true;
+}
+
+bool SdpImageattrAttributeList::Imageattr::Parse(std::istream& is,
+ std::string* error) {
+ if (!SkipChar(is, '*', error)) {
+ uint16_t value;
+ if (!GetUnsigned<uint16_t>(is, 0, UINT16_MAX, &value, error)) {
+ return false;
+ }
+ pt = Some(value);
+ }
+
+ is >> std::ws;
+ if (!ParseSets(is, error)) {
+ return false;
+ }
+
+ // There might be a second one
+ is >> std::ws;
+ if (is.eof()) {
+ return true;
+ }
+
+ if (!ParseSets(is, error)) {
+ return false;
+ }
+
+ is >> std::ws;
+ if (!is.eof()) {
+ *error = "Trailing characters";
+ return false;
+ }
+
+ return true;
+}
+
+void SdpImageattrAttributeList::Imageattr::Serialize(std::ostream& os) const {
+ if (pt.isSome()) {
+ os << *pt;
+ } else {
+ os << "*";
+ }
+
+ if (sendAll) {
+ os << " send *";
+ } else if (!sendSets.empty()) {
+ os << " send";
+ for (auto& set : sendSets) {
+ os << " ";
+ set.Serialize(os);
+ }
+ }
+
+ if (recvAll) {
+ os << " recv *";
+ } else if (!recvSets.empty()) {
+ os << " recv";
+ for (auto& set : recvSets) {
+ os << " ";
+ set.Serialize(os);
+ }
+ }
+}
+
+void SdpImageattrAttributeList::Serialize(std::ostream& os) const {
+ for (auto& imageattr : mImageattrs) {
+ os << "a=" << mType << ":";
+ imageattr.Serialize(os);
+ os << CRLF;
+ }
+}
+
+bool SdpImageattrAttributeList::PushEntry(const std::string& raw,
+ std::string* error,
+ size_t* errorPos) {
+ std::istringstream is(raw);
+
+ Imageattr imageattr;
+ if (!imageattr.Parse(is, error)) {
+ is.clear();
+ *errorPos = is.tellg();
+ return false;
+ }
+
+ mImageattrs.push_back(imageattr);
+ return true;
+}
+
+void SdpMsidAttributeList::Serialize(std::ostream& os) const {
+ for (auto i = mMsids.begin(); i != mMsids.end(); ++i) {
+ os << "a=" << mType << ":" << i->identifier;
+ if (i->appdata.length()) {
+ os << " " << i->appdata;
+ }
+ os << CRLF;
+ }
+}
+
+void SdpMsidSemanticAttributeList::Serialize(std::ostream& os) const {
+ for (auto i = mMsidSemantics.begin(); i != mMsidSemantics.end(); ++i) {
+ os << "a=" << mType << ":" << i->semantic;
+ for (auto j = i->msids.begin(); j != i->msids.end(); ++j) {
+ os << " " << *j;
+ }
+ os << CRLF;
+ }
+}
+
+void SdpRemoteCandidatesAttribute::Serialize(std::ostream& os) const {
+ if (mCandidates.empty()) {
+ return;
+ }
+
+ os << "a=" << mType;
+ for (auto i = mCandidates.begin(); i != mCandidates.end(); i++) {
+ os << (i == mCandidates.begin() ? ":" : " ") << i->id << " " << i->address
+ << " " << i->port;
+ }
+ os << CRLF;
+}
+
+// Remove this function. See Bug 1469702
+bool SdpRidAttributeList::Rid::ParseParameters(std::istream& is,
+ std::string* error) {
+ if (!PeekChar(is, error)) {
+ // No parameters
+ return true;
+ }
+
+ do {
+ is >> std::ws;
+ std::string key = ParseKey(is, error);
+ if (key.empty()) {
+ return false; // Illegal trailing cruft
+ }
+
+ // This allows pt= to appear anywhere, instead of only at the beginning, but
+ // this ends up being significantly less code.
+ if (key == "pt") {
+ if (!ParseFormats(is, error)) {
+ return false;
+ }
+ } else if (key == "max-width") {
+ if (!GetUnsigned<uint32_t>(is, 0, UINT32_MAX, &constraints.maxWidth,
+ error)) {
+ return false;
+ }
+ } else if (key == "max-height") {
+ if (!GetUnsigned<uint32_t>(is, 0, UINT32_MAX, &constraints.maxHeight,
+ error)) {
+ return false;
+ }
+ } else if (key == "max-fps") {
+ uint32_t maxFps;
+ if (!GetUnsigned<uint32_t>(is, 0, UINT32_MAX, &maxFps, error)) {
+ return false;
+ }
+ constraints.maxFps = Some(maxFps);
+ } else if (key == "max-fs") {
+ if (!GetUnsigned<uint32_t>(is, 0, UINT32_MAX, &constraints.maxFs,
+ error)) {
+ return false;
+ }
+ } else if (key == "max-br") {
+ if (!GetUnsigned<uint32_t>(is, 0, UINT32_MAX, &constraints.maxBr,
+ error)) {
+ return false;
+ }
+ } else if (key == "max-pps") {
+ if (!GetUnsigned<uint32_t>(is, 0, UINT32_MAX, &constraints.maxPps,
+ error)) {
+ return false;
+ }
+ } else if (key == "depend") {
+ if (!ParseDepend(is, error)) {
+ return false;
+ }
+ } else {
+ (void)ParseToken(is, ";", error);
+ }
+ } while (SkipChar(is, ';', error));
+ return true;
+}
+
+// Remove this function. See Bug 1469702
+bool SdpRidAttributeList::Rid::ParseDepend(std::istream& is,
+ std::string* error) {
+ do {
+ std::string id = ParseToken(is, ",;", error);
+ if (id.empty()) {
+ return false;
+ }
+ dependIds.push_back(id);
+ } while (SkipChar(is, ',', error));
+
+ return true;
+}
+
+// Remove this function. See Bug 1469702
+bool SdpRidAttributeList::Rid::ParseFormats(std::istream& is,
+ std::string* error) {
+ do {
+ uint16_t fmt;
+ if (!GetUnsigned<uint16_t>(is, 0, 127, &fmt, error)) {
+ return false;
+ }
+ formats.push_back(fmt);
+ } while (SkipChar(is, ',', error));
+
+ return true;
+}
+
+void SdpRidAttributeList::Rid::SerializeParameters(std::ostream& os) const {
+ if (!HasParameters()) {
+ return;
+ }
+
+ os << " ";
+
+ SkipFirstDelimiter semic(";");
+
+ if (!formats.empty()) {
+ os << semic << "pt=";
+ SkipFirstDelimiter comma(",");
+ for (uint16_t fmt : formats) {
+ os << comma << fmt;
+ }
+ }
+
+ if (constraints.maxWidth) {
+ os << semic << "max-width=" << constraints.maxWidth;
+ }
+
+ if (constraints.maxHeight) {
+ os << semic << "max-height=" << constraints.maxHeight;
+ }
+
+ if (constraints.maxFps) {
+ os << semic << "max-fps=" << constraints.maxFps;
+ }
+
+ if (constraints.maxFs) {
+ os << semic << "max-fs=" << constraints.maxFs;
+ }
+
+ if (constraints.maxBr) {
+ os << semic << "max-br=" << constraints.maxBr;
+ }
+
+ if (constraints.maxPps) {
+ os << semic << "max-pps=" << constraints.maxPps;
+ }
+
+ if (!dependIds.empty()) {
+ os << semic << "depend=";
+ SkipFirstDelimiter comma(",");
+ for (const std::string& id : dependIds) {
+ os << comma << id;
+ }
+ }
+}
+
+// Remove this function. See Bug 1469702
+bool SdpRidAttributeList::Rid::Parse(std::istream& is, std::string* error) {
+ id = ParseToken(is, " ", error);
+ if (!CheckRidValidity(id, error)) {
+ return false;
+ }
+
+ is >> std::ws;
+ std::string directionToken = ParseToken(is, " ", error);
+ if (directionToken == "send") {
+ direction = sdp::kSend;
+ } else if (directionToken == "recv") {
+ direction = sdp::kRecv;
+ } else {
+ *error = "Invalid direction, must be either send or recv";
+ return false;
+ }
+
+ return ParseParameters(is, error);
+}
+
+static std::bitset<256> GetAllowedRidCharacters() {
+ // From RFC 8851:
+ // rid-id = 1*(alpha-numeric / "-" / "_")
+ std::bitset<256> result;
+ for (unsigned char c = 'a'; c <= 'z'; ++c) {
+ result.set(c);
+ }
+ for (unsigned char c = 'A'; c <= 'Z'; ++c) {
+ result.set(c);
+ }
+ for (unsigned char c = '0'; c <= '9'; ++c) {
+ result.set(c);
+ }
+ // NOTE: RFC 8851 says these are allowed, but RFC 8852 says they are not
+ // https://www.rfc-editor.org/errata/eid7132
+ // result.set('-');
+ // result.set('_');
+ return result;
+}
+
+/* static */
+bool SdpRidAttributeList::CheckRidValidity(const std::string& aRid,
+ std::string* aError) {
+ if (aRid.empty()) {
+ *aError = "Rid must be non-empty (according to RFC 8851)";
+ return false;
+ }
+
+ // We need to check against a maximum length, but that is nowhere
+ // specified in webrtc-pc right now.
+ if (aRid.size() > 255) {
+ *aError = "Rid can be at most 255 characters long (according to RFC 8852)";
+ return false;
+ }
+
+ // TODO: Right now, if the rid is longer than kMaxRidLength, we don't treat it
+ // as a parse error, since the grammar does not have this restriction.
+ // Instead, our JSEP code ignores rids that exceed this limit. However, there
+ // is a possibility that the IETF grammar (in RFC 8852) will change the limit
+ // from 255 to 16, in which case we will need to revise this code.
+
+ static const std::bitset<256> allowed = GetAllowedRidCharacters();
+ for (unsigned char c : aRid) {
+ if (!allowed[c]) {
+ *aError =
+ "Rid can contain only alphanumeric characters (according to RFC "
+ "8852)";
+ return false;
+ }
+ }
+
+ return true;
+}
+
+// This can be overridden if necessary
+size_t SdpRidAttributeList::kMaxRidLength = 255;
+
+void SdpRidAttributeList::Rid::Serialize(std::ostream& os) const {
+ os << id << " " << direction;
+ SerializeParameters(os);
+}
+
+bool SdpRidAttributeList::Rid::HasFormat(const std::string& format) const {
+ if (formats.empty()) {
+ return true;
+ }
+
+ uint16_t formatAsInt;
+ if (!SdpHelper::GetPtAsInt(format, &formatAsInt)) {
+ return false;
+ }
+
+ return (std::find(formats.begin(), formats.end(), formatAsInt) !=
+ formats.end());
+}
+
+void SdpRidAttributeList::Serialize(std::ostream& os) const {
+ for (const Rid& rid : mRids) {
+ os << "a=" << mType << ":";
+ rid.Serialize(os);
+ os << CRLF;
+ }
+}
+
+// Remove this function. See Bug 1469702
+bool SdpRidAttributeList::PushEntry(const std::string& raw, std::string* error,
+ size_t* errorPos) {
+ std::istringstream is(raw);
+
+ Rid rid;
+ if (!rid.Parse(is, error)) {
+ is.clear();
+ *errorPos = is.tellg();
+ return false;
+ }
+
+ mRids.push_back(rid);
+ return true;
+}
+
+void SdpRidAttributeList::PushEntry(const std::string& id, sdp::Direction dir,
+ const std::vector<uint16_t>& formats,
+ const EncodingConstraints& constraints,
+ const std::vector<std::string>& dependIds) {
+ SdpRidAttributeList::Rid rid;
+
+ rid.id = id;
+ rid.direction = dir;
+ rid.formats = formats;
+ rid.constraints = constraints;
+ rid.dependIds = dependIds;
+
+ mRids.push_back(std::move(rid));
+}
+
+void SdpRtcpAttribute::Serialize(std::ostream& os) const {
+ os << "a=" << mType << ":" << mPort;
+ if (!mAddress.empty()) {
+ os << " " << mNetType << " " << mAddrType << " " << mAddress;
+ }
+ os << CRLF;
+}
+
+const char* SdpRtcpFbAttributeList::pli = "pli";
+const char* SdpRtcpFbAttributeList::sli = "sli";
+const char* SdpRtcpFbAttributeList::rpsi = "rpsi";
+const char* SdpRtcpFbAttributeList::app = "app";
+
+const char* SdpRtcpFbAttributeList::fir = "fir";
+const char* SdpRtcpFbAttributeList::tmmbr = "tmmbr";
+const char* SdpRtcpFbAttributeList::tstr = "tstr";
+const char* SdpRtcpFbAttributeList::vbcm = "vbcm";
+
+void SdpRtcpFbAttributeList::Serialize(std::ostream& os) const {
+ for (auto i = mFeedbacks.begin(); i != mFeedbacks.end(); ++i) {
+ os << "a=" << mType << ":" << i->pt << " " << i->type;
+ if (i->parameter.length()) {
+ os << " " << i->parameter;
+ if (i->extra.length()) {
+ os << " " << i->extra;
+ }
+ }
+ os << CRLF;
+ }
+}
+
+static bool ShouldSerializeChannels(SdpRtpmapAttributeList::CodecType type) {
+ switch (type) {
+ case SdpRtpmapAttributeList::kOpus:
+ case SdpRtpmapAttributeList::kG722:
+ return true;
+ case SdpRtpmapAttributeList::kPCMU:
+ case SdpRtpmapAttributeList::kPCMA:
+ case SdpRtpmapAttributeList::kVP8:
+ case SdpRtpmapAttributeList::kVP9:
+ case SdpRtpmapAttributeList::kiLBC:
+ case SdpRtpmapAttributeList::kiSAC:
+ case SdpRtpmapAttributeList::kH264:
+ case SdpRtpmapAttributeList::kRed:
+ case SdpRtpmapAttributeList::kUlpfec:
+ case SdpRtpmapAttributeList::kTelephoneEvent:
+ case SdpRtpmapAttributeList::kRtx:
+ return false;
+ case SdpRtpmapAttributeList::kOtherCodec:
+ return true;
+ }
+ MOZ_CRASH();
+}
+
+void SdpRtpmapAttributeList::Serialize(std::ostream& os) const {
+ for (auto i = mRtpmaps.begin(); i != mRtpmaps.end(); ++i) {
+ os << "a=" << mType << ":" << i->pt << " " << i->name << "/" << i->clock;
+ if (i->channels && ShouldSerializeChannels(i->codec)) {
+ os << "/" << i->channels;
+ }
+ os << CRLF;
+ }
+}
+
+void SdpSctpmapAttributeList::Serialize(std::ostream& os) const {
+ for (auto i = mSctpmaps.begin(); i != mSctpmaps.end(); ++i) {
+ os << "a=" << mType << ":" << i->pt << " " << i->name << " " << i->streams
+ << CRLF;
+ }
+}
+
+void SdpSetupAttribute::Serialize(std::ostream& os) const {
+ os << "a=" << mType << ":" << mRole << CRLF;
+}
+
+void SdpSimulcastAttribute::Version::Serialize(std::ostream& os) const {
+ SkipFirstDelimiter comma(",");
+ for (const Encoding& choice : choices) {
+ os << comma;
+ if (choice.paused) {
+ os << '~';
+ }
+ os << choice.rid;
+ }
+}
+
+bool SdpSimulcastAttribute::Version::Parse(std::istream& is,
+ std::string* error) {
+ do {
+ bool paused = SkipChar(is, '~', error);
+ std::string value = ParseToken(is, ",; ", error);
+ if (value.empty()) {
+ *error = "Missing rid";
+ return false;
+ }
+ if (!SdpRidAttributeList::CheckRidValidity(value, error)) {
+ return false;
+ }
+ choices.push_back(Encoding(value, paused));
+ } while (SkipChar(is, ',', error));
+
+ return true;
+}
+
+void SdpSimulcastAttribute::Versions::Serialize(std::ostream& os) const {
+ SkipFirstDelimiter semic(";");
+ for (const Version& version : *this) {
+ if (!version.IsSet()) {
+ continue;
+ }
+ os << semic;
+ version.Serialize(os);
+ }
+}
+
+bool SdpSimulcastAttribute::Versions::Parse(std::istream& is,
+ std::string* error) {
+ do {
+ Version version;
+ if (!version.Parse(is, error)) {
+ return false;
+ }
+ push_back(version);
+ } while (SkipChar(is, ';', error));
+
+ return true;
+}
+
+void SdpSimulcastAttribute::Serialize(std::ostream& os) const {
+ MOZ_ASSERT(sendVersions.IsSet() || recvVersions.IsSet());
+
+ os << "a=" << mType << ":";
+
+ if (sendVersions.IsSet()) {
+ os << "send ";
+ sendVersions.Serialize(os);
+ }
+
+ if (recvVersions.IsSet()) {
+ if (sendVersions.IsSet()) {
+ os << " ";
+ }
+ os << "recv ";
+ recvVersions.Serialize(os);
+ }
+
+ os << CRLF;
+}
+
+bool SdpSimulcastAttribute::Parse(std::istream& is, std::string* error) {
+ bool gotRecv = false;
+ bool gotSend = false;
+
+ while (true) {
+ is >> std::ws;
+ std::string token = ParseToken(is, " \t", error);
+ if (token.empty()) {
+ break;
+ }
+
+ if (token == "send") {
+ if (gotSend) {
+ *error = "Already got a send list";
+ return false;
+ }
+ gotSend = true;
+
+ is >> std::ws;
+ if (!sendVersions.Parse(is, error)) {
+ return false;
+ }
+ } else if (token == "recv") {
+ if (gotRecv) {
+ *error = "Already got a recv list";
+ return false;
+ }
+ gotRecv = true;
+
+ is >> std::ws;
+ if (!recvVersions.Parse(is, error)) {
+ return false;
+ }
+ } else {
+ *error = "Type must be either 'send' or 'recv'";
+ return false;
+ }
+ }
+
+ if (!gotSend && !gotRecv) {
+ *error = "Empty simulcast attribute";
+ return false;
+ }
+
+ return true;
+}
+
+void SdpSsrcAttributeList::Serialize(std::ostream& os) const {
+ for (auto i = mSsrcs.begin(); i != mSsrcs.end(); ++i) {
+ os << "a=" << mType << ":" << i->ssrc << " " << i->attribute << CRLF;
+ }
+}
+
+void SdpSsrcGroupAttributeList::Serialize(std::ostream& os) const {
+ for (auto i = mSsrcGroups.begin(); i != mSsrcGroups.end(); ++i) {
+ os << "a=" << mType << ":" << i->semantics;
+ for (auto j = i->ssrcs.begin(); j != i->ssrcs.end(); ++j) {
+ os << " " << (*j);
+ }
+ os << CRLF;
+ }
+}
+
+void SdpMultiStringAttribute::Serialize(std::ostream& os) const {
+ for (auto i = mValues.begin(); i != mValues.end(); ++i) {
+ os << "a=" << mType << ":" << *i << CRLF;
+ }
+}
+
+void SdpOptionsAttribute::Serialize(std::ostream& os) const {
+ if (mValues.empty()) {
+ return;
+ }
+
+ os << "a=" << mType << ":";
+
+ for (auto i = mValues.begin(); i != mValues.end(); ++i) {
+ if (i != mValues.begin()) {
+ os << " ";
+ }
+ os << *i;
+ }
+ os << CRLF;
+}
+
+void SdpOptionsAttribute::Load(const std::string& value) {
+ size_t start = 0;
+ size_t end = value.find(' ');
+ while (end != std::string::npos) {
+ PushEntry(value.substr(start, end));
+ start = end + 1;
+ end = value.find(' ', start);
+ }
+ PushEntry(value.substr(start));
+}
+
+void SdpFlagAttribute::Serialize(std::ostream& os) const {
+ os << "a=" << mType << CRLF;
+}
+
+void SdpStringAttribute::Serialize(std::ostream& os) const {
+ os << "a=" << mType << ":" << mValue << CRLF;
+}
+
+void SdpNumberAttribute::Serialize(std::ostream& os) const {
+ os << "a=" << mType << ":" << mValue << CRLF;
+}
+
+bool SdpAttribute::IsAllowedAtMediaLevel(AttributeType type) {
+ switch (type) {
+ case kBundleOnlyAttribute:
+ return true;
+ case kCandidateAttribute:
+ return true;
+ case kConnectionAttribute:
+ return true;
+ case kDirectionAttribute:
+ return true;
+ case kDtlsMessageAttribute:
+ return false;
+ case kEndOfCandidatesAttribute:
+ return true;
+ case kExtmapAttribute:
+ return true;
+ case kFingerprintAttribute:
+ return true;
+ case kFmtpAttribute:
+ return true;
+ case kGroupAttribute:
+ return false;
+ case kIceLiteAttribute:
+ return false;
+ case kIceMismatchAttribute:
+ return true;
+ // RFC 5245 says this is session-level only, but
+ // draft-ietf-mmusic-ice-sip-sdp-03 updates this to allow at the media
+ // level.
+ case kIceOptionsAttribute:
+ return true;
+ case kIcePwdAttribute:
+ return true;
+ case kIceUfragAttribute:
+ return true;
+ case kIdentityAttribute:
+ return false;
+ case kImageattrAttribute:
+ return true;
+ case kLabelAttribute:
+ return true;
+ case kMaxptimeAttribute:
+ return true;
+ case kMidAttribute:
+ return true;
+ case kMsidAttribute:
+ return true;
+ case kMsidSemanticAttribute:
+ return false;
+ case kPtimeAttribute:
+ return true;
+ case kRemoteCandidatesAttribute:
+ return true;
+ case kRidAttribute:
+ return true;
+ case kRtcpAttribute:
+ return true;
+ case kRtcpFbAttribute:
+ return true;
+ case kRtcpMuxAttribute:
+ return true;
+ case kRtcpRsizeAttribute:
+ return true;
+ case kRtpmapAttribute:
+ return true;
+ case kSctpmapAttribute:
+ return true;
+ case kSetupAttribute:
+ return true;
+ case kSimulcastAttribute:
+ return true;
+ case kSsrcAttribute:
+ return true;
+ case kSsrcGroupAttribute:
+ return true;
+ case kSctpPortAttribute:
+ return true;
+ case kMaxMessageSizeAttribute:
+ return true;
+ }
+ MOZ_CRASH("Unknown attribute type");
+}
+
+bool SdpAttribute::IsAllowedAtSessionLevel(AttributeType type) {
+ switch (type) {
+ case kBundleOnlyAttribute:
+ return false;
+ case kCandidateAttribute:
+ return false;
+ case kConnectionAttribute:
+ return true;
+ case kDirectionAttribute:
+ return true;
+ case kDtlsMessageAttribute:
+ return true;
+ case kEndOfCandidatesAttribute:
+ return true;
+ case kExtmapAttribute:
+ return true;
+ case kFingerprintAttribute:
+ return true;
+ case kFmtpAttribute:
+ return false;
+ case kGroupAttribute:
+ return true;
+ case kIceLiteAttribute:
+ return true;
+ case kIceMismatchAttribute:
+ return false;
+ case kIceOptionsAttribute:
+ return true;
+ case kIcePwdAttribute:
+ return true;
+ case kIceUfragAttribute:
+ return true;
+ case kIdentityAttribute:
+ return true;
+ case kImageattrAttribute:
+ return false;
+ case kLabelAttribute:
+ return false;
+ case kMaxptimeAttribute:
+ return false;
+ case kMidAttribute:
+ return false;
+ case kMsidSemanticAttribute:
+ return true;
+ case kMsidAttribute:
+ return false;
+ case kPtimeAttribute:
+ return false;
+ case kRemoteCandidatesAttribute:
+ return false;
+ case kRidAttribute:
+ return false;
+ case kRtcpAttribute:
+ return false;
+ case kRtcpFbAttribute:
+ return false;
+ case kRtcpMuxAttribute:
+ return false;
+ case kRtcpRsizeAttribute:
+ return false;
+ case kRtpmapAttribute:
+ return false;
+ case kSctpmapAttribute:
+ return false;
+ case kSetupAttribute:
+ return true;
+ case kSimulcastAttribute:
+ return false;
+ case kSsrcAttribute:
+ return false;
+ case kSsrcGroupAttribute:
+ return false;
+ case kSctpPortAttribute:
+ return false;
+ case kMaxMessageSizeAttribute:
+ return false;
+ }
+ MOZ_CRASH("Unknown attribute type");
+}
+
+const std::string SdpAttribute::GetAttributeTypeString(AttributeType type) {
+ switch (type) {
+ case kBundleOnlyAttribute:
+ return "bundle-only";
+ case kCandidateAttribute:
+ return "candidate";
+ case kConnectionAttribute:
+ return "connection";
+ case kDtlsMessageAttribute:
+ return "dtls-message";
+ case kEndOfCandidatesAttribute:
+ return "end-of-candidates";
+ case kExtmapAttribute:
+ return "extmap";
+ case kFingerprintAttribute:
+ return "fingerprint";
+ case kFmtpAttribute:
+ return "fmtp";
+ case kGroupAttribute:
+ return "group";
+ case kIceLiteAttribute:
+ return "ice-lite";
+ case kIceMismatchAttribute:
+ return "ice-mismatch";
+ case kIceOptionsAttribute:
+ return "ice-options";
+ case kIcePwdAttribute:
+ return "ice-pwd";
+ case kIceUfragAttribute:
+ return "ice-ufrag";
+ case kIdentityAttribute:
+ return "identity";
+ case kImageattrAttribute:
+ return "imageattr";
+ case kLabelAttribute:
+ return "label";
+ case kMaxptimeAttribute:
+ return "maxptime";
+ case kMidAttribute:
+ return "mid";
+ case kMsidAttribute:
+ return "msid";
+ case kMsidSemanticAttribute:
+ return "msid-semantic";
+ case kPtimeAttribute:
+ return "ptime";
+ case kRemoteCandidatesAttribute:
+ return "remote-candidates";
+ case kRidAttribute:
+ return "rid";
+ case kRtcpAttribute:
+ return "rtcp";
+ case kRtcpFbAttribute:
+ return "rtcp-fb";
+ case kRtcpMuxAttribute:
+ return "rtcp-mux";
+ case kRtcpRsizeAttribute:
+ return "rtcp-rsize";
+ case kRtpmapAttribute:
+ return "rtpmap";
+ case kSctpmapAttribute:
+ return "sctpmap";
+ case kSetupAttribute:
+ return "setup";
+ case kSimulcastAttribute:
+ return "simulcast";
+ case kSsrcAttribute:
+ return "ssrc";
+ case kSsrcGroupAttribute:
+ return "ssrc-group";
+ case kSctpPortAttribute:
+ return "sctp-port";
+ case kMaxMessageSizeAttribute:
+ return "max-message-size";
+ case kDirectionAttribute:
+ MOZ_CRASH("kDirectionAttribute not valid here");
+ }
+ MOZ_CRASH("Unknown attribute type");
+}
+
+} // namespace mozilla
diff --git a/dom/media/webrtc/sdp/SdpAttribute.h b/dom/media/webrtc/sdp/SdpAttribute.h
new file mode 100644
index 0000000000..6af497ac18
--- /dev/null
+++ b/dom/media/webrtc/sdp/SdpAttribute.h
@@ -0,0 +1,1907 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _SDPATTRIBUTE_H_
+#define _SDPATTRIBUTE_H_
+
+#include <algorithm>
+#include <cctype>
+#include <vector>
+#include <ostream>
+#include <sstream>
+#include <cstring>
+#include <iomanip>
+#include <string>
+
+#include "mozilla/UniquePtr.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/Maybe.h"
+
+#include "sdp/SdpEnum.h"
+#include "common/EncodingConstraints.h"
+
+namespace mozilla {
+
+/**
+ * Base class for SDP attributes
+ */
+class SdpAttribute {
+ public:
+ enum AttributeType {
+ kFirstAttribute = 0,
+ kBundleOnlyAttribute = 0,
+ kCandidateAttribute,
+ kConnectionAttribute,
+ kDirectionAttribute,
+ kDtlsMessageAttribute,
+ kEndOfCandidatesAttribute,
+ kExtmapAttribute,
+ kFingerprintAttribute,
+ kFmtpAttribute,
+ kGroupAttribute,
+ kIceLiteAttribute,
+ kIceMismatchAttribute,
+ kIceOptionsAttribute,
+ kIcePwdAttribute,
+ kIceUfragAttribute,
+ kIdentityAttribute,
+ kImageattrAttribute,
+ kLabelAttribute,
+ kMaxptimeAttribute,
+ kMidAttribute,
+ kMsidAttribute,
+ kMsidSemanticAttribute,
+ kPtimeAttribute,
+ kRemoteCandidatesAttribute,
+ kRidAttribute,
+ kRtcpAttribute,
+ kRtcpFbAttribute,
+ kRtcpMuxAttribute,
+ kRtcpRsizeAttribute,
+ kRtpmapAttribute,
+ kSctpmapAttribute,
+ kSetupAttribute,
+ kSimulcastAttribute,
+ kSsrcAttribute,
+ kSsrcGroupAttribute,
+ kSctpPortAttribute,
+ kMaxMessageSizeAttribute,
+ kLastAttribute = kMaxMessageSizeAttribute
+ };
+
+ explicit SdpAttribute(AttributeType type) : mType(type) {}
+ virtual ~SdpAttribute() {}
+
+ virtual SdpAttribute* Clone() const = 0;
+
+ AttributeType GetType() const { return mType; }
+
+ virtual void Serialize(std::ostream&) const = 0;
+
+ static bool IsAllowedAtSessionLevel(AttributeType type);
+ static bool IsAllowedAtMediaLevel(AttributeType type);
+ static const std::string GetAttributeTypeString(AttributeType type);
+
+ protected:
+ AttributeType mType;
+};
+
+inline std::ostream& operator<<(std::ostream& os, const SdpAttribute& attr) {
+ attr.Serialize(os);
+ return os;
+}
+
+inline std::ostream& operator<<(std::ostream& os,
+ const SdpAttribute::AttributeType type) {
+ os << SdpAttribute::GetAttributeTypeString(type);
+ return os;
+}
+
+///////////////////////////////////////////////////////////////////////////
+// a=candidate, RFC5245
+//-------------------------------------------------------------------------
+//
+// candidate-attribute = "candidate" ":" foundation SP component-id SP
+// transport SP
+// priority SP
+// connection-address SP ;from RFC 4566
+// port ;port from RFC 4566
+// SP cand-type
+// [SP rel-addr]
+// [SP rel-port]
+// *(SP extension-att-name SP
+// extension-att-value)
+// foundation = 1*32ice-char
+// component-id = 1*5DIGIT
+// transport = "UDP" / transport-extension
+// transport-extension = token ; from RFC 3261
+// priority = 1*10DIGIT
+// cand-type = "typ" SP candidate-types
+// candidate-types = "host" / "srflx" / "prflx" / "relay" / token
+// rel-addr = "raddr" SP connection-address
+// rel-port = "rport" SP port
+// extension-att-name = byte-string ;from RFC 4566
+// extension-att-value = byte-string
+// ice-char = ALPHA / DIGIT / "+" / "/"
+
+// We use a SdpMultiStringAttribute for candidates
+
+///////////////////////////////////////////////////////////////////////////
+// a=connection, RFC4145
+//-------------------------------------------------------------------------
+// connection-attr = "a=connection:" conn-value
+// conn-value = "new" / "existing"
+class SdpConnectionAttribute : public SdpAttribute {
+ public:
+ enum ConnValue { kNew, kExisting };
+
+ explicit SdpConnectionAttribute(SdpConnectionAttribute::ConnValue value)
+ : SdpAttribute(kConnectionAttribute), mValue(value) {}
+
+ SdpAttribute* Clone() const override {
+ return new SdpConnectionAttribute(*this);
+ }
+
+ virtual void Serialize(std::ostream& os) const override;
+
+ ConnValue mValue;
+};
+
+inline std::ostream& operator<<(std::ostream& os,
+ SdpConnectionAttribute::ConnValue c) {
+ switch (c) {
+ case SdpConnectionAttribute::kNew:
+ os << "new";
+ break;
+ case SdpConnectionAttribute::kExisting:
+ os << "existing";
+ break;
+ default:
+ MOZ_ASSERT(false);
+ os << "?";
+ }
+ return os;
+}
+
+///////////////////////////////////////////////////////////////////////////
+// a=sendrecv / a=sendonly / a=recvonly / a=inactive, RFC 4566
+//-------------------------------------------------------------------------
+class SdpDirectionAttribute : public SdpAttribute {
+ public:
+ enum Direction {
+ kInactive = 0,
+ kSendonly = sdp::kSend,
+ kRecvonly = sdp::kRecv,
+ kSendrecv = sdp::kSend | sdp::kRecv
+ };
+
+ explicit SdpDirectionAttribute(Direction value)
+ : SdpAttribute(kDirectionAttribute), mValue(value) {}
+
+ SdpAttribute* Clone() const override {
+ return new SdpDirectionAttribute(*this);
+ }
+
+ virtual void Serialize(std::ostream& os) const override;
+
+ Direction mValue;
+};
+
+inline std::ostream& operator<<(std::ostream& os,
+ SdpDirectionAttribute::Direction d) {
+ switch (d) {
+ case SdpDirectionAttribute::kSendonly:
+ os << "sendonly";
+ break;
+ case SdpDirectionAttribute::kRecvonly:
+ os << "recvonly";
+ break;
+ case SdpDirectionAttribute::kSendrecv:
+ os << "sendrecv";
+ break;
+ case SdpDirectionAttribute::kInactive:
+ os << "inactive";
+ break;
+ default:
+ MOZ_ASSERT(false);
+ os << "?";
+ }
+ return os;
+}
+
+inline SdpDirectionAttribute::Direction reverse(
+ SdpDirectionAttribute::Direction d) {
+ switch (d) {
+ case SdpDirectionAttribute::Direction::kInactive:
+ return SdpDirectionAttribute::Direction::kInactive;
+ case SdpDirectionAttribute::Direction::kSendonly:
+ return SdpDirectionAttribute::Direction::kRecvonly;
+ case SdpDirectionAttribute::Direction::kRecvonly:
+ return SdpDirectionAttribute::Direction::kSendonly;
+ case SdpDirectionAttribute::Direction::kSendrecv:
+ return SdpDirectionAttribute::Direction::kSendrecv;
+ }
+ MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Invalid direction!");
+ MOZ_RELEASE_ASSERT(false);
+}
+
+inline SdpDirectionAttribute::Direction operator|(
+ SdpDirectionAttribute::Direction d1, SdpDirectionAttribute::Direction d2) {
+ return (SdpDirectionAttribute::Direction)((unsigned)d1 | (unsigned)d2);
+}
+
+inline SdpDirectionAttribute::Direction operator&(
+ SdpDirectionAttribute::Direction d1, SdpDirectionAttribute::Direction d2) {
+ return (SdpDirectionAttribute::Direction)((unsigned)d1 & (unsigned)d2);
+}
+
+inline SdpDirectionAttribute::Direction operator|=(
+ SdpDirectionAttribute::Direction& d1, SdpDirectionAttribute::Direction d2) {
+ d1 = d1 | d2;
+ return d1;
+}
+
+inline SdpDirectionAttribute::Direction operator&=(
+ SdpDirectionAttribute::Direction& d1, SdpDirectionAttribute::Direction d2) {
+ d1 = d1 & d2;
+ return d1;
+}
+
+///////////////////////////////////////////////////////////////////////////
+// a=dtls-message, draft-rescorla-dtls-in-sdp
+//-------------------------------------------------------------------------
+// attribute =/ dtls-message-attribute
+//
+// dtls-message-attribute = "dtls-message" ":" role SP value
+//
+// role = "client" / "server"
+//
+// value = 1*(ALPHA / DIGIT / "+" / "/" / "=" )
+// ; base64 encoded message
+class SdpDtlsMessageAttribute : public SdpAttribute {
+ public:
+ enum Role { kClient, kServer };
+
+ explicit SdpDtlsMessageAttribute(Role role, const std::string& value)
+ : SdpAttribute(kDtlsMessageAttribute), mRole(role), mValue(value) {}
+
+ // TODO: remove this, Bug 1469702
+ explicit SdpDtlsMessageAttribute(const std::string& unparsed)
+ : SdpAttribute(kDtlsMessageAttribute), mRole(kClient) {
+ std::istringstream is(unparsed);
+ std::string error;
+ // We're not really worried about errors here if we don't parse;
+ // this attribute is a pure optimization.
+ Parse(is, &error);
+ }
+
+ SdpAttribute* Clone() const override {
+ return new SdpDtlsMessageAttribute(*this);
+ }
+
+ virtual void Serialize(std::ostream& os) const override;
+
+ // TODO: remove this, Bug 1469702
+ bool Parse(std::istream& is, std::string* error);
+
+ Role mRole;
+ std::string mValue;
+};
+
+inline std::ostream& operator<<(std::ostream& os,
+ SdpDtlsMessageAttribute::Role r) {
+ switch (r) {
+ case SdpDtlsMessageAttribute::kClient:
+ os << "client";
+ break;
+ case SdpDtlsMessageAttribute::kServer:
+ os << "server";
+ break;
+ default:
+ MOZ_ASSERT(false);
+ os << "?";
+ }
+ return os;
+}
+
+///////////////////////////////////////////////////////////////////////////
+// a=extmap, RFC5285
+//-------------------------------------------------------------------------
+// RFC5285
+// extmap = mapentry SP extensionname [SP extensionattributes]
+//
+// extensionname = URI
+//
+// direction = "sendonly" / "recvonly" / "sendrecv" / "inactive"
+//
+// mapentry = "extmap:" 1*5DIGIT ["/" direction]
+//
+// extensionattributes = byte-string
+//
+// URI = <Defined in RFC 3986>
+//
+// byte-string = <Defined in RFC 4566>
+//
+// SP = <Defined in RFC 5234>
+//
+// DIGIT = <Defined in RFC 5234>
+class SdpExtmapAttributeList : public SdpAttribute {
+ public:
+ SdpExtmapAttributeList() : SdpAttribute(kExtmapAttribute) {}
+
+ struct Extmap {
+ uint16_t entry;
+ SdpDirectionAttribute::Direction direction;
+ bool direction_specified;
+ std::string extensionname;
+ std::string extensionattributes;
+ };
+
+ void PushEntry(uint16_t entry, SdpDirectionAttribute::Direction direction,
+ bool direction_specified, const std::string& extensionname,
+ const std::string& extensionattributes = "") {
+ Extmap value = {entry, direction, direction_specified, extensionname,
+ extensionattributes};
+ mExtmaps.push_back(value);
+ }
+
+ SdpAttribute* Clone() const override {
+ return new SdpExtmapAttributeList(*this);
+ }
+
+ virtual void Serialize(std::ostream& os) const override;
+
+ std::vector<Extmap> mExtmaps;
+};
+
+///////////////////////////////////////////////////////////////////////////
+// a=fingerprint, RFC4572
+//-------------------------------------------------------------------------
+// fingerprint-attribute = "fingerprint" ":" hash-func SP fingerprint
+//
+// hash-func = "sha-1" / "sha-224" / "sha-256" /
+// "sha-384" / "sha-512" /
+// "md5" / "md2" / token
+// ; Additional hash functions can only come
+// ; from updates to RFC 3279
+//
+// fingerprint = 2UHEX *(":" 2UHEX)
+// ; Each byte in upper-case hex, separated
+// ; by colons.
+//
+// UHEX = DIGIT / %x41-46 ; A-F uppercase
+class SdpFingerprintAttributeList : public SdpAttribute {
+ public:
+ SdpFingerprintAttributeList() : SdpAttribute(kFingerprintAttribute) {}
+
+ enum HashAlgorithm {
+ kSha1,
+ kSha224,
+ kSha256,
+ kSha384,
+ kSha512,
+ kMd5,
+ kMd2,
+ kUnknownAlgorithm
+ };
+
+ struct Fingerprint {
+ HashAlgorithm hashFunc;
+ std::vector<uint8_t> fingerprint;
+ };
+
+ // For use by application programmers. Enforces that it's a known and
+ // reasonable algorithm.
+ void PushEntry(std::string algorithm_str,
+ const std::vector<uint8_t>& fingerprint,
+ bool enforcePlausible = true) {
+ std::transform(algorithm_str.begin(), algorithm_str.end(),
+ algorithm_str.begin(), ::tolower);
+
+ SdpFingerprintAttributeList::HashAlgorithm algorithm =
+ SdpFingerprintAttributeList::kUnknownAlgorithm;
+
+ if (algorithm_str == "sha-1") {
+ algorithm = SdpFingerprintAttributeList::kSha1;
+ } else if (algorithm_str == "sha-224") {
+ algorithm = SdpFingerprintAttributeList::kSha224;
+ } else if (algorithm_str == "sha-256") {
+ algorithm = SdpFingerprintAttributeList::kSha256;
+ } else if (algorithm_str == "sha-384") {
+ algorithm = SdpFingerprintAttributeList::kSha384;
+ } else if (algorithm_str == "sha-512") {
+ algorithm = SdpFingerprintAttributeList::kSha512;
+ } else if (algorithm_str == "md5") {
+ algorithm = SdpFingerprintAttributeList::kMd5;
+ } else if (algorithm_str == "md2") {
+ algorithm = SdpFingerprintAttributeList::kMd2;
+ }
+
+ if ((algorithm == SdpFingerprintAttributeList::kUnknownAlgorithm) ||
+ fingerprint.empty()) {
+ if (enforcePlausible) {
+ MOZ_ASSERT(false, "Unknown fingerprint algorithm");
+ } else {
+ return;
+ }
+ }
+
+ PushEntry(algorithm, fingerprint);
+ }
+
+ void PushEntry(HashAlgorithm hashFunc,
+ const std::vector<uint8_t>& fingerprint) {
+ Fingerprint value = {hashFunc, fingerprint};
+ mFingerprints.push_back(value);
+ }
+
+ SdpAttribute* Clone() const override {
+ return new SdpFingerprintAttributeList(*this);
+ }
+
+ virtual void Serialize(std::ostream& os) const override;
+
+ std::vector<Fingerprint> mFingerprints;
+
+ static std::string FormatFingerprint(const std::vector<uint8_t>& fp);
+ static std::vector<uint8_t> ParseFingerprint(const std::string& str);
+};
+
+inline std::ostream& operator<<(std::ostream& os,
+ SdpFingerprintAttributeList::HashAlgorithm a) {
+ switch (a) {
+ case SdpFingerprintAttributeList::kSha1:
+ os << "sha-1";
+ break;
+ case SdpFingerprintAttributeList::kSha224:
+ os << "sha-224";
+ break;
+ case SdpFingerprintAttributeList::kSha256:
+ os << "sha-256";
+ break;
+ case SdpFingerprintAttributeList::kSha384:
+ os << "sha-384";
+ break;
+ case SdpFingerprintAttributeList::kSha512:
+ os << "sha-512";
+ break;
+ case SdpFingerprintAttributeList::kMd5:
+ os << "md5";
+ break;
+ case SdpFingerprintAttributeList::kMd2:
+ os << "md2";
+ break;
+ default:
+ MOZ_ASSERT(false);
+ os << "?";
+ }
+ return os;
+}
+
+///////////////////////////////////////////////////////////////////////////
+// a=group, RFC5888
+//-------------------------------------------------------------------------
+// group-attribute = "a=group:" semantics
+// *(SP identification-tag)
+// semantics = "LS" / "FID" / semantics-extension
+// semantics-extension = token
+// identification-tag = token
+class SdpGroupAttributeList : public SdpAttribute {
+ public:
+ SdpGroupAttributeList() : SdpAttribute(kGroupAttribute) {}
+
+ enum Semantics {
+ kLs, // RFC5888
+ kFid, // RFC5888
+ kSrf, // RFC3524
+ kAnat, // RFC4091
+ kFec, // RFC5956
+ kFecFr, // RFC5956
+ kCs, // draft-mehta-rmt-flute-sdp-05
+ kDdp, // RFC5583
+ kDup, // RFC7104
+ kBundle // draft-ietf-mmusic-bundle
+ };
+
+ struct Group {
+ Semantics semantics;
+ std::vector<std::string> tags;
+ };
+
+ void PushEntry(Semantics semantics, const std::vector<std::string>& tags) {
+ Group value = {semantics, tags};
+ mGroups.push_back(value);
+ }
+
+ void RemoveMid(const std::string& mid) {
+ for (auto i = mGroups.begin(); i != mGroups.end();) {
+ auto tag = std::find(i->tags.begin(), i->tags.end(), mid);
+ if (tag != i->tags.end()) {
+ i->tags.erase(tag);
+ }
+
+ if (i->tags.empty()) {
+ i = mGroups.erase(i);
+ } else {
+ ++i;
+ }
+ }
+ }
+
+ SdpAttribute* Clone() const override {
+ return new SdpGroupAttributeList(*this);
+ }
+
+ virtual void Serialize(std::ostream& os) const override;
+
+ std::vector<Group> mGroups;
+};
+
+inline std::ostream& operator<<(std::ostream& os,
+ SdpGroupAttributeList::Semantics s) {
+ switch (s) {
+ case SdpGroupAttributeList::kLs:
+ os << "LS";
+ break;
+ case SdpGroupAttributeList::kFid:
+ os << "FID";
+ break;
+ case SdpGroupAttributeList::kSrf:
+ os << "SRF";
+ break;
+ case SdpGroupAttributeList::kAnat:
+ os << "ANAT";
+ break;
+ case SdpGroupAttributeList::kFec:
+ os << "FEC";
+ break;
+ case SdpGroupAttributeList::kFecFr:
+ os << "FEC-FR";
+ break;
+ case SdpGroupAttributeList::kCs:
+ os << "CS";
+ break;
+ case SdpGroupAttributeList::kDdp:
+ os << "DDP";
+ break;
+ case SdpGroupAttributeList::kDup:
+ os << "DUP";
+ break;
+ case SdpGroupAttributeList::kBundle:
+ os << "BUNDLE";
+ break;
+ default:
+ MOZ_ASSERT(false);
+ os << "?";
+ }
+ return os;
+}
+
+///////////////////////////////////////////////////////////////////////////
+// a=identity, draft-ietf-rtcweb-security-arch
+//-------------------------------------------------------------------------
+// identity-attribute = "identity:" identity-assertion
+// [ SP identity-extension
+// *(";" [ SP ] identity-extension) ]
+// identity-assertion = base64
+// base64 = 1*(ALPHA / DIGIT / "+" / "/" / "=" )
+// identity-extension = extension-att-name [ "=" extension-att-value ]
+// extension-att-name = token
+// extension-att-value = 1*(%x01-09 / %x0b-0c / %x0e-3a / %x3c-ff)
+// ; byte-string from [RFC4566] omitting ";"
+
+// We're just using an SdpStringAttribute for this right now
+#if 0
+class SdpIdentityAttribute : public SdpAttribute
+{
+public:
+ explicit SdpIdentityAttribute(const std::string &assertion,
+ const std::vector<std::string> &extensions =
+ std::vector<std::string>()) :
+ SdpAttribute(kIdentityAttribute),
+ mAssertion(assertion),
+ mExtensions(extensions) {}
+
+ virtual void Serialize(std::ostream& os) const override;
+
+ std::string mAssertion;
+ std::vector<std::string> mExtensions;
+}
+#endif
+
+///////////////////////////////////////////////////////////////////////////
+// a=imageattr, RFC6236
+//-------------------------------------------------------------------------
+// image-attr = "imageattr:" PT 1*2( 1*WSP ( "send" / "recv" )
+// 1*WSP attr-list )
+// PT = 1*DIGIT / "*"
+// attr-list = ( set *(1*WSP set) ) / "*"
+// ; WSP and DIGIT defined in [RFC5234]
+//
+// set= "[" "x=" xyrange "," "y=" xyrange *( "," key-value ) "]"
+// ; x is the horizontal image size range (pixel count)
+// ; y is the vertical image size range (pixel count)
+//
+// key-value = ( "sar=" srange )
+// / ( "par=" prange )
+// / ( "q=" qvalue )
+// ; Key-value MAY be extended with other keyword
+// ; parameters.
+// ; At most, one instance each of sar, par, or q
+// ; is allowed in a set.
+// ;
+// ; sar (sample aspect ratio) is the sample aspect ratio
+// ; associated with the set (optional, MAY be ignored)
+// ; par (picture aspect ratio) is the allowed
+// ; ratio between the display's x and y physical
+// ; size (optional)
+// ; q (optional, range [0.0..1.0], default value 0.5)
+// ; is the preference for the given set,
+// ; a higher value means a higher preference
+//
+// onetonine = "1" / "2" / "3" / "4" / "5" / "6" / "7" / "8" / "9"
+// ; Digit between 1 and 9
+// xyvalue = onetonine *5DIGIT
+// ; Digit between 1 and 9 that is
+// ; followed by 0 to 5 other digits
+// step = xyvalue
+// xyrange = ( "[" xyvalue ":" [ step ":" ] xyvalue "]" )
+// ; Range between a lower and an upper value
+// ; with an optional step, default step = 1
+// ; The rightmost occurrence of xyvalue MUST have a
+// ; higher value than the leftmost occurrence.
+// / ( "[" xyvalue 1*( "," xyvalue ) "]" )
+// ; Discrete values separated by ','
+// / ( xyvalue )
+// ; A single value
+// spvalue = ( "0" "." onetonine *3DIGIT )
+// ; Values between 0.1000 and 0.9999
+// / ( onetonine "." 1*4DIGIT )
+// ; Values between 1.0000 and 9.9999
+// srange = ( "[" spvalue 1*( "," spvalue ) "]" )
+// ; Discrete values separated by ','.
+// ; Each occurrence of spvalue MUST be
+// ; greater than the previous occurrence.
+// / ( "[" spvalue "-" spvalue "]" )
+// ; Range between a lower and an upper level (inclusive)
+// ; The second occurrence of spvalue MUST have a higher
+// ; value than the first
+// / ( spvalue )
+// ; A single value
+//
+// prange = ( "[" spvalue "-" spvalue "]" )
+// ; Range between a lower and an upper level (inclusive)
+// ; The second occurrence of spvalue MUST have a higher
+// ; value than the first
+//
+// qvalue = ( "0" "." 1*2DIGIT )
+// / ( "1" "." 1*2("0") )
+// ; Values between 0.00 and 1.00
+//
+// XXX TBD -- We don't use this yet, and it's a project unto itself.
+//
+
+class SdpImageattrAttributeList : public SdpAttribute {
+ public:
+ SdpImageattrAttributeList() : SdpAttribute(kImageattrAttribute) {}
+
+ class XYRange {
+ public:
+ XYRange() : min(0), max(0), step(1) {}
+ void Serialize(std::ostream& os) const;
+ // TODO: Remove this Bug 1469702
+ bool Parse(std::istream& is, std::string* error);
+ // TODO: Remove this Bug 1469702
+ bool ParseAfterBracket(std::istream& is, std::string* error);
+ // TODO: Remove this Bug 1469702
+ bool ParseAfterMin(std::istream& is, std::string* error);
+ // TODO: Remove this Bug 1469702
+ bool ParseDiscreteValues(std::istream& is, std::string* error);
+ std::vector<uint32_t> discreteValues;
+ // min/max are used iff discreteValues is empty
+ uint32_t min;
+ uint32_t max;
+ uint32_t step;
+ };
+
+ class SRange {
+ public:
+ SRange() : min(0), max(0) {}
+ void Serialize(std::ostream& os) const;
+ // TODO: Remove this Bug 1469702
+ bool Parse(std::istream& is, std::string* error);
+ // TODO: Remove this Bug 1469702
+ bool ParseAfterBracket(std::istream& is, std::string* error);
+ // TODO: Remove this Bug 1469702
+ bool ParseAfterMin(std::istream& is, std::string* error);
+ // TODO: Remove this Bug 1469702
+ bool ParseDiscreteValues(std::istream& is, std::string* error);
+ bool IsSet() const { return !discreteValues.empty() || (min && max); }
+ std::vector<float> discreteValues;
+ // min/max are used iff discreteValues is empty
+ float min;
+ float max;
+ };
+
+ class PRange {
+ public:
+ PRange() : min(0), max(0) {}
+ void Serialize(std::ostream& os) const;
+ // TODO: Remove this Bug 1469702
+ bool Parse(std::istream& is, std::string* error);
+ bool IsSet() const { return min && max; }
+ float min;
+ float max;
+ };
+
+ class Set {
+ public:
+ Set() : qValue(-1) {}
+ void Serialize(std::ostream& os) const;
+ // TODO: Remove this Bug 1469702
+ bool Parse(std::istream& is, std::string* error);
+ XYRange xRange;
+ XYRange yRange;
+ SRange sRange;
+ PRange pRange;
+ float qValue;
+ };
+
+ class Imageattr {
+ public:
+ Imageattr() : pt(), sendAll(false), recvAll(false) {}
+ void Serialize(std::ostream& os) const;
+ // TODO: Remove this Bug 1469702
+ bool Parse(std::istream& is, std::string* error);
+ // TODO: Remove this Bug 1469702
+ bool ParseSets(std::istream& is, std::string* error);
+ // If not set, this means all payload types
+ Maybe<uint16_t> pt;
+ bool sendAll;
+ std::vector<Set> sendSets;
+ bool recvAll;
+ std::vector<Set> recvSets;
+ };
+
+ SdpAttribute* Clone() const override {
+ return new SdpImageattrAttributeList(*this);
+ }
+
+ virtual void Serialize(std::ostream& os) const override;
+
+ // TODO: Remove this Bug 1469702
+ bool PushEntry(const std::string& raw, std::string* error, size_t* errorPos);
+
+ std::vector<Imageattr> mImageattrs;
+};
+
+///////////////////////////////////////////////////////////////////////////
+// a=msid, draft-ietf-mmusic-msid
+//-------------------------------------------------------------------------
+// msid-attr = "msid:" identifier [ SP appdata ]
+// identifier = 1*64token-char ; see RFC 4566
+// appdata = 1*64token-char ; see RFC 4566
+class SdpMsidAttributeList : public SdpAttribute {
+ public:
+ SdpMsidAttributeList() : SdpAttribute(kMsidAttribute) {}
+
+ struct Msid {
+ std::string identifier;
+ std::string appdata;
+ };
+
+ void PushEntry(const std::string& identifier,
+ const std::string& appdata = "") {
+ Msid value = {identifier, appdata};
+ mMsids.push_back(value);
+ }
+
+ SdpAttribute* Clone() const override {
+ return new SdpMsidAttributeList(*this);
+ }
+
+ virtual void Serialize(std::ostream& os) const override;
+
+ std::vector<Msid> mMsids;
+};
+
+///////////////////////////////////////////////////////////////////////////
+// a=msid-semantic, draft-ietf-mmusic-msid
+//-------------------------------------------------------------------------
+// msid-semantic-attr = "msid-semantic:" msid-semantic msid-list
+// msid-semantic = token ; see RFC 4566
+// msid-list = *(" " msid-id) / " *"
+class SdpMsidSemanticAttributeList : public SdpAttribute {
+ public:
+ SdpMsidSemanticAttributeList() : SdpAttribute(kMsidSemanticAttribute) {}
+
+ struct MsidSemantic {
+ // TODO: Once we have some more of these, we might want to make an enum
+ std::string semantic;
+ std::vector<std::string> msids;
+ };
+
+ void PushEntry(const std::string& semantic,
+ const std::vector<std::string>& msids) {
+ MsidSemantic value = {semantic, msids};
+ mMsidSemantics.push_back(value);
+ }
+
+ SdpAttribute* Clone() const override {
+ return new SdpMsidSemanticAttributeList(*this);
+ }
+
+ virtual void Serialize(std::ostream& os) const override;
+
+ std::vector<MsidSemantic> mMsidSemantics;
+};
+
+///////////////////////////////////////////////////////////////////////////
+// a=remote-candiate, RFC5245
+//-------------------------------------------------------------------------
+// remote-candidate-att = "remote-candidates" ":" remote-candidate
+// 0*(SP remote-candidate)
+// remote-candidate = component-ID SP connection-address SP port
+class SdpRemoteCandidatesAttribute : public SdpAttribute {
+ public:
+ struct Candidate {
+ std::string id;
+ std::string address;
+ uint16_t port;
+ };
+
+ explicit SdpRemoteCandidatesAttribute(
+ const std::vector<Candidate>& candidates)
+ : SdpAttribute(kRemoteCandidatesAttribute), mCandidates(candidates) {}
+
+ SdpAttribute* Clone() const override {
+ return new SdpRemoteCandidatesAttribute(*this);
+ }
+
+ virtual void Serialize(std::ostream& os) const override;
+
+ std::vector<Candidate> mCandidates;
+};
+
+/*
+a=rid, draft-pthatcher-mmusic-rid-01
+
+ rid-syntax = "a=rid:" rid-identifier SP rid-dir
+ [ rid-pt-param-list / rid-param-list ]
+
+ rid-identifier = 1*(alpha-numeric / "-" / "_")
+
+ rid-dir = "send" / "recv"
+
+ rid-pt-param-list = SP rid-fmt-list *(";" rid-param)
+
+ rid-param-list = SP rid-param *(";" rid-param)
+
+ rid-fmt-list = "pt=" fmt *( "," fmt )
+ ; fmt defined in {{RFC4566}}
+
+ rid-param = rid-width-param
+ / rid-height-param
+ / rid-fps-param
+ / rid-fs-param
+ / rid-br-param
+ / rid-pps-param
+ / rid-depend-param
+ / rid-param-other
+
+ rid-width-param = "max-width" [ "=" int-param-val ]
+
+ rid-height-param = "max-height" [ "=" int-param-val ]
+
+ rid-fps-param = "max-fps" [ "=" int-param-val ]
+
+ rid-fs-param = "max-fs" [ "=" int-param-val ]
+
+ rid-br-param = "max-br" [ "=" int-param-val ]
+
+ rid-pps-param = "max-pps" [ "=" int-param-val ]
+
+ rid-depend-param = "depend=" rid-list
+
+ rid-param-other = 1*(alpha-numeric / "-") [ "=" param-val ]
+
+ rid-list = rid-identifier *( "," rid-identifier )
+
+ int-param-val = 1*DIGIT
+
+ param-val = *( %x20-58 / %x60-7E )
+ ; Any printable character except semicolon
+*/
+class SdpRidAttributeList : public SdpAttribute {
+ public:
+ explicit SdpRidAttributeList() : SdpAttribute(kRidAttribute) {}
+
+ struct Rid {
+ Rid() : direction(sdp::kSend) {}
+
+ // Remove this function. See Bug 1469702
+ bool Parse(std::istream& is, std::string* error);
+ // Remove this function. See Bug 1469702
+ bool ParseParameters(std::istream& is, std::string* error);
+ // Remove this function. See Bug 1469702
+ bool ParseDepend(std::istream& is, std::string* error);
+ // Remove this function. See Bug 1469702
+ bool ParseFormats(std::istream& is, std::string* error);
+
+ void Serialize(std::ostream& os) const;
+ void SerializeParameters(std::ostream& os) const;
+ bool HasFormat(const std::string& format) const;
+ bool HasParameters() const {
+ return !formats.empty() || constraints.maxWidth ||
+ constraints.maxHeight || constraints.maxFps || constraints.maxFs ||
+ constraints.maxBr || constraints.maxPps || !dependIds.empty();
+ }
+
+ std::string id;
+ sdp::Direction direction;
+ std::vector<uint16_t> formats; // Empty implies all
+ EncodingConstraints constraints;
+ std::vector<std::string> dependIds;
+ };
+
+ SdpAttribute* Clone() const override {
+ return new SdpRidAttributeList(*this);
+ }
+
+ static bool CheckRidValidity(const std::string& aRid, std::string* aError);
+ static size_t kMaxRidLength;
+
+ virtual void Serialize(std::ostream& os) const override;
+
+ // Remove this function. See Bug 1469702
+ bool PushEntry(const std::string& raw, std::string* error, size_t* errorPos);
+
+ void PushEntry(const std::string& id, sdp::Direction dir,
+ const std::vector<uint16_t>& formats,
+ const EncodingConstraints& constraints,
+ const std::vector<std::string>& dependIds);
+
+ std::vector<Rid> mRids;
+};
+
+///////////////////////////////////////////////////////////////////////////
+// a=rtcp, RFC3605
+//-------------------------------------------------------------------------
+// rtcp-attribute = "a=rtcp:" port [nettype space addrtype space
+// connection-address] CRLF
+class SdpRtcpAttribute : public SdpAttribute {
+ public:
+ explicit SdpRtcpAttribute(uint16_t port)
+ : SdpAttribute(kRtcpAttribute),
+ mPort(port),
+ mNetType(sdp::kNetTypeNone),
+ mAddrType(sdp::kAddrTypeNone) {}
+
+ SdpRtcpAttribute(uint16_t port, sdp::NetType netType, sdp::AddrType addrType,
+ const std::string& address)
+ : SdpAttribute(kRtcpAttribute),
+ mPort(port),
+ mNetType(netType),
+ mAddrType(addrType),
+ mAddress(address) {
+ MOZ_ASSERT(netType != sdp::kNetTypeNone);
+ MOZ_ASSERT(addrType != sdp::kAddrTypeNone);
+ MOZ_ASSERT(!address.empty());
+ }
+
+ SdpAttribute* Clone() const override { return new SdpRtcpAttribute(*this); }
+
+ virtual void Serialize(std::ostream& os) const override;
+
+ uint16_t mPort;
+ sdp::NetType mNetType;
+ sdp::AddrType mAddrType;
+ std::string mAddress;
+};
+
+///////////////////////////////////////////////////////////////////////////
+// a=rtcp-fb, RFC4585
+//-------------------------------------------------------------------------
+// rtcp-fb-syntax = "a=rtcp-fb:" rtcp-fb-pt SP rtcp-fb-val CRLF
+//
+// rtcp-fb-pt = "*" ; wildcard: applies to all formats
+// / fmt ; as defined in SDP spec
+//
+// rtcp-fb-val = "ack" rtcp-fb-ack-param
+// / "nack" rtcp-fb-nack-param
+// / "trr-int" SP 1*DIGIT
+// / rtcp-fb-id rtcp-fb-param
+//
+// rtcp-fb-id = 1*(alpha-numeric / "-" / "_")
+//
+// rtcp-fb-param = SP "app" [SP byte-string]
+// / SP token [SP byte-string]
+// / ; empty
+//
+// rtcp-fb-ack-param = SP "rpsi"
+// / SP "app" [SP byte-string]
+// / SP token [SP byte-string]
+// / ; empty
+//
+// rtcp-fb-nack-param = SP "pli"
+// / SP "sli"
+// / SP "rpsi"
+// / SP "app" [SP byte-string]
+// / SP token [SP byte-string]
+// / ; empty
+//
+class SdpRtcpFbAttributeList : public SdpAttribute {
+ public:
+ SdpRtcpFbAttributeList() : SdpAttribute(kRtcpFbAttribute) {}
+
+ enum Type { kAck, kApp, kCcm, kNack, kTrrInt, kRemb, kTransportCC };
+
+ static const char* pli;
+ static const char* sli;
+ static const char* rpsi;
+ static const char* app;
+
+ static const char* fir;
+ static const char* tmmbr;
+ static const char* tstr;
+ static const char* vbcm;
+
+ struct Feedback {
+ std::string pt;
+ Type type;
+ std::string parameter;
+ std::string extra;
+ // TODO(bug 1744307): Use =default here once it is supported
+ bool operator==(const Feedback& aOther) const {
+ return pt == aOther.pt && type == aOther.type &&
+ parameter == aOther.parameter && extra == aOther.extra;
+ }
+ };
+
+ void PushEntry(const std::string& pt, Type type,
+ const std::string& parameter = "",
+ const std::string& extra = "") {
+ Feedback value = {pt, type, parameter, extra};
+ mFeedbacks.push_back(value);
+ }
+
+ SdpAttribute* Clone() const override {
+ return new SdpRtcpFbAttributeList(*this);
+ }
+
+ virtual void Serialize(std::ostream& os) const override;
+
+ std::vector<Feedback> mFeedbacks;
+};
+
+inline std::ostream& operator<<(std::ostream& os,
+ SdpRtcpFbAttributeList::Type type) {
+ switch (type) {
+ case SdpRtcpFbAttributeList::kAck:
+ os << "ack";
+ break;
+ case SdpRtcpFbAttributeList::kApp:
+ os << "app";
+ break;
+ case SdpRtcpFbAttributeList::kCcm:
+ os << "ccm";
+ break;
+ case SdpRtcpFbAttributeList::kNack:
+ os << "nack";
+ break;
+ case SdpRtcpFbAttributeList::kTrrInt:
+ os << "trr-int";
+ break;
+ case SdpRtcpFbAttributeList::kRemb:
+ os << "goog-remb";
+ break;
+ case SdpRtcpFbAttributeList::kTransportCC:
+ os << "transport-cc";
+ break;
+ default:
+ MOZ_ASSERT(false);
+ os << "?";
+ }
+ return os;
+}
+
+///////////////////////////////////////////////////////////////////////////
+// a=rtpmap, RFC4566
+//-------------------------------------------------------------------------
+// a=rtpmap:<payload type> <encoding name>/<clock rate> [/<encoding parameters>]
+class SdpRtpmapAttributeList : public SdpAttribute {
+ public:
+ SdpRtpmapAttributeList() : SdpAttribute(kRtpmapAttribute) {}
+
+ // Minimal set to get going
+ enum CodecType {
+ kOpus,
+ kG722,
+ kPCMU,
+ kPCMA,
+ kVP8,
+ kVP9,
+ kiLBC,
+ kiSAC,
+ kH264,
+ kRed,
+ kUlpfec,
+ kTelephoneEvent,
+ kRtx,
+ kOtherCodec
+ };
+
+ struct Rtpmap {
+ std::string pt;
+ CodecType codec;
+ std::string name;
+ uint32_t clock;
+ // Technically, this could mean something else in the future.
+ // In practice, that's probably not going to happen.
+ uint32_t channels;
+ };
+
+ void PushEntry(const std::string& pt, CodecType codec,
+ const std::string& name, uint32_t clock,
+ uint32_t channels = 0) {
+ Rtpmap value = {pt, codec, name, clock, channels};
+ mRtpmaps.push_back(value);
+ }
+
+ SdpAttribute* Clone() const override {
+ return new SdpRtpmapAttributeList(*this);
+ }
+
+ virtual void Serialize(std::ostream& os) const override;
+
+ bool HasEntry(const std::string& pt) const {
+ for (auto it = mRtpmaps.begin(); it != mRtpmaps.end(); ++it) {
+ if (it->pt == pt) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ const Rtpmap& GetEntry(const std::string& pt) const {
+ for (auto it = mRtpmaps.begin(); it != mRtpmaps.end(); ++it) {
+ if (it->pt == pt) {
+ return *it;
+ }
+ }
+ MOZ_CRASH();
+ }
+
+ std::vector<Rtpmap> mRtpmaps;
+};
+
+inline std::ostream& operator<<(std::ostream& os,
+ SdpRtpmapAttributeList::CodecType c) {
+ switch (c) {
+ case SdpRtpmapAttributeList::kOpus:
+ os << "opus";
+ break;
+ case SdpRtpmapAttributeList::kG722:
+ os << "G722";
+ break;
+ case SdpRtpmapAttributeList::kPCMU:
+ os << "PCMU";
+ break;
+ case SdpRtpmapAttributeList::kPCMA:
+ os << "PCMA";
+ break;
+ case SdpRtpmapAttributeList::kVP8:
+ os << "VP8";
+ break;
+ case SdpRtpmapAttributeList::kVP9:
+ os << "VP9";
+ break;
+ case SdpRtpmapAttributeList::kiLBC:
+ os << "iLBC";
+ break;
+ case SdpRtpmapAttributeList::kiSAC:
+ os << "iSAC";
+ break;
+ case SdpRtpmapAttributeList::kH264:
+ os << "H264";
+ break;
+ case SdpRtpmapAttributeList::kRed:
+ os << "red";
+ break;
+ case SdpRtpmapAttributeList::kUlpfec:
+ os << "ulpfec";
+ break;
+ case SdpRtpmapAttributeList::kTelephoneEvent:
+ os << "telephone-event";
+ break;
+ case SdpRtpmapAttributeList::kRtx:
+ os << "rtx";
+ break;
+ default:
+ MOZ_ASSERT(false);
+ os << "?";
+ }
+ return os;
+}
+
+///////////////////////////////////////////////////////////////////////////
+// a=fmtp, RFC4566, RFC5576
+//-------------------------------------------------------------------------
+// a=fmtp:<format> <format specific parameters>
+//
+class SdpFmtpAttributeList : public SdpAttribute {
+ public:
+ SdpFmtpAttributeList() : SdpAttribute(kFmtpAttribute) {}
+
+ // Base class for format parameters
+ class Parameters {
+ public:
+ explicit Parameters(SdpRtpmapAttributeList::CodecType aCodec)
+ : codec_type(aCodec) {}
+
+ virtual ~Parameters() {}
+ virtual Parameters* Clone() const = 0;
+ virtual void Serialize(std::ostream& os) const = 0;
+ virtual bool CompareEq(const Parameters& other) const = 0;
+
+ bool operator==(const Parameters& other) const {
+ return codec_type == other.codec_type && CompareEq(other);
+ }
+
+ SdpRtpmapAttributeList::CodecType codec_type;
+ };
+
+ class RedParameters : public Parameters {
+ public:
+ RedParameters() : Parameters(SdpRtpmapAttributeList::kRed) {}
+
+ virtual Parameters* Clone() const override {
+ return new RedParameters(*this);
+ }
+
+ virtual void Serialize(std::ostream& os) const override {
+ for (size_t i = 0; i < encodings.size(); ++i) {
+ os << (i != 0 ? "/" : "") << std::to_string(encodings[i]);
+ }
+ }
+
+ virtual bool CompareEq(const Parameters& other) const override {
+ return encodings == static_cast<const RedParameters&>(other).encodings;
+ }
+
+ std::vector<uint8_t> encodings;
+ };
+
+ class RtxParameters : public Parameters {
+ public:
+ uint8_t apt = 255; // Valid payload types are 0 - 127, use 255 to represent
+ // unset value.
+ Maybe<uint32_t> rtx_time;
+
+ RtxParameters() : Parameters(SdpRtpmapAttributeList::kRtx) {}
+
+ virtual ~RtxParameters() {}
+
+ virtual Parameters* Clone() const override {
+ return new RtxParameters(*this);
+ }
+
+ virtual void Serialize(std::ostream& os) const override {
+ if (apt <= 127) {
+ os << "apt=" << static_cast<uint32_t>(apt);
+ rtx_time.apply([&](const auto& time) { os << ";rtx-time=" << time; });
+ }
+ }
+
+ virtual bool CompareEq(const Parameters& aOther) const override {
+ if (aOther.codec_type != codec_type) {
+ return false;
+ }
+ auto other = static_cast<const RtxParameters&>(aOther);
+ return other.apt == apt && other.rtx_time == rtx_time;
+ }
+ };
+
+ class H264Parameters : public Parameters {
+ public:
+ static const uint32_t kDefaultProfileLevelId = 0x420010;
+
+ H264Parameters()
+ : Parameters(SdpRtpmapAttributeList::kH264),
+ packetization_mode(0),
+ level_asymmetry_allowed(false),
+ profile_level_id(kDefaultProfileLevelId),
+ max_mbps(0),
+ max_fs(0),
+ max_cpb(0),
+ max_dpb(0),
+ max_br(0) {
+ memset(sprop_parameter_sets, 0, sizeof(sprop_parameter_sets));
+ }
+
+ virtual Parameters* Clone() const override {
+ return new H264Parameters(*this);
+ }
+
+ virtual void Serialize(std::ostream& os) const override {
+ // Note: don't move this, since having an unconditional param up top
+ // lets us avoid a whole bunch of conditional streaming of ';' below
+ os << "profile-level-id=" << std::hex << std::setfill('0') << std::setw(6)
+ << profile_level_id << std::dec << std::setfill(' ');
+
+ os << ";level-asymmetry-allowed=" << (level_asymmetry_allowed ? 1 : 0);
+
+ if (strlen(sprop_parameter_sets)) {
+ os << ";sprop-parameter-sets=" << sprop_parameter_sets;
+ }
+
+ if (packetization_mode != 0) {
+ os << ";packetization-mode=" << packetization_mode;
+ }
+
+ if (max_mbps != 0) {
+ os << ";max-mbps=" << max_mbps;
+ }
+
+ if (max_fs != 0) {
+ os << ";max-fs=" << max_fs;
+ }
+
+ if (max_cpb != 0) {
+ os << ";max-cpb=" << max_cpb;
+ }
+
+ if (max_dpb != 0) {
+ os << ";max-dpb=" << max_dpb;
+ }
+
+ if (max_br != 0) {
+ os << ";max-br=" << max_br;
+ }
+ }
+
+ virtual bool CompareEq(const Parameters& other) const override {
+ const auto& otherH264 = static_cast<const H264Parameters&>(other);
+
+ // sprop is not comapred here as it does not get parsed in the rsdparsa
+ return packetization_mode == otherH264.packetization_mode &&
+ level_asymmetry_allowed == otherH264.level_asymmetry_allowed &&
+ profile_level_id == otherH264.profile_level_id &&
+ max_mbps == otherH264.max_mbps && max_fs == otherH264.max_fs &&
+ max_cpb == otherH264.max_cpb && max_dpb == otherH264.max_dpb &&
+ max_br == otherH264.max_br;
+ }
+
+ static const size_t max_sprop_len = 128;
+ char sprop_parameter_sets[max_sprop_len];
+ unsigned int packetization_mode;
+ bool level_asymmetry_allowed;
+ unsigned int profile_level_id;
+ unsigned int max_mbps;
+ unsigned int max_fs;
+ unsigned int max_cpb;
+ unsigned int max_dpb;
+ unsigned int max_br;
+ };
+
+ // Also used for VP9 since they share parameters
+ class VP8Parameters : public Parameters {
+ public:
+ explicit VP8Parameters(SdpRtpmapAttributeList::CodecType type)
+ : Parameters(type), max_fs(0), max_fr(0) {}
+
+ virtual Parameters* Clone() const override {
+ return new VP8Parameters(*this);
+ }
+
+ virtual void Serialize(std::ostream& os) const override {
+ // draft-ietf-payload-vp8-11 says these are mandatory, upper layer
+ // needs to ensure they're set properly.
+ os << "max-fs=" << max_fs;
+ os << ";max-fr=" << max_fr;
+ }
+
+ virtual bool CompareEq(const Parameters& other) const override {
+ const auto& otherVP8 = static_cast<const VP8Parameters&>(other);
+
+ return max_fs == otherVP8.max_fs && max_fr == otherVP8.max_fr;
+ }
+
+ unsigned int max_fs;
+ unsigned int max_fr;
+ };
+
+ class OpusParameters : public Parameters {
+ public:
+ enum {
+ kDefaultMaxPlaybackRate = 48000,
+ kDefaultStereo = 0,
+ kDefaultUseInBandFec = 0,
+ kDefaultMaxAverageBitrate = 0,
+ kDefaultUseDTX = 0,
+ kDefaultFrameSize = 0,
+ kDefaultMinFrameSize = 0,
+ kDefaultMaxFrameSize = 0,
+ kDefaultUseCbr = 0
+ };
+ OpusParameters()
+ : Parameters(SdpRtpmapAttributeList::kOpus),
+ maxplaybackrate(kDefaultMaxPlaybackRate),
+ stereo(kDefaultStereo),
+ useInBandFec(kDefaultUseInBandFec),
+ maxAverageBitrate(kDefaultMaxAverageBitrate),
+ useDTX(kDefaultUseDTX),
+ frameSizeMs(kDefaultFrameSize),
+ minFrameSizeMs(kDefaultMinFrameSize),
+ maxFrameSizeMs(kDefaultMaxFrameSize),
+ useCbr(kDefaultUseCbr) {}
+
+ Parameters* Clone() const override { return new OpusParameters(*this); }
+
+ void Serialize(std::ostream& os) const override {
+ os << "maxplaybackrate=" << maxplaybackrate << ";stereo=" << stereo
+ << ";useinbandfec=" << useInBandFec;
+
+ if (useDTX) {
+ os << ";usedtx=1";
+ }
+ if (maxAverageBitrate) {
+ os << ";maxaveragebitrate=" << maxAverageBitrate;
+ }
+ if (frameSizeMs) {
+ os << ";ptime=" << frameSizeMs;
+ }
+ if (minFrameSizeMs) {
+ os << ";minptime=" << minFrameSizeMs;
+ }
+ if (maxFrameSizeMs) {
+ os << ";maxptime=" << maxFrameSizeMs;
+ }
+ if (useCbr) {
+ os << ";cbr=1";
+ }
+ }
+
+ virtual bool CompareEq(const Parameters& other) const override {
+ const auto& otherOpus = static_cast<const OpusParameters&>(other);
+
+ bool maxplaybackrateIsEq = (maxplaybackrate == otherOpus.maxplaybackrate);
+
+ // This is due to a bug in sipcc that causes maxplaybackrate to
+ // always be 0 if it appears in the fmtp
+ if (((maxplaybackrate == 0) && (otherOpus.maxplaybackrate != 0)) ||
+ ((maxplaybackrate != 0) && (otherOpus.maxplaybackrate == 0))) {
+ maxplaybackrateIsEq = true;
+ }
+
+ return maxplaybackrateIsEq && stereo == otherOpus.stereo &&
+ useInBandFec == otherOpus.useInBandFec &&
+ maxAverageBitrate == otherOpus.maxAverageBitrate &&
+ useDTX == otherOpus.useDTX &&
+ frameSizeMs == otherOpus.frameSizeMs &&
+ minFrameSizeMs == otherOpus.minFrameSizeMs &&
+ maxFrameSizeMs == otherOpus.maxFrameSizeMs &&
+ useCbr == otherOpus.useCbr;
+ }
+
+ unsigned int maxplaybackrate;
+ unsigned int stereo;
+ unsigned int useInBandFec;
+ uint32_t maxAverageBitrate;
+ bool useDTX;
+ uint32_t frameSizeMs;
+ uint32_t minFrameSizeMs;
+ uint32_t maxFrameSizeMs;
+ bool useCbr;
+ };
+
+ class TelephoneEventParameters : public Parameters {
+ public:
+ TelephoneEventParameters()
+ : Parameters(SdpRtpmapAttributeList::kTelephoneEvent),
+ dtmfTones("0-15") {}
+
+ virtual Parameters* Clone() const override {
+ return new TelephoneEventParameters(*this);
+ }
+
+ void Serialize(std::ostream& os) const override { os << dtmfTones; }
+
+ virtual bool CompareEq(const Parameters& other) const override {
+ return dtmfTones ==
+ static_cast<const TelephoneEventParameters&>(other).dtmfTones;
+ }
+
+ std::string dtmfTones;
+ };
+
+ class Fmtp {
+ public:
+ Fmtp(const std::string& aFormat, const Parameters& aParameters)
+ : format(aFormat), parameters(aParameters.Clone()) {}
+
+ // TODO: Rip all of this out when we have move semantics in the stl.
+ Fmtp(const Fmtp& orig) { *this = orig; }
+
+ Fmtp& operator=(const Fmtp& rhs) {
+ if (this != &rhs) {
+ format = rhs.format;
+ parameters.reset(rhs.parameters ? rhs.parameters->Clone() : nullptr);
+ }
+ return *this;
+ }
+
+ bool operator==(const Fmtp& other) const {
+ return format == other.format && *parameters == *other.parameters;
+ }
+
+ // The contract around these is as follows:
+ // * |parameters| is only set if we recognized the media type and had
+ // a subclass of Parameters to represent that type of parameters
+ // * |parameters| is a best-effort representation; it might be missing
+ // stuff
+ // * Parameters::codec_type tells you the concrete class, eg
+ // kH264 -> H264Parameters
+ std::string format;
+ UniquePtr<Parameters> parameters;
+ };
+
+ bool operator==(const SdpFmtpAttributeList& other) const;
+
+ SdpAttribute* Clone() const override {
+ return new SdpFmtpAttributeList(*this);
+ }
+
+ virtual void Serialize(std::ostream& os) const override;
+
+ void PushEntry(const std::string& format, const Parameters& parameters) {
+ mFmtps.push_back(Fmtp(format, parameters));
+ }
+
+ std::vector<Fmtp> mFmtps;
+};
+
+///////////////////////////////////////////////////////////////////////////
+// a=sctpmap, draft-ietf-mmusic-sctp-sdp-05
+//-------------------------------------------------------------------------
+// sctpmap-attr = "a=sctpmap:" sctpmap-number media-subtypes
+// [streams]
+// sctpmap-number = 1*DIGIT
+// protocol = labelstring
+// labelstring = text
+// text = byte-string
+// streams = 1*DIGIT
+//
+// We're going to pretend that there are spaces where they make sense.
+class SdpSctpmapAttributeList : public SdpAttribute {
+ public:
+ SdpSctpmapAttributeList() : SdpAttribute(kSctpmapAttribute) {}
+
+ struct Sctpmap {
+ std::string pt;
+ std::string name;
+ uint32_t streams;
+ };
+
+ void PushEntry(const std::string& pt, const std::string& name,
+ uint32_t streams = 0) {
+ Sctpmap value = {pt, name, streams};
+ mSctpmaps.push_back(value);
+ }
+
+ SdpAttribute* Clone() const override {
+ return new SdpSctpmapAttributeList(*this);
+ }
+
+ virtual void Serialize(std::ostream& os) const override;
+
+ bool HasEntry(const std::string& pt) const {
+ for (auto it = mSctpmaps.begin(); it != mSctpmaps.end(); ++it) {
+ if (it->pt == pt) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ const Sctpmap& GetFirstEntry() const { return mSctpmaps[0]; }
+
+ std::vector<Sctpmap> mSctpmaps;
+};
+
+///////////////////////////////////////////////////////////////////////////
+// a=setup, RFC4145
+//-------------------------------------------------------------------------
+// setup-attr = "a=setup:" role
+// role = "active" / "passive" / "actpass" / "holdconn"
+class SdpSetupAttribute : public SdpAttribute {
+ public:
+ enum Role { kActive, kPassive, kActpass, kHoldconn };
+
+ explicit SdpSetupAttribute(Role role)
+ : SdpAttribute(kSetupAttribute), mRole(role) {}
+
+ SdpAttribute* Clone() const override { return new SdpSetupAttribute(*this); }
+
+ virtual void Serialize(std::ostream& os) const override;
+
+ Role mRole;
+};
+
+inline std::ostream& operator<<(std::ostream& os, SdpSetupAttribute::Role r) {
+ switch (r) {
+ case SdpSetupAttribute::kActive:
+ os << "active";
+ break;
+ case SdpSetupAttribute::kPassive:
+ os << "passive";
+ break;
+ case SdpSetupAttribute::kActpass:
+ os << "actpass";
+ break;
+ case SdpSetupAttribute::kHoldconn:
+ os << "holdconn";
+ break;
+ default:
+ MOZ_ASSERT(false);
+ os << "?";
+ }
+ return os;
+}
+
+// Old draft-04
+// sc-attr = "a=simulcast:" 1*2( WSP sc-str-list ) [WSP sc-pause-list]
+// sc-str-list = sc-dir WSP sc-id-type "=" sc-alt-list *( ";" sc-alt-list )
+// sc-pause-list = "paused=" sc-alt-list
+// sc-dir = "send" / "recv"
+// sc-id-type = "pt" / "rid" / token
+// sc-alt-list = sc-id *( "," sc-id )
+// sc-id = fmt / rid-identifier / token
+// ; WSP defined in [RFC5234]
+// ; fmt, token defined in [RFC4566]
+// ; rid-identifier defined in [I-D.pthatcher-mmusic-rid]
+//
+// New draft 14, need to parse this for now, will eventually emit it
+// 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
+// ; SP defined in [RFC5234]
+// ; rid-id defined in [I-D.ietf-mmusic-rid]
+
+class SdpSimulcastAttribute : public SdpAttribute {
+ public:
+ SdpSimulcastAttribute() : SdpAttribute(kSimulcastAttribute) {}
+
+ SdpAttribute* Clone() const override {
+ return new SdpSimulcastAttribute(*this);
+ }
+
+ void Serialize(std::ostream& os) const override;
+ bool Parse(std::istream& is, std::string* error);
+
+ class Encoding {
+ public:
+ Encoding(const std::string& aRid, bool aPaused)
+ : rid(aRid), paused(aPaused) {}
+ std::string rid;
+ bool paused = false;
+ };
+
+ class Version {
+ public:
+ void Serialize(std::ostream& os) const;
+ bool IsSet() const { return !choices.empty(); }
+ bool Parse(std::istream& is, std::string* error);
+
+ std::vector<Encoding> choices;
+ };
+
+ class Versions : public std::vector<Version> {
+ public:
+ void Serialize(std::ostream& os) const;
+ bool IsSet() const {
+ if (empty()) {
+ return false;
+ }
+
+ for (const Version& version : *this) {
+ if (version.IsSet()) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ bool Parse(std::istream& is, std::string* error);
+ };
+
+ Versions sendVersions;
+ Versions recvVersions;
+};
+
+///////////////////////////////////////////////////////////////////////////
+// a=ssrc, RFC5576
+//-------------------------------------------------------------------------
+// ssrc-attr = "ssrc:" ssrc-id SP attribute
+// ; The base definition of "attribute" is in RFC 4566.
+// ; (It is the content of "a=" lines.)
+//
+// ssrc-id = integer ; 0 .. 2**32 - 1
+//-------------------------------------------------------------------------
+// TODO -- In the future, it might be nice if we ran a parse on the
+// attribute section of this so that we could interpret it semantically.
+// For WebRTC, the key use case for a=ssrc is assocaiting SSRCs with
+// media sections, and we're not really going to care about the attribute
+// itself. So we're just going to store it as a string for the time being.
+// Issue 187.
+class SdpSsrcAttributeList : public SdpAttribute {
+ public:
+ SdpSsrcAttributeList() : SdpAttribute(kSsrcAttribute) {}
+
+ struct Ssrc {
+ uint32_t ssrc;
+ std::string attribute;
+ };
+
+ void PushEntry(uint32_t ssrc, const std::string& attribute) {
+ Ssrc value = {ssrc, attribute};
+ mSsrcs.push_back(value);
+ }
+
+ SdpAttribute* Clone() const override {
+ return new SdpSsrcAttributeList(*this);
+ }
+
+ virtual void Serialize(std::ostream& os) const override;
+
+ std::vector<Ssrc> mSsrcs;
+};
+
+///////////////////////////////////////////////////////////////////////////
+// a=ssrc-group, RFC5576
+//-------------------------------------------------------------------------
+// ssrc-group-attr = "ssrc-group:" semantics *(SP ssrc-id)
+//
+// semantics = "FEC" / "FID" / token
+//
+// ssrc-id = integer ; 0 .. 2**32 - 1
+class SdpSsrcGroupAttributeList : public SdpAttribute {
+ public:
+ enum Semantics {
+ kFec, // RFC5576
+ kFid, // RFC5576
+ kFecFr, // RFC5956
+ kDup, // RFC7104
+ kSim // non-standard, used by hangouts
+ };
+
+ struct SsrcGroup {
+ Semantics semantics;
+ std::vector<uint32_t> ssrcs;
+ };
+
+ SdpSsrcGroupAttributeList() : SdpAttribute(kSsrcGroupAttribute) {}
+
+ void PushEntry(Semantics semantics, const std::vector<uint32_t>& ssrcs) {
+ SsrcGroup value = {semantics, ssrcs};
+ mSsrcGroups.push_back(value);
+ }
+
+ SdpAttribute* Clone() const override {
+ return new SdpSsrcGroupAttributeList(*this);
+ }
+
+ virtual void Serialize(std::ostream& os) const override;
+
+ std::vector<SsrcGroup> mSsrcGroups;
+};
+
+inline std::ostream& operator<<(std::ostream& os,
+ SdpSsrcGroupAttributeList::Semantics s) {
+ switch (s) {
+ case SdpSsrcGroupAttributeList::kFec:
+ os << "FEC";
+ break;
+ case SdpSsrcGroupAttributeList::kFid:
+ os << "FID";
+ break;
+ case SdpSsrcGroupAttributeList::kFecFr:
+ os << "FEC-FR";
+ break;
+ case SdpSsrcGroupAttributeList::kDup:
+ os << "DUP";
+ break;
+ case SdpSsrcGroupAttributeList::kSim:
+ os << "SIM";
+ break;
+ default:
+ MOZ_ASSERT(false);
+ os << "?";
+ }
+ return os;
+}
+
+///////////////////////////////////////////////////////////////////////////
+class SdpMultiStringAttribute : public SdpAttribute {
+ public:
+ explicit SdpMultiStringAttribute(AttributeType type) : SdpAttribute(type) {}
+
+ void PushEntry(const std::string& entry) { mValues.push_back(entry); }
+
+ SdpAttribute* Clone() const override {
+ return new SdpMultiStringAttribute(*this);
+ }
+
+ virtual void Serialize(std::ostream& os) const override;
+
+ std::vector<std::string> mValues;
+};
+
+// otherwise identical to SdpMultiStringAttribute, this is used for
+// ice-options and other places where the value is serialized onto
+// a single line with space separating tokens
+class SdpOptionsAttribute : public SdpAttribute {
+ public:
+ explicit SdpOptionsAttribute(AttributeType type) : SdpAttribute(type) {}
+
+ void PushEntry(const std::string& entry) { mValues.push_back(entry); }
+
+ void Load(const std::string& value);
+
+ SdpAttribute* Clone() const override {
+ return new SdpOptionsAttribute(*this);
+ }
+
+ virtual void Serialize(std::ostream& os) const override;
+
+ std::vector<std::string> mValues;
+};
+
+// Used for attributes that take no value (eg; a=ice-lite)
+class SdpFlagAttribute : public SdpAttribute {
+ public:
+ explicit SdpFlagAttribute(AttributeType type) : SdpAttribute(type) {}
+
+ SdpAttribute* Clone() const override { return new SdpFlagAttribute(*this); }
+
+ virtual void Serialize(std::ostream& os) const override;
+};
+
+// Used for any other kind of single-valued attribute not otherwise specialized
+class SdpStringAttribute : public SdpAttribute {
+ public:
+ explicit SdpStringAttribute(AttributeType type, const std::string& value)
+ : SdpAttribute(type), mValue(value) {}
+
+ SdpAttribute* Clone() const override { return new SdpStringAttribute(*this); }
+
+ virtual void Serialize(std::ostream& os) const override;
+
+ std::string mValue;
+};
+
+// Used for any purely (non-negative) numeric attribute
+class SdpNumberAttribute : public SdpAttribute {
+ public:
+ explicit SdpNumberAttribute(AttributeType type, uint32_t value = 0)
+ : SdpAttribute(type), mValue(value) {}
+
+ SdpAttribute* Clone() const override { return new SdpNumberAttribute(*this); }
+
+ virtual void Serialize(std::ostream& os) const override;
+
+ uint32_t mValue;
+};
+
+} // namespace mozilla
+
+#endif
diff --git a/dom/media/webrtc/sdp/SdpAttributeList.h b/dom/media/webrtc/sdp/SdpAttributeList.h
new file mode 100644
index 0000000000..23088dc967
--- /dev/null
+++ b/dom/media/webrtc/sdp/SdpAttributeList.h
@@ -0,0 +1,90 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _SDPATTRIBUTELIST_H_
+#define _SDPATTRIBUTELIST_H_
+
+#include "mozilla/UniquePtr.h"
+#include "mozilla/Attributes.h"
+
+#include "sdp/SdpAttribute.h"
+
+namespace mozilla {
+
+class SdpAttributeList {
+ public:
+ virtual ~SdpAttributeList() {}
+ typedef SdpAttribute::AttributeType AttributeType;
+
+ // Avoid default params on virtual functions
+ bool HasAttribute(AttributeType type) const {
+ return HasAttribute(type, true);
+ }
+
+ const SdpAttribute* GetAttribute(AttributeType type) const {
+ return GetAttribute(type, true);
+ }
+
+ virtual bool HasAttribute(AttributeType type, bool sessionFallback) const = 0;
+ virtual const SdpAttribute* GetAttribute(AttributeType type,
+ bool sessionFallback) const = 0;
+ // The setter takes an attribute of any type, and takes ownership
+ virtual void SetAttribute(SdpAttribute* attr) = 0;
+ virtual void RemoveAttribute(AttributeType type) = 0;
+ virtual void Clear() = 0;
+ virtual uint32_t Count() const = 0;
+
+ virtual const SdpConnectionAttribute& GetConnection() const = 0;
+ virtual const SdpOptionsAttribute& GetIceOptions() const = 0;
+ virtual const SdpRtcpAttribute& GetRtcp() const = 0;
+ virtual const SdpRemoteCandidatesAttribute& GetRemoteCandidates() const = 0;
+ virtual const SdpSetupAttribute& GetSetup() const = 0;
+ virtual const SdpDtlsMessageAttribute& GetDtlsMessage() const = 0;
+
+ // These attributes can appear multiple times, so the returned
+ // classes actually represent a collection of values.
+ virtual const std::vector<std::string>& GetCandidate() const = 0;
+ virtual const SdpExtmapAttributeList& GetExtmap() const = 0;
+ virtual const SdpFingerprintAttributeList& GetFingerprint() const = 0;
+ virtual const SdpFmtpAttributeList& GetFmtp() const = 0;
+ virtual const SdpGroupAttributeList& GetGroup() const = 0;
+ virtual const SdpImageattrAttributeList& GetImageattr() const = 0;
+ virtual const SdpSimulcastAttribute& GetSimulcast() const = 0;
+ virtual const SdpMsidAttributeList& GetMsid() const = 0;
+ virtual const SdpMsidSemanticAttributeList& GetMsidSemantic() const = 0;
+ virtual const SdpRidAttributeList& GetRid() const = 0;
+ virtual const SdpRtcpFbAttributeList& GetRtcpFb() const = 0;
+ virtual const SdpRtpmapAttributeList& GetRtpmap() const = 0;
+ virtual const SdpSctpmapAttributeList& GetSctpmap() const = 0;
+ virtual uint32_t GetSctpPort() const = 0;
+ virtual uint32_t GetMaxMessageSize() const = 0;
+ virtual const SdpSsrcAttributeList& GetSsrc() const = 0;
+ virtual const SdpSsrcGroupAttributeList& GetSsrcGroup() const = 0;
+
+ // These attributes are effectively simple types, so we'll make life
+ // easy by just returning their value.
+ virtual const std::string& GetIcePwd() const = 0;
+ virtual const std::string& GetIceUfrag() const = 0;
+ virtual const std::string& GetIdentity() const = 0;
+ virtual const std::string& GetLabel() const = 0;
+ virtual unsigned int GetMaxptime() const = 0;
+ virtual const std::string& GetMid() const = 0;
+ virtual unsigned int GetPtime() const = 0;
+
+ // This is "special", because it's multiple things
+ virtual SdpDirectionAttribute::Direction GetDirection() const = 0;
+
+ virtual void Serialize(std::ostream&) const = 0;
+};
+
+inline std::ostream& operator<<(std::ostream& os, const SdpAttributeList& al) {
+ al.Serialize(os);
+ return os;
+}
+
+} // namespace mozilla
+
+#endif
diff --git a/dom/media/webrtc/sdp/SdpEnum.h b/dom/media/webrtc/sdp/SdpEnum.h
new file mode 100644
index 0000000000..c5d67352e0
--- /dev/null
+++ b/dom/media/webrtc/sdp/SdpEnum.h
@@ -0,0 +1,64 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _SDPENUM_H_
+#define _SDPENUM_H_
+
+#include <ostream>
+
+#include "mozilla/Assertions.h"
+
+namespace mozilla::sdp {
+
+enum NetType { kNetTypeNone, kInternet };
+
+inline std::ostream& operator<<(std::ostream& os, sdp::NetType t) {
+ switch (t) {
+ case sdp::kNetTypeNone:
+ MOZ_ASSERT(false);
+ return os << "NONE";
+ case sdp::kInternet:
+ return os << "IN";
+ }
+ MOZ_CRASH("Unknown NetType");
+}
+
+enum AddrType { kAddrTypeNone, kIPv4, kIPv6 };
+
+inline std::ostream& operator<<(std::ostream& os, sdp::AddrType t) {
+ switch (t) {
+ case sdp::kAddrTypeNone:
+ MOZ_ASSERT(false);
+ return os << "NONE";
+ case sdp::kIPv4:
+ return os << "IP4";
+ case sdp::kIPv6:
+ return os << "IP6";
+ }
+ MOZ_CRASH("Unknown AddrType");
+}
+
+enum Direction {
+ // Start at 1 so these can be used as flags
+ kSend = 1,
+ kRecv = 2
+};
+
+inline std::ostream& operator<<(std::ostream& os, sdp::Direction d) {
+ switch (d) {
+ case sdp::kSend:
+ return os << "send";
+ case sdp::kRecv:
+ return os << "recv";
+ }
+ MOZ_CRASH("Unknown Direction");
+}
+
+enum SdpType { kOffer, kAnswer };
+
+} // namespace mozilla::sdp
+
+#endif
diff --git a/dom/media/webrtc/sdp/SdpHelper.cpp b/dom/media/webrtc/sdp/SdpHelper.cpp
new file mode 100644
index 0000000000..d24a7d199d
--- /dev/null
+++ b/dom/media/webrtc/sdp/SdpHelper.cpp
@@ -0,0 +1,801 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "sdp/SdpHelper.h"
+
+#include "sdp/Sdp.h"
+#include "sdp/SdpMediaSection.h"
+#include "transport/logging.h"
+
+#include "nsDebug.h"
+#include "nsError.h"
+#include "prprf.h"
+
+#include <string.h>
+#include <set>
+
+namespace mozilla {
+MOZ_MTLOG_MODULE("sdp")
+
+#define SDP_SET_ERROR(error) \
+ do { \
+ std::ostringstream os; \
+ os << error; \
+ mLastError = os.str(); \
+ MOZ_MTLOG(ML_ERROR, mLastError); \
+ } while (0);
+
+nsresult SdpHelper::CopyTransportParams(size_t numComponents,
+ const SdpMediaSection& oldLocal,
+ SdpMediaSection* newLocal) {
+ const SdpAttributeList& oldLocalAttrs = oldLocal.GetAttributeList();
+ // Copy over m-section details
+ if (!oldLocalAttrs.HasAttribute(SdpAttribute::kBundleOnlyAttribute)) {
+ // Do not copy port 0 from an offer with a=bundle-only; this could cause
+ // an answer msection to be erroneously rejected.
+ newLocal->SetPort(oldLocal.GetPort());
+ }
+ newLocal->GetConnection() = oldLocal.GetConnection();
+
+ SdpAttributeList& newLocalAttrs = newLocal->GetAttributeList();
+
+ // Now we copy over attributes that won't be added by the usual logic
+ if (oldLocalAttrs.HasAttribute(SdpAttribute::kCandidateAttribute) &&
+ numComponents) {
+ UniquePtr<SdpMultiStringAttribute> candidateAttrs(
+ new SdpMultiStringAttribute(SdpAttribute::kCandidateAttribute));
+ for (const std::string& candidate : oldLocalAttrs.GetCandidate()) {
+ size_t component;
+ nsresult rv = GetComponent(candidate, &component);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (numComponents >= component) {
+ candidateAttrs->mValues.push_back(candidate);
+ }
+ }
+ if (!candidateAttrs->mValues.empty()) {
+ newLocalAttrs.SetAttribute(candidateAttrs.release());
+ }
+ }
+
+ if (oldLocalAttrs.HasAttribute(SdpAttribute::kEndOfCandidatesAttribute)) {
+ newLocalAttrs.SetAttribute(
+ new SdpFlagAttribute(SdpAttribute::kEndOfCandidatesAttribute));
+ }
+
+ if (numComponents == 2 &&
+ oldLocalAttrs.HasAttribute(SdpAttribute::kRtcpAttribute)) {
+ // copy rtcp attribute if we had one that we are using
+ newLocalAttrs.SetAttribute(new SdpRtcpAttribute(oldLocalAttrs.GetRtcp()));
+ }
+
+ return NS_OK;
+}
+
+bool SdpHelper::AreOldTransportParamsValid(const Sdp& oldAnswer,
+ const Sdp& offerersPreviousSdp,
+ const Sdp& newOffer, size_t level) {
+ if (MsectionIsDisabled(oldAnswer.GetMediaSection(level)) ||
+ MsectionIsDisabled(newOffer.GetMediaSection(level))) {
+ // Obvious
+ return false;
+ }
+
+ if (!OwnsTransport(oldAnswer, level, sdp::kAnswer)) {
+ // The transport attributes on this m-section were thrown away, because it
+ // was bundled.
+ return false;
+ }
+
+ if (!OwnsTransport(newOffer, level, sdp::kOffer)) {
+ return false;
+ }
+
+ if (IceCredentialsDiffer(newOffer.GetMediaSection(level),
+ offerersPreviousSdp.GetMediaSection(level))) {
+ return false;
+ }
+
+ return true;
+}
+
+bool SdpHelper::IceCredentialsDiffer(const SdpMediaSection& msection1,
+ const SdpMediaSection& msection2) {
+ const SdpAttributeList& attrs1(msection1.GetAttributeList());
+ const SdpAttributeList& attrs2(msection2.GetAttributeList());
+
+ if ((attrs1.GetIceUfrag() != attrs2.GetIceUfrag()) ||
+ (attrs1.GetIcePwd() != attrs2.GetIcePwd())) {
+ return true;
+ }
+
+ return false;
+}
+
+nsresult SdpHelper::GetComponent(const std::string& candidate,
+ size_t* component) {
+ unsigned int temp;
+ int32_t result = PR_sscanf(candidate.c_str(), "%*s %u", &temp);
+ if (result == 1) {
+ *component = temp;
+ return NS_OK;
+ }
+ SDP_SET_ERROR("Malformed ICE candidate: " << candidate);
+ return NS_ERROR_INVALID_ARG;
+}
+
+bool SdpHelper::MsectionIsDisabled(const SdpMediaSection& msection) const {
+ return !msection.GetPort() && !msection.GetAttributeList().HasAttribute(
+ SdpAttribute::kBundleOnlyAttribute);
+}
+
+void SdpHelper::DisableMsection(Sdp* sdp, SdpMediaSection* msection) {
+ std::string mid;
+
+ // Make sure to remove the mid from any group attributes
+ if (msection->GetAttributeList().HasAttribute(SdpAttribute::kMidAttribute)) {
+ mid = msection->GetAttributeList().GetMid();
+ if (sdp->GetAttributeList().HasAttribute(SdpAttribute::kGroupAttribute)) {
+ UniquePtr<SdpGroupAttributeList> newGroupAttr(
+ new SdpGroupAttributeList(sdp->GetAttributeList().GetGroup()));
+ newGroupAttr->RemoveMid(mid);
+ sdp->GetAttributeList().SetAttribute(newGroupAttr.release());
+ }
+ }
+
+ // Clear out attributes.
+ msection->GetAttributeList().Clear();
+
+ auto* direction = new SdpDirectionAttribute(SdpDirectionAttribute::kInactive);
+ msection->GetAttributeList().SetAttribute(direction);
+ msection->SetPort(0);
+
+ // maintain the mid for easier identification on other side
+ if (!mid.empty()) {
+ msection->GetAttributeList().SetAttribute(
+ new SdpStringAttribute(SdpAttribute::kMidAttribute, mid));
+ }
+
+ msection->ClearCodecs();
+
+ auto mediaType = msection->GetMediaType();
+ switch (mediaType) {
+ case SdpMediaSection::kAudio:
+ msection->AddCodec("0", "PCMU", 8000, 1);
+ break;
+ case SdpMediaSection::kVideo:
+ msection->AddCodec("120", "VP8", 90000, 1);
+ break;
+ case SdpMediaSection::kApplication:
+ msection->AddDataChannel("webrtc-datachannel", 0, 0, 0);
+ break;
+ default:
+ // We need to have something here to fit the grammar, this seems safe
+ // and 19 is a reserved payload type which should not be used by anyone.
+ msection->AddCodec("19", "reserved", 8000, 1);
+ }
+}
+
+void SdpHelper::GetBundleGroups(
+ const Sdp& sdp,
+ std::vector<SdpGroupAttributeList::Group>* bundleGroups) const {
+ if (sdp.GetAttributeList().HasAttribute(SdpAttribute::kGroupAttribute)) {
+ for (auto& group : sdp.GetAttributeList().GetGroup().mGroups) {
+ if (group.semantics == SdpGroupAttributeList::kBundle) {
+ bundleGroups->push_back(group);
+ }
+ }
+ }
+}
+
+nsresult SdpHelper::GetBundledMids(const Sdp& sdp, BundledMids* bundledMids) {
+ std::vector<SdpGroupAttributeList::Group> bundleGroups;
+ GetBundleGroups(sdp, &bundleGroups);
+
+ for (SdpGroupAttributeList::Group& group : bundleGroups) {
+ if (group.tags.empty()) {
+ continue;
+ }
+
+ const SdpMediaSection* msection(FindMsectionByMid(sdp, group.tags[0]));
+
+ if (!msection) {
+ SDP_SET_ERROR(
+ "mid specified for bundle transport in group attribute"
+ " does not exist in the SDP. (mid="
+ << group.tags[0] << ")");
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ if (MsectionIsDisabled(*msection)) {
+ SDP_SET_ERROR(
+ "mid specified for bundle transport in group attribute"
+ " points at a disabled m-section. (mid="
+ << group.tags[0] << ")");
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ for (const std::string& mid : group.tags) {
+ if (bundledMids->count(mid)) {
+ SDP_SET_ERROR("mid \'" << mid
+ << "\' appears more than once in a "
+ "BUNDLE group");
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ (*bundledMids)[mid] = msection;
+ }
+ }
+
+ return NS_OK;
+}
+
+bool SdpHelper::OwnsTransport(const Sdp& sdp, uint16_t level,
+ sdp::SdpType type) {
+ auto& msection = sdp.GetMediaSection(level);
+
+ BundledMids bundledMids;
+ nsresult rv = GetBundledMids(sdp, &bundledMids);
+ if (NS_FAILED(rv)) {
+ // Should have been caught sooner.
+ MOZ_ASSERT(false);
+ return true;
+ }
+
+ return OwnsTransport(msection, bundledMids, type);
+}
+
+bool SdpHelper::OwnsTransport(const SdpMediaSection& msection,
+ const BundledMids& bundledMids,
+ sdp::SdpType type) {
+ if (MsectionIsDisabled(msection)) {
+ return false;
+ }
+
+ if (!msection.GetAttributeList().HasAttribute(SdpAttribute::kMidAttribute)) {
+ // No mid, definitely no bundle for this m-section
+ return true;
+ }
+ std::string mid(msection.GetAttributeList().GetMid());
+ if (type != sdp::kOffer || msection.GetAttributeList().HasAttribute(
+ SdpAttribute::kBundleOnlyAttribute)) {
+ // If this is an answer, or this m-section is marked bundle-only, the group
+ // attribute is authoritative. Otherwise, we aren't sure.
+ if (bundledMids.count(mid) && &msection != bundledMids.at(mid)) {
+ // mid is bundled, and isn't the bundle m-section
+ return false;
+ }
+ }
+
+ return true;
+}
+
+nsresult SdpHelper::GetMidFromLevel(const Sdp& sdp, uint16_t level,
+ std::string* mid) {
+ if (level >= sdp.GetMediaSectionCount()) {
+ SDP_SET_ERROR("Index " << level << " out of range");
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ const SdpMediaSection& msection = sdp.GetMediaSection(level);
+ const SdpAttributeList& attrList = msection.GetAttributeList();
+
+ // grab the mid and set the outparam
+ if (attrList.HasAttribute(SdpAttribute::kMidAttribute)) {
+ *mid = attrList.GetMid();
+ }
+
+ return NS_OK;
+}
+
+nsresult SdpHelper::AddCandidateToSdp(Sdp* sdp,
+ const std::string& candidateUntrimmed,
+ uint16_t level,
+ const std::string& ufrag) {
+ if (level >= sdp->GetMediaSectionCount()) {
+ SDP_SET_ERROR("Index " << level << " out of range");
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ SdpMediaSection& msection = sdp->GetMediaSection(level);
+ SdpAttributeList& attrList = msection.GetAttributeList();
+
+ if (!ufrag.empty()) {
+ if (!attrList.HasAttribute(SdpAttribute::kIceUfragAttribute) ||
+ attrList.GetIceUfrag() != ufrag) {
+ SDP_SET_ERROR("Unknown ufrag (" << ufrag << ")");
+ return NS_ERROR_INVALID_ARG;
+ }
+ }
+
+ if (candidateUntrimmed.empty()) {
+ SetIceGatheringComplete(sdp, level, ufrag);
+ return NS_OK;
+ }
+
+ // Trim off '[a=]candidate:'
+ size_t begin = candidateUntrimmed.find(':');
+ if (begin == std::string::npos) {
+ SDP_SET_ERROR("Invalid candidate, no ':' (" << candidateUntrimmed << ")");
+ return NS_ERROR_INVALID_ARG;
+ }
+ ++begin;
+
+ std::string candidate = candidateUntrimmed.substr(begin);
+
+ UniquePtr<SdpMultiStringAttribute> candidates;
+ if (!attrList.HasAttribute(SdpAttribute::kCandidateAttribute)) {
+ // Create new
+ candidates.reset(
+ new SdpMultiStringAttribute(SdpAttribute::kCandidateAttribute));
+ } else {
+ // Copy existing
+ candidates.reset(new SdpMultiStringAttribute(
+ *static_cast<const SdpMultiStringAttribute*>(
+ attrList.GetAttribute(SdpAttribute::kCandidateAttribute))));
+ }
+ candidates->PushEntry(candidate);
+ attrList.SetAttribute(candidates.release());
+
+ return NS_OK;
+}
+
+nsresult SdpHelper::SetIceGatheringComplete(Sdp* sdp,
+ const std::string& ufrag) {
+ for (uint16_t i = 0; i < sdp->GetMediaSectionCount(); ++i) {
+ nsresult rv = SetIceGatheringComplete(sdp, i, ufrag);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ return NS_OK;
+}
+
+nsresult SdpHelper::SetIceGatheringComplete(Sdp* sdp, uint16_t level,
+ const std::string& ufrag) {
+ if (level >= sdp->GetMediaSectionCount()) {
+ SDP_SET_ERROR("Index " << level << " out of range");
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ SdpMediaSection& msection = sdp->GetMediaSection(level);
+ SdpAttributeList& attrList = msection.GetAttributeList();
+
+ if (!ufrag.empty()) {
+ if (!attrList.HasAttribute(SdpAttribute::kIceUfragAttribute) ||
+ attrList.GetIceUfrag() != ufrag) {
+ SDP_SET_ERROR("Unknown ufrag (" << ufrag << ")");
+ return NS_ERROR_INVALID_ARG;
+ }
+ }
+
+ attrList.SetAttribute(
+ new SdpFlagAttribute(SdpAttribute::kEndOfCandidatesAttribute));
+ // Remove trickle-ice option
+ attrList.RemoveAttribute(SdpAttribute::kIceOptionsAttribute);
+ return NS_OK;
+}
+
+void SdpHelper::SetDefaultAddresses(const std::string& defaultCandidateAddr,
+ uint16_t defaultCandidatePort,
+ const std::string& defaultRtcpCandidateAddr,
+ uint16_t defaultRtcpCandidatePort,
+ SdpMediaSection* msection) {
+ SdpAttributeList& attrList = msection->GetAttributeList();
+
+ msection->GetConnection().SetAddress(defaultCandidateAddr);
+ msection->SetPort(defaultCandidatePort);
+ if (!defaultRtcpCandidateAddr.empty()) {
+ sdp::AddrType ipVersion = sdp::kIPv4;
+ if (defaultRtcpCandidateAddr.find(':') != std::string::npos) {
+ ipVersion = sdp::kIPv6;
+ }
+ attrList.SetAttribute(new SdpRtcpAttribute(defaultRtcpCandidatePort,
+ sdp::kInternet, ipVersion,
+ defaultRtcpCandidateAddr));
+ }
+}
+
+nsresult SdpHelper::GetIdsFromMsid(const Sdp& sdp,
+ const SdpMediaSection& msection,
+ std::vector<std::string>* streamIds) {
+ std::vector<SdpMsidAttributeList::Msid> allMsids;
+ nsresult rv = GetMsids(msection, &allMsids);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (allMsids.empty()) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ streamIds->clear();
+ for (const auto& msid : allMsids) {
+ // "-" means no stream, see draft-ietf-mmusic-msid
+ // Remove duplicates, but leave order the same
+ if (msid.identifier != "-" &&
+ !std::count(streamIds->begin(), streamIds->end(), msid.identifier)) {
+ streamIds->push_back(msid.identifier);
+ }
+ }
+
+ return NS_OK;
+}
+
+nsresult SdpHelper::GetMsids(const SdpMediaSection& msection,
+ std::vector<SdpMsidAttributeList::Msid>* msids) {
+ if (msection.GetAttributeList().HasAttribute(SdpAttribute::kMsidAttribute)) {
+ *msids = msection.GetAttributeList().GetMsid().mMsids;
+ return NS_OK;
+ }
+
+ // If there are no a=msid, can we find msids in ssrc attributes?
+ // (Chrome does not put plain-old msid attributes in its SDP)
+ if (msection.GetAttributeList().HasAttribute(SdpAttribute::kSsrcAttribute)) {
+ auto& ssrcs = msection.GetAttributeList().GetSsrc().mSsrcs;
+
+ for (auto i = ssrcs.begin(); i != ssrcs.end(); ++i) {
+ if (i->attribute.find("msid:") == 0) {
+ std::string streamId;
+ std::string trackId;
+ nsresult rv = ParseMsid(i->attribute, &streamId, &trackId);
+ NS_ENSURE_SUCCESS(rv, rv);
+ msids->push_back({streamId, trackId});
+ }
+ }
+ }
+
+ return NS_OK;
+}
+
+nsresult SdpHelper::ParseMsid(const std::string& msidAttribute,
+ std::string* streamId, std::string* trackId) {
+ // Would be nice if SdpSsrcAttributeList could parse out the contained
+ // attribute, but at least the parse here is simple.
+ // We are being very forgiving here wrt whitespace; tabs are not actually
+ // allowed, nor is leading/trailing whitespace.
+ size_t streamIdStart = msidAttribute.find_first_not_of(" \t", 5);
+ // We do not assume the appdata token is here, since this is not
+ // necessarily a webrtc msid
+ if (streamIdStart == std::string::npos) {
+ SDP_SET_ERROR("Malformed source-level msid attribute: " << msidAttribute);
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ size_t streamIdEnd = msidAttribute.find_first_of(" \t", streamIdStart);
+ if (streamIdEnd == std::string::npos) {
+ streamIdEnd = msidAttribute.size();
+ }
+
+ size_t trackIdStart = msidAttribute.find_first_not_of(" \t", streamIdEnd);
+ if (trackIdStart == std::string::npos) {
+ trackIdStart = msidAttribute.size();
+ }
+
+ size_t trackIdEnd = msidAttribute.find_first_of(" \t", trackIdStart);
+ if (trackIdEnd == std::string::npos) {
+ trackIdEnd = msidAttribute.size();
+ }
+
+ size_t streamIdSize = streamIdEnd - streamIdStart;
+ size_t trackIdSize = trackIdEnd - trackIdStart;
+
+ *streamId = msidAttribute.substr(streamIdStart, streamIdSize);
+ *trackId = msidAttribute.substr(trackIdStart, trackIdSize);
+ return NS_OK;
+}
+
+void SdpHelper::SetupMsidSemantic(const std::vector<std::string>& msids,
+ Sdp* sdp) const {
+ if (!msids.empty()) {
+ UniquePtr<SdpMsidSemanticAttributeList> msidSemantics(
+ new SdpMsidSemanticAttributeList);
+ msidSemantics->PushEntry("WMS", msids);
+ sdp->GetAttributeList().SetAttribute(msidSemantics.release());
+ }
+}
+
+std::string SdpHelper::GetCNAME(const SdpMediaSection& msection) const {
+ if (msection.GetAttributeList().HasAttribute(SdpAttribute::kSsrcAttribute)) {
+ auto& ssrcs = msection.GetAttributeList().GetSsrc().mSsrcs;
+ for (auto i = ssrcs.begin(); i != ssrcs.end(); ++i) {
+ if (i->attribute.find("cname:") == 0) {
+ return i->attribute.substr(6);
+ }
+ }
+ }
+ return "";
+}
+
+const SdpMediaSection* SdpHelper::FindMsectionByMid(
+ const Sdp& sdp, const std::string& mid) const {
+ for (size_t i = 0; i < sdp.GetMediaSectionCount(); ++i) {
+ auto& attrs = sdp.GetMediaSection(i).GetAttributeList();
+ if (attrs.HasAttribute(SdpAttribute::kMidAttribute) &&
+ attrs.GetMid() == mid) {
+ return &sdp.GetMediaSection(i);
+ }
+ }
+ return nullptr;
+}
+
+SdpMediaSection* SdpHelper::FindMsectionByMid(Sdp& sdp,
+ const std::string& mid) const {
+ for (size_t i = 0; i < sdp.GetMediaSectionCount(); ++i) {
+ auto& attrs = sdp.GetMediaSection(i).GetAttributeList();
+ if (attrs.HasAttribute(SdpAttribute::kMidAttribute) &&
+ attrs.GetMid() == mid) {
+ return &sdp.GetMediaSection(i);
+ }
+ }
+ return nullptr;
+}
+
+nsresult SdpHelper::CopyStickyParams(const SdpMediaSection& source,
+ SdpMediaSection* dest) {
+ auto& sourceAttrs = source.GetAttributeList();
+ auto& destAttrs = dest->GetAttributeList();
+
+ // There's no reason to renegotiate rtcp-mux
+ if (sourceAttrs.HasAttribute(SdpAttribute::kRtcpMuxAttribute)) {
+ destAttrs.SetAttribute(
+ new SdpFlagAttribute(SdpAttribute::kRtcpMuxAttribute));
+ }
+
+ // mid should stay the same
+ if (sourceAttrs.HasAttribute(SdpAttribute::kMidAttribute)) {
+ destAttrs.SetAttribute(new SdpStringAttribute(SdpAttribute::kMidAttribute,
+ sourceAttrs.GetMid()));
+ }
+
+ // Keep RTCP mode setting
+ if (sourceAttrs.HasAttribute(SdpAttribute::kRtcpRsizeAttribute) &&
+ source.GetMediaType() == SdpMediaSection::kVideo) {
+ destAttrs.SetAttribute(
+ new SdpFlagAttribute(SdpAttribute::kRtcpRsizeAttribute));
+ }
+
+ return NS_OK;
+}
+
+bool SdpHelper::HasRtcp(SdpMediaSection::Protocol proto) const {
+ switch (proto) {
+ case SdpMediaSection::kRtpAvpf:
+ case SdpMediaSection::kDccpRtpAvpf:
+ case SdpMediaSection::kDccpRtpSavpf:
+ case SdpMediaSection::kRtpSavpf:
+ case SdpMediaSection::kUdpTlsRtpSavpf:
+ case SdpMediaSection::kTcpDtlsRtpSavpf:
+ case SdpMediaSection::kDccpTlsRtpSavpf:
+ return true;
+ case SdpMediaSection::kRtpAvp:
+ case SdpMediaSection::kUdp:
+ case SdpMediaSection::kVat:
+ case SdpMediaSection::kRtp:
+ case SdpMediaSection::kUdptl:
+ case SdpMediaSection::kTcp:
+ case SdpMediaSection::kTcpRtpAvp:
+ case SdpMediaSection::kRtpSavp:
+ case SdpMediaSection::kTcpBfcp:
+ case SdpMediaSection::kTcpTlsBfcp:
+ case SdpMediaSection::kTcpTls:
+ case SdpMediaSection::kFluteUdp:
+ case SdpMediaSection::kTcpMsrp:
+ case SdpMediaSection::kTcpTlsMsrp:
+ case SdpMediaSection::kDccp:
+ case SdpMediaSection::kDccpRtpAvp:
+ case SdpMediaSection::kDccpRtpSavp:
+ case SdpMediaSection::kUdpTlsRtpSavp:
+ case SdpMediaSection::kTcpDtlsRtpSavp:
+ case SdpMediaSection::kDccpTlsRtpSavp:
+ case SdpMediaSection::kUdpMbmsFecRtpAvp:
+ case SdpMediaSection::kUdpMbmsFecRtpSavp:
+ case SdpMediaSection::kUdpMbmsRepair:
+ case SdpMediaSection::kFecUdp:
+ case SdpMediaSection::kUdpFec:
+ case SdpMediaSection::kTcpMrcpv2:
+ case SdpMediaSection::kTcpTlsMrcpv2:
+ case SdpMediaSection::kPstn:
+ case SdpMediaSection::kUdpTlsUdptl:
+ case SdpMediaSection::kSctp:
+ case SdpMediaSection::kDtlsSctp:
+ case SdpMediaSection::kUdpDtlsSctp:
+ case SdpMediaSection::kTcpDtlsSctp:
+ return false;
+ }
+ MOZ_CRASH("Unknown protocol, probably corruption.");
+}
+
+SdpMediaSection::Protocol SdpHelper::GetProtocolForMediaType(
+ SdpMediaSection::MediaType type) {
+ if (type == SdpMediaSection::kApplication) {
+ return SdpMediaSection::kUdpDtlsSctp;
+ }
+
+ return SdpMediaSection::kUdpTlsRtpSavpf;
+}
+
+void SdpHelper::AppendSdpParseErrors(
+ const std::vector<std::pair<size_t, std::string> >& aErrors,
+ std::string* aErrorString) {
+ std::ostringstream os;
+ for (auto i = aErrors.begin(); i != aErrors.end(); ++i) {
+ os << "SDP Parse Error on line " << i->first << ": " + i->second
+ << std::endl;
+ }
+ *aErrorString += os.str();
+}
+
+/* static */
+bool SdpHelper::GetPtAsInt(const std::string& ptString, uint16_t* ptOutparam) {
+ char* end;
+ unsigned long pt = strtoul(ptString.c_str(), &end, 10);
+ size_t length = static_cast<size_t>(end - ptString.c_str());
+ if ((pt > UINT16_MAX) || (length != ptString.size())) {
+ return false;
+ }
+ *ptOutparam = pt;
+ return true;
+}
+
+void SdpHelper::NegotiateAndAddExtmaps(
+ const SdpMediaSection& remoteMsection,
+ std::vector<SdpExtmapAttributeList::Extmap>& localExtensions,
+ SdpMediaSection* localMsection) {
+ if (!remoteMsection.GetAttributeList().HasAttribute(
+ SdpAttribute::kExtmapAttribute)) {
+ return;
+ }
+
+ UniquePtr<SdpExtmapAttributeList> localExtmap(new SdpExtmapAttributeList);
+ auto& theirExtmap = remoteMsection.GetAttributeList().GetExtmap().mExtmaps;
+ for (const auto& theirExt : theirExtmap) {
+ for (auto& ourExt : localExtensions) {
+ if (theirExt.entry == 0) {
+ // 0 is invalid, ignore it
+ continue;
+ }
+
+ if (theirExt.extensionname != ourExt.extensionname) {
+ continue;
+ }
+
+ ourExt.direction = reverse(theirExt.direction) & ourExt.direction;
+ if (ourExt.direction == SdpDirectionAttribute::Direction::kInactive) {
+ continue;
+ }
+
+ // RFC 5285 says that ids >= 4096 can be used by the offerer to
+ // force the answerer to pick, otherwise the value in the offer is
+ // used.
+ if (theirExt.entry < 4096) {
+ ourExt.entry = theirExt.entry;
+ }
+
+ localExtmap->mExtmaps.push_back(ourExt);
+ }
+ }
+
+ if (!localExtmap->mExtmaps.empty()) {
+ localMsection->GetAttributeList().SetAttribute(localExtmap.release());
+ }
+}
+
+static bool AttributeListMatch(const SdpAttributeList& list1,
+ const SdpAttributeList& list2) {
+ // TODO: Consider adding telemetry in this function to record which
+ // attributes don't match. See Bug 1432955.
+ for (int i = SdpAttribute::kFirstAttribute; i <= SdpAttribute::kLastAttribute;
+ i++) {
+ auto attributeType = static_cast<SdpAttribute::AttributeType>(i);
+ // TODO: We should do more thorough checking here, e.g. serialize and
+ // compare strings. See Bug 1439690.
+ if (list1.HasAttribute(attributeType, false) !=
+ list2.HasAttribute(attributeType, false)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+static bool MediaSectionMatch(const SdpMediaSection& mediaSection1,
+ const SdpMediaSection& mediaSection2) {
+ // TODO: We should do more thorough checking in this function.
+ // See Bug 1439690.
+ if (!AttributeListMatch(mediaSection1.GetAttributeList(),
+ mediaSection2.GetAttributeList())) {
+ return false;
+ }
+ if (mediaSection1.GetPort() != mediaSection2.GetPort()) {
+ return false;
+ }
+ const std::vector<std::string>& formats1 = mediaSection1.GetFormats();
+ const std::vector<std::string>& formats2 = mediaSection2.GetFormats();
+ auto formats1Set = std::set<std::string>(formats1.begin(), formats1.end());
+ auto formats2Set = std::set<std::string>(formats2.begin(), formats2.end());
+ if (formats1Set != formats2Set) {
+ return false;
+ }
+ return true;
+}
+
+bool SdpHelper::SdpMatch(const Sdp& sdp1, const Sdp& sdp2) {
+ if (sdp1.GetMediaSectionCount() != sdp2.GetMediaSectionCount()) {
+ return false;
+ }
+ if (!AttributeListMatch(sdp1.GetAttributeList(), sdp2.GetAttributeList())) {
+ return false;
+ }
+ for (size_t i = 0; i < sdp1.GetMediaSectionCount(); i++) {
+ const SdpMediaSection& mediaSection1 = sdp1.GetMediaSection(i);
+ const SdpMediaSection& mediaSection2 = sdp2.GetMediaSection(i);
+ if (!MediaSectionMatch(mediaSection1, mediaSection2)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+nsresult SdpHelper::ValidateTransportAttributes(const Sdp& aSdp,
+ sdp::SdpType aType) {
+ BundledMids bundledMids;
+ nsresult rv = GetBundledMids(aSdp, &bundledMids);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ for (size_t level = 0; level < aSdp.GetMediaSectionCount(); ++level) {
+ const auto& msection = aSdp.GetMediaSection(level);
+ if (OwnsTransport(msection, bundledMids, aType)) {
+ const auto& mediaAttrs = msection.GetAttributeList();
+ if (mediaAttrs.GetIceUfrag().empty()) {
+ SDP_SET_ERROR("Invalid description, no ice-ufrag attribute at level "
+ << level);
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ if (mediaAttrs.GetIcePwd().empty()) {
+ SDP_SET_ERROR("Invalid description, no ice-pwd attribute at level "
+ << level);
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ if (!mediaAttrs.HasAttribute(SdpAttribute::kFingerprintAttribute)) {
+ SDP_SET_ERROR("Invalid description, no fingerprint attribute at level "
+ << level);
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ const SdpFingerprintAttributeList& fingerprints(
+ mediaAttrs.GetFingerprint());
+ if (fingerprints.mFingerprints.empty()) {
+ SDP_SET_ERROR(
+ "Invalid description, no supported fingerprint algorithms present "
+ "at level "
+ << level);
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ if (mediaAttrs.HasAttribute(SdpAttribute::kSetupAttribute, true)) {
+ if (mediaAttrs.GetSetup().mRole == SdpSetupAttribute::kHoldconn) {
+ SDP_SET_ERROR(
+ "Invalid description, illegal setup attribute \"holdconn\" "
+ "at level "
+ << level);
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ if (aType == sdp::kAnswer &&
+ mediaAttrs.GetSetup().mRole == SdpSetupAttribute::kActpass) {
+ SDP_SET_ERROR(
+ "Invalid answer, illegal setup attribute \"actpass\" at level "
+ << level);
+ return NS_ERROR_INVALID_ARG;
+ }
+ } else if (aType == sdp::kOffer) {
+ SDP_SET_ERROR("Invalid offer, no setup attribute at level " << level);
+ return NS_ERROR_INVALID_ARG;
+ }
+ }
+ }
+ return NS_OK;
+}
+
+} // namespace mozilla
diff --git a/dom/media/webrtc/sdp/SdpHelper.h b/dom/media/webrtc/sdp/SdpHelper.h
new file mode 100644
index 0000000000..3c65d01442
--- /dev/null
+++ b/dom/media/webrtc/sdp/SdpHelper.h
@@ -0,0 +1,109 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _SDPHELPER_H_
+#define _SDPHELPER_H_
+
+#include "nsError.h"
+
+#include "sdp/SdpMediaSection.h"
+#include "sdp/SdpAttribute.h"
+
+#include "transport/m_cpp_utils.h"
+
+#include <string>
+#include <map>
+#include <vector>
+
+namespace mozilla {
+class SdpMediaSection;
+class Sdp;
+
+class SdpHelper {
+ public:
+ // Takes a std::string* into which error strings will be written for the
+ // lifetime of the SdpHelper.
+ explicit SdpHelper(std::string* errorDest) : mLastError(*errorDest) {}
+ ~SdpHelper() {}
+
+ nsresult GetComponent(const std::string& candidate, size_t* component);
+ nsresult CopyTransportParams(size_t numComponents,
+ const SdpMediaSection& source,
+ SdpMediaSection* dest);
+ bool AreOldTransportParamsValid(const Sdp& oldAnswer,
+ const Sdp& offerersPreviousSdp,
+ const Sdp& newOffer, size_t level);
+ bool IceCredentialsDiffer(const SdpMediaSection& msection1,
+ const SdpMediaSection& msection2);
+
+ bool MsectionIsDisabled(const SdpMediaSection& msection) const;
+ static void DisableMsection(Sdp* sdp, SdpMediaSection* msection);
+
+ // Maps each mid to the m-section that owns its bundle transport.
+ // Mids that do not appear in an a=group:BUNDLE do not appear here.
+ typedef std::map<std::string, const SdpMediaSection*> BundledMids;
+
+ nsresult GetBundledMids(const Sdp& sdp, BundledMids* bundledMids);
+
+ bool OwnsTransport(const Sdp& localSdp, uint16_t level, sdp::SdpType type);
+ bool OwnsTransport(const SdpMediaSection& msection,
+ const BundledMids& bundledMids, sdp::SdpType type);
+ void GetBundleGroups(const Sdp& sdp,
+ std::vector<SdpGroupAttributeList::Group>* groups) const;
+
+ nsresult GetMidFromLevel(const Sdp& sdp, uint16_t level, std::string* mid);
+ nsresult GetIdsFromMsid(const Sdp& sdp, const SdpMediaSection& msection,
+ std::vector<std::string>* streamId);
+ nsresult GetMsids(const SdpMediaSection& msection,
+ std::vector<SdpMsidAttributeList::Msid>* msids);
+ nsresult ParseMsid(const std::string& msidAttribute, std::string* streamId,
+ std::string* trackId);
+ nsresult AddCandidateToSdp(Sdp* sdp, const std::string& candidate,
+ uint16_t level, const std::string& ufrag);
+ nsresult SetIceGatheringComplete(Sdp* sdp, const std::string& ufrag);
+ nsresult SetIceGatheringComplete(Sdp* sdp, uint16_t level,
+ const std::string& ufrag);
+ void SetDefaultAddresses(const std::string& defaultCandidateAddr,
+ uint16_t defaultCandidatePort,
+ const std::string& defaultRtcpCandidateAddr,
+ uint16_t defaultRtcpCandidatePort,
+ SdpMediaSection* msection);
+ void SetupMsidSemantic(const std::vector<std::string>& msids, Sdp* sdp) const;
+
+ std::string GetCNAME(const SdpMediaSection& msection) const;
+
+ SdpMediaSection* FindMsectionByMid(Sdp& sdp, const std::string& mid) const;
+
+ const SdpMediaSection* FindMsectionByMid(const Sdp& sdp,
+ const std::string& mid) const;
+
+ nsresult CopyStickyParams(const SdpMediaSection& source,
+ SdpMediaSection* dest);
+ bool HasRtcp(SdpMediaSection::Protocol proto) const;
+ static SdpMediaSection::Protocol GetProtocolForMediaType(
+ SdpMediaSection::MediaType type);
+ void AppendSdpParseErrors(
+ const std::vector<std::pair<size_t, std::string> >& aErrors,
+ std::string* aErrorString);
+
+ static bool GetPtAsInt(const std::string& ptString, uint16_t* ptOutparam);
+
+ void NegotiateAndAddExtmaps(
+ const SdpMediaSection& remoteMsection,
+ std::vector<SdpExtmapAttributeList::Extmap>& localExtensions,
+ SdpMediaSection* localMsection);
+
+ bool SdpMatch(const Sdp& sdp1, const Sdp& sdp2);
+ nsresult ValidateTransportAttributes(const Sdp& aSdp, sdp::SdpType aType);
+
+ private:
+ std::string& mLastError;
+
+ DISALLOW_COPY_ASSIGN(SdpHelper);
+};
+} // namespace mozilla
+
+#endif // _SDPHELPER_H_
diff --git a/dom/media/webrtc/sdp/SdpLog.cpp b/dom/media/webrtc/sdp/SdpLog.cpp
new file mode 100644
index 0000000000..a841ac7412
--- /dev/null
+++ b/dom/media/webrtc/sdp/SdpLog.cpp
@@ -0,0 +1,68 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+#include <type_traits>
+
+#include "sdp/SdpLog.h"
+#include "common/browser_logging/CSFLog.h"
+
+namespace mozilla {
+LazyLogModule SdpLog("sdp");
+} // namespace mozilla
+
+// For compile time enum comparison
+template <typename E, typename F>
+constexpr bool compareEnum(E e, F f) {
+ return static_cast<typename std::underlying_type<E>::type>(e) ==
+ static_cast<typename std::underlying_type<F>::type>(f);
+}
+
+CSFLogLevel SDPToCSFLogLevel(const SDPLogLevel priority) {
+ static_assert(compareEnum(SDP_LOG_ERROR, CSF_LOG_ERROR));
+ static_assert(compareEnum(SDP_LOG_WARNING, CSF_LOG_WARNING));
+ static_assert(compareEnum(SDP_LOG_INFO, CSF_LOG_INFO));
+ static_assert(compareEnum(SDP_LOG_DEBUG, CSF_LOG_DEBUG));
+ static_assert(compareEnum(SDP_LOG_VERBOSE, CSF_LOG_VERBOSE));
+
+ // Check that all SDP_LOG_* cases are covered. It compiles to nothing.
+ switch (priority) {
+ case SDP_LOG_ERROR:
+ case SDP_LOG_WARNING:
+ case SDP_LOG_INFO:
+ case SDP_LOG_DEBUG:
+ case SDP_LOG_VERBOSE:
+ break;
+ }
+
+ // Ditto for CSF_LOG_*
+ switch (static_cast<CSFLogLevel>(priority)) {
+ case CSF_LOG_ERROR:
+ case CSF_LOG_WARNING:
+ case CSF_LOG_INFO:
+ case CSF_LOG_DEBUG:
+ case CSF_LOG_VERBOSE:
+ break;
+ }
+
+ return static_cast<CSFLogLevel>(priority);
+}
+
+void SDPLog(SDPLogLevel priority, const char* sourceFile, int sourceLine,
+ const char* tag, const char* format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ CSFLogV(SDPToCSFLogLevel(priority), sourceFile, sourceLine, tag, format, ap);
+ va_end(ap);
+}
+
+void SDPLogV(SDPLogLevel priority, const char* sourceFile, int sourceLine,
+ const char* tag, const char* format, va_list args) {
+ CSFLogV(SDPToCSFLogLevel(priority), sourceFile, sourceLine, tag, format,
+ args);
+}
+
+int SDPLogTestLevel(SDPLogLevel priority) {
+ return CSFLogTestLevel(SDPToCSFLogLevel(priority));
+}
diff --git a/dom/media/webrtc/sdp/SdpLog.h b/dom/media/webrtc/sdp/SdpLog.h
new file mode 100644
index 0000000000..63bceff889
--- /dev/null
+++ b/dom/media/webrtc/sdp/SdpLog.h
@@ -0,0 +1,17 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _SDP_LOG_H_
+#define _SDP_LOG_H_
+
+#include "mozilla/Logging.h"
+#include "sdp_log.h"
+
+namespace mozilla {
+extern mozilla::LazyLogModule SdpLog;
+} // namespace mozilla
+
+#endif
diff --git a/dom/media/webrtc/sdp/SdpMediaSection.cpp b/dom/media/webrtc/sdp/SdpMediaSection.cpp
new file mode 100644
index 0000000000..dd8f6e54fe
--- /dev/null
+++ b/dom/media/webrtc/sdp/SdpMediaSection.cpp
@@ -0,0 +1,197 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "sdp/SdpMediaSection.h"
+
+namespace mozilla {
+const SdpFmtpAttributeList::Parameters* SdpMediaSection::FindFmtp(
+ const std::string& pt) const {
+ const SdpAttributeList& attrs = GetAttributeList();
+
+ if (attrs.HasAttribute(SdpAttribute::kFmtpAttribute)) {
+ for (auto& fmtpAttr : attrs.GetFmtp().mFmtps) {
+ if (fmtpAttr.format == pt && fmtpAttr.parameters) {
+ return fmtpAttr.parameters.get();
+ }
+ }
+ }
+ return nullptr;
+}
+
+void SdpMediaSection::SetFmtp(const SdpFmtpAttributeList::Fmtp& fmtpToSet) {
+ UniquePtr<SdpFmtpAttributeList> fmtps(new SdpFmtpAttributeList);
+
+ if (GetAttributeList().HasAttribute(SdpAttribute::kFmtpAttribute)) {
+ *fmtps = GetAttributeList().GetFmtp();
+ }
+
+ bool found = false;
+ for (SdpFmtpAttributeList::Fmtp& fmtp : fmtps->mFmtps) {
+ if (fmtp.format == fmtpToSet.format) {
+ fmtp = fmtpToSet;
+ found = true;
+ }
+ }
+
+ if (!found) {
+ fmtps->mFmtps.push_back(fmtpToSet);
+ }
+
+ GetAttributeList().SetAttribute(fmtps.release());
+}
+
+void SdpMediaSection::RemoveFmtp(const std::string& pt) {
+ UniquePtr<SdpFmtpAttributeList> fmtps(new SdpFmtpAttributeList);
+
+ SdpAttributeList& attrList = GetAttributeList();
+ if (attrList.HasAttribute(SdpAttribute::kFmtpAttribute)) {
+ *fmtps = attrList.GetFmtp();
+ }
+
+ for (size_t i = 0; i < fmtps->mFmtps.size(); ++i) {
+ if (pt == fmtps->mFmtps[i].format) {
+ fmtps->mFmtps.erase(fmtps->mFmtps.begin() + i);
+ break;
+ }
+ }
+
+ attrList.SetAttribute(fmtps.release());
+}
+
+const SdpRtpmapAttributeList::Rtpmap* SdpMediaSection::FindRtpmap(
+ const std::string& pt) const {
+ auto& attrs = GetAttributeList();
+ if (!attrs.HasAttribute(SdpAttribute::kRtpmapAttribute)) {
+ return nullptr;
+ }
+
+ const SdpRtpmapAttributeList& rtpmap = attrs.GetRtpmap();
+ if (!rtpmap.HasEntry(pt)) {
+ return nullptr;
+ }
+
+ return &rtpmap.GetEntry(pt);
+}
+
+const SdpSctpmapAttributeList::Sctpmap* SdpMediaSection::GetSctpmap() const {
+ auto& attrs = GetAttributeList();
+ if (!attrs.HasAttribute(SdpAttribute::kSctpmapAttribute)) {
+ return nullptr;
+ }
+
+ const SdpSctpmapAttributeList& sctpmap = attrs.GetSctpmap();
+ if (sctpmap.mSctpmaps.empty()) {
+ return nullptr;
+ }
+
+ return &sctpmap.GetFirstEntry();
+}
+
+uint32_t SdpMediaSection::GetSctpPort() const {
+ auto& attrs = GetAttributeList();
+ if (!attrs.HasAttribute(SdpAttribute::kSctpPortAttribute)) {
+ return 0;
+ }
+
+ return attrs.GetSctpPort();
+}
+
+bool SdpMediaSection::GetMaxMessageSize(uint32_t* size) const {
+ *size = 0;
+
+ auto& attrs = GetAttributeList();
+ if (!attrs.HasAttribute(SdpAttribute::kMaxMessageSizeAttribute)) {
+ return false;
+ }
+
+ *size = attrs.GetMaxMessageSize();
+ return true;
+}
+
+bool SdpMediaSection::HasRtcpFb(const std::string& pt,
+ SdpRtcpFbAttributeList::Type type,
+ const std::string& subType) const {
+ const SdpAttributeList& attrs(GetAttributeList());
+
+ if (!attrs.HasAttribute(SdpAttribute::kRtcpFbAttribute)) {
+ return false;
+ }
+
+ for (auto& rtcpfb : attrs.GetRtcpFb().mFeedbacks) {
+ if (rtcpfb.type == type) {
+ if (rtcpfb.pt == "*" || rtcpfb.pt == pt) {
+ if (rtcpfb.parameter == subType) {
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+SdpRtcpFbAttributeList SdpMediaSection::GetRtcpFbs() const {
+ SdpRtcpFbAttributeList result;
+ if (GetAttributeList().HasAttribute(SdpAttribute::kRtcpFbAttribute)) {
+ result = GetAttributeList().GetRtcpFb();
+ }
+ return result;
+}
+
+void SdpMediaSection::SetRtcpFbs(const SdpRtcpFbAttributeList& rtcpfbs) {
+ if (rtcpfbs.mFeedbacks.empty()) {
+ GetAttributeList().RemoveAttribute(SdpAttribute::kRtcpFbAttribute);
+ return;
+ }
+
+ GetAttributeList().SetAttribute(new SdpRtcpFbAttributeList(rtcpfbs));
+}
+
+void SdpMediaSection::SetSsrcs(const std::vector<uint32_t>& ssrcs,
+ const std::string& cname) {
+ if (ssrcs.empty()) {
+ GetAttributeList().RemoveAttribute(SdpAttribute::kSsrcAttribute);
+ return;
+ }
+
+ UniquePtr<SdpSsrcAttributeList> ssrcAttr(new SdpSsrcAttributeList);
+ for (auto ssrc : ssrcs) {
+ // When using ssrc attributes, we are required to at least have a cname.
+ // (See https://tools.ietf.org/html/rfc5576#section-6.1)
+ std::string cnameAttr("cname:");
+ cnameAttr += cname;
+ ssrcAttr->PushEntry(ssrc, cnameAttr);
+ }
+
+ GetAttributeList().SetAttribute(ssrcAttr.release());
+}
+
+void SdpMediaSection::AddMsid(const std::string& id,
+ const std::string& appdata) {
+ UniquePtr<SdpMsidAttributeList> msids(new SdpMsidAttributeList);
+ if (GetAttributeList().HasAttribute(SdpAttribute::kMsidAttribute)) {
+ msids->mMsids = GetAttributeList().GetMsid().mMsids;
+ }
+ msids->PushEntry(id, appdata);
+ GetAttributeList().SetAttribute(msids.release());
+}
+
+const SdpRidAttributeList::Rid* SdpMediaSection::FindRid(
+ const std::string& id) const {
+ if (!GetAttributeList().HasAttribute(SdpAttribute::kRidAttribute)) {
+ return nullptr;
+ }
+
+ for (const auto& rid : GetAttributeList().GetRid().mRids) {
+ if (rid.id == id) {
+ return &rid;
+ }
+ }
+
+ return nullptr;
+}
+
+} // namespace mozilla
diff --git a/dom/media/webrtc/sdp/SdpMediaSection.h b/dom/media/webrtc/sdp/SdpMediaSection.h
new file mode 100644
index 0000000000..a205551a1d
--- /dev/null
+++ b/dom/media/webrtc/sdp/SdpMediaSection.h
@@ -0,0 +1,317 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _SDPMEDIASECTION_H_
+#define _SDPMEDIASECTION_H_
+
+#include "mozilla/Maybe.h"
+#include "sdp/SdpEnum.h"
+#include "sdp/SdpAttributeList.h"
+#include <string>
+#include <vector>
+#include <sstream>
+
+namespace mozilla {
+
+class SdpAttributeList;
+
+class SdpConnection;
+
+class SdpMediaSection {
+ public:
+ enum MediaType { kAudio, kVideo, kText, kApplication, kMessage };
+ // don't add to enum to avoid warnings about unhandled enum values
+ static const size_t kMediaTypes = static_cast<size_t>(kMessage) + 1;
+
+ enum Protocol {
+ kRtpAvp, // RTP/AVP [RFC4566]
+ kUdp, // udp [RFC4566]
+ kVat, // vat [historic]
+ kRtp, // rtp [historic]
+ kUdptl, // udptl [ITU-T]
+ kTcp, // TCP [RFC4145]
+ kRtpAvpf, // RTP/AVPF [RFC4585]
+ kTcpRtpAvp, // TCP/RTP/AVP [RFC4571]
+ kRtpSavp, // RTP/SAVP [RFC3711]
+ kTcpBfcp, // TCP/BFCP [RFC4583]
+ kTcpTlsBfcp, // TCP/TLS/BFCP [RFC4583]
+ kTcpTls, // TCP/TLS [RFC4572]
+ kFluteUdp, // FLUTE/UDP [RFC-mehta-rmt-flute-sdp-05]
+ kTcpMsrp, // TCP/MSRP [RFC4975]
+ kTcpTlsMsrp, // TCP/TLS/MSRP [RFC4975]
+ kDccp, // DCCP [RFC5762]
+ kDccpRtpAvp, // DCCP/RTP/AVP [RFC5762]
+ kDccpRtpSavp, // DCCP/RTP/SAVP [RFC5762]
+ kDccpRtpAvpf, // DCCP/RTP/AVPF [RFC5762]
+ kDccpRtpSavpf, // DCCP/RTP/SAVPF [RFC5762]
+ kRtpSavpf, // RTP/SAVPF [RFC5124]
+ kUdpTlsRtpSavp, // UDP/TLS/RTP/SAVP [RFC5764]
+ kTcpDtlsRtpSavp, // TCP/DTLS/RTP/SAVP [RFC7850]
+ kDccpTlsRtpSavp, // DCCP/TLS/RTP/SAVP [RFC5764]
+ kUdpTlsRtpSavpf, // UDP/TLS/RTP/SAVPF [RFC5764]
+ kTcpDtlsRtpSavpf, // TCP/DTLS/RTP/SAVPF [RFC7850]
+ kDccpTlsRtpSavpf, // DCCP/TLS/RTP/SAVPF [RFC5764]
+ kUdpMbmsFecRtpAvp, // UDP/MBMS-FEC/RTP/AVP [RFC6064]
+ kUdpMbmsFecRtpSavp, // UDP/MBMS-FEC/RTP/SAVP [RFC6064]
+ kUdpMbmsRepair, // UDP/MBMS-REPAIR [RFC6064]
+ kFecUdp, // FEC/UDP [RFC6364]
+ kUdpFec, // UDP/FEC [RFC6364]
+ kTcpMrcpv2, // TCP/MRCPv2 [RFC6787]
+ kTcpTlsMrcpv2, // TCP/TLS/MRCPv2 [RFC6787]
+ kPstn, // PSTN [RFC7195]
+ kUdpTlsUdptl, // UDP/TLS/UDPTL [RFC7345]
+ kSctp, // SCTP [draft-ietf-mmusic-sctp-sdp-07]
+ kDtlsSctp, // DTLS/SCTP [draft-ietf-mmusic-sctp-sdp-07]
+ kUdpDtlsSctp, // UDP/DTLS/SCTP [draft-ietf-mmusic-sctp-sdp-21]
+ kTcpDtlsSctp // TCP/DTLS/SCTP [draft-ietf-mmusic-sctp-sdp-21]
+ };
+
+ explicit SdpMediaSection(size_t level) : mLevel(level) {}
+
+ virtual MediaType GetMediaType() const = 0;
+ virtual unsigned int GetPort() const = 0;
+ virtual void SetPort(unsigned int port) = 0;
+ virtual unsigned int GetPortCount() const = 0;
+ virtual Protocol GetProtocol() const = 0;
+ virtual const SdpConnection& GetConnection() const = 0;
+ virtual SdpConnection& GetConnection() = 0;
+ virtual uint32_t GetBandwidth(const std::string& type) const = 0;
+ virtual const std::vector<std::string>& GetFormats() const = 0;
+
+ std::vector<std::string> GetFormatsForSimulcastVersion(
+ size_t simulcastVersion, bool send, bool recv) const;
+ virtual const SdpAttributeList& GetAttributeList() const = 0;
+ virtual SdpAttributeList& GetAttributeList() = 0;
+
+ virtual SdpDirectionAttribute GetDirectionAttribute() const = 0;
+
+ virtual void Serialize(std::ostream&) const = 0;
+
+ virtual void AddCodec(const std::string& pt, const std::string& name,
+ uint32_t clockrate, uint16_t channels) = 0;
+ virtual void ClearCodecs() = 0;
+
+ virtual void AddDataChannel(const std::string& name, uint16_t port,
+ uint16_t streams, uint32_t message_size) = 0;
+
+ size_t GetLevel() const { return mLevel; }
+
+ inline bool IsReceiving() const { return GetDirection() & sdp::kRecv; }
+
+ inline bool IsSending() const { return GetDirection() & sdp::kSend; }
+
+ inline void SetReceiving(bool receiving) {
+ auto direction = GetDirection();
+ if (direction & sdp::kSend) {
+ SetDirection(receiving ? SdpDirectionAttribute::kSendrecv
+ : SdpDirectionAttribute::kSendonly);
+ } else {
+ SetDirection(receiving ? SdpDirectionAttribute::kRecvonly
+ : SdpDirectionAttribute::kInactive);
+ }
+ }
+
+ inline void SetSending(bool sending) {
+ auto direction = GetDirection();
+ if (direction & sdp::kRecv) {
+ SetDirection(sending ? SdpDirectionAttribute::kSendrecv
+ : SdpDirectionAttribute::kRecvonly);
+ } else {
+ SetDirection(sending ? SdpDirectionAttribute::kSendonly
+ : SdpDirectionAttribute::kInactive);
+ }
+ }
+
+ inline void SetDirection(SdpDirectionAttribute::Direction direction) {
+ GetAttributeList().SetAttribute(new SdpDirectionAttribute(direction));
+ }
+
+ inline SdpDirectionAttribute::Direction GetDirection() const {
+ return GetDirectionAttribute().mValue;
+ }
+
+ const SdpFmtpAttributeList::Parameters* FindFmtp(const std::string& pt) const;
+ void SetFmtp(const SdpFmtpAttributeList::Fmtp& fmtp);
+ void RemoveFmtp(const std::string& pt);
+ const SdpRtpmapAttributeList::Rtpmap* FindRtpmap(const std::string& pt) const;
+ const SdpSctpmapAttributeList::Sctpmap* GetSctpmap() const;
+ uint32_t GetSctpPort() const;
+ bool GetMaxMessageSize(uint32_t* size) const;
+ bool HasRtcpFb(const std::string& pt, SdpRtcpFbAttributeList::Type type,
+ const std::string& subType) const;
+ SdpRtcpFbAttributeList GetRtcpFbs() const;
+ void SetRtcpFbs(const SdpRtcpFbAttributeList& rtcpfbs);
+ bool HasFormat(const std::string& format) const {
+ return std::find(GetFormats().begin(), GetFormats().end(), format) !=
+ GetFormats().end();
+ }
+ void SetSsrcs(const std::vector<uint32_t>& ssrcs, const std::string& cname);
+ void AddMsid(const std::string& id, const std::string& appdata);
+ const SdpRidAttributeList::Rid* FindRid(const std::string& id) const;
+
+ private:
+ size_t mLevel;
+};
+
+inline std::ostream& operator<<(std::ostream& os, const SdpMediaSection& ms) {
+ ms.Serialize(os);
+ return os;
+}
+
+inline std::ostream& operator<<(std::ostream& os,
+ SdpMediaSection::MediaType t) {
+ switch (t) {
+ case SdpMediaSection::kAudio:
+ return os << "audio";
+ case SdpMediaSection::kVideo:
+ return os << "video";
+ case SdpMediaSection::kText:
+ return os << "text";
+ case SdpMediaSection::kApplication:
+ return os << "application";
+ case SdpMediaSection::kMessage:
+ return os << "message";
+ }
+ MOZ_ASSERT(false, "Unknown MediaType");
+ return os << "?";
+}
+
+inline std::ostream& operator<<(std::ostream& os, SdpMediaSection::Protocol p) {
+ switch (p) {
+ case SdpMediaSection::kRtpAvp:
+ return os << "RTP/AVP";
+ case SdpMediaSection::kUdp:
+ return os << "udp";
+ case SdpMediaSection::kVat:
+ return os << "vat";
+ case SdpMediaSection::kRtp:
+ return os << "rtp";
+ case SdpMediaSection::kUdptl:
+ return os << "udptl";
+ case SdpMediaSection::kTcp:
+ return os << "TCP";
+ case SdpMediaSection::kRtpAvpf:
+ return os << "RTP/AVPF";
+ case SdpMediaSection::kTcpRtpAvp:
+ return os << "TCP/RTP/AVP";
+ case SdpMediaSection::kRtpSavp:
+ return os << "RTP/SAVP";
+ case SdpMediaSection::kTcpBfcp:
+ return os << "TCP/BFCP";
+ case SdpMediaSection::kTcpTlsBfcp:
+ return os << "TCP/TLS/BFCP";
+ case SdpMediaSection::kTcpTls:
+ return os << "TCP/TLS";
+ case SdpMediaSection::kFluteUdp:
+ return os << "FLUTE/UDP";
+ case SdpMediaSection::kTcpMsrp:
+ return os << "TCP/MSRP";
+ case SdpMediaSection::kTcpTlsMsrp:
+ return os << "TCP/TLS/MSRP";
+ case SdpMediaSection::kDccp:
+ return os << "DCCP";
+ case SdpMediaSection::kDccpRtpAvp:
+ return os << "DCCP/RTP/AVP";
+ case SdpMediaSection::kDccpRtpSavp:
+ return os << "DCCP/RTP/SAVP";
+ case SdpMediaSection::kDccpRtpAvpf:
+ return os << "DCCP/RTP/AVPF";
+ case SdpMediaSection::kDccpRtpSavpf:
+ return os << "DCCP/RTP/SAVPF";
+ case SdpMediaSection::kRtpSavpf:
+ return os << "RTP/SAVPF";
+ case SdpMediaSection::kUdpTlsRtpSavp:
+ return os << "UDP/TLS/RTP/SAVP";
+ case SdpMediaSection::kTcpDtlsRtpSavp:
+ return os << "TCP/DTLS/RTP/SAVP";
+ case SdpMediaSection::kDccpTlsRtpSavp:
+ return os << "DCCP/TLS/RTP/SAVP";
+ case SdpMediaSection::kUdpTlsRtpSavpf:
+ return os << "UDP/TLS/RTP/SAVPF";
+ case SdpMediaSection::kTcpDtlsRtpSavpf:
+ return os << "TCP/DTLS/RTP/SAVPF";
+ case SdpMediaSection::kDccpTlsRtpSavpf:
+ return os << "DCCP/TLS/RTP/SAVPF";
+ case SdpMediaSection::kUdpMbmsFecRtpAvp:
+ return os << "UDP/MBMS-FEC/RTP/AVP";
+ case SdpMediaSection::kUdpMbmsFecRtpSavp:
+ return os << "UDP/MBMS-FEC/RTP/SAVP";
+ case SdpMediaSection::kUdpMbmsRepair:
+ return os << "UDP/MBMS-REPAIR";
+ case SdpMediaSection::kFecUdp:
+ return os << "FEC/UDP";
+ case SdpMediaSection::kUdpFec:
+ return os << "UDP/FEC";
+ case SdpMediaSection::kTcpMrcpv2:
+ return os << "TCP/MRCPv2";
+ case SdpMediaSection::kTcpTlsMrcpv2:
+ return os << "TCP/TLS/MRCPv2";
+ case SdpMediaSection::kPstn:
+ return os << "PSTN";
+ case SdpMediaSection::kUdpTlsUdptl:
+ return os << "UDP/TLS/UDPTL";
+ case SdpMediaSection::kSctp:
+ return os << "SCTP";
+ case SdpMediaSection::kDtlsSctp:
+ return os << "DTLS/SCTP";
+ case SdpMediaSection::kUdpDtlsSctp:
+ return os << "UDP/DTLS/SCTP";
+ case SdpMediaSection::kTcpDtlsSctp:
+ return os << "TCP/DTLS/SCTP";
+ }
+ MOZ_ASSERT(false, "Unknown Protocol");
+ return os << "?";
+}
+
+class SdpConnection {
+ public:
+ SdpConnection(sdp::AddrType addrType, std::string addr, uint8_t ttl = 0,
+ uint32_t count = 0)
+ : mAddrType(addrType), mAddr(addr), mTtl(ttl), mCount(count) {}
+ ~SdpConnection() {}
+
+ sdp::AddrType GetAddrType() const { return mAddrType; }
+ const std::string& GetAddress() const { return mAddr; }
+ void SetAddress(const std::string& address) {
+ mAddr = address;
+ if (mAddr.find(':') != std::string::npos) {
+ mAddrType = sdp::kIPv6;
+ } else {
+ mAddrType = sdp::kIPv4;
+ }
+ }
+ uint8_t GetTtl() const { return mTtl; }
+ uint32_t GetCount() const { return mCount; }
+
+ void Serialize(std::ostream& os) const {
+ sdp::NetType netType = sdp::kInternet;
+
+ os << "c=" << netType << " " << mAddrType << " " << mAddr;
+
+ if (mTtl) {
+ os << "/" << static_cast<uint32_t>(mTtl);
+ if (mCount) {
+ os << "/" << mCount;
+ }
+ }
+ os << "\r\n";
+ }
+
+ private:
+ sdp::AddrType mAddrType;
+ std::string mAddr;
+ uint8_t mTtl; // 0-255; 0 when unset
+ uint32_t mCount; // 0 when unset
+};
+
+inline std::ostream& operator<<(std::ostream& os, const SdpConnection& c) {
+ c.Serialize(os);
+ return os;
+}
+
+} // namespace mozilla
+
+#endif
diff --git a/dom/media/webrtc/sdp/SdpParser.h b/dom/media/webrtc/sdp/SdpParser.h
new file mode 100644
index 0000000000..4b23e93705
--- /dev/null
+++ b/dom/media/webrtc/sdp/SdpParser.h
@@ -0,0 +1,81 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _SDPPARSER_H_
+#define _SDPPARSER_H_
+
+#include <vector>
+#include <string>
+#include "sdp/Sdp.h"
+#include "sdp/SdpLog.h"
+
+#include "mozilla/Telemetry.h"
+
+namespace mozilla {
+
+class SdpParser {
+ public:
+ SdpParser() = default;
+ virtual ~SdpParser() = default;
+
+ class Results {
+ public:
+ typedef std::pair<size_t, std::string> Anomaly;
+ typedef std::vector<Anomaly> AnomalyVec;
+ virtual ~Results() = default;
+ UniquePtr<mozilla::Sdp>& Sdp() { return mSdp; }
+ AnomalyVec& Errors() { return mErrors; }
+ AnomalyVec& Warnings() { return mWarnings; }
+ virtual const std::string& ParserName() const = 0;
+ bool Ok() const { return mErrors.empty(); }
+
+ protected:
+ UniquePtr<mozilla::Sdp> mSdp;
+ AnomalyVec mErrors;
+ AnomalyVec mWarnings;
+ };
+
+ // The name of the parser implementation
+ virtual const std::string& Name() const = 0;
+
+ /**
+ * This parses the provided text into an SDP object.
+ * This returns a nullptr-valued pointer if things go poorly.
+ */
+ virtual UniquePtr<SdpParser::Results> Parse(const std::string& aText) = 0;
+
+ class InternalResults : public Results {
+ public:
+ explicit InternalResults(const std::string& aParserName)
+ : mParserName(aParserName) {}
+ virtual ~InternalResults() = default;
+
+ void SetSdp(UniquePtr<mozilla::Sdp>&& aSdp) { mSdp = std::move(aSdp); }
+
+ void AddParseError(size_t line, const std::string& message) {
+ MOZ_LOG(SdpLog, LogLevel::Error,
+ ("%s: parser error %s, at line %zu", mParserName.c_str(),
+ message.c_str(), line));
+ mErrors.push_back(std::make_pair(line, message));
+ }
+
+ void AddParseWarning(size_t line, const std::string& message) {
+ MOZ_LOG(SdpLog, LogLevel::Warning,
+ ("%s: parser warning %s, at line %zu", mParserName.c_str(),
+ message.c_str(), line));
+ mWarnings.push_back(std::make_pair(line, message));
+ }
+
+ const std::string& ParserName() const override { return mParserName; }
+
+ private:
+ const std::string mParserName;
+ };
+};
+
+} // namespace mozilla
+
+#endif
diff --git a/dom/media/webrtc/sdp/SdpPref.cpp b/dom/media/webrtc/sdp/SdpPref.cpp
new file mode 100644
index 0000000000..d0fc03c4d0
--- /dev/null
+++ b/dom/media/webrtc/sdp/SdpPref.cpp
@@ -0,0 +1,107 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "sdp/SdpPref.h"
+#include "sdp/RsdparsaSdpParser.h"
+#include "sdp/SipccSdpParser.h"
+
+namespace mozilla {
+
+const std::string SdpPref::PRIMARY_PREF = "media.peerconnection.sdp.parser";
+const std::string SdpPref::ALTERNATE_PREF =
+ "media.peerconnection.sdp.alternate_parse_mode";
+const std::string SdpPref::STRICT_SUCCESS_PREF =
+ "media.peerconnection.sdp.strict_success";
+const std::string SdpPref::DEFAULT = "default";
+
+auto SdpPref::ToString(const Parsers& aParser) -> std::string {
+ switch (aParser) {
+ case Parsers::Sipcc:
+ return "sipcc";
+ case Parsers::WebRtcSdp:
+ return "webrtc-sdp";
+ };
+ MOZ_CRASH("ALL Parsers CASES ARE NOT COVERED");
+ return "";
+}
+
+auto SdpPref::ToString(const AlternateParseModes& aMode) -> std::string {
+ switch (aMode) {
+ case AlternateParseModes::Parallel:
+ return "parallel";
+ case AlternateParseModes::Failover:
+ return "failover";
+ case AlternateParseModes::Never:
+ return "never";
+ };
+ MOZ_CRASH("ALL AlternateParseModes CASES ARE NOT COVERED");
+ return "";
+}
+
+auto SdpPref::Parser() -> Parsers {
+ static const auto values = std::unordered_map<std::string, Parsers>{
+ {"sipcc", Parsers::Sipcc},
+ {"webrtc-sdp", Parsers::WebRtcSdp},
+ {DEFAULT, Parsers::Sipcc},
+ };
+ return Pref(PRIMARY_PREF, values);
+}
+
+auto SdpPref::AlternateParseMode() -> AlternateParseModes {
+ static const auto values =
+ std::unordered_map<std::string, AlternateParseModes>{
+ {"parallel", AlternateParseModes::Parallel},
+ {"failover", AlternateParseModes::Failover},
+ {"never", AlternateParseModes::Never},
+ {DEFAULT, AlternateParseModes::Parallel},
+ };
+ return Pref(ALTERNATE_PREF, values);
+}
+
+auto SdpPref::Primary() -> UniquePtr<SdpParser> {
+ switch (Parser()) {
+ case Parsers::Sipcc:
+ return UniquePtr<SdpParser>(new SipccSdpParser());
+ case Parsers::WebRtcSdp:
+ return UniquePtr<SdpParser>(new RsdparsaSdpParser());
+ }
+ MOZ_CRASH("ALL Parsers CASES ARE NOT COVERED");
+ return nullptr;
+}
+
+auto SdpPref::Secondary() -> Maybe<UniquePtr<SdpParser>> {
+ if (AlternateParseMode() != AlternateParseModes::Parallel) {
+ return Nothing();
+ }
+ switch (Parser()) { // Choose whatever the primary parser isn't
+ case Parsers::Sipcc:
+ return Some(UniquePtr<SdpParser>(new RsdparsaSdpParser()));
+ case Parsers::WebRtcSdp:
+ return Some(UniquePtr<SdpParser>(new SipccSdpParser()));
+ }
+ MOZ_CRASH("ALL Parsers CASES ARE NOT COVERED");
+ return Nothing();
+}
+
+auto SdpPref::Failover() -> Maybe<UniquePtr<SdpParser>> {
+ if (AlternateParseMode() != AlternateParseModes::Failover) {
+ return Nothing();
+ }
+ switch (Parser()) {
+ case Parsers::Sipcc:
+ return Some(UniquePtr<SdpParser>(new RsdparsaSdpParser()));
+ case Parsers::WebRtcSdp:
+ return Some(UniquePtr<SdpParser>(new SipccSdpParser()));
+ }
+ MOZ_CRASH("ALL Parsers CASES ARE NOT COVERED");
+ return Nothing();
+}
+
+auto SdpPref::StrictSuccess() -> bool {
+ return Preferences::GetBool(STRICT_SUCCESS_PREF.c_str(), false);
+}
+
+} // namespace mozilla
diff --git a/dom/media/webrtc/sdp/SdpPref.h b/dom/media/webrtc/sdp/SdpPref.h
new file mode 100644
index 0000000000..5f24fb12a7
--- /dev/null
+++ b/dom/media/webrtc/sdp/SdpPref.h
@@ -0,0 +1,82 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _SDPPREF_H_
+#define _SDPPREF_H_
+
+#include "mozilla/Maybe.h"
+#include "mozilla/Preferences.h"
+
+#include <string>
+#include <unordered_map>
+
+namespace mozilla {
+
+class SdpParser;
+
+// Interprets about:config SDP parsing preferences
+class SdpPref {
+ private:
+ static const std::string PRIMARY_PREF;
+ static const std::string ALTERNATE_PREF;
+ static const std::string STRICT_SUCCESS_PREF;
+ static const std::string DEFAULT;
+
+ public:
+ // Supported Parsers
+ enum class Parsers {
+ Sipcc,
+ WebRtcSdp,
+ };
+ static auto ToString(const Parsers& aParser) -> std::string;
+
+ // How is the alternate used
+ enum class AlternateParseModes {
+ Parallel, // Alternate is always run, if A succedes it is used, otherwise B
+ // is used
+ Failover, // Alternate is only run on failure of the primary to parse
+ Never, // Alternate is never run; this is effectively a kill switch
+ };
+ static auto ToString(const AlternateParseModes& aMode) -> std::string;
+
+ private:
+ // Finds the mapping between a pref string and pref value, if none exists the
+ // default is used
+ template <class T>
+ static auto Pref(const std::string& aPrefName,
+ const std::unordered_map<std::string, T>& aMap) -> T {
+ MOZ_ASSERT(aMap.find(DEFAULT) != aMap.end());
+
+ nsCString value;
+ if (NS_FAILED(Preferences::GetCString(aPrefName.c_str(), value))) {
+ return aMap.at(DEFAULT);
+ }
+ const auto found = aMap.find(value.get());
+ if (found != aMap.end()) {
+ return found->second;
+ }
+ return aMap.at(DEFAULT);
+ }
+ // The value of the parser pref
+ static auto Parser() -> Parsers;
+
+ // The value of the alternate parse mode pref
+ static auto AlternateParseMode() -> AlternateParseModes;
+
+ public:
+ // Do non-fatal parsing errors count as failure
+ static auto StrictSuccess() -> bool;
+ // Functions to create the primary, secondary and failover parsers.
+
+ // Reads about:config to choose the primary Parser
+ static auto Primary() -> UniquePtr<SdpParser>;
+ static auto Secondary() -> Maybe<UniquePtr<SdpParser>>;
+ static auto Failover() -> Maybe<UniquePtr<SdpParser>>;
+};
+
+} // namespace mozilla
+
+#endif
diff --git a/dom/media/webrtc/sdp/SdpTelemetry.cpp b/dom/media/webrtc/sdp/SdpTelemetry.cpp
new file mode 100644
index 0000000000..7b3a6cec18
--- /dev/null
+++ b/dom/media/webrtc/sdp/SdpTelemetry.cpp
@@ -0,0 +1,63 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "sdp/SdpTelemetry.h"
+
+#include "mozilla/Telemetry.h"
+
+namespace mozilla {
+
+auto SdpTelemetry::RecordParse(const SdpTelemetry::Results& aResult,
+ const SdpTelemetry::Modes& aMode,
+ const SdpTelemetry::Roles& aRole) -> void {
+ Telemetry::ScalarAdd(Telemetry::ScalarID::WEBRTC_SDP_PARSER_DIFF,
+ BucketNameFragment(aResult, aMode, aRole), 1);
+}
+
+auto SdpTelemetry::RecordCompare(const SdpTelemetry::Results& aFirst,
+ const SdpTelemetry::Results& aSecond,
+ const SdpTelemetry::Modes& aMode) -> void {
+ const nsAutoString bucket =
+ BucketNameFragment(aFirst, aMode, Roles::Primary) +
+ NS_ConvertASCIItoUTF16("__") +
+ BucketNameFragment(aSecond, aMode, Roles::Secondary);
+ Telemetry::ScalarAdd(Telemetry::ScalarID::WEBRTC_SDP_PARSER_DIFF, bucket, 1);
+}
+
+auto SdpTelemetry::BucketNameFragment(const SdpTelemetry::Results& aResult,
+ const SdpTelemetry::Modes& aMode,
+ const SdpTelemetry::Roles& aRole)
+ -> nsAutoString {
+ auto mode = [&]() -> std::string {
+ switch (aMode) {
+ case Modes::Parallel:
+ return "parallel";
+ case Modes::Failover:
+ return "failover";
+ case Modes::Never:
+ return "standalone";
+ }
+ MOZ_CRASH("Unknown SDP Parse Mode!");
+ };
+ auto role = [&]() -> std::string {
+ switch (aRole) {
+ case Roles::Primary:
+ return "primary";
+ case Roles::Secondary:
+ return "secondary";
+ }
+ MOZ_CRASH("Unknown SDP Parse Role!");
+ };
+ auto success = [&]() -> std::string {
+ return aResult->Ok() ? "success" : "failure";
+ };
+ nsAutoString name;
+ name.AssignASCII(nsCString(aResult->ParserName() + "_" + mode() + "_" +
+ role() + "_" + success()));
+ return name;
+}
+
+} // namespace mozilla
diff --git a/dom/media/webrtc/sdp/SdpTelemetry.h b/dom/media/webrtc/sdp/SdpTelemetry.h
new file mode 100644
index 0000000000..ce308a04ec
--- /dev/null
+++ b/dom/media/webrtc/sdp/SdpTelemetry.h
@@ -0,0 +1,43 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _SDPTELEMETRY_H_
+#define _SDPTELEMETRY_H_
+
+#include "sdp/SdpParser.h"
+#include "sdp/SdpPref.h"
+
+namespace mozilla {
+
+class SdpTelemetry {
+ public:
+ SdpTelemetry() = delete;
+
+ using Results = UniquePtr<SdpParser::Results>;
+ using Modes = SdpPref::AlternateParseModes;
+
+ enum class Roles {
+ Primary,
+ Secondary,
+ };
+
+ static auto RecordParse(const Results& aResults, const Modes& aMode,
+ const Roles& aRole) -> void;
+
+ static auto RecordSecondaryParse(const Results& aResult, const Modes& aMode)
+ -> void;
+
+ static auto RecordCompare(const Results& aFirst, const Results& aSecond,
+ const Modes& aMode) -> void;
+
+ private:
+ static auto BucketNameFragment(const Results& aResult, const Modes& aModes,
+ const Roles& aRoles) -> nsAutoString;
+};
+
+} // namespace mozilla
+
+#endif
diff --git a/dom/media/webrtc/sdp/SipccSdp.cpp b/dom/media/webrtc/sdp/SipccSdp.cpp
new file mode 100644
index 0000000000..7e36f9e930
--- /dev/null
+++ b/dom/media/webrtc/sdp/SipccSdp.cpp
@@ -0,0 +1,173 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "sdp/SipccSdp.h"
+
+#include <cstdlib>
+#include "mozilla/UniquePtr.h"
+#include "mozilla/Assertions.h"
+#include "sdp/SdpParser.h"
+
+#ifdef CRLF
+# undef CRLF
+#endif
+#define CRLF "\r\n"
+
+namespace mozilla {
+
+SipccSdp::SipccSdp(const SipccSdp& aOrig)
+ : mOrigin(aOrig.mOrigin),
+ mBandwidths(aOrig.mBandwidths),
+ mAttributeList(aOrig.mAttributeList, nullptr) {
+ for (const auto& msection : aOrig.mMediaSections) {
+ mMediaSections.emplace_back(
+ new SipccSdpMediaSection(*msection, &mAttributeList));
+ }
+}
+
+Sdp* SipccSdp::Clone() const { return new SipccSdp(*this); }
+
+const SdpOrigin& SipccSdp::GetOrigin() const { return mOrigin; }
+
+uint32_t SipccSdp::GetBandwidth(const std::string& type) const {
+ auto found = mBandwidths.find(type);
+ if (found == mBandwidths.end()) {
+ return 0;
+ }
+ return found->second;
+}
+
+const SdpMediaSection& SipccSdp::GetMediaSection(size_t level) const {
+ if (level > mMediaSections.size()) {
+ MOZ_CRASH();
+ }
+ return *mMediaSections[level];
+}
+
+SdpMediaSection& SipccSdp::GetMediaSection(size_t level) {
+ if (level > mMediaSections.size()) {
+ MOZ_CRASH();
+ }
+ return *mMediaSections[level];
+}
+
+SdpMediaSection& SipccSdp::AddMediaSection(SdpMediaSection::MediaType mediaType,
+ SdpDirectionAttribute::Direction dir,
+ uint16_t port,
+ SdpMediaSection::Protocol protocol,
+ sdp::AddrType addrType,
+ const std::string& addr) {
+ size_t level = mMediaSections.size();
+ SipccSdpMediaSection* media =
+ new SipccSdpMediaSection(level, &mAttributeList);
+ media->mMediaType = mediaType;
+ media->mPort = port;
+ media->mPortCount = 0;
+ media->mProtocol = protocol;
+ media->mConnection = MakeUnique<SdpConnection>(addrType, addr);
+ media->GetAttributeList().SetAttribute(new SdpDirectionAttribute(dir));
+ mMediaSections.emplace_back(media);
+ return *media;
+}
+
+bool SipccSdp::LoadOrigin(sdp_t* sdp, InternalResults& results) {
+ std::string username = sdp_get_owner_username(sdp);
+ uint64_t sessId = strtoull(sdp_get_owner_sessionid(sdp), nullptr, 10);
+ uint64_t sessVer = strtoull(sdp_get_owner_version(sdp), nullptr, 10);
+
+ sdp_nettype_e type = sdp_get_owner_network_type(sdp);
+ if (type != SDP_NT_INTERNET) {
+ results.AddParseError(2, "Unsupported network type");
+ return false;
+ }
+
+ sdp::AddrType addrType;
+ switch (sdp_get_owner_address_type(sdp)) {
+ case SDP_AT_IP4:
+ addrType = sdp::kIPv4;
+ break;
+ case SDP_AT_IP6:
+ addrType = sdp::kIPv6;
+ break;
+ default:
+ results.AddParseError(2, "Unsupported address type");
+ return false;
+ }
+
+ std::string address = sdp_get_owner_address(sdp);
+ mOrigin = SdpOrigin(username, sessId, sessVer, addrType, address);
+ return true;
+}
+
+bool SipccSdp::Load(sdp_t* sdp, InternalResults& results) {
+ // Believe it or not, SDP_SESSION_LEVEL is 0xFFFF
+ if (!mAttributeList.Load(sdp, SDP_SESSION_LEVEL, results)) {
+ return false;
+ }
+
+ if (!LoadOrigin(sdp, results)) {
+ return false;
+ }
+
+ if (!mBandwidths.Load(sdp, SDP_SESSION_LEVEL, results)) {
+ return false;
+ }
+
+ for (int i = 0; i < sdp_get_num_media_lines(sdp); ++i) {
+ // note that we pass a "level" here that is one higher
+ // sipcc counts media sections from 1, using 0xFFFF as the "session"
+ UniquePtr<SipccSdpMediaSection> section(
+ new SipccSdpMediaSection(i, &mAttributeList));
+ if (!section->Load(sdp, i + 1, results)) {
+ return false;
+ }
+ mMediaSections.push_back(std::move(section));
+ }
+ return true;
+}
+
+void SipccSdp::Serialize(std::ostream& os) const {
+ os << "v=0" << CRLF << mOrigin << "s=-" << CRLF;
+
+ // We don't support creating i=, u=, e=, p=
+ // We don't generate c= at the session level (only in media)
+
+ mBandwidths.Serialize(os);
+ os << "t=0 0" << CRLF;
+
+ // We don't support r= or z=
+
+ // attributes
+ os << mAttributeList;
+
+ // media sections
+ for (const auto& msection : mMediaSections) {
+ os << *msection;
+ }
+}
+
+bool SipccSdpBandwidths::Load(sdp_t* sdp, uint16_t level,
+ InternalResults& results) {
+ size_t count = sdp_get_num_bw_lines(sdp, level);
+ for (size_t i = 1; i <= count; ++i) {
+ sdp_bw_modifier_e bwtype = sdp_get_bw_modifier(sdp, level, i);
+ uint32_t bandwidth = sdp_get_bw_value(sdp, level, i);
+ if (bwtype != SDP_BW_MODIFIER_UNSUPPORTED) {
+ const char* typeName = sdp_get_bw_modifier_name(bwtype);
+ (*this)[typeName] = bandwidth;
+ }
+ }
+
+ return true;
+}
+
+void SipccSdpBandwidths::Serialize(std::ostream& os) const {
+ for (auto i = begin(); i != end(); ++i) {
+ os << "b=" << i->first << ":" << i->second << CRLF;
+ }
+}
+
+} // namespace mozilla
diff --git a/dom/media/webrtc/sdp/SipccSdp.h b/dom/media/webrtc/sdp/SipccSdp.h
new file mode 100644
index 0000000000..4915821cee
--- /dev/null
+++ b/dom/media/webrtc/sdp/SipccSdp.h
@@ -0,0 +1,82 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _SIPCCSDP_H_
+#define _SIPCCSDP_H_
+
+#include <map>
+#include <vector>
+#include "mozilla/Attributes.h"
+
+#include "sdp/Sdp.h"
+#include "sdp/SdpParser.h"
+#include "sdp/SipccSdpMediaSection.h"
+#include "sdp/SipccSdpAttributeList.h"
+extern "C" {
+#include "sipcc_sdp.h"
+}
+
+namespace mozilla {
+
+class SipccSdpParser;
+
+class SipccSdp final : public Sdp {
+ friend class SipccSdpParser;
+
+ public:
+ explicit SipccSdp(const SdpOrigin& origin)
+ : mOrigin(origin), mAttributeList(nullptr) {}
+ SipccSdp(const SipccSdp& aOrig);
+
+ virtual Sdp* Clone() const override;
+
+ virtual const SdpOrigin& GetOrigin() const override;
+
+ // Note: connection information is always retrieved from media sections
+ virtual uint32_t GetBandwidth(const std::string& type) const override;
+
+ virtual size_t GetMediaSectionCount() const override {
+ return mMediaSections.size();
+ }
+
+ virtual const SdpAttributeList& GetAttributeList() const override {
+ return mAttributeList;
+ }
+
+ virtual SdpAttributeList& GetAttributeList() override {
+ return mAttributeList;
+ }
+
+ virtual const SdpMediaSection& GetMediaSection(size_t level) const override;
+
+ virtual SdpMediaSection& GetMediaSection(size_t level) override;
+
+ virtual SdpMediaSection& AddMediaSection(SdpMediaSection::MediaType media,
+ SdpDirectionAttribute::Direction dir,
+ uint16_t port,
+ SdpMediaSection::Protocol proto,
+ sdp::AddrType addrType,
+ const std::string& addr) override;
+
+ virtual void Serialize(std::ostream&) const override;
+
+ private:
+ using InternalResults = SdpParser::InternalResults;
+
+ SipccSdp() : mOrigin("", 0, 0, sdp::kIPv4, ""), mAttributeList(nullptr) {}
+
+ bool Load(sdp_t* sdp, InternalResults& results);
+ bool LoadOrigin(sdp_t* sdp, InternalResults& results);
+
+ SdpOrigin mOrigin;
+ SipccSdpBandwidths mBandwidths;
+ SipccSdpAttributeList mAttributeList;
+ std::vector<UniquePtr<SipccSdpMediaSection>> mMediaSections;
+};
+
+} // namespace mozilla
+
+#endif // _sdp_h_
diff --git a/dom/media/webrtc/sdp/SipccSdpAttributeList.cpp b/dom/media/webrtc/sdp/SipccSdpAttributeList.cpp
new file mode 100644
index 0000000000..15572b6dd3
--- /dev/null
+++ b/dom/media/webrtc/sdp/SipccSdpAttributeList.cpp
@@ -0,0 +1,1386 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "sdp/SipccSdpAttributeList.h"
+
+#include <ostream>
+#include "mozilla/Assertions.h"
+
+extern "C" {
+#include "sdp_private.h"
+}
+
+namespace mozilla {
+
+using InternalResults = SdpParser::InternalResults;
+
+/* static */
+const std::string SipccSdpAttributeList::kEmptyString = "";
+
+SipccSdpAttributeList::SipccSdpAttributeList(
+ const SipccSdpAttributeList* sessionLevel)
+ : mSessionLevel(sessionLevel) {
+ memset(&mAttributes, 0, sizeof(mAttributes));
+}
+
+SipccSdpAttributeList::SipccSdpAttributeList(
+ const SipccSdpAttributeList& aOrig,
+ const SipccSdpAttributeList* sessionLevel)
+ : SipccSdpAttributeList(sessionLevel) {
+ for (size_t i = 0; i < kNumAttributeTypes; ++i) {
+ if (aOrig.mAttributes[i]) {
+ mAttributes[i] = aOrig.mAttributes[i]->Clone();
+ }
+ }
+}
+
+SipccSdpAttributeList::~SipccSdpAttributeList() {
+ for (size_t i = 0; i < kNumAttributeTypes; ++i) {
+ delete mAttributes[i];
+ }
+}
+
+bool SipccSdpAttributeList::HasAttribute(AttributeType type,
+ bool sessionFallback) const {
+ return !!GetAttribute(type, sessionFallback);
+}
+
+const SdpAttribute* SipccSdpAttributeList::GetAttribute(
+ AttributeType type, bool sessionFallback) const {
+ const SdpAttribute* value = mAttributes[static_cast<size_t>(type)];
+ // Only do fallback when the attribute can appear at both the media and
+ // session level
+ if (!value && !AtSessionLevel() && sessionFallback &&
+ SdpAttribute::IsAllowedAtSessionLevel(type) &&
+ SdpAttribute::IsAllowedAtMediaLevel(type)) {
+ return mSessionLevel->GetAttribute(type, false);
+ }
+ return value;
+}
+
+void SipccSdpAttributeList::RemoveAttribute(AttributeType type) {
+ delete mAttributes[static_cast<size_t>(type)];
+ mAttributes[static_cast<size_t>(type)] = nullptr;
+}
+
+void SipccSdpAttributeList::Clear() {
+ for (size_t i = 0; i < kNumAttributeTypes; ++i) {
+ RemoveAttribute(static_cast<AttributeType>(i));
+ }
+}
+
+uint32_t SipccSdpAttributeList::Count() const {
+ uint32_t count = 0;
+ for (size_t i = 0; i < kNumAttributeTypes; ++i) {
+ if (mAttributes[i]) {
+ count++;
+ }
+ }
+ return count;
+}
+
+void SipccSdpAttributeList::SetAttribute(SdpAttribute* attr) {
+ if (!IsAllowedHere(attr->GetType())) {
+ MOZ_ASSERT(false, "This type of attribute is not allowed here");
+ return;
+ }
+ RemoveAttribute(attr->GetType());
+ mAttributes[attr->GetType()] = attr;
+}
+
+void SipccSdpAttributeList::LoadSimpleString(sdp_t* sdp, uint16_t level,
+ sdp_attr_e attr,
+ AttributeType targetType,
+ InternalResults& results) {
+ const char* value = sdp_attr_get_simple_string(sdp, attr, level, 0, 1);
+ if (value) {
+ if (!IsAllowedHere(targetType)) {
+ uint32_t lineNumber = sdp_attr_line_number(sdp, attr, level, 0, 1);
+ WarnAboutMisplacedAttribute(targetType, lineNumber, results);
+ } else {
+ SetAttribute(new SdpStringAttribute(targetType, std::string(value)));
+ }
+ }
+}
+
+void SipccSdpAttributeList::LoadSimpleStrings(sdp_t* sdp, uint16_t level,
+ InternalResults& results) {
+ LoadSimpleString(sdp, level, SDP_ATTR_MID, SdpAttribute::kMidAttribute,
+ results);
+ LoadSimpleString(sdp, level, SDP_ATTR_LABEL, SdpAttribute::kLabelAttribute,
+ results);
+}
+
+void SipccSdpAttributeList::LoadSimpleNumber(sdp_t* sdp, uint16_t level,
+ sdp_attr_e attr,
+ AttributeType targetType,
+ InternalResults& results) {
+ if (sdp_attr_valid(sdp, attr, level, 0, 1)) {
+ if (!IsAllowedHere(targetType)) {
+ uint32_t lineNumber = sdp_attr_line_number(sdp, attr, level, 0, 1);
+ WarnAboutMisplacedAttribute(targetType, lineNumber, results);
+ } else {
+ uint32_t value = sdp_attr_get_simple_u32(sdp, attr, level, 0, 1);
+ SetAttribute(new SdpNumberAttribute(targetType, value));
+ }
+ }
+}
+
+void SipccSdpAttributeList::LoadSimpleNumbers(sdp_t* sdp, uint16_t level,
+ InternalResults& results) {
+ LoadSimpleNumber(sdp, level, SDP_ATTR_PTIME, SdpAttribute::kPtimeAttribute,
+ results);
+ LoadSimpleNumber(sdp, level, SDP_ATTR_MAXPTIME,
+ SdpAttribute::kMaxptimeAttribute, results);
+ LoadSimpleNumber(sdp, level, SDP_ATTR_SCTPPORT,
+ SdpAttribute::kSctpPortAttribute, results);
+ LoadSimpleNumber(sdp, level, SDP_ATTR_MAXMESSAGESIZE,
+ SdpAttribute::kMaxMessageSizeAttribute, results);
+}
+
+void SipccSdpAttributeList::LoadFlags(sdp_t* sdp, uint16_t level) {
+ if (AtSessionLevel()) {
+ if (sdp_attr_valid(sdp, SDP_ATTR_ICE_LITE, level, 0, 1)) {
+ SetAttribute(new SdpFlagAttribute(SdpAttribute::kIceLiteAttribute));
+ }
+ } else { // media-level
+ if (sdp_attr_valid(sdp, SDP_ATTR_RTCP_MUX, level, 0, 1)) {
+ SetAttribute(new SdpFlagAttribute(SdpAttribute::kRtcpMuxAttribute));
+ }
+ if (sdp_attr_valid(sdp, SDP_ATTR_END_OF_CANDIDATES, level, 0, 1)) {
+ SetAttribute(
+ new SdpFlagAttribute(SdpAttribute::kEndOfCandidatesAttribute));
+ }
+ if (sdp_attr_valid(sdp, SDP_ATTR_BUNDLE_ONLY, level, 0, 1)) {
+ SetAttribute(new SdpFlagAttribute(SdpAttribute::kBundleOnlyAttribute));
+ }
+ if (sdp_attr_valid(sdp, SDP_ATTR_RTCP_RSIZE, level, 0, 1))
+ SetAttribute(new SdpFlagAttribute(SdpAttribute::kRtcpRsizeAttribute));
+ }
+}
+
+static void ConvertDirection(sdp_direction_e sipcc_direction,
+ SdpDirectionAttribute::Direction* dir_outparam) {
+ switch (sipcc_direction) {
+ case SDP_DIRECTION_SENDRECV:
+ *dir_outparam = SdpDirectionAttribute::kSendrecv;
+ return;
+ case SDP_DIRECTION_SENDONLY:
+ *dir_outparam = SdpDirectionAttribute::kSendonly;
+ return;
+ case SDP_DIRECTION_RECVONLY:
+ *dir_outparam = SdpDirectionAttribute::kRecvonly;
+ return;
+ case SDP_DIRECTION_INACTIVE:
+ *dir_outparam = SdpDirectionAttribute::kInactive;
+ return;
+ case SDP_MAX_QOS_DIRECTIONS:
+ // Nothing actually sets this value.
+ // Fall through to MOZ_CRASH below.
+ {}
+ }
+
+ MOZ_CRASH("Invalid direction from sipcc; this is probably corruption");
+}
+
+void SipccSdpAttributeList::LoadDirection(sdp_t* sdp, uint16_t level,
+ InternalResults& results) {
+ SdpDirectionAttribute::Direction dir;
+ ConvertDirection(sdp_get_media_direction(sdp, level, 0), &dir);
+ SetAttribute(new SdpDirectionAttribute(dir));
+}
+
+void SipccSdpAttributeList::LoadIceAttributes(sdp_t* sdp, uint16_t level) {
+ char* value;
+ sdp_result_e sdpres =
+ sdp_attr_get_ice_attribute(sdp, level, 0, SDP_ATTR_ICE_UFRAG, 1, &value);
+ if (sdpres == SDP_SUCCESS) {
+ SetAttribute(new SdpStringAttribute(SdpAttribute::kIceUfragAttribute,
+ std::string(value)));
+ }
+ sdpres =
+ sdp_attr_get_ice_attribute(sdp, level, 0, SDP_ATTR_ICE_PWD, 1, &value);
+ if (sdpres == SDP_SUCCESS) {
+ SetAttribute(new SdpStringAttribute(SdpAttribute::kIcePwdAttribute,
+ std::string(value)));
+ }
+
+ const char* iceOptVal =
+ sdp_attr_get_simple_string(sdp, SDP_ATTR_ICE_OPTIONS, level, 0, 1);
+ if (iceOptVal) {
+ auto* iceOptions =
+ new SdpOptionsAttribute(SdpAttribute::kIceOptionsAttribute);
+ iceOptions->Load(iceOptVal);
+ SetAttribute(iceOptions);
+ }
+}
+
+bool SipccSdpAttributeList::LoadFingerprint(sdp_t* sdp, uint16_t level,
+ InternalResults& results) {
+ char* value;
+ UniquePtr<SdpFingerprintAttributeList> fingerprintAttrs;
+
+ for (uint16_t i = 1; i < UINT16_MAX; ++i) {
+ sdp_result_e result = sdp_attr_get_dtls_fingerprint_attribute(
+ sdp, level, 0, SDP_ATTR_DTLS_FINGERPRINT, i, &value);
+
+ if (result != SDP_SUCCESS) {
+ break;
+ }
+
+ std::string fingerprintAttr(value);
+ uint32_t lineNumber =
+ sdp_attr_line_number(sdp, SDP_ATTR_DTLS_FINGERPRINT, level, 0, i);
+
+ // sipcc does not expose parse code for this
+ size_t start = fingerprintAttr.find_first_not_of(" \t");
+ if (start == std::string::npos) {
+ results.AddParseError(lineNumber, "Empty fingerprint attribute");
+ return false;
+ }
+
+ size_t end = fingerprintAttr.find_first_of(" \t", start);
+ if (end == std::string::npos) {
+ // One token, no trailing ws
+ results.AddParseError(lineNumber,
+ "Only one token in fingerprint attribute");
+ return false;
+ }
+
+ std::string algorithmToken(fingerprintAttr.substr(start, end - start));
+
+ start = fingerprintAttr.find_first_not_of(" \t", end);
+ if (start == std::string::npos) {
+ // One token, trailing ws
+ results.AddParseError(lineNumber,
+ "Only one token in fingerprint attribute");
+ return false;
+ }
+
+ std::string fingerprintToken(fingerprintAttr.substr(start));
+
+ std::vector<uint8_t> fingerprint =
+ SdpFingerprintAttributeList::ParseFingerprint(fingerprintToken);
+ if (fingerprint.empty()) {
+ results.AddParseError(lineNumber, "Malformed fingerprint token");
+ return false;
+ }
+
+ if (!fingerprintAttrs) {
+ fingerprintAttrs.reset(new SdpFingerprintAttributeList);
+ }
+
+ // Don't assert on unknown algorithm, just skip
+ fingerprintAttrs->PushEntry(algorithmToken, fingerprint, false);
+ }
+
+ if (fingerprintAttrs) {
+ SetAttribute(fingerprintAttrs.release());
+ }
+
+ return true;
+}
+
+void SipccSdpAttributeList::LoadCandidate(sdp_t* sdp, uint16_t level) {
+ char* value;
+ auto candidates =
+ MakeUnique<SdpMultiStringAttribute>(SdpAttribute::kCandidateAttribute);
+
+ for (uint16_t i = 1; i < UINT16_MAX; ++i) {
+ sdp_result_e result = sdp_attr_get_ice_attribute(
+ sdp, level, 0, SDP_ATTR_ICE_CANDIDATE, i, &value);
+
+ if (result != SDP_SUCCESS) {
+ break;
+ }
+
+ candidates->mValues.push_back(value);
+ }
+
+ if (!candidates->mValues.empty()) {
+ SetAttribute(candidates.release());
+ }
+}
+
+bool SipccSdpAttributeList::LoadSctpmap(sdp_t* sdp, uint16_t level,
+ InternalResults& results) {
+ auto sctpmap = MakeUnique<SdpSctpmapAttributeList>();
+ for (uint16_t i = 0; i < UINT16_MAX; ++i) {
+ sdp_attr_t* attr = sdp_find_attr(sdp, level, 0, SDP_ATTR_SCTPMAP, i + 1);
+
+ if (!attr) {
+ break;
+ }
+
+ // Yeah, this is a little weird, but for now we'll just store this as a
+ // payload type.
+ uint16_t payloadType = attr->attr.sctpmap.port;
+ uint16_t streams = attr->attr.sctpmap.streams;
+ const char* name = attr->attr.sctpmap.protocol;
+
+ std::ostringstream osPayloadType;
+ osPayloadType << payloadType;
+ sctpmap->PushEntry(osPayloadType.str(), name, streams);
+ }
+
+ if (!sctpmap->mSctpmaps.empty()) {
+ SetAttribute(sctpmap.release());
+ }
+
+ return true;
+}
+
+SdpRtpmapAttributeList::CodecType SipccSdpAttributeList::GetCodecType(
+ rtp_ptype type) {
+ switch (type) {
+ case RTP_PCMU:
+ return SdpRtpmapAttributeList::kPCMU;
+ case RTP_PCMA:
+ return SdpRtpmapAttributeList::kPCMA;
+ case RTP_G722:
+ return SdpRtpmapAttributeList::kG722;
+ case RTP_H264_P0:
+ case RTP_H264_P1:
+ return SdpRtpmapAttributeList::kH264;
+ case RTP_OPUS:
+ return SdpRtpmapAttributeList::kOpus;
+ case RTP_VP8:
+ return SdpRtpmapAttributeList::kVP8;
+ case RTP_VP9:
+ return SdpRtpmapAttributeList::kVP9;
+ case RTP_RED:
+ return SdpRtpmapAttributeList::kRed;
+ case RTP_ULPFEC:
+ return SdpRtpmapAttributeList::kUlpfec;
+ case RTP_RTX:
+ return SdpRtpmapAttributeList::kRtx;
+ case RTP_TELEPHONE_EVENT:
+ return SdpRtpmapAttributeList::kTelephoneEvent;
+ case RTP_NONE:
+ // Happens when sipcc doesn't know how to translate to the enum
+ case RTP_CELP:
+ case RTP_G726:
+ case RTP_GSM:
+ case RTP_G723:
+ case RTP_DVI4:
+ case RTP_DVI4_II:
+ case RTP_LPC:
+ case RTP_G728:
+ case RTP_G729:
+ case RTP_JPEG:
+ case RTP_NV:
+ case RTP_H261:
+ case RTP_L16:
+ case RTP_H263:
+ case RTP_ILBC:
+ case RTP_I420:
+ return SdpRtpmapAttributeList::kOtherCodec;
+ }
+ MOZ_CRASH("Invalid codec type from sipcc. Probably corruption.");
+}
+
+bool SipccSdpAttributeList::LoadRtpmap(sdp_t* sdp, uint16_t level,
+ InternalResults& results) {
+ auto rtpmap = MakeUnique<SdpRtpmapAttributeList>();
+ uint16_t count;
+ sdp_result_e result =
+ sdp_attr_num_instances(sdp, level, 0, SDP_ATTR_RTPMAP, &count);
+ if (result != SDP_SUCCESS) {
+ MOZ_ASSERT(false, "Unable to get rtpmap size");
+ results.AddParseError(sdp_get_media_line_number(sdp, level),
+ "Unable to get rtpmap size");
+ return false;
+ }
+ for (uint16_t i = 0; i < count; ++i) {
+ uint16_t pt = sdp_attr_get_rtpmap_payload_type(sdp, level, 0, i + 1);
+ const char* ccName = sdp_attr_get_rtpmap_encname(sdp, level, 0, i + 1);
+
+ if (!ccName) {
+ // Probably no rtpmap attribute for a pt in an m-line
+ results.AddParseError(sdp_get_media_line_number(sdp, level),
+ "No rtpmap attribute for payload type");
+ continue;
+ }
+
+ std::string name(ccName);
+
+ SdpRtpmapAttributeList::CodecType codec =
+ GetCodecType(sdp_get_known_payload_type(sdp, level, pt));
+
+ uint32_t clock = sdp_attr_get_rtpmap_clockrate(sdp, level, 0, i + 1);
+ uint16_t channels = 0;
+
+ // sipcc gives us a channels value of "1" for video
+ if (sdp_get_media_type(sdp, level) == SDP_MEDIA_AUDIO) {
+ channels = sdp_attr_get_rtpmap_num_chan(sdp, level, 0, i + 1);
+ }
+
+ std::ostringstream osPayloadType;
+ osPayloadType << pt;
+ rtpmap->PushEntry(osPayloadType.str(), codec, name, clock, channels);
+ }
+
+ if (!rtpmap->mRtpmaps.empty()) {
+ SetAttribute(rtpmap.release());
+ }
+
+ return true;
+}
+
+void SipccSdpAttributeList::LoadSetup(sdp_t* sdp, uint16_t level) {
+ sdp_setup_type_e setupType;
+ auto sdpres = sdp_attr_get_setup_attribute(sdp, level, 0, 1, &setupType);
+
+ if (sdpres != SDP_SUCCESS) {
+ return;
+ }
+
+ switch (setupType) {
+ case SDP_SETUP_ACTIVE:
+ SetAttribute(new SdpSetupAttribute(SdpSetupAttribute::kActive));
+ return;
+ case SDP_SETUP_PASSIVE:
+ SetAttribute(new SdpSetupAttribute(SdpSetupAttribute::kPassive));
+ return;
+ case SDP_SETUP_ACTPASS:
+ SetAttribute(new SdpSetupAttribute(SdpSetupAttribute::kActpass));
+ return;
+ case SDP_SETUP_HOLDCONN:
+ SetAttribute(new SdpSetupAttribute(SdpSetupAttribute::kHoldconn));
+ return;
+ case SDP_SETUP_UNKNOWN:
+ return;
+ case SDP_SETUP_NOT_FOUND:
+ case SDP_MAX_SETUP:
+ // There is no code that will set these.
+ // Fall through to MOZ_CRASH() below.
+ {}
+ }
+
+ MOZ_CRASH("Invalid setup type from sipcc. This is probably corruption.");
+}
+
+void SipccSdpAttributeList::LoadSsrc(sdp_t* sdp, uint16_t level) {
+ auto ssrcs = MakeUnique<SdpSsrcAttributeList>();
+
+ for (uint16_t i = 1; i < UINT16_MAX; ++i) {
+ sdp_attr_t* attr = sdp_find_attr(sdp, level, 0, SDP_ATTR_SSRC, i);
+
+ if (!attr) {
+ break;
+ }
+
+ sdp_ssrc_t* ssrc = &(attr->attr.ssrc);
+ ssrcs->PushEntry(ssrc->ssrc, ssrc->attribute);
+ }
+
+ if (!ssrcs->mSsrcs.empty()) {
+ SetAttribute(ssrcs.release());
+ }
+}
+
+void SipccSdpAttributeList::LoadSsrcGroup(sdp_t* sdp, uint16_t level) {
+ auto ssrcGroups = MakeUnique<SdpSsrcGroupAttributeList>();
+
+ for (uint16_t i = 1; i < UINT16_MAX; ++i) {
+ sdp_attr_t* attr = sdp_find_attr(sdp, level, 0, SDP_ATTR_SSRC_GROUP, i);
+
+ if (!attr) {
+ break;
+ }
+
+ sdp_ssrc_group_t* ssrc_group = &(attr->attr.ssrc_group);
+
+ SdpSsrcGroupAttributeList::Semantics semantic;
+ switch (ssrc_group->semantic) {
+ case SDP_SSRC_GROUP_ATTR_FEC:
+ semantic = SdpSsrcGroupAttributeList::kFec;
+ break;
+ case SDP_SSRC_GROUP_ATTR_FID:
+ semantic = SdpSsrcGroupAttributeList::kFid;
+ break;
+ case SDP_SSRC_GROUP_ATTR_FECFR:
+ semantic = SdpSsrcGroupAttributeList::kFecFr;
+ break;
+ case SDP_SSRC_GROUP_ATTR_DUP:
+ semantic = SdpSsrcGroupAttributeList::kDup;
+ break;
+ case SDP_SSRC_GROUP_ATTR_SIM:
+ semantic = SdpSsrcGroupAttributeList::kSim;
+ break;
+ case SDP_MAX_SSRC_GROUP_ATTR_VAL:
+ continue;
+ case SDP_SSRC_GROUP_ATTR_UNSUPPORTED:
+ continue;
+ }
+
+ std::vector<uint32_t> ssrcs;
+ ssrcs.reserve(ssrc_group->num_ssrcs);
+ for (int i = 0; i < ssrc_group->num_ssrcs; ++i) {
+ ssrcs.push_back(ssrc_group->ssrcs[i]);
+ }
+
+ ssrcGroups->PushEntry(semantic, ssrcs);
+ }
+
+ if (!ssrcGroups->mSsrcGroups.empty()) {
+ SetAttribute(ssrcGroups.release());
+ }
+}
+
+bool SipccSdpAttributeList::LoadImageattr(sdp_t* sdp, uint16_t level,
+ InternalResults& results) {
+ UniquePtr<SdpImageattrAttributeList> imageattrs(
+ new SdpImageattrAttributeList);
+
+ for (uint16_t i = 1; i < UINT16_MAX; ++i) {
+ const char* imageattrRaw =
+ sdp_attr_get_simple_string(sdp, SDP_ATTR_IMAGEATTR, level, 0, i);
+ if (!imageattrRaw) {
+ break;
+ }
+
+ std::string error;
+ size_t errorPos;
+ if (!imageattrs->PushEntry(imageattrRaw, &error, &errorPos)) {
+ std::ostringstream fullError;
+ fullError << error << " at column " << errorPos;
+ results.AddParseError(
+ sdp_attr_line_number(sdp, SDP_ATTR_IMAGEATTR, level, 0, i),
+ fullError.str());
+ return false;
+ }
+ }
+
+ if (!imageattrs->mImageattrs.empty()) {
+ SetAttribute(imageattrs.release());
+ }
+ return true;
+}
+
+bool SipccSdpAttributeList::LoadSimulcast(sdp_t* sdp, uint16_t level,
+ InternalResults& results) {
+ const char* simulcastRaw =
+ sdp_attr_get_simple_string(sdp, SDP_ATTR_SIMULCAST, level, 0, 1);
+ if (!simulcastRaw) {
+ return true;
+ }
+
+ UniquePtr<SdpSimulcastAttribute> simulcast(new SdpSimulcastAttribute);
+
+ std::istringstream is(simulcastRaw);
+ std::string error;
+ if (!simulcast->Parse(is, &error)) {
+ std::ostringstream fullError;
+ fullError << error << " at column " << is.tellg();
+ results.AddParseError(
+ sdp_attr_line_number(sdp, SDP_ATTR_SIMULCAST, level, 0, 1),
+ fullError.str());
+ return false;
+ }
+
+ SetAttribute(simulcast.release());
+ return true;
+}
+
+bool SipccSdpAttributeList::LoadGroups(sdp_t* sdp, uint16_t level,
+ InternalResults& results) {
+ uint16_t attrCount = 0;
+ if (sdp_attr_num_instances(sdp, level, 0, SDP_ATTR_GROUP, &attrCount) !=
+ SDP_SUCCESS) {
+ MOZ_ASSERT(false, "Could not get count of group attributes");
+ results.AddParseError(0, "Could not get count of group attributes");
+ return false;
+ }
+
+ UniquePtr<SdpGroupAttributeList> groups = MakeUnique<SdpGroupAttributeList>();
+ for (uint16_t attr = 1; attr <= attrCount; ++attr) {
+ SdpGroupAttributeList::Semantics semantics;
+ std::vector<std::string> tags;
+
+ switch (sdp_get_group_attr(sdp, level, 0, attr)) {
+ case SDP_GROUP_ATTR_FID:
+ semantics = SdpGroupAttributeList::kFid;
+ break;
+ case SDP_GROUP_ATTR_LS:
+ semantics = SdpGroupAttributeList::kLs;
+ break;
+ case SDP_GROUP_ATTR_ANAT:
+ semantics = SdpGroupAttributeList::kAnat;
+ break;
+ case SDP_GROUP_ATTR_BUNDLE:
+ semantics = SdpGroupAttributeList::kBundle;
+ break;
+ default:
+ continue;
+ }
+
+ uint16_t idCount = sdp_get_group_num_id(sdp, level, 0, attr);
+ for (uint16_t id = 1; id <= idCount; ++id) {
+ const char* idStr = sdp_get_group_id(sdp, level, 0, attr, id);
+ if (!idStr) {
+ std::ostringstream os;
+ os << "bad a=group identifier at " << (attr - 1) << ", " << (id - 1);
+ results.AddParseError(0, os.str());
+ return false;
+ }
+ tags.push_back(std::string(idStr));
+ }
+ groups->PushEntry(semantics, tags);
+ }
+
+ if (!groups->mGroups.empty()) {
+ SetAttribute(groups.release());
+ }
+
+ return true;
+}
+
+bool SipccSdpAttributeList::LoadMsidSemantics(sdp_t* sdp, uint16_t level,
+ InternalResults& results) {
+ auto msidSemantics = MakeUnique<SdpMsidSemanticAttributeList>();
+
+ for (uint16_t i = 1; i < UINT16_MAX; ++i) {
+ sdp_attr_t* attr = sdp_find_attr(sdp, level, 0, SDP_ATTR_MSID_SEMANTIC, i);
+
+ if (!attr) {
+ break;
+ }
+
+ sdp_msid_semantic_t* msid_semantic = &(attr->attr.msid_semantic);
+ std::vector<std::string> msids;
+ for (size_t i = 0; i < SDP_MAX_MEDIA_STREAMS; ++i) {
+ if (!msid_semantic->msids[i]) {
+ break;
+ }
+
+ msids.push_back(msid_semantic->msids[i]);
+ }
+
+ msidSemantics->PushEntry(msid_semantic->semantic, msids);
+ }
+
+ if (!msidSemantics->mMsidSemantics.empty()) {
+ SetAttribute(msidSemantics.release());
+ }
+ return true;
+}
+
+void SipccSdpAttributeList::LoadIdentity(sdp_t* sdp, uint16_t level) {
+ const char* val =
+ sdp_attr_get_long_string(sdp, SDP_ATTR_IDENTITY, level, 0, 1);
+ if (val) {
+ SetAttribute(new SdpStringAttribute(SdpAttribute::kIdentityAttribute,
+ std::string(val)));
+ }
+}
+
+void SipccSdpAttributeList::LoadDtlsMessage(sdp_t* sdp, uint16_t level) {
+ const char* val =
+ sdp_attr_get_long_string(sdp, SDP_ATTR_DTLS_MESSAGE, level, 0, 1);
+ if (val) {
+ // sipcc does not expose parse code for this, so we use a SDParta-provided
+ // parser
+ std::string strval(val);
+ SetAttribute(new SdpDtlsMessageAttribute(strval));
+ }
+}
+
+void SipccSdpAttributeList::LoadFmtp(sdp_t* sdp, uint16_t level) {
+ auto fmtps = MakeUnique<SdpFmtpAttributeList>();
+
+ for (uint16_t i = 1; i < UINT16_MAX; ++i) {
+ sdp_attr_t* attr = sdp_find_attr(sdp, level, 0, SDP_ATTR_FMTP, i);
+
+ if (!attr) {
+ break;
+ }
+
+ sdp_fmtp_t* fmtp = &(attr->attr.fmtp);
+
+ // Get the payload type
+ std::stringstream osPayloadType;
+ // payload_num is the number in the fmtp attribute, verbatim
+ osPayloadType << fmtp->payload_num;
+
+ // Get parsed form of parameters, if supported
+ UniquePtr<SdpFmtpAttributeList::Parameters> parameters;
+
+ rtp_ptype codec = sdp_get_known_payload_type(sdp, level, fmtp->payload_num);
+
+ switch (codec) {
+ case RTP_H264_P0:
+ case RTP_H264_P1: {
+ SdpFmtpAttributeList::H264Parameters* h264Parameters(
+ new SdpFmtpAttributeList::H264Parameters);
+
+ sstrncpy(h264Parameters->sprop_parameter_sets, fmtp->parameter_sets,
+ sizeof(h264Parameters->sprop_parameter_sets));
+
+ h264Parameters->level_asymmetry_allowed =
+ !!(fmtp->level_asymmetry_allowed);
+
+ h264Parameters->packetization_mode = fmtp->packetization_mode;
+ sscanf(fmtp->profile_level_id, "%x", &h264Parameters->profile_level_id);
+ h264Parameters->max_mbps = fmtp->max_mbps;
+ h264Parameters->max_fs = fmtp->max_fs;
+ h264Parameters->max_cpb = fmtp->max_cpb;
+ h264Parameters->max_dpb = fmtp->max_dpb;
+ h264Parameters->max_br = fmtp->max_br;
+
+ parameters.reset(h264Parameters);
+ } break;
+ case RTP_VP9: {
+ SdpFmtpAttributeList::VP8Parameters* vp9Parameters(
+ new SdpFmtpAttributeList::VP8Parameters(
+ SdpRtpmapAttributeList::kVP9));
+
+ vp9Parameters->max_fs = fmtp->max_fs;
+ vp9Parameters->max_fr = fmtp->max_fr;
+
+ parameters.reset(vp9Parameters);
+ } break;
+ case RTP_VP8: {
+ SdpFmtpAttributeList::VP8Parameters* vp8Parameters(
+ new SdpFmtpAttributeList::VP8Parameters(
+ SdpRtpmapAttributeList::kVP8));
+
+ vp8Parameters->max_fs = fmtp->max_fs;
+ vp8Parameters->max_fr = fmtp->max_fr;
+
+ parameters.reset(vp8Parameters);
+ } break;
+ case RTP_RED: {
+ SdpFmtpAttributeList::RedParameters* redParameters(
+ new SdpFmtpAttributeList::RedParameters);
+ for (int i = 0; i < SDP_FMTP_MAX_REDUNDANT_ENCODINGS &&
+ fmtp->redundant_encodings[i];
+ ++i) {
+ redParameters->encodings.push_back(fmtp->redundant_encodings[i]);
+ }
+
+ parameters.reset(redParameters);
+ } break;
+ case RTP_OPUS: {
+ SdpFmtpAttributeList::OpusParameters* opusParameters(
+ new SdpFmtpAttributeList::OpusParameters);
+ opusParameters->maxplaybackrate = fmtp->maxplaybackrate;
+ opusParameters->stereo = fmtp->stereo;
+ opusParameters->useInBandFec = fmtp->useinbandfec;
+ opusParameters->maxAverageBitrate = fmtp->maxaveragebitrate;
+ opusParameters->useDTX = fmtp->usedtx;
+ parameters.reset(opusParameters);
+ } break;
+ case RTP_TELEPHONE_EVENT: {
+ SdpFmtpAttributeList::TelephoneEventParameters* teParameters(
+ new SdpFmtpAttributeList::TelephoneEventParameters);
+ if (strlen(fmtp->dtmf_tones) > 0) {
+ teParameters->dtmfTones = fmtp->dtmf_tones;
+ }
+ parameters.reset(teParameters);
+ } break;
+ case RTP_RTX: {
+ SdpFmtpAttributeList::RtxParameters* rtxParameters(
+ new SdpFmtpAttributeList::RtxParameters);
+ rtxParameters->apt = fmtp->apt;
+ if (fmtp->has_rtx_time == TRUE) {
+ rtxParameters->rtx_time = Some(fmtp->rtx_time);
+ }
+ parameters.reset(rtxParameters);
+ } break;
+ default: {
+ }
+ }
+
+ if (parameters) {
+ fmtps->PushEntry(osPayloadType.str(), *parameters);
+ }
+ }
+
+ if (!fmtps->mFmtps.empty()) {
+ SetAttribute(fmtps.release());
+ }
+}
+
+void SipccSdpAttributeList::LoadMsids(sdp_t* sdp, uint16_t level,
+ InternalResults& results) {
+ uint16_t attrCount = 0;
+ if (sdp_attr_num_instances(sdp, level, 0, SDP_ATTR_MSID, &attrCount) !=
+ SDP_SUCCESS) {
+ MOZ_ASSERT(false, "Unable to get count of msid attributes");
+ results.AddParseError(0, "Unable to get count of msid attributes");
+ return;
+ }
+ auto msids = MakeUnique<SdpMsidAttributeList>();
+ for (uint16_t i = 1; i <= attrCount; ++i) {
+ uint32_t lineNumber = sdp_attr_line_number(sdp, SDP_ATTR_MSID, level, 0, i);
+
+ const char* identifier = sdp_attr_get_msid_identifier(sdp, level, 0, i);
+ if (!identifier) {
+ results.AddParseError(lineNumber, "msid attribute with bad identity");
+ continue;
+ }
+
+ const char* appdata = sdp_attr_get_msid_appdata(sdp, level, 0, i);
+ if (!appdata) {
+ results.AddParseError(lineNumber, "msid attribute with bad appdata");
+ continue;
+ }
+
+ msids->PushEntry(identifier, appdata);
+ }
+
+ if (!msids->mMsids.empty()) {
+ SetAttribute(msids.release());
+ }
+}
+
+bool SipccSdpAttributeList::LoadRid(sdp_t* sdp, uint16_t level,
+ InternalResults& results) {
+ UniquePtr<SdpRidAttributeList> rids(new SdpRidAttributeList);
+
+ for (uint16_t i = 1; i < UINT16_MAX; ++i) {
+ const char* ridRaw =
+ sdp_attr_get_simple_string(sdp, SDP_ATTR_RID, level, 0, i);
+ if (!ridRaw) {
+ break;
+ }
+
+ std::string error;
+ size_t errorPos;
+ if (!rids->PushEntry(ridRaw, &error, &errorPos)) {
+ std::ostringstream fullError;
+ fullError << error << " at column " << errorPos;
+ results.AddParseError(
+ sdp_attr_line_number(sdp, SDP_ATTR_RID, level, 0, i),
+ fullError.str());
+ return false;
+ }
+ }
+
+ if (!rids->mRids.empty()) {
+ SetAttribute(rids.release());
+ }
+ return true;
+}
+
+void SipccSdpAttributeList::LoadExtmap(sdp_t* sdp, uint16_t level,
+ InternalResults& results) {
+ auto extmaps = MakeUnique<SdpExtmapAttributeList>();
+
+ for (uint16_t i = 1; i < UINT16_MAX; ++i) {
+ sdp_attr_t* attr = sdp_find_attr(sdp, level, 0, SDP_ATTR_EXTMAP, i);
+
+ if (!attr) {
+ break;
+ }
+
+ sdp_extmap_t* extmap = &(attr->attr.extmap);
+
+ SdpDirectionAttribute::Direction dir = SdpDirectionAttribute::kSendrecv;
+
+ if (extmap->media_direction_specified) {
+ ConvertDirection(extmap->media_direction, &dir);
+ }
+
+ extmaps->PushEntry(extmap->id, dir, extmap->media_direction_specified,
+ extmap->uri, extmap->extension_attributes);
+ }
+
+ if (!extmaps->mExtmaps.empty()) {
+ if (!AtSessionLevel() &&
+ mSessionLevel->HasAttribute(SdpAttribute::kExtmapAttribute)) {
+ uint32_t lineNumber =
+ sdp_attr_line_number(sdp, SDP_ATTR_EXTMAP, level, 0, 1);
+ results.AddParseError(
+ lineNumber, "extmap attributes in both session and media level");
+ }
+ SetAttribute(extmaps.release());
+ }
+}
+
+void SipccSdpAttributeList::LoadRtcpFb(sdp_t* sdp, uint16_t level,
+ InternalResults& results) {
+ auto rtcpfbs = MakeUnique<SdpRtcpFbAttributeList>();
+
+ for (uint16_t i = 1; i < UINT16_MAX; ++i) {
+ sdp_attr_t* attr = sdp_find_attr(sdp, level, 0, SDP_ATTR_RTCP_FB, i);
+
+ if (!attr) {
+ break;
+ }
+
+ sdp_fmtp_fb_t* rtcpfb = &attr->attr.rtcp_fb;
+
+ SdpRtcpFbAttributeList::Type type;
+ std::string parameter;
+
+ // Set type and parameter
+ switch (rtcpfb->feedback_type) {
+ case SDP_RTCP_FB_ACK:
+ type = SdpRtcpFbAttributeList::kAck;
+ switch (rtcpfb->param.ack) {
+ // TODO: sipcc doesn't seem to support ack with no following token.
+ // Issue 189.
+ case SDP_RTCP_FB_ACK_RPSI:
+ parameter = SdpRtcpFbAttributeList::rpsi;
+ break;
+ case SDP_RTCP_FB_ACK_APP:
+ parameter = SdpRtcpFbAttributeList::app;
+ break;
+ default:
+ // Type we don't care about, ignore.
+ continue;
+ }
+ break;
+ case SDP_RTCP_FB_CCM:
+ type = SdpRtcpFbAttributeList::kCcm;
+ switch (rtcpfb->param.ccm) {
+ case SDP_RTCP_FB_CCM_FIR:
+ parameter = SdpRtcpFbAttributeList::fir;
+ break;
+ case SDP_RTCP_FB_CCM_TMMBR:
+ parameter = SdpRtcpFbAttributeList::tmmbr;
+ break;
+ case SDP_RTCP_FB_CCM_TSTR:
+ parameter = SdpRtcpFbAttributeList::tstr;
+ break;
+ case SDP_RTCP_FB_CCM_VBCM:
+ parameter = SdpRtcpFbAttributeList::vbcm;
+ break;
+ default:
+ // Type we don't care about, ignore.
+ continue;
+ }
+ break;
+ case SDP_RTCP_FB_NACK:
+ type = SdpRtcpFbAttributeList::kNack;
+ switch (rtcpfb->param.nack) {
+ case SDP_RTCP_FB_NACK_BASIC:
+ break;
+ case SDP_RTCP_FB_NACK_SLI:
+ parameter = SdpRtcpFbAttributeList::sli;
+ break;
+ case SDP_RTCP_FB_NACK_PLI:
+ parameter = SdpRtcpFbAttributeList::pli;
+ break;
+ case SDP_RTCP_FB_NACK_RPSI:
+ parameter = SdpRtcpFbAttributeList::rpsi;
+ break;
+ case SDP_RTCP_FB_NACK_APP:
+ parameter = SdpRtcpFbAttributeList::app;
+ break;
+ default:
+ // Type we don't care about, ignore.
+ continue;
+ }
+ break;
+ case SDP_RTCP_FB_TRR_INT: {
+ type = SdpRtcpFbAttributeList::kTrrInt;
+ std::ostringstream os;
+ os << rtcpfb->param.trr_int;
+ parameter = os.str();
+ } break;
+ case SDP_RTCP_FB_REMB: {
+ type = SdpRtcpFbAttributeList::kRemb;
+ } break;
+ case SDP_RTCP_FB_TRANSPORT_CC: {
+ type = SdpRtcpFbAttributeList::kTransportCC;
+ } break;
+ default:
+ // Type we don't care about, ignore.
+ continue;
+ }
+
+ std::stringstream osPayloadType;
+ if (rtcpfb->payload_num == UINT16_MAX) {
+ osPayloadType << "*";
+ } else {
+ osPayloadType << rtcpfb->payload_num;
+ }
+
+ std::string pt(osPayloadType.str());
+ std::string extra(rtcpfb->extra);
+
+ rtcpfbs->PushEntry(pt, type, parameter, extra);
+ }
+
+ if (!rtcpfbs->mFeedbacks.empty()) {
+ SetAttribute(rtcpfbs.release());
+ }
+}
+
+void SipccSdpAttributeList::LoadRtcp(sdp_t* sdp, uint16_t level,
+ InternalResults& results) {
+ sdp_attr_t* attr = sdp_find_attr(sdp, level, 0, SDP_ATTR_RTCP, 1);
+
+ if (!attr) {
+ return;
+ }
+
+ sdp_rtcp_t* rtcp = &attr->attr.rtcp;
+
+ if (rtcp->nettype != SDP_NT_INTERNET) {
+ return;
+ }
+
+ if (rtcp->addrtype != SDP_AT_IP4 && rtcp->addrtype != SDP_AT_IP6) {
+ return;
+ }
+
+ if (!strlen(rtcp->addr)) {
+ SetAttribute(new SdpRtcpAttribute(rtcp->port));
+ } else {
+ SetAttribute(new SdpRtcpAttribute(
+ rtcp->port, sdp::kInternet,
+ rtcp->addrtype == SDP_AT_IP4 ? sdp::kIPv4 : sdp::kIPv6, rtcp->addr));
+ }
+}
+
+bool SipccSdpAttributeList::Load(sdp_t* sdp, uint16_t level,
+ InternalResults& results) {
+ LoadSimpleStrings(sdp, level, results);
+ LoadSimpleNumbers(sdp, level, results);
+ LoadFlags(sdp, level);
+ LoadDirection(sdp, level, results);
+
+ if (AtSessionLevel()) {
+ if (!LoadGroups(sdp, level, results)) {
+ return false;
+ }
+
+ if (!LoadMsidSemantics(sdp, level, results)) {
+ return false;
+ }
+
+ LoadIdentity(sdp, level);
+ LoadDtlsMessage(sdp, level);
+ } else {
+ sdp_media_e mtype = sdp_get_media_type(sdp, level);
+ if (mtype == SDP_MEDIA_APPLICATION) {
+ LoadSctpmap(sdp, level, results);
+ } else {
+ if (!LoadRtpmap(sdp, level, results)) {
+ return false;
+ }
+ }
+ LoadCandidate(sdp, level);
+ LoadFmtp(sdp, level);
+ LoadMsids(sdp, level, results);
+ LoadRtcpFb(sdp, level, results);
+ LoadRtcp(sdp, level, results);
+ LoadSsrc(sdp, level);
+ LoadSsrcGroup(sdp, level);
+ if (!LoadImageattr(sdp, level, results)) {
+ return false;
+ }
+ if (!LoadSimulcast(sdp, level, results)) {
+ return false;
+ }
+ if (!LoadRid(sdp, level, results)) {
+ return false;
+ }
+ }
+
+ LoadIceAttributes(sdp, level);
+ if (!LoadFingerprint(sdp, level, results)) {
+ return false;
+ }
+ LoadSetup(sdp, level);
+ LoadExtmap(sdp, level, results);
+
+ return true;
+}
+
+bool SipccSdpAttributeList::IsAllowedHere(
+ SdpAttribute::AttributeType type) const {
+ if (AtSessionLevel() && !SdpAttribute::IsAllowedAtSessionLevel(type)) {
+ return false;
+ }
+
+ if (!AtSessionLevel() && !SdpAttribute::IsAllowedAtMediaLevel(type)) {
+ return false;
+ }
+
+ return true;
+}
+
+void SipccSdpAttributeList::WarnAboutMisplacedAttribute(
+ SdpAttribute::AttributeType type, uint32_t lineNumber,
+ InternalResults& results) {
+ std::string warning = SdpAttribute::GetAttributeTypeString(type) +
+ (AtSessionLevel() ? " at session level. Ignoring."
+ : " at media level. Ignoring.");
+ results.AddParseError(lineNumber, warning);
+}
+
+const std::vector<std::string>& SipccSdpAttributeList::GetCandidate() const {
+ if (!HasAttribute(SdpAttribute::kCandidateAttribute)) {
+ MOZ_CRASH();
+ }
+
+ return static_cast<const SdpMultiStringAttribute*>(
+ GetAttribute(SdpAttribute::kCandidateAttribute))
+ ->mValues;
+}
+
+const SdpConnectionAttribute& SipccSdpAttributeList::GetConnection() const {
+ if (!HasAttribute(SdpAttribute::kConnectionAttribute)) {
+ MOZ_CRASH();
+ }
+
+ return *static_cast<const SdpConnectionAttribute*>(
+ GetAttribute(SdpAttribute::kConnectionAttribute));
+}
+
+SdpDirectionAttribute::Direction SipccSdpAttributeList::GetDirection() const {
+ if (!HasAttribute(SdpAttribute::kDirectionAttribute)) {
+ MOZ_CRASH();
+ }
+
+ const SdpAttribute* attr = GetAttribute(SdpAttribute::kDirectionAttribute);
+ return static_cast<const SdpDirectionAttribute*>(attr)->mValue;
+}
+
+const SdpDtlsMessageAttribute& SipccSdpAttributeList::GetDtlsMessage() const {
+ if (!HasAttribute(SdpAttribute::kDtlsMessageAttribute)) {
+ MOZ_CRASH();
+ }
+ const SdpAttribute* attr = GetAttribute(SdpAttribute::kDtlsMessageAttribute);
+ return *static_cast<const SdpDtlsMessageAttribute*>(attr);
+}
+
+const SdpExtmapAttributeList& SipccSdpAttributeList::GetExtmap() const {
+ if (!HasAttribute(SdpAttribute::kExtmapAttribute)) {
+ MOZ_CRASH();
+ }
+
+ return *static_cast<const SdpExtmapAttributeList*>(
+ GetAttribute(SdpAttribute::kExtmapAttribute));
+}
+
+const SdpFingerprintAttributeList& SipccSdpAttributeList::GetFingerprint()
+ const {
+ if (!HasAttribute(SdpAttribute::kFingerprintAttribute)) {
+ MOZ_CRASH();
+ }
+ const SdpAttribute* attr = GetAttribute(SdpAttribute::kFingerprintAttribute);
+ return *static_cast<const SdpFingerprintAttributeList*>(attr);
+}
+
+const SdpFmtpAttributeList& SipccSdpAttributeList::GetFmtp() const {
+ if (!HasAttribute(SdpAttribute::kFmtpAttribute)) {
+ MOZ_CRASH();
+ }
+
+ return *static_cast<const SdpFmtpAttributeList*>(
+ GetAttribute(SdpAttribute::kFmtpAttribute));
+}
+
+const SdpGroupAttributeList& SipccSdpAttributeList::GetGroup() const {
+ if (!HasAttribute(SdpAttribute::kGroupAttribute)) {
+ MOZ_CRASH();
+ }
+
+ return *static_cast<const SdpGroupAttributeList*>(
+ GetAttribute(SdpAttribute::kGroupAttribute));
+}
+
+const SdpOptionsAttribute& SipccSdpAttributeList::GetIceOptions() const {
+ if (!HasAttribute(SdpAttribute::kIceOptionsAttribute)) {
+ MOZ_CRASH();
+ }
+
+ const SdpAttribute* attr = GetAttribute(SdpAttribute::kIceOptionsAttribute);
+ return *static_cast<const SdpOptionsAttribute*>(attr);
+}
+
+const std::string& SipccSdpAttributeList::GetIcePwd() const {
+ if (!HasAttribute(SdpAttribute::kIcePwdAttribute)) {
+ return kEmptyString;
+ }
+ const SdpAttribute* attr = GetAttribute(SdpAttribute::kIcePwdAttribute);
+ return static_cast<const SdpStringAttribute*>(attr)->mValue;
+}
+
+const std::string& SipccSdpAttributeList::GetIceUfrag() const {
+ if (!HasAttribute(SdpAttribute::kIceUfragAttribute)) {
+ return kEmptyString;
+ }
+ const SdpAttribute* attr = GetAttribute(SdpAttribute::kIceUfragAttribute);
+ return static_cast<const SdpStringAttribute*>(attr)->mValue;
+}
+
+const std::string& SipccSdpAttributeList::GetIdentity() const {
+ if (!HasAttribute(SdpAttribute::kIdentityAttribute)) {
+ return kEmptyString;
+ }
+ const SdpAttribute* attr = GetAttribute(SdpAttribute::kIdentityAttribute);
+ return static_cast<const SdpStringAttribute*>(attr)->mValue;
+}
+
+const SdpImageattrAttributeList& SipccSdpAttributeList::GetImageattr() const {
+ if (!HasAttribute(SdpAttribute::kImageattrAttribute)) {
+ MOZ_CRASH();
+ }
+ const SdpAttribute* attr = GetAttribute(SdpAttribute::kImageattrAttribute);
+ return *static_cast<const SdpImageattrAttributeList*>(attr);
+}
+
+const SdpSimulcastAttribute& SipccSdpAttributeList::GetSimulcast() const {
+ if (!HasAttribute(SdpAttribute::kSimulcastAttribute)) {
+ MOZ_CRASH();
+ }
+ const SdpAttribute* attr = GetAttribute(SdpAttribute::kSimulcastAttribute);
+ return *static_cast<const SdpSimulcastAttribute*>(attr);
+}
+
+const std::string& SipccSdpAttributeList::GetLabel() const {
+ if (!HasAttribute(SdpAttribute::kLabelAttribute)) {
+ return kEmptyString;
+ }
+ const SdpAttribute* attr = GetAttribute(SdpAttribute::kLabelAttribute);
+ return static_cast<const SdpStringAttribute*>(attr)->mValue;
+}
+
+uint32_t SipccSdpAttributeList::GetMaxptime() const {
+ if (!HasAttribute(SdpAttribute::kMaxptimeAttribute)) {
+ MOZ_CRASH();
+ }
+ const SdpAttribute* attr = GetAttribute(SdpAttribute::kMaxptimeAttribute);
+ return static_cast<const SdpNumberAttribute*>(attr)->mValue;
+}
+
+const std::string& SipccSdpAttributeList::GetMid() const {
+ if (!HasAttribute(SdpAttribute::kMidAttribute)) {
+ return kEmptyString;
+ }
+ const SdpAttribute* attr = GetAttribute(SdpAttribute::kMidAttribute);
+ return static_cast<const SdpStringAttribute*>(attr)->mValue;
+}
+
+const SdpMsidAttributeList& SipccSdpAttributeList::GetMsid() const {
+ if (!HasAttribute(SdpAttribute::kMsidAttribute)) {
+ MOZ_CRASH();
+ }
+ const SdpAttribute* attr = GetAttribute(SdpAttribute::kMsidAttribute);
+ return *static_cast<const SdpMsidAttributeList*>(attr);
+}
+
+const SdpMsidSemanticAttributeList& SipccSdpAttributeList::GetMsidSemantic()
+ const {
+ if (!HasAttribute(SdpAttribute::kMsidSemanticAttribute)) {
+ MOZ_CRASH();
+ }
+ const SdpAttribute* attr = GetAttribute(SdpAttribute::kMsidSemanticAttribute);
+ return *static_cast<const SdpMsidSemanticAttributeList*>(attr);
+}
+
+const SdpRidAttributeList& SipccSdpAttributeList::GetRid() const {
+ if (!HasAttribute(SdpAttribute::kRidAttribute)) {
+ MOZ_CRASH();
+ }
+ const SdpAttribute* attr = GetAttribute(SdpAttribute::kRidAttribute);
+ return *static_cast<const SdpRidAttributeList*>(attr);
+}
+
+uint32_t SipccSdpAttributeList::GetPtime() const {
+ if (!HasAttribute(SdpAttribute::kPtimeAttribute)) {
+ MOZ_CRASH();
+ }
+ const SdpAttribute* attr = GetAttribute(SdpAttribute::kPtimeAttribute);
+ return static_cast<const SdpNumberAttribute*>(attr)->mValue;
+}
+
+const SdpRtcpAttribute& SipccSdpAttributeList::GetRtcp() const {
+ if (!HasAttribute(SdpAttribute::kRtcpAttribute)) {
+ MOZ_CRASH();
+ }
+ const SdpAttribute* attr = GetAttribute(SdpAttribute::kRtcpAttribute);
+ return *static_cast<const SdpRtcpAttribute*>(attr);
+}
+
+const SdpRtcpFbAttributeList& SipccSdpAttributeList::GetRtcpFb() const {
+ if (!HasAttribute(SdpAttribute::kRtcpFbAttribute)) {
+ MOZ_CRASH();
+ }
+ const SdpAttribute* attr = GetAttribute(SdpAttribute::kRtcpFbAttribute);
+ return *static_cast<const SdpRtcpFbAttributeList*>(attr);
+}
+
+const SdpRemoteCandidatesAttribute& SipccSdpAttributeList::GetRemoteCandidates()
+ const {
+ MOZ_CRASH("Not yet implemented");
+}
+
+const SdpRtpmapAttributeList& SipccSdpAttributeList::GetRtpmap() const {
+ if (!HasAttribute(SdpAttribute::kRtpmapAttribute)) {
+ MOZ_CRASH();
+ }
+ const SdpAttribute* attr = GetAttribute(SdpAttribute::kRtpmapAttribute);
+ return *static_cast<const SdpRtpmapAttributeList*>(attr);
+}
+
+const SdpSctpmapAttributeList& SipccSdpAttributeList::GetSctpmap() const {
+ if (!HasAttribute(SdpAttribute::kSctpmapAttribute)) {
+ MOZ_CRASH();
+ }
+ const SdpAttribute* attr = GetAttribute(SdpAttribute::kSctpmapAttribute);
+ return *static_cast<const SdpSctpmapAttributeList*>(attr);
+}
+
+uint32_t SipccSdpAttributeList::GetSctpPort() const {
+ if (!HasAttribute(SdpAttribute::kSctpPortAttribute)) {
+ MOZ_CRASH();
+ }
+
+ const SdpAttribute* attr = GetAttribute(SdpAttribute::kSctpPortAttribute);
+ return static_cast<const SdpNumberAttribute*>(attr)->mValue;
+}
+
+uint32_t SipccSdpAttributeList::GetMaxMessageSize() const {
+ if (!HasAttribute(SdpAttribute::kMaxMessageSizeAttribute)) {
+ MOZ_CRASH();
+ }
+
+ const SdpAttribute* attr =
+ GetAttribute(SdpAttribute::kMaxMessageSizeAttribute);
+ return static_cast<const SdpNumberAttribute*>(attr)->mValue;
+}
+
+const SdpSetupAttribute& SipccSdpAttributeList::GetSetup() const {
+ if (!HasAttribute(SdpAttribute::kSetupAttribute)) {
+ MOZ_CRASH();
+ }
+ const SdpAttribute* attr = GetAttribute(SdpAttribute::kSetupAttribute);
+ return *static_cast<const SdpSetupAttribute*>(attr);
+}
+
+const SdpSsrcAttributeList& SipccSdpAttributeList::GetSsrc() const {
+ if (!HasAttribute(SdpAttribute::kSsrcAttribute)) {
+ MOZ_CRASH();
+ }
+ const SdpAttribute* attr = GetAttribute(SdpAttribute::kSsrcAttribute);
+ return *static_cast<const SdpSsrcAttributeList*>(attr);
+}
+
+const SdpSsrcGroupAttributeList& SipccSdpAttributeList::GetSsrcGroup() const {
+ if (!HasAttribute(SdpAttribute::kSsrcGroupAttribute)) {
+ MOZ_CRASH();
+ }
+ const SdpAttribute* attr = GetAttribute(SdpAttribute::kSsrcGroupAttribute);
+ return *static_cast<const SdpSsrcGroupAttributeList*>(attr);
+}
+
+void SipccSdpAttributeList::Serialize(std::ostream& os) const {
+ for (size_t i = 0; i < kNumAttributeTypes; ++i) {
+ if (mAttributes[i]) {
+ os << *mAttributes[i];
+ }
+ }
+}
+
+} // namespace mozilla
diff --git a/dom/media/webrtc/sdp/SipccSdpAttributeList.h b/dom/media/webrtc/sdp/SipccSdpAttributeList.h
new file mode 100644
index 0000000000..4499e84bb9
--- /dev/null
+++ b/dom/media/webrtc/sdp/SipccSdpAttributeList.h
@@ -0,0 +1,145 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _SIPCCSDPATTRIBUTELIST_H_
+#define _SIPCCSDPATTRIBUTELIST_H_
+
+#include "sdp/SdpParser.h"
+#include "sdp/SdpAttributeList.h"
+
+extern "C" {
+#include "sipcc_sdp.h"
+}
+
+namespace mozilla {
+
+class SipccSdp;
+class SipccSdpMediaSection;
+
+class SipccSdpAttributeList : public SdpAttributeList {
+ friend class SipccSdpMediaSection;
+ friend class SipccSdp;
+
+ public:
+ // Make sure we don't hide the default arg thunks
+ using SdpAttributeList::GetAttribute;
+ using SdpAttributeList::HasAttribute;
+
+ virtual bool HasAttribute(AttributeType type,
+ bool sessionFallback) const override;
+ virtual const SdpAttribute* GetAttribute(AttributeType type,
+ bool sessionFallback) const override;
+ virtual void SetAttribute(SdpAttribute* attr) override;
+ virtual void RemoveAttribute(AttributeType type) override;
+ virtual void Clear() override;
+ virtual uint32_t Count() const override;
+
+ virtual const SdpConnectionAttribute& GetConnection() const override;
+ virtual const SdpFingerprintAttributeList& GetFingerprint() const override;
+ virtual const SdpGroupAttributeList& GetGroup() const override;
+ virtual const SdpOptionsAttribute& GetIceOptions() const override;
+ virtual const SdpRtcpAttribute& GetRtcp() const override;
+ virtual const SdpRemoteCandidatesAttribute& GetRemoteCandidates()
+ const override;
+ virtual const SdpSetupAttribute& GetSetup() const override;
+ virtual const SdpSsrcAttributeList& GetSsrc() const override;
+ virtual const SdpSsrcGroupAttributeList& GetSsrcGroup() const override;
+ virtual const SdpDtlsMessageAttribute& GetDtlsMessage() const override;
+
+ // These attributes can appear multiple times, so the returned
+ // classes actually represent a collection of values.
+ virtual const std::vector<std::string>& GetCandidate() const override;
+ virtual const SdpExtmapAttributeList& GetExtmap() const override;
+ virtual const SdpFmtpAttributeList& GetFmtp() const override;
+ virtual const SdpImageattrAttributeList& GetImageattr() const override;
+ const SdpSimulcastAttribute& GetSimulcast() const override;
+ virtual const SdpMsidAttributeList& GetMsid() const override;
+ virtual const SdpMsidSemanticAttributeList& GetMsidSemantic() const override;
+ const SdpRidAttributeList& GetRid() const override;
+ virtual const SdpRtcpFbAttributeList& GetRtcpFb() const override;
+ virtual const SdpRtpmapAttributeList& GetRtpmap() const override;
+ virtual const SdpSctpmapAttributeList& GetSctpmap() const override;
+ virtual uint32_t GetSctpPort() const override;
+ virtual uint32_t GetMaxMessageSize() const override;
+
+ // These attributes are effectively simple types, so we'll make life
+ // easy by just returning their value.
+ virtual const std::string& GetIcePwd() const override;
+ virtual const std::string& GetIceUfrag() const override;
+ virtual const std::string& GetIdentity() const override;
+ virtual const std::string& GetLabel() const override;
+ virtual unsigned int GetMaxptime() const override;
+ virtual const std::string& GetMid() const override;
+ virtual unsigned int GetPtime() const override;
+
+ virtual SdpDirectionAttribute::Direction GetDirection() const override;
+
+ virtual void Serialize(std::ostream&) const override;
+
+ virtual ~SipccSdpAttributeList();
+
+ private:
+ static const std::string kEmptyString;
+ static const size_t kNumAttributeTypes = SdpAttribute::kLastAttribute + 1;
+
+ // Pass a session-level attribute list if constructing a media-level one,
+ // otherwise pass nullptr
+ explicit SipccSdpAttributeList(const SipccSdpAttributeList* sessionLevel);
+
+ // Copy c'tor, sort of
+ SipccSdpAttributeList(const SipccSdpAttributeList& aOrig,
+ const SipccSdpAttributeList* sessionLevel);
+
+ using InternalResults = SdpParser::InternalResults;
+
+ bool Load(sdp_t* sdp, uint16_t level, InternalResults& results);
+ void LoadSimpleStrings(sdp_t* sdp, uint16_t level, InternalResults& results);
+ void LoadSimpleString(sdp_t* sdp, uint16_t level, sdp_attr_e attr,
+ AttributeType targetType, InternalResults& results);
+ void LoadSimpleNumbers(sdp_t* sdp, uint16_t level, InternalResults& results);
+ void LoadSimpleNumber(sdp_t* sdp, uint16_t level, sdp_attr_e attr,
+ AttributeType targetType, InternalResults& results);
+ void LoadFlags(sdp_t* sdp, uint16_t level);
+ void LoadDirection(sdp_t* sdp, uint16_t level, InternalResults& results);
+ bool LoadRtpmap(sdp_t* sdp, uint16_t level, InternalResults& results);
+ bool LoadSctpmap(sdp_t* sdp, uint16_t level, InternalResults& results);
+ void LoadIceAttributes(sdp_t* sdp, uint16_t level);
+ bool LoadFingerprint(sdp_t* sdp, uint16_t level, InternalResults& results);
+ void LoadCandidate(sdp_t* sdp, uint16_t level);
+ void LoadSetup(sdp_t* sdp, uint16_t level);
+ void LoadSsrc(sdp_t* sdp, uint16_t level);
+ void LoadSsrcGroup(sdp_t* sdp, uint16_t level);
+ bool LoadImageattr(sdp_t* sdp, uint16_t level, InternalResults& results);
+ bool LoadSimulcast(sdp_t* sdp, uint16_t level, InternalResults& results);
+ bool LoadGroups(sdp_t* sdp, uint16_t level, InternalResults& results);
+ bool LoadMsidSemantics(sdp_t* sdp, uint16_t level, InternalResults& results);
+ void LoadIdentity(sdp_t* sdp, uint16_t level);
+ void LoadDtlsMessage(sdp_t* sdp, uint16_t level);
+ void LoadFmtp(sdp_t* sdp, uint16_t level);
+ void LoadMsids(sdp_t* sdp, uint16_t level, InternalResults& results);
+ bool LoadRid(sdp_t* sdp, uint16_t level, InternalResults& results);
+ void LoadExtmap(sdp_t* sdp, uint16_t level, InternalResults& results);
+ void LoadRtcpFb(sdp_t* sdp, uint16_t level, InternalResults& results);
+ void LoadRtcp(sdp_t* sdp, uint16_t level, InternalResults& results);
+ static SdpRtpmapAttributeList::CodecType GetCodecType(rtp_ptype type);
+
+ bool AtSessionLevel() const { return !mSessionLevel; }
+ bool IsAllowedHere(SdpAttribute::AttributeType type) const;
+ void WarnAboutMisplacedAttribute(SdpAttribute::AttributeType type,
+ uint32_t lineNumber,
+ InternalResults& results);
+
+ const SipccSdpAttributeList* mSessionLevel;
+
+ SdpAttribute* mAttributes[kNumAttributeTypes];
+
+ SipccSdpAttributeList(const SipccSdpAttributeList& orig) = delete;
+ SipccSdpAttributeList& operator=(const SipccSdpAttributeList& rhs) = delete;
+};
+
+} // namespace mozilla
+
+#endif
diff --git a/dom/media/webrtc/sdp/SipccSdpMediaSection.cpp b/dom/media/webrtc/sdp/SipccSdpMediaSection.cpp
new file mode 100644
index 0000000000..4304ca541e
--- /dev/null
+++ b/dom/media/webrtc/sdp/SipccSdpMediaSection.cpp
@@ -0,0 +1,401 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "sdp/SipccSdpMediaSection.h"
+
+#include <ostream>
+#include "sdp/SdpParser.h"
+
+extern "C" {
+#include "sipcc_sdp.h"
+}
+
+#ifdef CRLF
+# undef CRLF
+#endif
+#define CRLF "\r\n"
+
+namespace mozilla {
+
+SipccSdpMediaSection::SipccSdpMediaSection(
+ const SipccSdpMediaSection& aOrig,
+ const SipccSdpAttributeList* sessionLevel)
+ : SdpMediaSection(aOrig),
+ mMediaType(aOrig.mMediaType),
+ mPort(aOrig.mPort),
+ mPortCount(aOrig.mPortCount),
+ mProtocol(aOrig.mProtocol),
+ mFormats(aOrig.mFormats),
+ mConnection(new SdpConnection(*aOrig.mConnection)),
+ mBandwidths(aOrig.mBandwidths),
+ mAttributeList(aOrig.mAttributeList, sessionLevel) {}
+
+unsigned int SipccSdpMediaSection::GetPort() const { return mPort; }
+
+void SipccSdpMediaSection::SetPort(unsigned int port) { mPort = port; }
+
+unsigned int SipccSdpMediaSection::GetPortCount() const { return mPortCount; }
+
+SdpMediaSection::Protocol SipccSdpMediaSection::GetProtocol() const {
+ return mProtocol;
+}
+
+const SdpConnection& SipccSdpMediaSection::GetConnection() const {
+ return *mConnection;
+}
+
+SdpConnection& SipccSdpMediaSection::GetConnection() { return *mConnection; }
+
+uint32_t SipccSdpMediaSection::GetBandwidth(const std::string& type) const {
+ auto found = mBandwidths.find(type);
+ if (found == mBandwidths.end()) {
+ return 0;
+ }
+ return found->second;
+}
+
+const std::vector<std::string>& SipccSdpMediaSection::GetFormats() const {
+ return mFormats;
+}
+
+const SdpAttributeList& SipccSdpMediaSection::GetAttributeList() const {
+ return mAttributeList;
+}
+
+SdpAttributeList& SipccSdpMediaSection::GetAttributeList() {
+ return mAttributeList;
+}
+
+SdpDirectionAttribute SipccSdpMediaSection::GetDirectionAttribute() const {
+ return SdpDirectionAttribute(mAttributeList.GetDirection());
+}
+
+bool SipccSdpMediaSection::Load(sdp_t* sdp, uint16_t level,
+ InternalResults& results) {
+ switch (sdp_get_media_type(sdp, level)) {
+ case SDP_MEDIA_AUDIO:
+ mMediaType = kAudio;
+ break;
+ case SDP_MEDIA_VIDEO:
+ mMediaType = kVideo;
+ break;
+ case SDP_MEDIA_APPLICATION:
+ mMediaType = kApplication;
+ break;
+ case SDP_MEDIA_TEXT:
+ mMediaType = kText;
+ break;
+
+ default:
+ results.AddParseError(sdp_get_media_line_number(sdp, level),
+ "Unsupported media section type");
+ return false;
+ }
+
+ mPort = sdp_get_media_portnum(sdp, level);
+ int32_t pc = sdp_get_media_portcount(sdp, level);
+ if (pc == SDP_INVALID_VALUE) {
+ // SDP_INVALID_VALUE (ie; -2) is used when there is no port count. :(
+ mPortCount = 0;
+ } else if (pc > static_cast<int32_t>(UINT16_MAX) || pc < 0) {
+ results.AddParseError(sdp_get_media_line_number(sdp, level),
+ "Invalid port count");
+ return false;
+ } else {
+ mPortCount = pc;
+ }
+
+ if (!LoadProtocol(sdp, level, results)) {
+ return false;
+ }
+
+ if (!LoadFormats(sdp, level, results)) {
+ return false;
+ }
+
+ if (!mAttributeList.Load(sdp, level, results)) {
+ return false;
+ }
+
+ if (!ValidateSimulcast(sdp, level, results)) {
+ return false;
+ }
+
+ if (!mBandwidths.Load(sdp, level, results)) {
+ return false;
+ }
+
+ return LoadConnection(sdp, level, results);
+}
+
+bool SipccSdpMediaSection::LoadProtocol(sdp_t* sdp, uint16_t level,
+ InternalResults& results) {
+ switch (sdp_get_media_transport(sdp, level)) {
+ case SDP_TRANSPORT_RTPAVP:
+ mProtocol = kRtpAvp;
+ break;
+ case SDP_TRANSPORT_RTPSAVP:
+ mProtocol = kRtpSavp;
+ break;
+ case SDP_TRANSPORT_RTPAVPF:
+ mProtocol = kRtpAvpf;
+ break;
+ case SDP_TRANSPORT_RTPSAVPF:
+ mProtocol = kRtpSavpf;
+ break;
+ case SDP_TRANSPORT_UDPTLSRTPSAVP:
+ mProtocol = kUdpTlsRtpSavp;
+ break;
+ case SDP_TRANSPORT_UDPTLSRTPSAVPF:
+ mProtocol = kUdpTlsRtpSavpf;
+ break;
+ case SDP_TRANSPORT_TCPDTLSRTPSAVP:
+ mProtocol = kTcpDtlsRtpSavp;
+ break;
+ case SDP_TRANSPORT_TCPDTLSRTPSAVPF:
+ mProtocol = kTcpDtlsRtpSavpf;
+ break;
+ case SDP_TRANSPORT_DTLSSCTP:
+ mProtocol = kDtlsSctp;
+ break;
+ case SDP_TRANSPORT_UDPDTLSSCTP:
+ mProtocol = kUdpDtlsSctp;
+ break;
+ case SDP_TRANSPORT_TCPDTLSSCTP:
+ mProtocol = kTcpDtlsSctp;
+ break;
+
+ default:
+ results.AddParseError(sdp_get_media_line_number(sdp, level),
+ "Unsupported media transport type");
+ return false;
+ }
+ return true;
+}
+
+bool SipccSdpMediaSection::LoadFormats(sdp_t* sdp, uint16_t level,
+ InternalResults& results) {
+ sdp_media_e mtype = sdp_get_media_type(sdp, level);
+
+ if (mtype == SDP_MEDIA_APPLICATION) {
+ sdp_transport_e ttype = sdp_get_media_transport(sdp, level);
+ if ((ttype == SDP_TRANSPORT_UDPDTLSSCTP) ||
+ (ttype == SDP_TRANSPORT_TCPDTLSSCTP)) {
+ if (sdp_get_media_sctp_fmt(sdp, level) ==
+ SDP_SCTP_MEDIA_FMT_WEBRTC_DATACHANNEL) {
+ mFormats.push_back("webrtc-datachannel");
+ }
+ } else {
+ uint32_t ptype = sdp_get_media_sctp_port(sdp, level);
+ std::ostringstream osPayloadType;
+ osPayloadType << ptype;
+ mFormats.push_back(osPayloadType.str());
+ }
+ } else if (mtype == SDP_MEDIA_AUDIO || mtype == SDP_MEDIA_VIDEO) {
+ uint16_t count = sdp_get_media_num_payload_types(sdp, level);
+ for (uint16_t i = 0; i < count; ++i) {
+ sdp_payload_ind_e indicator; // we ignore this, which is fine
+ uint32_t ptype =
+ sdp_get_media_payload_type(sdp, level, i + 1, &indicator);
+
+ if (GET_DYN_PAYLOAD_TYPE_VALUE(ptype) > UINT8_MAX) {
+ results.AddParseError(sdp_get_media_line_number(sdp, level),
+ "Format is too large");
+ return false;
+ }
+
+ std::ostringstream osPayloadType;
+ // sipcc stores payload types in a funny way. When sipcc and the SDP it
+ // parsed differ on what payload type number should be used for a given
+ // codec, sipcc's value goes in the lower byte, and the SDP's value in
+ // the upper byte. When they do not differ, only the lower byte is used.
+ // We want what was in the SDP, verbatim.
+ osPayloadType << GET_DYN_PAYLOAD_TYPE_VALUE(ptype);
+ mFormats.push_back(osPayloadType.str());
+ }
+ }
+
+ return true;
+}
+
+bool SipccSdpMediaSection::ValidateSimulcast(sdp_t* sdp, uint16_t level,
+ InternalResults& results) const {
+ if (!GetAttributeList().HasAttribute(SdpAttribute::kSimulcastAttribute)) {
+ return true;
+ }
+
+ const SdpSimulcastAttribute& simulcast(GetAttributeList().GetSimulcast());
+ if (!ValidateSimulcastVersions(sdp, level, simulcast.sendVersions, sdp::kSend,
+ results)) {
+ return false;
+ }
+ if (!ValidateSimulcastVersions(sdp, level, simulcast.recvVersions, sdp::kRecv,
+ results)) {
+ return false;
+ }
+ return true;
+}
+
+bool SipccSdpMediaSection::ValidateSimulcastVersions(
+ sdp_t* sdp, uint16_t level, const SdpSimulcastAttribute::Versions& versions,
+ sdp::Direction direction, InternalResults& results) const {
+ for (const SdpSimulcastAttribute::Version& version : versions) {
+ for (const SdpSimulcastAttribute::Encoding& encoding : version.choices) {
+ const SdpRidAttributeList::Rid* ridAttr = FindRid(encoding.rid);
+ if (!ridAttr || (ridAttr->direction != direction)) {
+ std::ostringstream os;
+ os << "No rid attribute for \'" << encoding.rid << "\'";
+ results.AddParseError(sdp_get_media_line_number(sdp, level), os.str());
+ results.AddParseError(sdp_get_media_line_number(sdp, level), os.str());
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+bool SipccSdpMediaSection::LoadConnection(sdp_t* sdp, uint16_t level,
+ InternalResults& results) {
+ if (!sdp_connection_valid(sdp, level)) {
+ level = SDP_SESSION_LEVEL;
+ if (!sdp_connection_valid(sdp, level)) {
+ results.AddParseError(sdp_get_media_line_number(sdp, level),
+ "Missing c= line");
+ return false;
+ }
+ }
+
+ sdp_nettype_e type = sdp_get_conn_nettype(sdp, level);
+ if (type != SDP_NT_INTERNET) {
+ results.AddParseError(sdp_get_media_line_number(sdp, level),
+ "Unsupported network type");
+ return false;
+ }
+
+ sdp::AddrType addrType;
+ switch (sdp_get_conn_addrtype(sdp, level)) {
+ case SDP_AT_IP4:
+ addrType = sdp::kIPv4;
+ break;
+ case SDP_AT_IP6:
+ addrType = sdp::kIPv6;
+ break;
+ default:
+ results.AddParseError(sdp_get_media_line_number(sdp, level),
+ "Unsupported address type");
+ return false;
+ }
+
+ std::string address = sdp_get_conn_address(sdp, level);
+ int16_t ttl = static_cast<uint16_t>(sdp_get_mcast_ttl(sdp, level));
+ if (ttl < 0) {
+ ttl = 0;
+ }
+ int32_t numAddr =
+ static_cast<uint32_t>(sdp_get_mcast_num_of_addresses(sdp, level));
+ if (numAddr < 0) {
+ numAddr = 0;
+ }
+ mConnection = MakeUnique<SdpConnection>(addrType, address, ttl, numAddr);
+ return true;
+}
+
+void SipccSdpMediaSection::AddCodec(const std::string& pt,
+ const std::string& name, uint32_t clockrate,
+ uint16_t channels) {
+ mFormats.push_back(pt);
+
+ SdpRtpmapAttributeList* rtpmap = new SdpRtpmapAttributeList();
+ if (mAttributeList.HasAttribute(SdpAttribute::kRtpmapAttribute)) {
+ const SdpRtpmapAttributeList& old = mAttributeList.GetRtpmap();
+ for (auto it = old.mRtpmaps.begin(); it != old.mRtpmaps.end(); ++it) {
+ rtpmap->mRtpmaps.push_back(*it);
+ }
+ }
+ SdpRtpmapAttributeList::CodecType codec = SdpRtpmapAttributeList::kOtherCodec;
+ if (name == "opus") {
+ codec = SdpRtpmapAttributeList::kOpus;
+ } else if (name == "G722") {
+ codec = SdpRtpmapAttributeList::kG722;
+ } else if (name == "PCMU") {
+ codec = SdpRtpmapAttributeList::kPCMU;
+ } else if (name == "PCMA") {
+ codec = SdpRtpmapAttributeList::kPCMA;
+ } else if (name == "VP8") {
+ codec = SdpRtpmapAttributeList::kVP8;
+ } else if (name == "VP9") {
+ codec = SdpRtpmapAttributeList::kVP9;
+ } else if (name == "H264") {
+ codec = SdpRtpmapAttributeList::kH264;
+ }
+
+ rtpmap->PushEntry(pt, codec, name, clockrate, channels);
+ mAttributeList.SetAttribute(rtpmap);
+}
+
+void SipccSdpMediaSection::ClearCodecs() {
+ mFormats.clear();
+ mAttributeList.RemoveAttribute(SdpAttribute::kRtpmapAttribute);
+ mAttributeList.RemoveAttribute(SdpAttribute::kFmtpAttribute);
+ mAttributeList.RemoveAttribute(SdpAttribute::kSctpmapAttribute);
+ mAttributeList.RemoveAttribute(SdpAttribute::kRtcpFbAttribute);
+}
+
+void SipccSdpMediaSection::AddDataChannel(const std::string& name,
+ uint16_t port, uint16_t streams,
+ uint32_t message_size) {
+ // Only one allowed, for now. This may change as the specs (and deployments)
+ // evolve.
+ mFormats.clear();
+ if ((mProtocol == kUdpDtlsSctp) || (mProtocol == kTcpDtlsSctp)) {
+ // new data channel format according to draft 21
+ mFormats.push_back(name);
+ mAttributeList.SetAttribute(
+ new SdpNumberAttribute(SdpAttribute::kSctpPortAttribute, port));
+ if (message_size) {
+ mAttributeList.SetAttribute(new SdpNumberAttribute(
+ SdpAttribute::kMaxMessageSizeAttribute, message_size));
+ }
+ } else {
+ // old data channels format according to draft 05
+ std::string port_str = std::to_string(port);
+ mFormats.push_back(port_str);
+ SdpSctpmapAttributeList* sctpmap = new SdpSctpmapAttributeList();
+ sctpmap->PushEntry(port_str, name, streams);
+ mAttributeList.SetAttribute(sctpmap);
+ if (message_size) {
+ // This is a workaround to allow detecting Firefox's w/o EOR support
+ mAttributeList.SetAttribute(new SdpNumberAttribute(
+ SdpAttribute::kMaxMessageSizeAttribute, message_size));
+ }
+ }
+}
+
+void SipccSdpMediaSection::Serialize(std::ostream& os) const {
+ os << "m=" << mMediaType << " " << mPort;
+ if (mPortCount) {
+ os << "/" << mPortCount;
+ }
+ os << " " << mProtocol;
+ for (auto i = mFormats.begin(); i != mFormats.end(); ++i) {
+ os << " " << (*i);
+ }
+ os << CRLF;
+
+ // We dont do i=
+
+ if (mConnection) {
+ os << *mConnection;
+ }
+
+ mBandwidths.Serialize(os);
+
+ // We dont do k= because they're evil
+
+ os << mAttributeList;
+}
+
+} // namespace mozilla
diff --git a/dom/media/webrtc/sdp/SipccSdpMediaSection.h b/dom/media/webrtc/sdp/SipccSdpMediaSection.h
new file mode 100644
index 0000000000..c9abb4d33c
--- /dev/null
+++ b/dom/media/webrtc/sdp/SipccSdpMediaSection.h
@@ -0,0 +1,101 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _SIPCCSDPMEDIASECTION_H_
+#define _SIPCCSDPMEDIASECTION_H_
+
+#include "mozilla/Attributes.h"
+#include "mozilla/UniquePtr.h"
+#include "sdp/SdpMediaSection.h"
+#include "sdp/SipccSdpAttributeList.h"
+
+#include <map>
+
+extern "C" {
+#include "sipcc_sdp.h"
+}
+
+namespace mozilla {
+
+class SipccSdp;
+class SdpParser;
+
+using InternalResults = SdpParser::InternalResults;
+
+class SipccSdpBandwidths final : public std::map<std::string, uint32_t> {
+ public:
+ bool Load(sdp_t* sdp, uint16_t level, InternalResults& results);
+ void Serialize(std::ostream& os) const;
+};
+
+class SipccSdpMediaSection final : public SdpMediaSection {
+ friend class SipccSdp;
+
+ public:
+ ~SipccSdpMediaSection() {}
+
+ virtual MediaType GetMediaType() const override { return mMediaType; }
+
+ virtual unsigned int GetPort() const override;
+ virtual void SetPort(unsigned int port) override;
+ virtual unsigned int GetPortCount() const override;
+ virtual Protocol GetProtocol() const override;
+ virtual const SdpConnection& GetConnection() const override;
+ virtual SdpConnection& GetConnection() override;
+ virtual uint32_t GetBandwidth(const std::string& type) const override;
+ virtual const std::vector<std::string>& GetFormats() const override;
+
+ virtual const SdpAttributeList& GetAttributeList() const override;
+ virtual SdpAttributeList& GetAttributeList() override;
+ virtual SdpDirectionAttribute GetDirectionAttribute() const override;
+
+ virtual void AddCodec(const std::string& pt, const std::string& name,
+ uint32_t clockrate, uint16_t channels) override;
+ virtual void ClearCodecs() override;
+
+ virtual void AddDataChannel(const std::string& name, uint16_t port,
+ uint16_t streams, uint32_t message_size) override;
+
+ virtual void Serialize(std::ostream&) const override;
+
+ private:
+ SipccSdpMediaSection(size_t level, const SipccSdpAttributeList* sessionLevel)
+ : SdpMediaSection(level),
+ mMediaType(static_cast<MediaType>(0)),
+ mPort(0),
+ mPortCount(0),
+ mProtocol(static_cast<Protocol>(0)),
+ mAttributeList(sessionLevel) {}
+
+ SipccSdpMediaSection(const SipccSdpMediaSection& aOrig,
+ const SipccSdpAttributeList* sessionLevel);
+
+ bool Load(sdp_t* sdp, uint16_t level, InternalResults& results);
+ bool LoadConnection(sdp_t* sdp, uint16_t level, InternalResults& results);
+ bool LoadProtocol(sdp_t* sdp, uint16_t level, InternalResults& results);
+ bool LoadFormats(sdp_t* sdp, uint16_t level, InternalResults& results);
+ bool ValidateSimulcast(sdp_t* sdp, uint16_t level,
+ InternalResults& results) const;
+ bool ValidateSimulcastVersions(
+ sdp_t* sdp, uint16_t level,
+ const SdpSimulcastAttribute::Versions& versions, sdp::Direction direction,
+ InternalResults& results) const;
+
+ // the following values are cached on first get
+ MediaType mMediaType;
+ uint16_t mPort;
+ uint16_t mPortCount;
+ Protocol mProtocol;
+ std::vector<std::string> mFormats;
+
+ UniquePtr<SdpConnection> mConnection;
+ SipccSdpBandwidths mBandwidths;
+
+ SipccSdpAttributeList mAttributeList;
+};
+} // namespace mozilla
+
+#endif
diff --git a/dom/media/webrtc/sdp/SipccSdpParser.cpp b/dom/media/webrtc/sdp/SipccSdpParser.cpp
new file mode 100644
index 0000000000..901efd041f
--- /dev/null
+++ b/dom/media/webrtc/sdp/SipccSdpParser.cpp
@@ -0,0 +1,88 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "sdp/SipccSdpParser.h"
+#include "sdp/SipccSdp.h"
+
+#include <utility>
+extern "C" {
+#include "sipcc_sdp.h"
+}
+
+namespace mozilla {
+
+extern "C" {
+
+void sipcc_sdp_parser_results_handler(void* context, uint32_t line,
+ const char* message) {
+ auto* results = static_cast<UniquePtr<InternalResults>*>(context);
+ std::string err(message);
+ (*results)->AddParseError(line, err);
+}
+
+} // extern "C"
+
+const std::string& SipccSdpParser::ParserName() {
+ static const std::string SIPCC_NAME = "SIPCC";
+ return SIPCC_NAME;
+}
+
+UniquePtr<SdpParser::Results> SipccSdpParser::Parse(const std::string& aText) {
+ UniquePtr<InternalResults> results(new InternalResults(Name()));
+ sdp_conf_options_t* sipcc_config = sdp_init_config();
+ if (!sipcc_config) {
+ return UniquePtr<SdpParser::Results>();
+ }
+
+ sdp_nettype_supported(sipcc_config, SDP_NT_INTERNET, true);
+ sdp_addrtype_supported(sipcc_config, SDP_AT_IP4, true);
+ sdp_addrtype_supported(sipcc_config, SDP_AT_IP6, true);
+ sdp_transport_supported(sipcc_config, SDP_TRANSPORT_RTPAVP, true);
+ sdp_transport_supported(sipcc_config, SDP_TRANSPORT_RTPAVPF, true);
+ sdp_transport_supported(sipcc_config, SDP_TRANSPORT_RTPSAVP, true);
+ sdp_transport_supported(sipcc_config, SDP_TRANSPORT_RTPSAVPF, true);
+ sdp_transport_supported(sipcc_config, SDP_TRANSPORT_UDPTLSRTPSAVP, true);
+ sdp_transport_supported(sipcc_config, SDP_TRANSPORT_UDPTLSRTPSAVPF, true);
+ sdp_transport_supported(sipcc_config, SDP_TRANSPORT_TCPDTLSRTPSAVP, true);
+ sdp_transport_supported(sipcc_config, SDP_TRANSPORT_TCPDTLSRTPSAVPF, true);
+ sdp_transport_supported(sipcc_config, SDP_TRANSPORT_DTLSSCTP, true);
+ sdp_transport_supported(sipcc_config, SDP_TRANSPORT_UDPDTLSSCTP, true);
+ sdp_transport_supported(sipcc_config, SDP_TRANSPORT_TCPDTLSSCTP, true);
+ sdp_require_session_name(sipcc_config, false);
+
+ sdp_config_set_error_handler(sipcc_config, &sipcc_sdp_parser_results_handler,
+ &results);
+
+ // Takes ownership of |sipcc_config| iff it succeeds
+ sdp_t* sdp = sdp_init_description(sipcc_config);
+ if (!sdp) {
+ sdp_free_config(sipcc_config);
+ return results;
+ }
+
+ const char* rawString = aText.c_str();
+ sdp_result_e sdpres = sdp_parse(sdp, rawString, aText.length());
+ if (sdpres != SDP_SUCCESS) {
+ sdp_free_description(sdp);
+ return results;
+ }
+
+ UniquePtr<SipccSdp> sipccSdp(new SipccSdp);
+
+ bool success = sipccSdp->Load(sdp, *results);
+ sdp_free_description(sdp);
+ if (success) {
+ results->SetSdp(UniquePtr<mozilla::Sdp>(std::move(sipccSdp)));
+ }
+
+ return results;
+}
+
+bool SipccSdpParser::IsNamed(const std::string& aName) {
+ return aName == ParserName();
+}
+
+} // namespace mozilla
diff --git a/dom/media/webrtc/sdp/SipccSdpParser.h b/dom/media/webrtc/sdp/SipccSdpParser.h
new file mode 100644
index 0000000000..dc9c1ea02d
--- /dev/null
+++ b/dom/media/webrtc/sdp/SipccSdpParser.h
@@ -0,0 +1,35 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _SIPCCSDPPARSER_H_
+#define _SIPCCSDPPARSER_H_
+
+#include <string>
+
+#include "mozilla/UniquePtr.h"
+
+#include "sdp/Sdp.h"
+#include "sdp/SdpParser.h"
+
+namespace mozilla {
+
+class SipccSdpParser final : public SdpParser {
+ static const std::string& ParserName();
+
+ public:
+ SipccSdpParser() = default;
+ virtual ~SipccSdpParser() = default;
+
+ const std::string& Name() const override { return ParserName(); }
+
+ UniquePtr<SdpParser::Results> Parse(const std::string& aText) override;
+
+ static bool IsNamed(const std::string& aName);
+};
+
+} // namespace mozilla
+
+#endif
diff --git a/dom/media/webrtc/sdp/moz.build b/dom/media/webrtc/sdp/moz.build
new file mode 100644
index 0000000000..675009acd0
--- /dev/null
+++ b/dom/media/webrtc/sdp/moz.build
@@ -0,0 +1,48 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+if CONFIG["OS_TARGET"] == "WINNT":
+ DEFINES["SIP_OS_WINDOWS"] = True
+elif CONFIG["OS_TARGET"] == "Darwin":
+ DEFINES["SIP_OS_OSX"] = True
+else:
+ DEFINES["SIP_OS_LINUX"] = True
+
+# Add libFuzzer configuration directives
+include("/tools/fuzzing/libfuzzer-config.mozbuild")
+
+LOCAL_INCLUDES += [
+ "/dom/media/webrtc",
+ "/media/webrtc",
+ "/third_party/sipcc",
+]
+
+UNIFIED_SOURCES += [
+ "HybridSdpParser.cpp",
+ "ParsingResultComparer.cpp",
+ "SdpAttribute.cpp",
+ "SdpHelper.cpp",
+ "SdpLog.cpp",
+ "SdpMediaSection.cpp",
+ "SdpPref.cpp",
+ "SdpTelemetry.cpp",
+ "SipccSdp.cpp",
+ "SipccSdpAttributeList.cpp",
+ "SipccSdpMediaSection.cpp",
+ "SipccSdpParser.cpp",
+]
+
+SOURCES += [
+ # Building these as part of the unified build leads to multiply defined
+ # symbols on windows.
+ "RsdparsaSdp.cpp",
+ "RsdparsaSdpAttributeList.cpp",
+ "RsdparsaSdpGlue.cpp",
+ "RsdparsaSdpMediaSection.cpp",
+ "RsdparsaSdpParser.cpp",
+]
+
+FINAL_LIBRARY = "xul"
diff --git a/dom/media/webrtc/sdp/rsdparsa_capi/Cargo.toml b/dom/media/webrtc/sdp/rsdparsa_capi/Cargo.toml
new file mode 100644
index 0000000000..a0aeff086f
--- /dev/null
+++ b/dom/media/webrtc/sdp/rsdparsa_capi/Cargo.toml
@@ -0,0 +1,12 @@
+[package]
+name = "rsdparsa_capi"
+version = "0.1.0"
+authors = ["Paul Ellenbogen <pe5@cs.princeton.edu>",
+ "Nils Ohlmeier <github@ohlmeier.org>"]
+license = "MPL-2.0"
+
+[dependencies]
+libc = "^0.2.0"
+log = "0.4"
+rsdparsa = {package = "webrtc-sdp", version = "0.3.10"}
+nserror = { path = "../../../../../xpcom/rust/nserror" }
diff --git a/dom/media/webrtc/sdp/rsdparsa_capi/src/attribute.rs b/dom/media/webrtc/sdp/rsdparsa_capi/src/attribute.rs
new file mode 100644
index 0000000000..82483f151f
--- /dev/null
+++ b/dom/media/webrtc/sdp/rsdparsa_capi/src/attribute.rs
@@ -0,0 +1,1472 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use libc::{c_float, size_t};
+use std::ptr;
+use std::slice;
+
+use nserror::{nsresult, NS_ERROR_INVALID_ARG, NS_OK};
+use rsdparsa::attribute_type::*;
+use rsdparsa::SdpSession;
+
+use network::{RustAddress, RustExplicitlyTypedAddress};
+use types::StringView;
+
+#[no_mangle]
+pub unsafe extern "C" fn num_attributes(session: *const SdpSession) -> u32 {
+ (*session).attribute.len() as u32
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn get_attribute_ptr(
+ session: *const SdpSession,
+ index: u32,
+ ret: *mut *const SdpAttribute,
+) -> nsresult {
+ match (*session).attribute.get(index as usize) {
+ Some(attribute) => {
+ *ret = attribute as *const SdpAttribute;
+ NS_OK
+ }
+ None => NS_ERROR_INVALID_ARG,
+ }
+}
+
+fn count_attribute(attributes: &[SdpAttribute], search: SdpAttributeType) -> usize {
+ let mut count = 0;
+ for attribute in (*attributes).iter() {
+ if SdpAttributeType::from(attribute) == search {
+ count += 1;
+ }
+ }
+ count
+}
+
+fn argsearch(attributes: &[SdpAttribute], attribute_type: SdpAttributeType) -> Option<usize> {
+ for (i, attribute) in (*attributes).iter().enumerate() {
+ if SdpAttributeType::from(attribute) == attribute_type {
+ return Some(i);
+ }
+ }
+ None
+}
+
+pub unsafe fn has_attribute(
+ attributes: *const Vec<SdpAttribute>,
+ attribute_type: SdpAttributeType,
+) -> bool {
+ argsearch((*attributes).as_slice(), attribute_type).is_some()
+}
+
+fn get_attribute(
+ attributes: &[SdpAttribute],
+ attribute_type: SdpAttributeType,
+) -> Option<&SdpAttribute> {
+ argsearch(attributes, attribute_type).map(|i| &attributes[i])
+}
+
+#[repr(C)]
+#[derive(Clone, Copy)]
+pub enum RustSdpAttributeDtlsMessageType {
+ Client,
+ Server,
+}
+
+#[repr(C)]
+#[derive(Clone, Copy)]
+pub struct RustSdpAttributeDtlsMessage {
+ pub role: u8,
+ pub value: StringView,
+}
+
+impl<'a> From<&'a SdpAttributeDtlsMessage> for RustSdpAttributeDtlsMessage {
+ fn from(other: &SdpAttributeDtlsMessage) -> Self {
+ match other {
+ &SdpAttributeDtlsMessage::Client(ref x) => RustSdpAttributeDtlsMessage {
+ role: RustSdpAttributeDtlsMessageType::Client as u8,
+ value: StringView::from(x.as_str()),
+ },
+ &SdpAttributeDtlsMessage::Server(ref x) => RustSdpAttributeDtlsMessage {
+ role: RustSdpAttributeDtlsMessageType::Server as u8,
+ value: StringView::from(x.as_str()),
+ },
+ }
+ }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn sdp_get_dtls_message(
+ attributes: *const Vec<SdpAttribute>,
+ ret: *mut RustSdpAttributeDtlsMessage,
+) -> nsresult {
+ let attr = get_attribute((*attributes).as_slice(), SdpAttributeType::DtlsMessage);
+ if let Some(&SdpAttribute::DtlsMessage(ref dtls_message)) = attr {
+ *ret = RustSdpAttributeDtlsMessage::from(dtls_message);
+ return NS_OK;
+ }
+ NS_ERROR_INVALID_ARG
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn sdp_get_iceufrag(
+ attributes: *const Vec<SdpAttribute>,
+ ret: *mut StringView,
+) -> nsresult {
+ let attr = get_attribute((*attributes).as_slice(), SdpAttributeType::IceUfrag);
+ if let Some(&SdpAttribute::IceUfrag(ref string)) = attr {
+ *ret = StringView::from(string.as_str());
+ return NS_OK;
+ }
+ NS_ERROR_INVALID_ARG
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn sdp_get_icepwd(
+ attributes: *const Vec<SdpAttribute>,
+ ret: *mut StringView,
+) -> nsresult {
+ let attr = get_attribute((*attributes).as_slice(), SdpAttributeType::IcePwd);
+ if let Some(&SdpAttribute::IcePwd(ref string)) = attr {
+ *ret = StringView::from(string.as_str());
+ return NS_OK;
+ }
+ NS_ERROR_INVALID_ARG
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn sdp_get_identity(
+ attributes: *const Vec<SdpAttribute>,
+ ret: *mut StringView,
+) -> nsresult {
+ let attr = get_attribute((*attributes).as_slice(), SdpAttributeType::Identity);
+ if let Some(&SdpAttribute::Identity(ref string)) = attr {
+ *ret = StringView::from(string.as_str());
+ return NS_OK;
+ }
+ NS_ERROR_INVALID_ARG
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn sdp_get_iceoptions(
+ attributes: *const Vec<SdpAttribute>,
+ ret: *mut *const Vec<String>,
+) -> nsresult {
+ let attr = get_attribute((*attributes).as_slice(), SdpAttributeType::IceOptions);
+ if let Some(&SdpAttribute::IceOptions(ref options)) = attr {
+ *ret = options;
+ return NS_OK;
+ }
+ NS_ERROR_INVALID_ARG
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn sdp_get_maxptime(
+ attributes: *const Vec<SdpAttribute>,
+ ret: *mut u64,
+) -> nsresult {
+ let attr = get_attribute((*attributes).as_slice(), SdpAttributeType::MaxPtime);
+ if let Some(&SdpAttribute::MaxPtime(ref max_ptime)) = attr {
+ *ret = *max_ptime;
+ return NS_OK;
+ }
+ NS_ERROR_INVALID_ARG
+}
+
+#[repr(C)]
+#[derive(Clone, Copy)]
+pub struct RustSdpAttributeFingerprint {
+ hash_algorithm: u16,
+ fingerprint: *const Vec<u8>,
+}
+
+impl<'a> From<&'a SdpAttributeFingerprint> for RustSdpAttributeFingerprint {
+ fn from(other: &SdpAttributeFingerprint) -> Self {
+ RustSdpAttributeFingerprint {
+ hash_algorithm: other.hash_algorithm as u16,
+ fingerprint: &other.fingerprint,
+ }
+ }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn sdp_get_fingerprint_count(attributes: *const Vec<SdpAttribute>) -> size_t {
+ count_attribute((*attributes).as_slice(), SdpAttributeType::Fingerprint)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn sdp_get_fingerprints(
+ attributes: *const Vec<SdpAttribute>,
+ ret_size: size_t,
+ ret_fingerprints: *mut RustSdpAttributeFingerprint,
+) {
+ let attrs: Vec<_> = (*attributes)
+ .iter()
+ .filter_map(|x| {
+ if let SdpAttribute::Fingerprint(ref data) = *x {
+ Some(RustSdpAttributeFingerprint::from(data))
+ } else {
+ None
+ }
+ })
+ .collect();
+ let fingerprints = slice::from_raw_parts_mut(ret_fingerprints, ret_size);
+ fingerprints.copy_from_slice(attrs.as_slice());
+}
+
+#[repr(C)]
+#[derive(Clone)]
+pub enum RustSdpAttributeSetup {
+ Active,
+ Actpass,
+ Holdconn,
+ Passive,
+}
+
+impl<'a> From<&'a SdpAttributeSetup> for RustSdpAttributeSetup {
+ fn from(other: &SdpAttributeSetup) -> Self {
+ match *other {
+ SdpAttributeSetup::Active => RustSdpAttributeSetup::Active,
+ SdpAttributeSetup::Actpass => RustSdpAttributeSetup::Actpass,
+ SdpAttributeSetup::Holdconn => RustSdpAttributeSetup::Holdconn,
+ SdpAttributeSetup::Passive => RustSdpAttributeSetup::Passive,
+ }
+ }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn sdp_get_setup(
+ attributes: *const Vec<SdpAttribute>,
+ ret: *mut RustSdpAttributeSetup,
+) -> nsresult {
+ let attr = get_attribute((*attributes).as_slice(), SdpAttributeType::Setup);
+ if let Some(&SdpAttribute::Setup(ref setup)) = attr {
+ *ret = RustSdpAttributeSetup::from(setup);
+ return NS_OK;
+ }
+ NS_ERROR_INVALID_ARG
+}
+
+#[repr(C)]
+#[derive(Clone, Copy)]
+pub struct RustSdpAttributeSsrc {
+ pub id: u32,
+ pub attribute: StringView,
+ pub value: StringView,
+}
+
+impl<'a> From<&'a SdpAttributeSsrc> for RustSdpAttributeSsrc {
+ fn from(other: &SdpAttributeSsrc) -> Self {
+ RustSdpAttributeSsrc {
+ id: other.id,
+ attribute: StringView::from(&other.attribute),
+ value: StringView::from(&other.value),
+ }
+ }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn sdp_get_ssrc_count(attributes: *const Vec<SdpAttribute>) -> size_t {
+ count_attribute((*attributes).as_slice(), SdpAttributeType::Ssrc)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn sdp_get_ssrcs(
+ attributes: *const Vec<SdpAttribute>,
+ ret_size: size_t,
+ ret_ssrcs: *mut RustSdpAttributeSsrc,
+) {
+ let attrs: Vec<_> = (*attributes)
+ .iter()
+ .filter_map(|x| {
+ if let SdpAttribute::Ssrc(ref data) = *x {
+ Some(RustSdpAttributeSsrc::from(data))
+ } else {
+ None
+ }
+ })
+ .collect();
+ let ssrcs = slice::from_raw_parts_mut(ret_ssrcs, ret_size);
+ ssrcs.copy_from_slice(attrs.as_slice());
+}
+
+#[repr(C)]
+#[derive(Clone, Copy)]
+pub enum RustSdpSsrcGroupSemantic {
+ Duplication,
+ FlowIdentification,
+ ForwardErrorCorrection,
+ ForwardErrorCorrectionFr,
+ SIM,
+}
+
+impl<'a> From<&'a SdpSsrcGroupSemantic> for RustSdpSsrcGroupSemantic {
+ fn from(other: &SdpSsrcGroupSemantic) -> Self {
+ match *other {
+ SdpSsrcGroupSemantic::Duplication => RustSdpSsrcGroupSemantic::Duplication,
+ SdpSsrcGroupSemantic::FlowIdentification => {
+ RustSdpSsrcGroupSemantic::FlowIdentification
+ }
+ SdpSsrcGroupSemantic::ForwardErrorCorrection => {
+ RustSdpSsrcGroupSemantic::ForwardErrorCorrection
+ }
+ SdpSsrcGroupSemantic::ForwardErrorCorrectionFr => {
+ RustSdpSsrcGroupSemantic::ForwardErrorCorrectionFr
+ }
+ SdpSsrcGroupSemantic::Sim => RustSdpSsrcGroupSemantic::SIM,
+ }
+ }
+}
+
+#[repr(C)]
+#[derive(Clone, Copy)]
+pub struct RustSdpSsrcGroup {
+ pub semantic: RustSdpSsrcGroupSemantic,
+ pub ssrcs: *const Vec<SdpAttributeSsrc>,
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn sdp_get_ssrc_group_count(attributes: *const Vec<SdpAttribute>) -> size_t {
+ count_attribute((*attributes).as_slice(), SdpAttributeType::SsrcGroup)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn sdp_get_ssrc_groups(
+ attributes: *const Vec<SdpAttribute>,
+ ret_size: size_t,
+ ret_ssrc_groups: *mut RustSdpSsrcGroup,
+) {
+ let attrs: Vec<_> = (*attributes)
+ .iter()
+ .filter_map(|x| {
+ if let SdpAttribute::SsrcGroup(ref semantic, ref ssrcs) = *x {
+ Some(RustSdpSsrcGroup {
+ semantic: RustSdpSsrcGroupSemantic::from(semantic),
+ ssrcs: ssrcs,
+ })
+ } else {
+ None
+ }
+ })
+ .collect();
+ let ssrc_groups = slice::from_raw_parts_mut(ret_ssrc_groups, ret_size);
+ ssrc_groups.copy_from_slice(attrs.as_slice());
+}
+
+#[repr(C)]
+#[derive(Clone, Copy)]
+pub struct RustSdpAttributeRtpmap {
+ pub payload_type: u8,
+ pub codec_name: StringView,
+ pub frequency: u32,
+ pub channels: u32,
+}
+
+impl<'a> From<&'a SdpAttributeRtpmap> for RustSdpAttributeRtpmap {
+ fn from(other: &SdpAttributeRtpmap) -> Self {
+ RustSdpAttributeRtpmap {
+ payload_type: other.payload_type as u8,
+ codec_name: StringView::from(other.codec_name.as_str()),
+ frequency: other.frequency as u32,
+ channels: other.channels.unwrap_or(0),
+ }
+ }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn sdp_get_rtpmap_count(attributes: *const Vec<SdpAttribute>) -> size_t {
+ count_attribute((*attributes).as_slice(), SdpAttributeType::Rtpmap)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn sdp_get_rtpmaps(
+ attributes: *const Vec<SdpAttribute>,
+ ret_size: size_t,
+ ret_rtpmaps: *mut RustSdpAttributeRtpmap,
+) {
+ let attrs: Vec<_> = (*attributes)
+ .iter()
+ .filter_map(|x| {
+ if let SdpAttribute::Rtpmap(ref data) = *x {
+ Some(RustSdpAttributeRtpmap::from(data))
+ } else {
+ None
+ }
+ })
+ .collect();
+ let rtpmaps = slice::from_raw_parts_mut(ret_rtpmaps, ret_size);
+ rtpmaps.copy_from_slice(attrs.as_slice());
+}
+
+#[repr(C)]
+#[derive(Clone, Copy)]
+pub struct RustRtxFmtpParameters {
+ pub apt: u8,
+ pub has_rtx_time: bool,
+ pub rtx_time: u32,
+}
+
+#[repr(C)]
+#[derive(Clone, Copy)]
+pub struct RustSdpAttributeFmtpParameters {
+ // H264
+ pub packetization_mode: u32,
+ pub level_asymmetry_allowed: bool,
+ pub profile_level_id: u32,
+ pub max_fs: u32,
+ pub max_cpb: u32,
+ pub max_dpb: u32,
+ pub max_br: u32,
+ pub max_mbps: u32,
+
+ // VP8 and VP9
+ // max_fs, already defined in H264
+ pub max_fr: u32,
+
+ // Opus
+ pub maxplaybackrate: u32,
+ pub maxaveragebitrate: u32,
+ pub usedtx: bool,
+ pub stereo: bool,
+ pub useinbandfec: bool,
+ pub cbr: bool,
+ pub ptime: u32,
+ pub minptime: u32,
+ pub maxptime: u32,
+
+ // telephone-event
+ pub dtmf_tones: StringView,
+
+ // RTX
+ pub rtx: RustRtxFmtpParameters,
+
+ // Red
+ pub encodings: *const Vec<u8>,
+
+ // Unknown
+ pub unknown_tokens: *const Vec<String>,
+}
+
+impl<'a> From<&'a SdpAttributeFmtpParameters> for RustSdpAttributeFmtpParameters {
+ fn from(other: &SdpAttributeFmtpParameters) -> Self {
+ let rtx = if let Some(rtx) = other.rtx {
+ RustRtxFmtpParameters {
+ apt: rtx.apt,
+ has_rtx_time: rtx.rtx_time.is_some(),
+ rtx_time: rtx.rtx_time.unwrap_or(0),
+ }
+ } else {
+ RustRtxFmtpParameters {
+ apt: 0,
+ has_rtx_time: false,
+ rtx_time: 0,
+ }
+ };
+
+ RustSdpAttributeFmtpParameters {
+ packetization_mode: other.packetization_mode,
+ level_asymmetry_allowed: other.level_asymmetry_allowed,
+ profile_level_id: other.profile_level_id,
+ max_fs: other.max_fs,
+ max_cpb: other.max_cpb,
+ max_dpb: other.max_dpb,
+ max_br: other.max_br,
+ max_mbps: other.max_mbps,
+ usedtx: other.usedtx,
+ stereo: other.stereo,
+ useinbandfec: other.useinbandfec,
+ cbr: other.cbr,
+ max_fr: other.max_fr,
+ maxplaybackrate: other.maxplaybackrate,
+ maxaveragebitrate: other.maxaveragebitrate,
+ ptime: other.ptime,
+ minptime: other.minptime,
+ maxptime: other.maxptime,
+ dtmf_tones: StringView::from(other.dtmf_tones.as_str()),
+ rtx,
+ encodings: &other.encodings,
+ unknown_tokens: &other.unknown_tokens,
+ }
+ }
+}
+
+#[repr(C)]
+#[derive(Clone, Copy)]
+pub struct RustSdpAttributeFmtp {
+ pub payload_type: u8,
+ pub codec_name: StringView,
+ pub parameters: RustSdpAttributeFmtpParameters,
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn sdp_get_fmtp_count(attributes: *const Vec<SdpAttribute>) -> size_t {
+ count_attribute((*attributes).as_slice(), SdpAttributeType::Fmtp)
+}
+
+fn find_payload_type(attributes: &[SdpAttribute], payload_type: u8) -> Option<&SdpAttributeRtpmap> {
+ attributes
+ .iter()
+ .filter_map(|x| {
+ if let SdpAttribute::Rtpmap(ref data) = *x {
+ if data.payload_type == payload_type {
+ Some(data)
+ } else {
+ None
+ }
+ } else {
+ None
+ }
+ })
+ .next()
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn sdp_get_fmtp(
+ attributes: *const Vec<SdpAttribute>,
+ ret_size: size_t,
+ ret_fmtp: *mut RustSdpAttributeFmtp,
+) -> size_t {
+ let fmtps = (*attributes).iter().filter_map(|x| {
+ if let SdpAttribute::Fmtp(ref data) = *x {
+ Some(data)
+ } else {
+ None
+ }
+ });
+ let mut rust_fmtps = Vec::new();
+ for fmtp in fmtps {
+ if let Some(rtpmap) = find_payload_type((*attributes).as_slice(), fmtp.payload_type) {
+ rust_fmtps.push(RustSdpAttributeFmtp {
+ payload_type: fmtp.payload_type as u8,
+ codec_name: StringView::from(rtpmap.codec_name.as_str()),
+ parameters: RustSdpAttributeFmtpParameters::from(&fmtp.parameters),
+ });
+ }
+ }
+ let fmtps = if ret_size <= rust_fmtps.len() {
+ slice::from_raw_parts_mut(ret_fmtp, ret_size)
+ } else {
+ slice::from_raw_parts_mut(ret_fmtp, rust_fmtps.len())
+ };
+ fmtps.copy_from_slice(rust_fmtps.as_slice());
+ fmtps.len()
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn sdp_get_ptime(attributes: *const Vec<SdpAttribute>) -> i64 {
+ for attribute in (*attributes).iter() {
+ if let SdpAttribute::Ptime(time) = *attribute {
+ return time as i64;
+ }
+ }
+ -1
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn sdp_get_max_msg_size(attributes: *const Vec<SdpAttribute>) -> i64 {
+ for attribute in (*attributes).iter() {
+ if let SdpAttribute::MaxMessageSize(max_msg_size) = *attribute {
+ return max_msg_size as i64;
+ }
+ }
+ -1
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn sdp_get_sctp_port(attributes: *const Vec<SdpAttribute>) -> i64 {
+ for attribute in (*attributes).iter() {
+ if let SdpAttribute::SctpPort(port) = *attribute {
+ return port as i64;
+ }
+ }
+ -1
+}
+
+#[repr(C)]
+#[derive(Clone, Copy)]
+pub struct RustSdpAttributeFlags {
+ pub ice_lite: bool,
+ pub rtcp_mux: bool,
+ pub rtcp_rsize: bool,
+ pub bundle_only: bool,
+ pub end_of_candidates: bool,
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn sdp_get_attribute_flags(
+ attributes: *const Vec<SdpAttribute>,
+) -> RustSdpAttributeFlags {
+ let mut ret = RustSdpAttributeFlags {
+ ice_lite: false,
+ rtcp_mux: false,
+ rtcp_rsize: false,
+ bundle_only: false,
+ end_of_candidates: false,
+ };
+ for attribute in (*attributes).iter() {
+ if let SdpAttribute::IceLite = *attribute {
+ ret.ice_lite = true;
+ } else if let SdpAttribute::RtcpMux = *attribute {
+ ret.rtcp_mux = true;
+ } else if let SdpAttribute::RtcpRsize = *attribute {
+ ret.rtcp_rsize = true;
+ } else if let SdpAttribute::BundleOnly = *attribute {
+ ret.bundle_only = true;
+ } else if let SdpAttribute::EndOfCandidates = *attribute {
+ ret.end_of_candidates = true;
+ }
+ }
+ ret
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn sdp_get_mid(
+ attributes: *const Vec<SdpAttribute>,
+ ret: *mut StringView,
+) -> nsresult {
+ for attribute in (*attributes).iter() {
+ if let SdpAttribute::Mid(ref data) = *attribute {
+ *ret = StringView::from(data.as_str());
+ return NS_OK;
+ }
+ }
+ NS_ERROR_INVALID_ARG
+}
+
+#[repr(C)]
+#[derive(Clone, Copy)]
+pub struct RustSdpAttributeMsid {
+ id: StringView,
+ appdata: StringView,
+}
+
+impl<'a> From<&'a SdpAttributeMsid> for RustSdpAttributeMsid {
+ fn from(other: &SdpAttributeMsid) -> Self {
+ RustSdpAttributeMsid {
+ id: StringView::from(other.id.as_str()),
+ appdata: StringView::from(&other.appdata),
+ }
+ }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn sdp_get_msid_count(attributes: *const Vec<SdpAttribute>) -> size_t {
+ count_attribute((*attributes).as_slice(), SdpAttributeType::Msid)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn sdp_get_msids(
+ attributes: *const Vec<SdpAttribute>,
+ ret_size: size_t,
+ ret_msids: *mut RustSdpAttributeMsid,
+) {
+ let attrs: Vec<_> = (*attributes)
+ .iter()
+ .filter_map(|x| {
+ if let SdpAttribute::Msid(ref data) = *x {
+ Some(RustSdpAttributeMsid::from(data))
+ } else {
+ None
+ }
+ })
+ .collect();
+ let msids = slice::from_raw_parts_mut(ret_msids, ret_size);
+ msids.copy_from_slice(attrs.as_slice());
+}
+
+// TODO: Finish msid attributes once parsing is changed upstream.
+#[repr(C)]
+#[derive(Clone, Copy)]
+pub struct RustSdpAttributeMsidSemantic {
+ pub semantic: StringView,
+ pub msids: *const Vec<String>,
+}
+
+impl<'a> From<&'a SdpAttributeMsidSemantic> for RustSdpAttributeMsidSemantic {
+ fn from(other: &SdpAttributeMsidSemantic) -> Self {
+ RustSdpAttributeMsidSemantic {
+ semantic: StringView::from(other.semantic.as_str()),
+ msids: &other.msids,
+ }
+ }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn sdp_get_msid_semantic_count(
+ attributes: *const Vec<SdpAttribute>,
+) -> size_t {
+ count_attribute((*attributes).as_slice(), SdpAttributeType::MsidSemantic)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn sdp_get_msid_semantics(
+ attributes: *const Vec<SdpAttribute>,
+ ret_size: size_t,
+ ret_msid_semantics: *mut RustSdpAttributeMsidSemantic,
+) {
+ let attrs: Vec<_> = (*attributes)
+ .iter()
+ .filter_map(|x| {
+ if let SdpAttribute::MsidSemantic(ref data) = *x {
+ Some(RustSdpAttributeMsidSemantic::from(data))
+ } else {
+ None
+ }
+ })
+ .collect();
+ let msid_semantics = slice::from_raw_parts_mut(ret_msid_semantics, ret_size);
+ msid_semantics.copy_from_slice(attrs.as_slice());
+}
+
+#[repr(C)]
+#[derive(Clone, Copy)]
+pub enum RustSdpAttributeGroupSemantic {
+ LipSynchronization,
+ FlowIdentification,
+ SingleReservationFlow,
+ AlternateNetworkAddressType,
+ ForwardErrorCorrection,
+ DecodingDependency,
+ Bundle,
+}
+
+impl<'a> From<&'a SdpAttributeGroupSemantic> for RustSdpAttributeGroupSemantic {
+ fn from(other: &SdpAttributeGroupSemantic) -> Self {
+ match *other {
+ SdpAttributeGroupSemantic::LipSynchronization => {
+ RustSdpAttributeGroupSemantic::LipSynchronization
+ }
+ SdpAttributeGroupSemantic::FlowIdentification => {
+ RustSdpAttributeGroupSemantic::FlowIdentification
+ }
+ SdpAttributeGroupSemantic::SingleReservationFlow => {
+ RustSdpAttributeGroupSemantic::SingleReservationFlow
+ }
+ SdpAttributeGroupSemantic::AlternateNetworkAddressType => {
+ RustSdpAttributeGroupSemantic::AlternateNetworkAddressType
+ }
+ SdpAttributeGroupSemantic::ForwardErrorCorrection => {
+ RustSdpAttributeGroupSemantic::ForwardErrorCorrection
+ }
+ SdpAttributeGroupSemantic::DecodingDependency => {
+ RustSdpAttributeGroupSemantic::DecodingDependency
+ }
+ SdpAttributeGroupSemantic::Bundle => RustSdpAttributeGroupSemantic::Bundle,
+ }
+ }
+}
+
+#[repr(C)]
+#[derive(Clone, Copy)]
+pub struct RustSdpAttributeGroup {
+ pub semantic: RustSdpAttributeGroupSemantic,
+ pub tags: *const Vec<String>,
+}
+
+impl<'a> From<&'a SdpAttributeGroup> for RustSdpAttributeGroup {
+ fn from(other: &SdpAttributeGroup) -> Self {
+ RustSdpAttributeGroup {
+ semantic: RustSdpAttributeGroupSemantic::from(&other.semantics),
+ tags: &other.tags,
+ }
+ }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn sdp_get_group_count(attributes: *const Vec<SdpAttribute>) -> size_t {
+ count_attribute((*attributes).as_slice(), SdpAttributeType::Group)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn sdp_get_groups(
+ attributes: *const Vec<SdpAttribute>,
+ ret_size: size_t,
+ ret_groups: *mut RustSdpAttributeGroup,
+) {
+ let attrs: Vec<_> = (*attributes)
+ .iter()
+ .filter_map(|x| {
+ if let SdpAttribute::Group(ref data) = *x {
+ Some(RustSdpAttributeGroup::from(data))
+ } else {
+ None
+ }
+ })
+ .collect();
+ let groups = slice::from_raw_parts_mut(ret_groups, ret_size);
+ groups.copy_from_slice(attrs.as_slice());
+}
+
+#[repr(C)]
+pub struct RustSdpAttributeRtcp {
+ pub port: u32,
+ pub unicast_addr: RustExplicitlyTypedAddress,
+ pub has_address: bool,
+}
+
+impl<'a> From<&'a SdpAttributeRtcp> for RustSdpAttributeRtcp {
+ fn from(other: &SdpAttributeRtcp) -> Self {
+ match other.unicast_addr {
+ Some(ref address) => RustSdpAttributeRtcp {
+ port: other.port as u32,
+ unicast_addr: address.into(),
+ has_address: true,
+ },
+ None => RustSdpAttributeRtcp {
+ port: other.port as u32,
+ unicast_addr: RustExplicitlyTypedAddress::default(),
+ has_address: false,
+ },
+ }
+ }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn sdp_get_rtcp(
+ attributes: *const Vec<SdpAttribute>,
+ ret: *mut RustSdpAttributeRtcp,
+) -> nsresult {
+ let attr = get_attribute((*attributes).as_slice(), SdpAttributeType::Rtcp);
+ if let Some(&SdpAttribute::Rtcp(ref data)) = attr {
+ *ret = RustSdpAttributeRtcp::from(data);
+ return NS_OK;
+ }
+ NS_ERROR_INVALID_ARG
+}
+
+#[repr(C)]
+#[derive(Clone, Copy)]
+pub struct RustSdpAttributeRtcpFb {
+ pub payload_type: u32,
+ pub feedback_type: u32,
+ pub parameter: StringView,
+ pub extra: StringView,
+}
+
+impl<'a> From<&'a SdpAttributeRtcpFb> for RustSdpAttributeRtcpFb {
+ fn from(other: &SdpAttributeRtcpFb) -> Self {
+ RustSdpAttributeRtcpFb {
+ payload_type: match other.payload_type {
+ SdpAttributePayloadType::Wildcard => u32::max_value(),
+ SdpAttributePayloadType::PayloadType(x) => x as u32,
+ },
+ feedback_type: other.feedback_type.clone() as u32,
+ parameter: StringView::from(other.parameter.as_str()),
+ extra: StringView::from(other.extra.as_str()),
+ }
+ }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn sdp_get_rtcpfb_count(attributes: *const Vec<SdpAttribute>) -> size_t {
+ count_attribute((*attributes).as_slice(), SdpAttributeType::Rtcpfb)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn sdp_get_rtcpfbs(
+ attributes: *const Vec<SdpAttribute>,
+ ret_size: size_t,
+ ret_rtcpfbs: *mut RustSdpAttributeRtcpFb,
+) {
+ let attrs: Vec<_> = (*attributes)
+ .iter()
+ .filter_map(|x| {
+ if let SdpAttribute::Rtcpfb(ref data) = *x {
+ Some(RustSdpAttributeRtcpFb::from(data))
+ } else {
+ None
+ }
+ })
+ .collect();
+ let rtcpfbs = slice::from_raw_parts_mut(ret_rtcpfbs, ret_size);
+ rtcpfbs.clone_from_slice(attrs.as_slice());
+}
+
+#[repr(C)]
+#[derive(Clone, Copy)]
+pub struct RustSdpAttributeImageAttrXyRange {
+ // range
+ pub min: u32,
+ pub max: u32,
+ pub step: u32,
+
+ // discrete values
+ pub discrete_values: *const Vec<u32>,
+}
+
+impl<'a> From<&'a SdpAttributeImageAttrXyRange> for RustSdpAttributeImageAttrXyRange {
+ fn from(other: &SdpAttributeImageAttrXyRange) -> Self {
+ match other {
+ &SdpAttributeImageAttrXyRange::Range(min, max, step) => {
+ RustSdpAttributeImageAttrXyRange {
+ min,
+ max,
+ step: step.unwrap_or(1),
+ discrete_values: ptr::null(),
+ }
+ }
+ &SdpAttributeImageAttrXyRange::DiscreteValues(ref discrete_values) => {
+ RustSdpAttributeImageAttrXyRange {
+ min: 0,
+ max: 1,
+ step: 1,
+ discrete_values,
+ }
+ }
+ }
+ }
+}
+
+#[repr(C)]
+#[derive(Clone, Copy)]
+pub struct RustSdpAttributeImageAttrSRange {
+ // range
+ pub min: c_float,
+ pub max: c_float,
+
+ // discrete values
+ pub discrete_values: *const Vec<c_float>,
+}
+
+impl<'a> From<&'a SdpAttributeImageAttrSRange> for RustSdpAttributeImageAttrSRange {
+ fn from(other: &SdpAttributeImageAttrSRange) -> Self {
+ match other {
+ &SdpAttributeImageAttrSRange::Range(min, max) => RustSdpAttributeImageAttrSRange {
+ min,
+ max,
+ discrete_values: ptr::null(),
+ },
+ &SdpAttributeImageAttrSRange::DiscreteValues(ref discrete_values) => {
+ RustSdpAttributeImageAttrSRange {
+ min: 0.0,
+ max: 1.0,
+ discrete_values,
+ }
+ }
+ }
+ }
+}
+
+#[repr(C)]
+#[derive(Clone, Copy)]
+pub struct RustSdpAttributeImageAttrPRange {
+ pub min: c_float,
+ pub max: c_float,
+}
+
+impl<'a> From<&'a SdpAttributeImageAttrPRange> for RustSdpAttributeImageAttrPRange {
+ fn from(other: &SdpAttributeImageAttrPRange) -> Self {
+ RustSdpAttributeImageAttrPRange {
+ min: other.min,
+ max: other.max,
+ }
+ }
+}
+
+#[repr(C)]
+#[derive(Clone, Copy)]
+pub struct RustSdpAttributeImageAttrSet {
+ pub x: RustSdpAttributeImageAttrXyRange,
+ pub y: RustSdpAttributeImageAttrXyRange,
+
+ pub has_sar: bool,
+ pub sar: RustSdpAttributeImageAttrSRange,
+
+ pub has_par: bool,
+ pub par: RustSdpAttributeImageAttrPRange,
+
+ pub q: c_float,
+}
+
+impl<'a> From<&'a SdpAttributeImageAttrSet> for RustSdpAttributeImageAttrSet {
+ fn from(other: &SdpAttributeImageAttrSet) -> Self {
+ RustSdpAttributeImageAttrSet {
+ x: RustSdpAttributeImageAttrXyRange::from(&other.x),
+ y: RustSdpAttributeImageAttrXyRange::from(&other.y),
+
+ has_sar: other.sar.is_some(),
+ sar: match other.sar {
+ Some(ref x) => RustSdpAttributeImageAttrSRange::from(x),
+ // This is just any valid value accepted by rust,
+ // it might as well by uninitilized
+ None => RustSdpAttributeImageAttrSRange::from(
+ &SdpAttributeImageAttrSRange::DiscreteValues(vec![]),
+ ),
+ },
+
+ has_par: other.par.is_some(),
+ par: match other.par {
+ Some(ref x) => RustSdpAttributeImageAttrPRange::from(x),
+ // This is just any valid value accepted by rust,
+ // it might as well by uninitilized
+ None => RustSdpAttributeImageAttrPRange { min: 0.0, max: 1.0 },
+ },
+
+ q: other.q.unwrap_or(0.5),
+ }
+ }
+}
+
+#[repr(C)]
+#[derive(Clone, Copy)]
+pub struct RustSdpAttributeImageAttrSetList {
+ pub sets: *const Vec<SdpAttributeImageAttrSet>,
+}
+
+impl<'a> From<&'a SdpAttributeImageAttrSetList> for RustSdpAttributeImageAttrSetList {
+ fn from(other: &SdpAttributeImageAttrSetList) -> Self {
+ match other {
+ &SdpAttributeImageAttrSetList::Wildcard => {
+ RustSdpAttributeImageAttrSetList { sets: ptr::null() }
+ }
+ &SdpAttributeImageAttrSetList::Sets(ref sets) => {
+ RustSdpAttributeImageAttrSetList { sets: sets }
+ }
+ }
+ }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn sdp_imageattr_get_set_count(
+ sets: *const Vec<SdpAttributeImageAttrSet>,
+) -> size_t {
+ (*sets).len()
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn sdp_imageattr_get_sets(
+ sets: *const Vec<SdpAttributeImageAttrSet>,
+ ret_size: size_t,
+ ret: *mut RustSdpAttributeImageAttrSet,
+) {
+ let rust_sets: Vec<_> = (*sets)
+ .iter()
+ .map(RustSdpAttributeImageAttrSet::from)
+ .collect();
+ let sets = slice::from_raw_parts_mut(ret, ret_size);
+ sets.clone_from_slice(rust_sets.as_slice());
+}
+
+#[repr(C)]
+#[derive(Clone, Copy)]
+pub struct RustSdpAttributeImageAttr {
+ pub pt: u32,
+ pub send: RustSdpAttributeImageAttrSetList,
+ pub recv: RustSdpAttributeImageAttrSetList,
+}
+
+impl<'a> From<&'a SdpAttributeImageAttr> for RustSdpAttributeImageAttr {
+ fn from(other: &SdpAttributeImageAttr) -> Self {
+ RustSdpAttributeImageAttr {
+ pt: match other.pt {
+ SdpAttributePayloadType::Wildcard => u32::max_value(),
+ SdpAttributePayloadType::PayloadType(x) => x as u32,
+ },
+ send: RustSdpAttributeImageAttrSetList::from(&other.send),
+ recv: RustSdpAttributeImageAttrSetList::from(&other.recv),
+ }
+ }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn sdp_get_imageattr_count(attributes: *const Vec<SdpAttribute>) -> size_t {
+ count_attribute((*attributes).as_slice(), SdpAttributeType::ImageAttr)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn sdp_get_imageattrs(
+ attributes: *const Vec<SdpAttribute>,
+ ret_size: size_t,
+ ret_attrs: *mut RustSdpAttributeImageAttr,
+) {
+ let attrs: Vec<_> = (*attributes)
+ .iter()
+ .filter_map(|x| {
+ if let SdpAttribute::ImageAttr(ref data) = *x {
+ Some(RustSdpAttributeImageAttr::from(data))
+ } else {
+ None
+ }
+ })
+ .collect();
+ let imageattrs = slice::from_raw_parts_mut(ret_attrs, ret_size);
+ imageattrs.copy_from_slice(attrs.as_slice());
+}
+
+#[repr(C)]
+#[derive(Clone, Copy)]
+pub struct RustSdpAttributeSctpmap {
+ pub port: u32,
+ pub channels: u32,
+}
+
+impl<'a> From<&'a SdpAttributeSctpmap> for RustSdpAttributeSctpmap {
+ fn from(other: &SdpAttributeSctpmap) -> Self {
+ RustSdpAttributeSctpmap {
+ port: other.port as u32,
+ channels: other.channels,
+ }
+ }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn sdp_get_sctpmap_count(attributes: *const Vec<SdpAttribute>) -> size_t {
+ count_attribute((*attributes).as_slice(), SdpAttributeType::Sctpmap)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn sdp_get_sctpmaps(
+ attributes: *const Vec<SdpAttribute>,
+ ret_size: size_t,
+ ret_sctpmaps: *mut RustSdpAttributeSctpmap,
+) {
+ let attrs: Vec<_> = (*attributes)
+ .iter()
+ .filter_map(|x| {
+ if let SdpAttribute::Sctpmap(ref data) = *x {
+ Some(RustSdpAttributeSctpmap::from(data))
+ } else {
+ None
+ }
+ })
+ .collect();
+ let sctpmaps = slice::from_raw_parts_mut(ret_sctpmaps, ret_size);
+ sctpmaps.copy_from_slice(attrs.as_slice());
+}
+
+#[repr(C)]
+#[derive(Clone, Copy)]
+pub struct RustSdpAttributeSimulcastId {
+ pub id: StringView,
+ pub paused: bool,
+}
+
+impl<'a> From<&'a SdpAttributeSimulcastId> for RustSdpAttributeSimulcastId {
+ fn from(other: &SdpAttributeSimulcastId) -> Self {
+ RustSdpAttributeSimulcastId {
+ id: StringView::from(other.id.as_str()),
+ paused: other.paused,
+ }
+ }
+}
+
+#[repr(C)]
+#[derive(Clone, Copy)]
+pub struct RustSdpAttributeSimulcastVersion {
+ pub ids: *const Vec<SdpAttributeSimulcastId>,
+}
+
+impl<'a> From<&'a SdpAttributeSimulcastVersion> for RustSdpAttributeSimulcastVersion {
+ fn from(other: &SdpAttributeSimulcastVersion) -> Self {
+ RustSdpAttributeSimulcastVersion { ids: &other.ids }
+ }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn sdp_simulcast_get_ids_count(
+ ids: *const Vec<SdpAttributeSimulcastId>,
+) -> size_t {
+ (*ids).len()
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn sdp_simulcast_get_ids(
+ ids: *const Vec<SdpAttributeSimulcastId>,
+ ret_size: size_t,
+ ret: *mut RustSdpAttributeSimulcastId,
+) {
+ let rust_ids: Vec<_> = (*ids)
+ .iter()
+ .map(RustSdpAttributeSimulcastId::from)
+ .collect();
+ let ids = slice::from_raw_parts_mut(ret, ret_size);
+ ids.clone_from_slice(rust_ids.as_slice());
+}
+
+#[repr(C)]
+pub struct RustSdpAttributeSimulcast {
+ pub send: *const Vec<SdpAttributeSimulcastVersion>,
+ pub receive: *const Vec<SdpAttributeSimulcastVersion>,
+}
+
+impl<'a> From<&'a SdpAttributeSimulcast> for RustSdpAttributeSimulcast {
+ fn from(other: &SdpAttributeSimulcast) -> Self {
+ RustSdpAttributeSimulcast {
+ send: &other.send,
+ receive: &other.receive,
+ }
+ }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn sdp_simulcast_get_version_count(
+ version_list: *const Vec<SdpAttributeSimulcastVersion>,
+) -> size_t {
+ (*version_list).len()
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn sdp_simulcast_get_versions(
+ version_list: *const Vec<SdpAttributeSimulcastVersion>,
+ ret_size: size_t,
+ ret: *mut RustSdpAttributeSimulcastVersion,
+) {
+ let rust_versions_list: Vec<_> = (*version_list)
+ .iter()
+ .map(RustSdpAttributeSimulcastVersion::from)
+ .collect();
+ let versions = slice::from_raw_parts_mut(ret, ret_size);
+ versions.clone_from_slice(rust_versions_list.as_slice())
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn sdp_get_simulcast(
+ attributes: *const Vec<SdpAttribute>,
+ ret: *mut RustSdpAttributeSimulcast,
+) -> nsresult {
+ let attr = get_attribute((*attributes).as_slice(), SdpAttributeType::Simulcast);
+ if let Some(&SdpAttribute::Simulcast(ref data)) = attr {
+ *ret = RustSdpAttributeSimulcast::from(data);
+ return NS_OK;
+ }
+ NS_ERROR_INVALID_ARG
+}
+
+#[repr(C)]
+#[derive(Clone, Copy)]
+pub enum RustDirection {
+ Recvonly,
+ Sendonly,
+ Sendrecv,
+ Inactive,
+}
+
+impl<'a> From<&'a Option<SdpAttributeDirection>> for RustDirection {
+ fn from(other: &Option<SdpAttributeDirection>) -> Self {
+ match *other {
+ Some(ref direction) => match *direction {
+ SdpAttributeDirection::Recvonly => RustDirection::Recvonly,
+ SdpAttributeDirection::Sendonly => RustDirection::Sendonly,
+ SdpAttributeDirection::Sendrecv => RustDirection::Sendrecv,
+ },
+ None => RustDirection::Inactive,
+ }
+ }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn sdp_get_direction(attributes: *const Vec<SdpAttribute>) -> RustDirection {
+ for attribute in (*attributes).iter() {
+ match *attribute {
+ SdpAttribute::Recvonly => {
+ return RustDirection::Recvonly;
+ }
+ SdpAttribute::Sendonly => {
+ return RustDirection::Sendonly;
+ }
+ SdpAttribute::Sendrecv => {
+ return RustDirection::Sendrecv;
+ }
+ SdpAttribute::Inactive => {
+ return RustDirection::Inactive;
+ }
+ _ => (),
+ }
+ }
+ RustDirection::Sendrecv
+}
+
+#[repr(C)]
+pub struct RustSdpAttributeRemoteCandidate {
+ pub component: u32,
+ pub address: RustAddress,
+ pub port: u32,
+}
+
+impl<'a> From<&'a SdpAttributeRemoteCandidate> for RustSdpAttributeRemoteCandidate {
+ fn from(other: &SdpAttributeRemoteCandidate) -> Self {
+ RustSdpAttributeRemoteCandidate {
+ component: other.component,
+ address: RustAddress::from(&other.address),
+ port: other.port,
+ }
+ }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn sdp_get_remote_candidate_count(
+ attributes: *const Vec<SdpAttribute>,
+) -> size_t {
+ count_attribute((*attributes).as_slice(), SdpAttributeType::RemoteCandidate)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn sdp_get_remote_candidates(
+ attributes: *const Vec<SdpAttribute>,
+ ret_size: size_t,
+ ret_candidates: *mut RustSdpAttributeRemoteCandidate,
+) {
+ let attrs = (*attributes).iter().filter_map(|x| {
+ if let SdpAttribute::RemoteCandidate(ref data) = *x {
+ Some(RustSdpAttributeRemoteCandidate::from(data))
+ } else {
+ None
+ }
+ });
+ let candidates = slice::from_raw_parts_mut(ret_candidates, ret_size);
+ for (source, destination) in attrs.zip(candidates) {
+ *destination = source
+ }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn sdp_get_candidate_count(attributes: *const Vec<SdpAttribute>) -> size_t {
+ count_attribute((*attributes).as_slice(), SdpAttributeType::Candidate)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn sdp_get_candidates(
+ attributes: *const Vec<SdpAttribute>,
+ _ret_size: size_t,
+ ret: *mut *const Vec<String>,
+) {
+ let attr_strings: Vec<String> = (*attributes)
+ .iter()
+ .filter_map(|x| {
+ if let SdpAttribute::Candidate(ref attr) = *x {
+ // The serialized attribute starts with "candidate:...", this needs to be removed
+ Some(attr.to_string())
+ } else {
+ None
+ }
+ })
+ .collect();
+
+ *ret = Box::into_raw(Box::from(attr_strings));
+}
+
+#[repr(C)]
+#[derive(Clone, Copy)]
+pub struct RustSdpAttributeRidParameters {
+ pub max_width: u32,
+ pub max_height: u32,
+ pub max_fps: u32,
+ pub max_fs: u32,
+ pub max_br: u32,
+ pub max_pps: u32,
+ pub unknown: *const Vec<String>,
+}
+
+impl<'a> From<&'a SdpAttributeRidParameters> for RustSdpAttributeRidParameters {
+ fn from(other: &SdpAttributeRidParameters) -> Self {
+ RustSdpAttributeRidParameters {
+ max_width: other.max_width,
+ max_height: other.max_height,
+ max_fps: other.max_fps,
+ max_fs: other.max_fs,
+ max_br: other.max_br,
+ max_pps: other.max_pps,
+
+ unknown: &other.unknown,
+ }
+ }
+}
+
+#[repr(C)]
+#[derive(Clone, Copy)]
+pub struct RustSdpAttributeRid {
+ pub id: StringView,
+ pub direction: u32,
+ pub formats: *const Vec<u16>,
+ pub params: RustSdpAttributeRidParameters,
+ pub depends: *const Vec<String>,
+}
+
+impl<'a> From<&'a SdpAttributeRid> for RustSdpAttributeRid {
+ fn from(other: &SdpAttributeRid) -> Self {
+ RustSdpAttributeRid {
+ id: StringView::from(other.id.as_str()),
+ direction: other.direction.clone() as u32,
+ formats: &other.formats,
+ params: RustSdpAttributeRidParameters::from(&other.params),
+ depends: &other.depends,
+ }
+ }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn sdp_get_rid_count(attributes: *const Vec<SdpAttribute>) -> size_t {
+ count_attribute((*attributes).as_slice(), SdpAttributeType::Rid)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn sdp_get_rids(
+ attributes: *const Vec<SdpAttribute>,
+ ret_size: size_t,
+ ret_rids: *mut RustSdpAttributeRid,
+) {
+ let attrs: Vec<_> = (*attributes)
+ .iter()
+ .filter_map(|x| {
+ if let SdpAttribute::Rid(ref data) = *x {
+ Some(RustSdpAttributeRid::from(data))
+ } else {
+ None
+ }
+ })
+ .collect();
+ let rids = slice::from_raw_parts_mut(ret_rids, ret_size);
+ rids.clone_from_slice(attrs.as_slice());
+}
+
+#[repr(C)]
+#[derive(Clone, Copy)]
+pub struct RustSdpAttributeExtmap {
+ pub id: u16,
+ pub direction_specified: bool,
+ pub direction: RustDirection,
+ pub url: StringView,
+ pub extension_attributes: StringView,
+}
+
+impl<'a> From<&'a SdpAttributeExtmap> for RustSdpAttributeExtmap {
+ fn from(other: &SdpAttributeExtmap) -> Self {
+ let dir = if other.direction.is_some() {
+ RustDirection::from(&other.direction)
+ } else {
+ RustDirection::from(&Some(SdpAttributeDirection::Sendrecv))
+ };
+ RustSdpAttributeExtmap {
+ id: other.id as u16,
+ direction_specified: other.direction.is_some(),
+ direction: dir,
+ url: StringView::from(other.url.as_str()),
+ extension_attributes: StringView::from(&other.extension_attributes),
+ }
+ }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn sdp_get_extmap_count(attributes: *const Vec<SdpAttribute>) -> size_t {
+ count_attribute((*attributes).as_slice(), SdpAttributeType::Extmap)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn sdp_get_extmaps(
+ attributes: *const Vec<SdpAttribute>,
+ ret_size: size_t,
+ ret_rids: *mut RustSdpAttributeExtmap,
+) {
+ let attrs: Vec<_> = (*attributes)
+ .iter()
+ .filter_map(|x| {
+ if let SdpAttribute::Extmap(ref data) = *x {
+ Some(RustSdpAttributeExtmap::from(data))
+ } else {
+ None
+ }
+ })
+ .collect();
+ let extmaps = slice::from_raw_parts_mut(ret_rids, ret_size);
+ extmaps.copy_from_slice(attrs.as_slice());
+}
diff --git a/dom/media/webrtc/sdp/rsdparsa_capi/src/lib.rs b/dom/media/webrtc/sdp/rsdparsa_capi/src/lib.rs
new file mode 100644
index 0000000000..20a13900a2
--- /dev/null
+++ b/dom/media/webrtc/sdp/rsdparsa_capi/src/lib.rs
@@ -0,0 +1,298 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+extern crate libc;
+extern crate nserror;
+extern crate rsdparsa;
+
+use std::ffi::CString;
+use std::os::raw::c_char;
+use std::ptr;
+
+use libc::size_t;
+
+use std::rc::Rc;
+
+use std::convert::{TryFrom, TryInto};
+
+use nserror::{nsresult, NS_ERROR_INVALID_ARG, NS_OK};
+use rsdparsa::address::ExplicitlyTypedAddress;
+use rsdparsa::anonymizer::{AnonymizingClone, StatefulSdpAnonymizer};
+use rsdparsa::attribute_type::SdpAttribute;
+use rsdparsa::error::SdpParserError;
+use rsdparsa::media_type::{SdpMediaValue, SdpProtocolValue};
+use rsdparsa::{SdpBandwidth, SdpSession, SdpTiming};
+
+#[macro_use]
+extern crate log;
+
+pub mod attribute;
+pub mod media_section;
+pub mod network;
+pub mod types;
+
+use network::{
+ get_bandwidth, origin_view_helper, RustAddressType, RustSdpConnection, RustSdpOrigin,
+};
+pub use types::{StringView, NULL_STRING};
+
+#[no_mangle]
+pub unsafe extern "C" fn parse_sdp(
+ sdp: StringView,
+ fail_on_warning: bool,
+ session: *mut *const SdpSession,
+ parser_error: *mut *const SdpParserError,
+) -> nsresult {
+ let sdp_str: String = match sdp.try_into() {
+ Ok(string) => string,
+ Err(boxed_error) => {
+ *session = ptr::null();
+ *parser_error = Box::into_raw(Box::new(SdpParserError::Sequence {
+ message: format!("{}", boxed_error),
+ line_number: 0,
+ }));
+ return NS_ERROR_INVALID_ARG;
+ }
+ };
+
+ let parser_result = rsdparsa::parse_sdp(&sdp_str, fail_on_warning);
+ match parser_result {
+ Ok(mut parsed) => {
+ *parser_error = match parsed.warnings.len() {
+ 0 => ptr::null(),
+ _ => Box::into_raw(Box::new(parsed.warnings.remove(0))),
+ };
+ *session = Rc::into_raw(Rc::new(parsed));
+ NS_OK
+ }
+ Err(e) => {
+ *session = ptr::null();
+ error!("Error parsing SDP in rust: {}", e);
+ *parser_error = Box::into_raw(Box::new(e));
+ NS_ERROR_INVALID_ARG
+ }
+ }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn create_anonymized_sdp_clone(
+ session: *const SdpSession,
+) -> *const SdpSession {
+ Rc::into_raw(Rc::new(
+ (*session).masked_clone(&mut StatefulSdpAnonymizer::new()),
+ ))
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn create_sdp_clone(session: *const SdpSession) -> *const SdpSession {
+ Rc::into_raw(Rc::new((*session).clone()))
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn sdp_free_session(sdp_ptr: *mut SdpSession) {
+ let sdp = Rc::from_raw(sdp_ptr);
+ drop(sdp);
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn sdp_new_reference(session: *mut SdpSession) -> *const SdpSession {
+ let original = Rc::from_raw(session);
+ let ret = Rc::into_raw(Rc::clone(&original));
+ Rc::into_raw(original); // So the original reference doesn't get dropped
+ ret
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn sdp_get_error_line_num(parser_error: *mut SdpParserError) -> size_t {
+ match *parser_error {
+ SdpParserError::Line { line_number, .. }
+ | SdpParserError::Unsupported { line_number, .. }
+ | SdpParserError::Sequence { line_number, .. } => line_number,
+ }
+}
+
+#[no_mangle]
+// Callee must check that a nullptr is not returned
+pub unsafe extern "C" fn sdp_get_error_message(parser_error: *mut SdpParserError) -> *mut c_char {
+ let message = format!("{}", *parser_error);
+ return match CString::new(message.as_str()) {
+ Ok(c_char_ptr) => c_char_ptr.into_raw(),
+ Err(_) => 0 as *mut c_char,
+ };
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn sdp_free_error_message(message: *mut c_char) {
+ if message != 0 as *mut c_char {
+ let _tmp = CString::from_raw(message);
+ }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn sdp_free_error(parser_error: *mut SdpParserError) {
+ let e = Box::from_raw(parser_error);
+ drop(e);
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn get_version(session: *const SdpSession) -> u64 {
+ (*session).get_version()
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn sdp_get_origin(session: *const SdpSession) -> RustSdpOrigin {
+ origin_view_helper((*session).get_origin())
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn session_view(session: *const SdpSession) -> StringView {
+ StringView::from((*session).get_session())
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn sdp_session_has_connection(session: *const SdpSession) -> bool {
+ (*session).connection.is_some()
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn sdp_get_session_connection(
+ session: *const SdpSession,
+ connection: *mut RustSdpConnection,
+) -> nsresult {
+ match (*session).connection {
+ Some(ref c) => {
+ *connection = RustSdpConnection::from(c);
+ NS_OK
+ }
+ None => NS_ERROR_INVALID_ARG,
+ }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn sdp_add_media_section(
+ session: *mut SdpSession,
+ media_type: u32,
+ direction: u32,
+ port: u16,
+ protocol: u32,
+ addr_type: u32,
+ address: StringView,
+) -> nsresult {
+ let addr_type = match RustAddressType::try_from(addr_type) {
+ Ok(a) => a.into(),
+ Err(e) => {
+ return e;
+ }
+ };
+ let address_string: String = match address.try_into() {
+ Ok(x) => x,
+ Err(boxed_error) => {
+ error!("Error while parsing string, description: {}", boxed_error);
+ return NS_ERROR_INVALID_ARG;
+ }
+ };
+ let address = match ExplicitlyTypedAddress::try_from((addr_type, address_string.as_str())) {
+ Ok(a) => a,
+ Err(_) => {
+ return NS_ERROR_INVALID_ARG;
+ }
+ };
+
+ let media_type = match media_type {
+ 0 => SdpMediaValue::Audio, // MediaType::kAudio
+ 1 => SdpMediaValue::Video, // MediaType::kVideo
+ 3 => SdpMediaValue::Application, // MediaType::kApplication
+ _ => {
+ return NS_ERROR_INVALID_ARG;
+ }
+ };
+ let protocol = match protocol {
+ 20 => SdpProtocolValue::RtpSavpf, // Protocol::kRtpSavpf
+ 21 => SdpProtocolValue::UdpTlsRtpSavp, // Protocol::kUdpTlsRtpSavp
+ 22 => SdpProtocolValue::TcpDtlsRtpSavp, // Protocol::kTcpDtlsRtpSavp
+ 24 => SdpProtocolValue::UdpTlsRtpSavpf, // Protocol::kUdpTlsRtpSavpf
+ 25 => SdpProtocolValue::TcpDtlsRtpSavpf, // Protocol::kTcpTlsRtpSavpf
+ 37 => SdpProtocolValue::DtlsSctp, // Protocol::kDtlsSctp
+ 38 => SdpProtocolValue::UdpDtlsSctp, // Protocol::kUdpDtlsSctp
+ 39 => SdpProtocolValue::TcpDtlsSctp, // Protocol::kTcpDtlsSctp
+ _ => {
+ return NS_ERROR_INVALID_ARG;
+ }
+ };
+ let direction = match direction {
+ 1 => SdpAttribute::Sendonly,
+ 2 => SdpAttribute::Recvonly,
+ 3 => SdpAttribute::Sendrecv,
+ _ => {
+ return NS_ERROR_INVALID_ARG;
+ }
+ };
+
+ match (*session).add_media(media_type, direction, port as u32, protocol, address) {
+ Ok(_) => NS_OK,
+ Err(_) => NS_ERROR_INVALID_ARG,
+ }
+}
+
+#[repr(C)]
+#[derive(Clone)]
+pub struct RustSdpTiming {
+ pub start: u64,
+ pub stop: u64,
+}
+
+impl<'a> From<&'a SdpTiming> for RustSdpTiming {
+ fn from(timing: &SdpTiming) -> Self {
+ RustSdpTiming {
+ start: timing.start,
+ stop: timing.stop,
+ }
+ }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn sdp_session_has_timing(session: *const SdpSession) -> bool {
+ (*session).timing.is_some()
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn sdp_session_timing(
+ session: *const SdpSession,
+ timing: *mut RustSdpTiming,
+) -> nsresult {
+ match (*session).timing {
+ Some(ref t) => {
+ *timing = RustSdpTiming::from(t);
+ NS_OK
+ }
+ None => NS_ERROR_INVALID_ARG,
+ }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn sdp_media_section_count(session: *const SdpSession) -> size_t {
+ (*session).media.len()
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn get_sdp_bandwidth(
+ session: *const SdpSession,
+ bandwidth_type: *const c_char,
+) -> u32 {
+ get_bandwidth(&(*session).bandwidth, bandwidth_type)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn sdp_get_session_bandwidth_vec(
+ session: *const SdpSession,
+) -> *const Vec<SdpBandwidth> {
+ &(*session).bandwidth
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn get_sdp_session_attributes(
+ session: *const SdpSession,
+) -> *const Vec<SdpAttribute> {
+ &(*session).attribute
+}
diff --git a/dom/media/webrtc/sdp/rsdparsa_capi/src/media_section.rs b/dom/media/webrtc/sdp/rsdparsa_capi/src/media_section.rs
new file mode 100644
index 0000000000..8429ab2815
--- /dev/null
+++ b/dom/media/webrtc/sdp/rsdparsa_capi/src/media_section.rs
@@ -0,0 +1,233 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use std::convert::TryInto;
+use std::os::raw::c_char;
+use std::ptr;
+
+use libc::size_t;
+
+use nserror::{nsresult, NS_ERROR_INVALID_ARG, NS_OK};
+use rsdparsa::attribute_type::{SdpAttribute, SdpAttributeRtpmap};
+use rsdparsa::media_type::{SdpFormatList, SdpMedia, SdpMediaValue, SdpProtocolValue};
+use rsdparsa::{SdpBandwidth, SdpSession};
+
+use network::{get_bandwidth, RustSdpConnection};
+use types::StringView;
+
+#[no_mangle]
+pub unsafe extern "C" fn sdp_get_media_section(
+ session: *const SdpSession,
+ index: size_t,
+) -> *const SdpMedia {
+ return match (*session).media.get(index) {
+ Some(m) => m,
+ None => ptr::null(),
+ };
+}
+
+#[repr(C)]
+#[derive(Clone, Copy)]
+pub enum RustSdpMediaValue {
+ Audio,
+ Video,
+ Application,
+}
+
+impl<'a> From<&'a SdpMediaValue> for RustSdpMediaValue {
+ fn from(val: &SdpMediaValue) -> Self {
+ match *val {
+ SdpMediaValue::Audio => RustSdpMediaValue::Audio,
+ SdpMediaValue::Video => RustSdpMediaValue::Video,
+ SdpMediaValue::Application => RustSdpMediaValue::Application,
+ }
+ }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn sdp_rust_get_media_type(sdp_media: *const SdpMedia) -> RustSdpMediaValue {
+ RustSdpMediaValue::from((*sdp_media).get_type())
+}
+
+#[repr(C)]
+#[derive(Clone, Copy)]
+pub enum RustSdpProtocolValue {
+ RtpSavpf,
+ UdpTlsRtpSavp,
+ TcpDtlsRtpSavp,
+ UdpTlsRtpSavpf,
+ TcpDtlsRtpSavpf,
+ DtlsSctp,
+ UdpDtlsSctp,
+ TcpDtlsSctp,
+ RtpAvp,
+ RtpAvpf,
+ RtpSavp,
+}
+
+impl<'a> From<&'a SdpProtocolValue> for RustSdpProtocolValue {
+ fn from(val: &SdpProtocolValue) -> Self {
+ match *val {
+ SdpProtocolValue::RtpSavpf => RustSdpProtocolValue::RtpSavpf,
+ SdpProtocolValue::UdpTlsRtpSavp => RustSdpProtocolValue::UdpTlsRtpSavp,
+ SdpProtocolValue::TcpDtlsRtpSavp => RustSdpProtocolValue::TcpDtlsRtpSavp,
+ SdpProtocolValue::UdpTlsRtpSavpf => RustSdpProtocolValue::UdpTlsRtpSavpf,
+ SdpProtocolValue::TcpDtlsRtpSavpf => RustSdpProtocolValue::TcpDtlsRtpSavpf,
+ SdpProtocolValue::DtlsSctp => RustSdpProtocolValue::DtlsSctp,
+ SdpProtocolValue::UdpDtlsSctp => RustSdpProtocolValue::UdpDtlsSctp,
+ SdpProtocolValue::TcpDtlsSctp => RustSdpProtocolValue::TcpDtlsSctp,
+ SdpProtocolValue::RtpAvp => RustSdpProtocolValue::RtpAvp,
+ SdpProtocolValue::RtpAvpf => RustSdpProtocolValue::RtpAvpf,
+ SdpProtocolValue::RtpSavp => RustSdpProtocolValue::RtpSavp,
+ }
+ }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn sdp_get_media_protocol(
+ sdp_media: *const SdpMedia,
+) -> RustSdpProtocolValue {
+ RustSdpProtocolValue::from((*sdp_media).get_proto())
+}
+
+#[repr(C)]
+#[derive(Clone, Copy)]
+pub enum RustSdpFormatType {
+ Integers,
+ Strings,
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn sdp_get_format_type(sdp_media: *const SdpMedia) -> RustSdpFormatType {
+ match *(*sdp_media).get_formats() {
+ SdpFormatList::Integers(_) => RustSdpFormatType::Integers,
+ SdpFormatList::Strings(_) => RustSdpFormatType::Strings,
+ }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn sdp_get_format_string_vec(
+ sdp_media: *const SdpMedia,
+) -> *const Vec<String> {
+ if let SdpFormatList::Strings(ref formats) = *(*sdp_media).get_formats() {
+ formats
+ } else {
+ ptr::null()
+ }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn sdp_get_format_u32_vec(sdp_media: *const SdpMedia) -> *const Vec<u32> {
+ if let SdpFormatList::Integers(ref formats) = *(*sdp_media).get_formats() {
+ formats
+ } else {
+ ptr::null()
+ }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn sdp_set_media_port(sdp_media: *mut SdpMedia, port: u32) {
+ (*sdp_media).set_port(port);
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn sdp_get_media_port(sdp_media: *const SdpMedia) -> u32 {
+ (*sdp_media).get_port()
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn sdp_get_media_port_count(sdp_media: *const SdpMedia) -> u32 {
+ (*sdp_media).get_port_count()
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn sdp_get_media_bandwidth(
+ sdp_media: *const SdpMedia,
+ bandwidth_type: *const c_char,
+) -> u32 {
+ get_bandwidth((*sdp_media).get_bandwidth(), bandwidth_type)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn sdp_get_media_bandwidth_vec(
+ sdp_media: *const SdpMedia,
+) -> *const Vec<SdpBandwidth> {
+ (*sdp_media).get_bandwidth()
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn sdp_media_has_connection(sdp_media: *const SdpMedia) -> bool {
+ (*sdp_media).get_connection().is_some()
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn sdp_get_media_connection(
+ sdp_media: *const SdpMedia,
+ ret: *mut RustSdpConnection,
+) -> nsresult {
+ if let &Some(ref connection) = (*sdp_media).get_connection() {
+ *ret = RustSdpConnection::from(connection);
+ return NS_OK;
+ }
+ NS_ERROR_INVALID_ARG
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn sdp_get_media_attribute_list(
+ sdp_media: *const SdpMedia,
+) -> *const Vec<SdpAttribute> {
+ (*sdp_media).get_attributes()
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn sdp_media_clear_codecs(sdp_media: *mut SdpMedia) {
+ (*sdp_media).remove_codecs()
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn sdp_media_add_codec(
+ sdp_media: *mut SdpMedia,
+ pt: u8,
+ codec_name: StringView,
+ clockrate: u32,
+ channels: u16,
+) -> nsresult {
+ let rtpmap = SdpAttributeRtpmap {
+ payload_type: pt,
+ codec_name: match codec_name.try_into() {
+ Ok(x) => x,
+ Err(boxed_error) => {
+ error!("Error while parsing string, description: {}", boxed_error);
+ return NS_ERROR_INVALID_ARG;
+ }
+ },
+ frequency: clockrate,
+ channels: Some(channels as u32),
+ };
+
+ match (*sdp_media).add_codec(rtpmap) {
+ Ok(_) => NS_OK,
+ Err(_) => NS_ERROR_INVALID_ARG,
+ }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn sdp_media_add_datachannel(
+ sdp_media: *mut SdpMedia,
+ name: StringView,
+ port: u16,
+ streams: u16,
+ message_size: u32,
+) -> nsresult {
+ let name_str = match name.try_into() {
+ Ok(x) => x,
+ Err(_) => {
+ return NS_ERROR_INVALID_ARG;
+ }
+ };
+ match (*sdp_media).add_datachannel(name_str, port, streams, message_size) {
+ Ok(_) => NS_OK,
+ Err(_) => NS_ERROR_INVALID_ARG,
+ }
+}
diff --git a/dom/media/webrtc/sdp/rsdparsa_capi/src/network.rs b/dom/media/webrtc/sdp/rsdparsa_capi/src/network.rs
new file mode 100644
index 0000000000..970f7c8dec
--- /dev/null
+++ b/dom/media/webrtc/sdp/rsdparsa_capi/src/network.rs
@@ -0,0 +1,266 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+extern crate nserror;
+
+use std::ffi::{CStr, CString};
+use std::net::IpAddr;
+use std::os::raw::c_char;
+
+use rsdparsa::address::{Address, AddressType, AddressTyped, ExplicitlyTypedAddress};
+use rsdparsa::{SdpBandwidth, SdpConnection, SdpOrigin};
+use std::convert::TryFrom;
+use types::{StringView, NULL_STRING};
+
+#[repr(C)]
+#[derive(Clone, Copy, PartialEq)]
+pub enum RustAddressType {
+ IP4,
+ IP6,
+}
+
+impl TryFrom<u32> for RustAddressType {
+ type Error = nserror::nsresult;
+ fn try_from(address_type: u32) -> Result<Self, Self::Error> {
+ match address_type {
+ 1 => Ok(RustAddressType::IP4),
+ 2 => Ok(RustAddressType::IP6),
+ _ => Err(nserror::NS_ERROR_INVALID_ARG),
+ }
+ }
+}
+
+impl From<AddressType> for RustAddressType {
+ fn from(address_type: AddressType) -> Self {
+ match address_type {
+ AddressType::IpV4 => RustAddressType::IP4,
+ AddressType::IpV6 => RustAddressType::IP6,
+ }
+ }
+}
+
+impl Into<AddressType> for RustAddressType {
+ fn into(self) -> AddressType {
+ match self {
+ RustAddressType::IP4 => AddressType::IpV4,
+ RustAddressType::IP6 => AddressType::IpV6,
+ }
+ }
+}
+
+impl<'a> From<&'a IpAddr> for RustAddressType {
+ fn from(addr: &IpAddr) -> RustAddressType {
+ addr.address_type().into()
+ }
+}
+
+pub fn get_octets(addr: &IpAddr) -> [u8; 16] {
+ let mut octets = [0; 16];
+ match *addr {
+ IpAddr::V4(v4_addr) => {
+ let v4_octets = v4_addr.octets();
+ (&mut octets[0..4]).copy_from_slice(&v4_octets);
+ }
+ IpAddr::V6(v6_addr) => {
+ let v6_octets = v6_addr.octets();
+ octets.copy_from_slice(&v6_octets);
+ }
+ }
+ octets
+}
+
+#[repr(C)]
+pub struct RustAddress {
+ ip_address: [u8; 50],
+ fqdn: StringView,
+ is_fqdn: bool,
+}
+
+impl<'a> From<&'a Address> for RustAddress {
+ fn from(address: &Address) -> Self {
+ match address {
+ Address::Ip(ip) => Self::from(ip),
+ Address::Fqdn(fqdn) => Self {
+ ip_address: [0; 50],
+ fqdn: fqdn.as_str().into(),
+ is_fqdn: true,
+ },
+ }
+ }
+}
+
+impl<'a> From<&'a IpAddr> for RustAddress {
+ fn from(addr: &IpAddr) -> Self {
+ let mut c_addr = [0; 50];
+ let str_addr = format!("{}", addr);
+ let str_bytes = str_addr.as_bytes();
+ if str_bytes.len() < 50 {
+ c_addr[..str_bytes.len()].copy_from_slice(&str_bytes);
+ }
+ Self {
+ ip_address: c_addr,
+ fqdn: NULL_STRING,
+ is_fqdn: false,
+ }
+ }
+}
+
+#[repr(C)]
+pub struct RustExplicitlyTypedAddress {
+ address_type: RustAddressType,
+ address: RustAddress,
+}
+
+impl Default for RustExplicitlyTypedAddress {
+ fn default() -> Self {
+ Self {
+ address_type: RustAddressType::IP4,
+ address: RustAddress {
+ ip_address: [0; 50],
+ fqdn: NULL_STRING,
+ is_fqdn: false,
+ },
+ }
+ }
+}
+
+impl<'a> From<&'a ExplicitlyTypedAddress> for RustExplicitlyTypedAddress {
+ fn from(address: &ExplicitlyTypedAddress) -> Self {
+ match address {
+ ExplicitlyTypedAddress::Fqdn { domain, .. } => Self {
+ address_type: address.address_type().into(),
+ address: RustAddress {
+ ip_address: [0; 50],
+ fqdn: StringView::from(domain.as_str()),
+ is_fqdn: true,
+ },
+ },
+ ExplicitlyTypedAddress::Ip(ip_address) => Self {
+ address_type: ip_address.address_type().into(),
+ address: ip_address.into(),
+ },
+ }
+ }
+}
+
+// TODO @@NG remove
+impl<'a> From<&'a Option<IpAddr>> for RustExplicitlyTypedAddress {
+ fn from(addr: &Option<IpAddr>) -> Self {
+ match *addr {
+ Some(ref x) => Self {
+ address_type: RustAddressType::from(x.address_type()),
+ address: RustAddress::from(x),
+ },
+ None => Self::default(),
+ }
+ }
+}
+
+#[repr(C)]
+pub struct RustSdpConnection {
+ pub addr: RustExplicitlyTypedAddress,
+ pub ttl: u8,
+ pub amount: u64,
+}
+
+impl<'a> From<&'a SdpConnection> for RustSdpConnection {
+ fn from(sdp_connection: &SdpConnection) -> Self {
+ let ttl = match sdp_connection.ttl {
+ Some(x) => x as u8,
+ None => 0,
+ };
+ let amount = match sdp_connection.amount {
+ Some(x) => x as u64,
+ None => 0,
+ };
+ RustSdpConnection {
+ addr: RustExplicitlyTypedAddress::from(&sdp_connection.address),
+ ttl: ttl,
+ amount: amount,
+ }
+ }
+}
+
+#[repr(C)]
+pub struct RustSdpOrigin {
+ username: StringView,
+ session_id: u64,
+ session_version: u64,
+ addr: RustExplicitlyTypedAddress,
+}
+
+fn bandwidth_match(str_bw: &str, enum_bw: &SdpBandwidth) -> bool {
+ match *enum_bw {
+ SdpBandwidth::As(_) => str_bw == "AS",
+ SdpBandwidth::Ct(_) => str_bw == "CT",
+ SdpBandwidth::Tias(_) => str_bw == "TIAS",
+ SdpBandwidth::Unknown(ref type_name, _) => str_bw == type_name,
+ }
+}
+
+fn bandwidth_value(bandwidth: &SdpBandwidth) -> u32 {
+ match *bandwidth {
+ SdpBandwidth::As(x) | SdpBandwidth::Ct(x) | SdpBandwidth::Tias(x) => x,
+ SdpBandwidth::Unknown(_, _) => 0,
+ }
+}
+
+pub unsafe fn get_bandwidth(bandwidths: &Vec<SdpBandwidth>, bandwidth_type: *const c_char) -> u32 {
+ let bw_type = match CStr::from_ptr(bandwidth_type).to_str() {
+ Ok(string) => string,
+ Err(_) => return 0,
+ };
+ for bandwidth in bandwidths.iter() {
+ if bandwidth_match(bw_type, bandwidth) {
+ return bandwidth_value(bandwidth);
+ }
+ }
+ 0
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn sdp_serialize_bandwidth(bw: *const Vec<SdpBandwidth>) -> *mut c_char {
+ let mut builder = String::new();
+ for bandwidth in (*bw).iter() {
+ match *bandwidth {
+ SdpBandwidth::As(val) => {
+ builder.push_str("b=AS:");
+ builder.push_str(&val.to_string());
+ builder.push_str("\r\n");
+ }
+ SdpBandwidth::Ct(val) => {
+ builder.push_str("b=CT:");
+ builder.push_str(&val.to_string());
+ builder.push_str("\r\n");
+ }
+ SdpBandwidth::Tias(val) => {
+ builder.push_str("b=TIAS:");
+ builder.push_str(&val.to_string());
+ builder.push_str("\r\n");
+ }
+ SdpBandwidth::Unknown(ref name, val) => {
+ builder.push_str("b=");
+ builder.push_str(name.as_str());
+ builder.push(':');
+ builder.push_str(&val.to_string());
+ builder.push_str("\r\n");
+ }
+ }
+ }
+ CString::from_vec_unchecked(builder.into_bytes()).into_raw()
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn sdp_free_string(s: *mut c_char) {
+ drop(CString::from_raw(s));
+}
+
+pub unsafe fn origin_view_helper(origin: &SdpOrigin) -> RustSdpOrigin {
+ RustSdpOrigin {
+ username: StringView::from(origin.username.as_str()),
+ session_id: origin.session_id,
+ session_version: origin.session_version,
+ addr: RustExplicitlyTypedAddress::from(&origin.unicast_addr),
+ }
+}
diff --git a/dom/media/webrtc/sdp/rsdparsa_capi/src/types.rs b/dom/media/webrtc/sdp/rsdparsa_capi/src/types.rs
new file mode 100644
index 0000000000..2522c8333d
--- /dev/null
+++ b/dom/media/webrtc/sdp/rsdparsa_capi/src/types.rs
@@ -0,0 +1,199 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use libc::size_t;
+use std::boxed::Box;
+use std::convert::TryInto;
+use std::error::Error;
+use std::ffi::CStr;
+use std::{slice, str};
+
+use nserror::{nsresult, NS_ERROR_INVALID_ARG, NS_OK};
+
+use rsdparsa::attribute_type::SdpAttributeSsrc;
+
+#[repr(C)]
+#[derive(Clone, Copy)]
+pub struct StringView {
+ buffer: *const u8,
+ len: size_t,
+}
+
+pub const NULL_STRING: StringView = StringView {
+ buffer: 0 as *const u8,
+ len: 0,
+};
+
+impl<'a> From<&'a str> for StringView {
+ fn from(input: &str) -> StringView {
+ StringView {
+ buffer: input.as_ptr(),
+ len: input.len(),
+ }
+ }
+}
+
+impl TryInto<String> for StringView {
+ type Error = Box<dyn Error>;
+ fn try_into(self) -> Result<String, Box<dyn Error>> {
+ // This block must be unsafe as it converts a StringView, most likly provided from the
+ // C++ code, into a rust String and thus needs to operate with raw pointers.
+ let string_slice: &[u8];
+ unsafe {
+ // Add one to the length as the length passed in the StringView is the length of
+ // the string and is missing the null terminator
+ string_slice = slice::from_raw_parts(self.buffer, self.len + 1 as usize);
+ }
+
+ let c_str = match CStr::from_bytes_with_nul(string_slice) {
+ Ok(string) => string,
+ Err(x) => {
+ return Err(Box::new(x));
+ }
+ };
+
+ let str_slice: &str = match str::from_utf8(c_str.to_bytes()) {
+ Ok(string) => string,
+ Err(x) => {
+ return Err(Box::new(x));
+ }
+ };
+
+ Ok(str_slice.to_string())
+ }
+}
+
+impl<'a, T: AsRef<str>> From<&'a Option<T>> for StringView {
+ fn from(input: &Option<T>) -> StringView {
+ match *input {
+ Some(ref x) => StringView {
+ buffer: x.as_ref().as_ptr(),
+ len: x.as_ref().len(),
+ },
+ None => NULL_STRING,
+ }
+ }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn string_vec_len(vec: *const Vec<String>) -> size_t {
+ (*vec).len() as size_t
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn string_vec_get_view(
+ vec: *const Vec<String>,
+ index: size_t,
+ ret: *mut StringView,
+) -> nsresult {
+ match (*vec).get(index) {
+ Some(ref string) => {
+ *ret = StringView::from(string.as_str());
+ NS_OK
+ }
+ None => NS_ERROR_INVALID_ARG,
+ }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn free_boxed_string_vec(ptr: *mut Vec<String>) -> nsresult {
+ drop(Box::from_raw(ptr));
+ NS_OK
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn f32_vec_len(vec: *const Vec<f32>) -> size_t {
+ (*vec).len()
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn f32_vec_get(
+ vec: *const Vec<f32>,
+ index: size_t,
+ ret: *mut f32,
+) -> nsresult {
+ match (*vec).get(index) {
+ Some(val) => {
+ *ret = *val;
+ NS_OK
+ }
+ None => NS_ERROR_INVALID_ARG,
+ }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn u32_vec_len(vec: *const Vec<u32>) -> size_t {
+ (*vec).len()
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn u32_vec_get(
+ vec: *const Vec<u32>,
+ index: size_t,
+ ret: *mut u32,
+) -> nsresult {
+ match (*vec).get(index) {
+ Some(val) => {
+ *ret = *val;
+ NS_OK
+ }
+ None => NS_ERROR_INVALID_ARG,
+ }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn u16_vec_len(vec: *const Vec<u16>) -> size_t {
+ (*vec).len()
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn u16_vec_get(
+ vec: *const Vec<u16>,
+ index: size_t,
+ ret: *mut u16,
+) -> nsresult {
+ match (*vec).get(index) {
+ Some(val) => {
+ *ret = *val;
+ NS_OK
+ }
+ None => NS_ERROR_INVALID_ARG,
+ }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn u8_vec_len(vec: *const Vec<u8>) -> size_t {
+ (*vec).len()
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn u8_vec_get(vec: *const Vec<u8>, index: size_t, ret: *mut u8) -> nsresult {
+ match (*vec).get(index) {
+ Some(val) => {
+ *ret = *val;
+ NS_OK
+ }
+ None => NS_ERROR_INVALID_ARG,
+ }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn ssrc_vec_len(vec: *const Vec<SdpAttributeSsrc>) -> size_t {
+ (*vec).len()
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn ssrc_vec_get_id(
+ vec: *const Vec<SdpAttributeSsrc>,
+ index: size_t,
+ ret: *mut u32,
+) -> nsresult {
+ match (*vec).get(index) {
+ Some(val) => {
+ *ret = val.id;
+ NS_OK
+ }
+ None => NS_ERROR_INVALID_ARG,
+ }
+}