diff --git a/media/webrtc/signaling/gtest/jsep_session_unittest.cpp b/media/webrtc/signaling/gtest/jsep_session_unittest.cpp index 531d8e87bbbc..15075f2a7b11 100644 --- a/media/webrtc/signaling/gtest/jsep_session_unittest.cpp +++ b/media/webrtc/signaling/gtest/jsep_session_unittest.cpp @@ -11,6 +11,7 @@ #include "nss.h" #include "ssl.h" +#include "mozilla/Preferences.h" #include "mozilla/RefPtr.h" #include "mozilla/Tuple.h" @@ -57,6 +58,9 @@ class JsepSessionTest : public JsepSessionTestBase, public ::testing::WithParamInterface { public: JsepSessionTest() : mSdpHelper(&mLastError) { + Preferences::SetCString("media.peerconnection.sdp.parser", "legacy"); + Preferences::SetCString("media.peerconnection.sdp.alternate_parse_mode", + "never"); mSessionOff = MakeUnique("Offerer", MakeUnique()); mSessionAns = MakeUnique("Answerer", diff --git a/media/webrtc/signaling/src/sdp/HybridSdpParser.cpp b/media/webrtc/signaling/src/sdp/HybridSdpParser.cpp new file mode 100644 index 000000000000..aed0f82d6197 --- /dev/null +++ b/media/webrtc/signaling/src/sdp/HybridSdpParser.cpp @@ -0,0 +1,169 @@ +/* -*- 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 "signaling/src/sdp/HybridSdpParser.h" +#include "signaling/src/sdp/SdpLog.h" +#include "signaling/src/sdp/SipccSdpParser.h" +#include "signaling/src/sdp/RsdparsaSdpParser.h" +#include "signaling/src/sdp/ParsingResultComparer.h" + +#include "mozilla/Logging.h" +#include "mozilla/Preferences.h" + +#include + +namespace mozilla { + +using mozilla::LogLevel; + +// 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 DEFAULT; + + // Supported Parsers + enum class Parsers { + Sipcc, + WebRtcSdp, + }; + + // 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 + }; + + // Finds the mapping between a pref string and pref value, if none exists the + // default is used + template + static auto Pref(const std::string& aPrefName, + const std::unordered_map& 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 { + static const auto values = std::unordered_map{ + {"legacy", Parsers::Sipcc}, + {"webrtc-sdp", Parsers::WebRtcSdp}, + {DEFAULT, Parsers::Sipcc}, + }; + return Pref(PRIMARY_PREF, values); + } + + // The value of the alternate parse mode pref + static auto AlternateParseMode() -> AlternateParseModes { + static const auto values = + std::unordered_map{ + {"parallel", AlternateParseModes::Parallel}, + {"failover", AlternateParseModes::Failover}, + {"never", AlternateParseModes::Never}, + {DEFAULT, AlternateParseModes::Parallel}, + }; + return Pref(ALTERNATE_PREF, values); + } + + public: + // Functions to get the primary, secondary and failover parsers. + // These exist as they do so that the coresponding fields in HybridSdpParser + // can be const initialized. + + // Reads about:config to choose the primary Parser + static auto Primary() -> UniquePtr { + switch (Parser()) { + case Parsers::Sipcc: + return UniquePtr(new SipccSdpParser()); + case Parsers::WebRtcSdp: + return UniquePtr(new RsdparsaSdpParser()); + } + } + + static auto Secondary() -> Maybe> { + if (AlternateParseMode() != AlternateParseModes::Parallel) { + return Nothing(); + } + switch (Parser()) { // Choose whatever the primary parser isn't + case Parsers::Sipcc: + return Some(UniquePtr(new RsdparsaSdpParser())); + case Parsers::WebRtcSdp: + return Some(UniquePtr(new SipccSdpParser())); + } + } + + static auto Failover() -> Maybe> { + if (AlternateParseMode() != AlternateParseModes::Failover) { + return Nothing(); + } + switch (Parser()) { + case Parsers::Sipcc: + return Some(UniquePtr(new RsdparsaSdpParser())); + case Parsers::WebRtcSdp: + return Some(UniquePtr(new SipccSdpParser())); + } + } +}; + +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::DEFAULT = "default"; + +HybridSdpParser::HybridSdpParser() + : 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 { + using Results = UniquePtr; + auto results = mPrimary->Parse(aText); + // Pass results on for comparison and return A if it was a success and B + // otherwise. + auto compare = [&results, &aText](Results&& aResB) -> Results { + ParsingResultComparer::Compare(results, aResB, aText); + return std::move(results->Ok() ? results : aResB); + }; + // Run secondary parser, if there is one, and update selected results. + mSecondary.apply( + [&](auto& sec) { 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 + if (!results->Ok()) { + results = compare(std::move(failover->Parse(aText))); + } + }); + return results; +} + +const std::string HybridSdpParser::PARSER_NAME = "hybrid"; + +} // namespace mozilla diff --git a/media/webrtc/signaling/src/sdp/HybridSdpParser.h b/media/webrtc/signaling/src/sdp/HybridSdpParser.h new file mode 100644 index 000000000000..d292d00b7486 --- /dev/null +++ b/media/webrtc/signaling/src/sdp/HybridSdpParser.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 _HYBRIDSDPPARSER_H_ +#define _HYBRIDSDPPARSER_H_ + +#include "signaling/src/sdp/SdpParser.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 { + public: + HybridSdpParser(); + virtual ~HybridSdpParser() = default; + + auto Name() const -> const std::string& override { return PARSER_NAME; } + auto Parse(const std::string& aText) + -> UniquePtr override; + + private: + const UniquePtr mPrimary; + const Maybe> mSecondary; + const Maybe> mFailover; + static const std::string PARSER_NAME; +}; + +} // namespace mozilla + +#endif \ No newline at end of file diff --git a/media/webrtc/signaling/src/sdp/ParsingResultComparer.cpp b/media/webrtc/signaling/src/sdp/ParsingResultComparer.cpp index 473cd65ffcb6..580307b6a91f 100644 --- a/media/webrtc/signaling/src/sdp/ParsingResultComparer.cpp +++ b/media/webrtc/signaling/src/sdp/ParsingResultComparer.cpp @@ -6,6 +6,8 @@ #include "signaling/src/sdp/Sdp.h" #include "signaling/src/sdp/ParsingResultComparer.h" +#include "signaling/src/sdp/SipccSdpParser.h" +#include "signaling/src/sdp/RsdparsaSdpParser.h" #include #include @@ -302,6 +304,7 @@ bool ParsingResultComparer::CompareAttrLists( return result; } +// TODO Track a tuple of failures? void ParsingResultComparer::TrackRustParsingFailed( size_t sipccErrorCount) const { if (sipccErrorCount) { @@ -315,6 +318,19 @@ void ParsingResultComparer::TrackRustParsingFailed( } } +void ParsingResultComparer::TrackSipccParsingFailed( + size_t webrtcSdpErrorCount) const { + if (webrtcSdpErrorCount) { + Telemetry::ScalarAdd( + Telemetry::ScalarID::WEBRTC_SDP_PARSER_DIFF, + NS_LITERAL_STRING("sipcc_failed__webrtcsdp_has_errors"), 1); + } else { + Telemetry::ScalarAdd(Telemetry::ScalarID::WEBRTC_SDP_PARSER_DIFF, + NS_LITERAL_STRING("sipcc_failed__webrtcsdp_succeeded"), + 1); + } +} + std::vector SplitLines(const std::string& sdp) { std::stringstream ss(sdp); std::string to; diff --git a/media/webrtc/signaling/src/sdp/moz.build b/media/webrtc/signaling/src/sdp/moz.build index ab42a1ec6e65..4a4311a6d687 100644 --- a/media/webrtc/signaling/src/sdp/moz.build +++ b/media/webrtc/signaling/src/sdp/moz.build @@ -30,6 +30,7 @@ LOCAL_INCLUDES += [ ] UNIFIED_SOURCES += [ + 'HybridSdpParser.cpp', 'ParsingResultComparer.cpp', 'SdpAttribute.cpp', 'SdpHelper.cpp',