Implement true negotiation for DatagramTransport with fallback to RTP.

In short, the caller places a x-opaque line in SDP for each m= section that
uses datagram transport.  If the answerer supports datagram transport, it will
parse this line and create a datagram transport.  It will then echo the x-opaque
line into the answer (to indicate that it accepted use of datagram transport).

If the offer and answer contain exactly the same x-opaque line, both peers will
use datagram transport.  If the x-opaque line is omitted from the answer (or is
different in the answer) they will fall back to RTP.

Note that a different x-opaque line in the answer means the answerer did not
understand something in the negotiation proto.  Since WebRTC cannot know what
was misunderstood, or whether it's still possible to use the datagram transport,
it must fall back to RTP.  This may change in the future, possibly by passing
the answer to the datagram transport, but it's good enough for now.

Negotiation consists of four parts:
 1. DatagramTransport exposes transport parameters for both client and server
 perspectives.  The client just echoes what it received from the server (modulo
 any fields it might not have understood).

 2. SDP adds a x-opaque line for opaque transport parameters.  Identical to
 x-mt, but this is specific to datagram transport and goes in each m= section,
 and appears in the answer as well as the offer.
  - This is propagated to Jsep as part of the TransportDescription.
  - SDP files: transport_description.h,cc, transport_description_factory.h,cc,
    media_session.cc, webrtc_sdp.cc

 3. JsepTransport/Controller:
  - Exposes opaque parameters for each mid (m= section).  On offerer, this means
    pre-allocating a datagram transport and getting its parameters.  On the
    answerer, this means echoing the offerer's parameters.
  - Uses a composite RTP transport to receive from either default RTP or
    datagram transport until both offer and answer arrive.
  - If a provisional answer arrives, sets the composite to send on the
    provisionally selected transport.
  - Once both offer and answer are set, deletes the unneeded transports and
    keeps whichever transport is selected.

 4. PeerConnection pulls transport parameters out of Jsep and adds them to SDP.

Bug: webrtc:9719
Change-Id: Id8996eb1871e79d93b7923a5d7eb3431548c798d
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/140700
Commit-Queue: Bjorn Mellem <mellem@webrtc.org>
Reviewed-by: Steve Anton <steveanton@webrtc.org>
Reviewed-by: Anton Sukhanov <sukhanov@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#28182}
This commit is contained in:
Bjorn A Mellem
2019-06-06 17:26:32 -07:00
committed by Commit Bot
parent 9e78458b3f
commit 71c6482baf
23 changed files with 1049 additions and 121 deletions

View File

@ -79,7 +79,8 @@ TransportDescription::TransportDescription(const TransportDescription& from)
ice_pwd(from.ice_pwd),
ice_mode(from.ice_mode),
connection_role(from.connection_role),
identity_fingerprint(CopyFingerprint(from.identity_fingerprint.get())) {}
identity_fingerprint(CopyFingerprint(from.identity_fingerprint.get())),
opaque_parameters(from.opaque_parameters) {}
TransportDescription::~TransportDescription() = default;
@ -96,6 +97,7 @@ TransportDescription& TransportDescription::operator=(
connection_role = from.connection_role;
identity_fingerprint.reset(CopyFingerprint(from.identity_fingerprint.get()));
opaque_parameters = from.opaque_parameters;
return *this;
}

View File

@ -16,6 +16,7 @@
#include <vector>
#include "absl/algorithm/container.h"
#include "absl/types/optional.h"
#include "p2p/base/p2p_constants.h"
#include "rtc_base/ssl_fingerprint.h"
@ -87,6 +88,28 @@ constexpr auto* ICE_OPTION_RENOMINATION = "renomination";
bool StringToConnectionRole(const std::string& role_str, ConnectionRole* role);
bool ConnectionRoleToString(const ConnectionRole& role, std::string* role_str);
// Parameters for an opaque transport protocol which may be plugged into WebRTC.
struct OpaqueTransportParameters {
// Protocol used by this opaque transport. Two endpoints that support the
// same protocol are expected to be able to understand the contents of each
// others' |parameters| fields. If those parameters are compatible, the
// endpoints are expected to use this transport protocol.
std::string protocol;
// Opaque parameters for this transport. These parameters are serialized in a
// manner determined by the |protocol|. They can be parsed and understood by
// the plugin that supports |protocol|.
std::string parameters;
bool operator==(const OpaqueTransportParameters& other) const {
return protocol == other.protocol && parameters == other.parameters;
}
bool operator!=(const OpaqueTransportParameters& other) const {
return !(*this == other);
}
};
struct TransportDescription {
TransportDescription();
TransportDescription(const std::vector<std::string>& transport_options,
@ -133,6 +156,7 @@ struct TransportDescription {
ConnectionRole connection_role;
std::unique_ptr<rtc::SSLFingerprint> identity_fingerprint;
absl::optional<OpaqueTransportParameters> opaque_parameters;
};
} // namespace cricket

View File

@ -55,6 +55,8 @@ std::unique_ptr<TransportDescription> TransportDescriptionFactory::CreateOffer(
}
}
desc->opaque_parameters = options.opaque_parameters;
return desc;
}
@ -108,6 +110,13 @@ std::unique_ptr<TransportDescription> TransportDescriptionFactory::CreateAnswer(
return NULL;
}
// Answers may only attach opaque parameters that exactly match parameters
// present in the offer. If the answerer cannot fully understand or accept
// the offered transport, it must reject it and fall back.
if (offer->opaque_parameters == options.opaque_parameters) {
desc->opaque_parameters = options.opaque_parameters;
}
return desc;
}

