diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /dom/media/webrtc/sdp | |
parent | Initial commit. (diff) | |
download | firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'dom/media/webrtc/sdp')
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, + } +} |