diff --git a/api/jsep.h b/api/jsep.h index acfffd4d0a..c05f3829ae 100644 --- a/api/jsep.h +++ b/api/jsep.h @@ -22,9 +22,11 @@ #include +#include #include #include +#include "api/optional.h" #include "rtc_base/refcount.h" namespace cricket { @@ -55,7 +57,7 @@ class IceCandidateInterface { // m= section in SDP, which identifies the m= section. virtual std::string sdp_mid() const = 0; // This indicates the index (starting at zero) of m= section this candidate - // is assocated with. Needed when an endpoint doesn't support MIDs. + // is associated with. Needed when an endpoint doesn't support MIDs. virtual int sdp_mline_index() const = 0; // Only for use internally. virtual const cricket::Candidate& candidate() const = 0; @@ -86,6 +88,26 @@ class IceCandidateCollection { virtual const IceCandidateInterface* at(size_t index) const = 0; }; +// Enum that describes the type of the SessionDescriptionInterface. +// Corresponds to RTCSdpType in the WebRTC specification. +// https://w3c.github.io/webrtc-pc/#dom-rtcsdptype +enum class SdpType { + kOffer, // Description must be treated as an SDP offer. + kPrAnswer, // Description must be treated as an SDP answer, but not a final + // answer. + kAnswer // Description must be treated as an SDP final answer, and the offer- + // answer exchange must be considered complete after receiving this. +}; + +// Returns the string form of the given SDP type. String forms are defined in +// SessionDescriptionInterface. +const char* SdpTypeToString(SdpType type); + +// Returns the SdpType from its string form. The string form can be one of the +// constants defined in SessionDescriptionInterface. Passing in any other string +// results in nullopt. +rtc::Optional SdpTypeFromString(const std::string& type_str); + // Class representation of an SDP session description. // // An instance of this interface is supposed to be owned by one class at a time @@ -94,7 +116,7 @@ class IceCandidateCollection { // An instance can be created by CreateSessionDescription. class SessionDescriptionInterface { public: - // Supported types: + // String representations of the supported SDP types. static const char kOffer[]; static const char kPrAnswer[]; static const char kAnswer[]; @@ -110,7 +132,14 @@ class SessionDescriptionInterface { virtual std::string session_id() const = 0; virtual std::string session_version() const = 0; + // Returns the type of this session description as an SdpType. Descriptions of + // the various types are found in the SdpType documentation. + // TODO(steveanton): Remove default implementation once Chromium has been + // updated. + virtual SdpType GetType() const; + // kOffer/kPrAnswer/kAnswer + // TODO(steveanton): Remove this in favor of |GetType| that returns SdpType. virtual std::string type() const = 0; // Adds the specified candidate to the description. @@ -143,10 +172,24 @@ class SessionDescriptionInterface { // Creates a SessionDescriptionInterface based on the SDP string and the type. // Returns null if the sdp string can't be parsed or the type is unsupported. // |error| may be null. +// TODO(steveanton): This function is deprecated. Please use the functions below +// which take an SdpType enum instead. Remove this once it is no longer used. SessionDescriptionInterface* CreateSessionDescription(const std::string& type, const std::string& sdp, SdpParseError* error); +// Creates a SessionDescriptionInterface based on the SDP string and the type. +// Returns null if the SDP string cannot be parsed. +// If using the signature with |error_out|, details of the parsing error may be +// written to |error_out| if it is not null. +std::unique_ptr CreateSessionDescription( + SdpType type, + const std::string& sdp); +std::unique_ptr CreateSessionDescription( + SdpType type, + const std::string& sdp, + SdpParseError* error_out); + // CreateOffer and CreateAnswer callback interface. class CreateSessionDescriptionObserver : public rtc::RefCountInterface { public: diff --git a/api/jsepsessiondescription.h b/api/jsepsessiondescription.h index 6b115ee2e9..70bb27710a 100644 --- a/api/jsepsessiondescription.h +++ b/api/jsepsessiondescription.h @@ -32,6 +32,8 @@ namespace webrtc { // Implementation of SessionDescriptionInterface. class JsepSessionDescription : public SessionDescriptionInterface { public: + explicit JsepSessionDescription(SdpType type); + // TODO(steveanton): Remove this once callers have switched to SdpType. explicit JsepSessionDescription(const std::string& type); virtual ~JsepSessionDescription(); @@ -54,11 +56,9 @@ class JsepSessionDescription : public SessionDescriptionInterface { virtual std::string session_version() const { return session_version_; } - virtual std::string type() const { - return type_; - } + virtual SdpType GetType() const { return type_; } + virtual std::string type() const { return SdpTypeToString(type_); } // Allows changing the type. Used for testing. - void set_type(const std::string& type) { type_ = type; } virtual bool AddCandidate(const IceCandidateInterface* candidate); virtual size_t RemoveCandidates( const std::vector& candidates); @@ -74,7 +74,7 @@ class JsepSessionDescription : public SessionDescriptionInterface { std::unique_ptr description_; std::string session_id_; std::string session_version_; - std::string type_; + SdpType type_; std::vector candidate_collection_; bool GetMediasectionIndex(const IceCandidateInterface* candidate, diff --git a/pc/jsepsessiondescription.cc b/pc/jsepsessiondescription.cc index e6b6d32bf5..5e6a1cb203 100644 --- a/pc/jsepsessiondescription.cc +++ b/pc/jsepsessiondescription.cc @@ -12,46 +12,31 @@ #include +#include "p2p/base/port.h" #include "pc/mediasession.h" #include "pc/webrtcsdp.h" -#include "p2p/base/port.h" #include "rtc_base/arraysize.h" +#include "rtc_base/ptr_util.h" #include "rtc_base/stringencode.h" using cricket::SessionDescription; namespace webrtc { - -static const char* kSupportedTypes[] = { - JsepSessionDescription::kOffer, - JsepSessionDescription::kPrAnswer, - JsepSessionDescription::kAnswer -}; - -static bool IsTypeSupported(const std::string& type) { - bool type_supported = false; - for (size_t i = 0; i < arraysize(kSupportedTypes); ++i) { - if (kSupportedTypes[i] == type) { - type_supported = true; - break; - } - } - return type_supported; -} +namespace { // RFC 5245 // It is RECOMMENDED that default candidates be chosen based on the // likelihood of those candidates to work with the peer that is being // contacted. It is RECOMMENDED that relayed > reflexive > host. -static const int kPreferenceUnknown = 0; -static const int kPreferenceHost = 1; -static const int kPreferenceReflexive = 2; -static const int kPreferenceRelayed = 3; +constexpr int kPreferenceUnknown = 0; +constexpr int kPreferenceHost = 1; +constexpr int kPreferenceReflexive = 2; +constexpr int kPreferenceRelayed = 3; -static const char kDummyAddress[] = "0.0.0.0"; -static const int kDummyPort = 9; +constexpr char kDummyAddress[] = "0.0.0.0"; +constexpr int kDummyPort = 9; -static int GetCandidatePreferenceFromType(const std::string& type) { +int GetCandidatePreferenceFromType(const std::string& type) { int preference = kPreferenceUnknown; if (type == cricket::LOCAL_PORT_TYPE) { preference = kPreferenceHost; @@ -67,7 +52,7 @@ static int GetCandidatePreferenceFromType(const std::string& type) { // Update the connection address for the MediaContentDescription based on the // candidates. -static void UpdateConnectionAddress( +void UpdateConnectionAddress( const JsepCandidateCollection& candidate_collection, cricket::ContentDescription* content_description) { int port = kDummyPort; @@ -107,6 +92,8 @@ static void UpdateConnectionAddress( ->set_connection_address(connection_addr); } +} // namespace + const char SessionDescriptionInterface::kOffer[] = "offer"; const char SessionDescriptionInterface::kPrAnswer[] = "pranswer"; const char SessionDescriptionInterface::kAnswer[] = "answer"; @@ -114,23 +101,85 @@ const char SessionDescriptionInterface::kAnswer[] = "answer"; const int JsepSessionDescription::kDefaultVideoCodecId = 100; const char JsepSessionDescription::kDefaultVideoCodecName[] = "VP8"; +const char* SdpTypeToString(SdpType type) { + switch (type) { + case SdpType::kOffer: + return SessionDescriptionInterface::kOffer; + case SdpType::kPrAnswer: + return SessionDescriptionInterface::kPrAnswer; + case SdpType::kAnswer: + return SessionDescriptionInterface::kAnswer; + } + return ""; +} + +rtc::Optional SdpTypeFromString(const std::string& type_str) { + if (type_str == SessionDescriptionInterface::kOffer) { + return SdpType::kOffer; + } else if (type_str == SessionDescriptionInterface::kPrAnswer) { + return SdpType::kPrAnswer; + } else if (type_str == SessionDescriptionInterface::kAnswer) { + return SdpType::kAnswer; + } else { + return rtc::nullopt; + } +} + +// TODO(steveanton): Remove this default implementation once Chromium has been +// updated. +SdpType SessionDescriptionInterface::GetType() const { + rtc::Optional maybe_type = SdpTypeFromString(type()); + if (maybe_type) { + return *maybe_type; + } else { + RTC_LOG(LS_WARNING) << "Default implementation of " + "SessionDescriptionInterface::GetType does not " + "recognize the result from type(), returning " + "kOffer."; + return SdpType::kOffer; + } +} + SessionDescriptionInterface* CreateSessionDescription(const std::string& type, const std::string& sdp, SdpParseError* error) { - if (!IsTypeSupported(type)) { - return NULL; + rtc::Optional maybe_type = SdpTypeFromString(type); + if (!maybe_type) { + return nullptr; } - JsepSessionDescription* jsep_desc = new JsepSessionDescription(type); - if (!SdpDeserialize(sdp, jsep_desc, error)) { - delete jsep_desc; - return NULL; - } - return jsep_desc; + return CreateSessionDescription(*maybe_type, sdp, error).release(); } -JsepSessionDescription::JsepSessionDescription(const std::string& type) - : type_(type) { +std::unique_ptr CreateSessionDescription( + SdpType type, + const std::string& sdp) { + return CreateSessionDescription(type, sdp, nullptr); +} + +std::unique_ptr CreateSessionDescription( + SdpType type, + const std::string& sdp, + SdpParseError* error_out) { + auto jsep_desc = rtc::MakeUnique(type); + if (!SdpDeserialize(sdp, jsep_desc.get(), error_out)) { + return nullptr; + } + return std::move(jsep_desc); +} + +JsepSessionDescription::JsepSessionDescription(SdpType type) : type_(type) {} + +JsepSessionDescription::JsepSessionDescription(const std::string& type) { + rtc::Optional maybe_type = SdpTypeFromString(type); + if (maybe_type) { + type_ = *maybe_type; + } else { + RTC_LOG(LS_WARNING) + << "JsepSessionDescription constructed with invalid type string: " + << type << ". Assuming it is an offer."; + type_ = SdpType::kOffer; + } } JsepSessionDescription::~JsepSessionDescription() {} diff --git a/pc/jsepsessiondescription_unittest.cc b/pc/jsepsessiondescription_unittest.cc index be24dcdb6b..27b2cf1636 100644 --- a/pc/jsepsessiondescription_unittest.cc +++ b/pc/jsepsessiondescription_unittest.cc @@ -22,10 +22,12 @@ #include "rtc_base/gunit.h" #include "rtc_base/stringencode.h" +using ::testing::Values; using webrtc::IceCandidateCollection; using webrtc::IceCandidateInterface; using webrtc::JsepIceCandidate; using webrtc::JsepSessionDescription; +using webrtc::SdpType; using webrtc::SessionDescriptionInterface; static const char kCandidateUfrag[] = "ufrag"; @@ -405,3 +407,20 @@ TEST_F(JsepSessionDescriptionTest, RemoveCandidateAndSetConnectionAddress) { ASSERT_TRUE(jsep_desc_->RemoveCandidates(candidates)); EXPECT_EQ("0.0.0.0:9", media_desc->connection_address().ToString()); } + +class EnumerateAllSdpTypesTest : public ::testing::Test, + public ::testing::WithParamInterface { +}; + +TEST_P(EnumerateAllSdpTypesTest, TestIdentity) { + SdpType type = GetParam(); + + const char* str = webrtc::SdpTypeToString(type); + EXPECT_EQ(type, webrtc::SdpTypeFromString(str)); +} + +INSTANTIATE_TEST_CASE_P(JsepSessionDescriptionTest, + EnumerateAllSdpTypesTest, + Values(SdpType::kOffer, + SdpType::kPrAnswer, + SdpType::kAnswer)); diff --git a/pc/peerconnection_crypto_unittest.cc b/pc/peerconnection_crypto_unittest.cc index 5d5296bcb4..ab341f54af 100644 --- a/pc/peerconnection_crypto_unittest.cc +++ b/pc/peerconnection_crypto_unittest.cc @@ -513,19 +513,6 @@ TEST_F(PeerConnectionCryptoUnitTest, // CreateOffer/CreateAnswer calls are made while waiting for the certificate, // they all finish after the certificate is generated. -// Whether the test will call CreateOffer or CreateAnswer. -enum class SdpType { kOffer, kAnswer }; -std::ostream& operator<<(std::ostream& out, SdpType value) { - switch (value) { - case SdpType::kOffer: - return out << "offer"; - case SdpType::kAnswer: - return out << "answer"; - default: - return out << "unknown"; - } -} - // Whether the certificate will be generated before calling CreateOffer or // while CreateOffer is executing. enum class CertGenTime { kBefore, kDuring };