View File

@ -29,6 +29,9 @@ struct TransportOptions {
// If true, ICE renomination is supported and will be used if it is also
// supported by the remote side.
bool enable_ice_renomination = false;
// Opaque parameters for plug-in transports.
absl::optional<OpaqueTransportParameters> opaque_parameters;
};
// Creates transport descriptions according to the supplied configuration.

View File

@ -24,6 +24,7 @@
#include "test/gmock.h"
#include "test/gtest.h"
using cricket::OpaqueTransportParameters;
using cricket::TransportDescription;
using cricket::TransportDescriptionFactory;
using cricket::TransportOptions;
@ -207,6 +208,97 @@ TEST_F(TransportDescriptionFactoryTest, TestOfferDtlsReofferDtls) {
CheckDesc(desc.get(), "", old_desc->ice_ufrag, old_desc->ice_pwd, digest_alg);
}
TEST_F(TransportDescriptionFactoryTest, TestOfferOpaqueTransportParameters) {
OpaqueTransportParameters params;
params.protocol = "fake";
params.parameters = "foobar";
TransportOptions options;
options.opaque_parameters = params;
std::unique_ptr<TransportDescription> desc =
f1_.CreateOffer(options, NULL, &ice_credentials_);
CheckDesc(desc.get(), "", "", "", "");
EXPECT_EQ(desc->opaque_parameters, params);
}
TEST_F(TransportDescriptionFactoryTest, TestAnswerOpaqueTransportParameters) {
OpaqueTransportParameters params;
params.protocol = "fake";
params.parameters = "foobar";
TransportOptions options;
options.opaque_parameters = params;
std::unique_ptr<TransportDescription> offer =
f1_.CreateOffer(options, NULL, &ice_credentials_);
std::unique_ptr<TransportDescription> answer =
f2_.CreateAnswer(offer.get(), options, true, NULL, &ice_credentials_);
CheckDesc(answer.get(), "", "", "", "");
EXPECT_EQ(answer->opaque_parameters, params);
}
TEST_F(TransportDescriptionFactoryTest, TestAnswerNoOpaqueTransportParameters) {
OpaqueTransportParameters params;
params.protocol = "fake";
params.parameters = "foobar";
TransportOptions options;
options.opaque_parameters = params;
std::unique_ptr<TransportDescription> offer =
f1_.CreateOffer(options, NULL, &ice_credentials_);
std::unique_ptr<TransportDescription> answer = f2_.CreateAnswer(
offer.get(), TransportOptions(), true, NULL, &ice_credentials_);
CheckDesc(answer.get(), "", "", "", "");
EXPECT_EQ(answer->opaque_parameters, absl::nullopt);
}
TEST_F(TransportDescriptionFactoryTest,
TestAnswerDifferentOpaqueTransportParameters) {
OpaqueTransportParameters offer_params;
offer_params.protocol = "fake";
offer_params.parameters = "foobar";
TransportOptions options;
options.opaque_parameters = offer_params;
std::unique_ptr<TransportDescription> offer =
f1_.CreateOffer(options, NULL, &ice_credentials_);
OpaqueTransportParameters answer_params;
answer_params.protocol = "fake";
answer_params.parameters = "baz";
options.opaque_parameters = answer_params;
std::unique_ptr<TransportDescription> answer =
f2_.CreateAnswer(offer.get(), options, true, NULL, &ice_credentials_);
CheckDesc(answer.get(), "", "", "", "");
EXPECT_EQ(answer->opaque_parameters, absl::nullopt);
}
TEST_F(TransportDescriptionFactoryTest,
TestAnswerNoOpaqueTransportParametersInOffer) {
std::unique_ptr<TransportDescription> offer =
f1_.CreateOffer(TransportOptions(), NULL, &ice_credentials_);
OpaqueTransportParameters params;
params.protocol = "fake";
params.parameters = "foobar";
TransportOptions options;
options.opaque_parameters = params;
std::unique_ptr<TransportDescription> answer =
f2_.CreateAnswer(offer.get(), options, true, NULL, &ice_credentials_);
CheckDesc(answer.get(), "", "", "", "");
EXPECT_EQ(answer->opaque_parameters, absl::nullopt);
}
TEST_F(TransportDescriptionFactoryTest, TestAnswerDefault) {
std::unique_ptr<TransportDescription> offer =
f1_.CreateOffer(TransportOptions(), NULL, &ice_credentials_);