Implement rollback for setRemoteDescription
Bug: chromium:980875 Change-Id: I4575e9ad1902a20937f9812f49edee2a2441f76d Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/153525 Commit-Queue: Eldar Rello <elrello@microsoft.com> Reviewed-by: Harald Alvestrand <hta@webrtc.org> Reviewed-by: Henrik Boström <hbos@webrtc.org> Reviewed-by: Steve Anton <steveanton@webrtc.org> Cr-Commit-Position: refs/heads/master@{#29422}
This commit is contained in:
parent
5963c7cf0a
commit
16d4c4d4fb
@ -41,6 +41,7 @@ void SetSessionDescriptionObserver::OnFailure(const std::string& error) {
|
|||||||
const char SessionDescriptionInterface::kOffer[] = "offer";
|
const char SessionDescriptionInterface::kOffer[] = "offer";
|
||||||
const char SessionDescriptionInterface::kPrAnswer[] = "pranswer";
|
const char SessionDescriptionInterface::kPrAnswer[] = "pranswer";
|
||||||
const char SessionDescriptionInterface::kAnswer[] = "answer";
|
const char SessionDescriptionInterface::kAnswer[] = "answer";
|
||||||
|
const char SessionDescriptionInterface::kRollback[] = "rollback";
|
||||||
|
|
||||||
const char* SdpTypeToString(SdpType type) {
|
const char* SdpTypeToString(SdpType type) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
@ -50,6 +51,8 @@ const char* SdpTypeToString(SdpType type) {
|
|||||||
return SessionDescriptionInterface::kPrAnswer;
|
return SessionDescriptionInterface::kPrAnswer;
|
||||||
case SdpType::kAnswer:
|
case SdpType::kAnswer:
|
||||||
return SessionDescriptionInterface::kAnswer;
|
return SessionDescriptionInterface::kAnswer;
|
||||||
|
case SdpType::kRollback:
|
||||||
|
return SessionDescriptionInterface::kRollback;
|
||||||
}
|
}
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
@ -61,6 +64,8 @@ absl::optional<SdpType> SdpTypeFromString(const std::string& type_str) {
|
|||||||
return SdpType::kPrAnswer;
|
return SdpType::kPrAnswer;
|
||||||
} else if (type_str == SessionDescriptionInterface::kAnswer) {
|
} else if (type_str == SessionDescriptionInterface::kAnswer) {
|
||||||
return SdpType::kAnswer;
|
return SdpType::kAnswer;
|
||||||
|
} else if (type_str == SessionDescriptionInterface::kRollback) {
|
||||||
|
return SdpType::kRollback;
|
||||||
} else {
|
} else {
|
||||||
return absl::nullopt;
|
return absl::nullopt;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -103,8 +103,11 @@ enum class SdpType {
|
|||||||
kOffer, // Description must be treated as an SDP offer.
|
kOffer, // Description must be treated as an SDP offer.
|
||||||
kPrAnswer, // Description must be treated as an SDP answer, but not a final
|
kPrAnswer, // Description must be treated as an SDP answer, but not a final
|
||||||
// answer.
|
// answer.
|
||||||
kAnswer // Description must be treated as an SDP final answer, and the offer-
|
kAnswer, // Description must be treated as an SDP final answer, and the
|
||||||
// answer exchange must be considered complete after receiving this.
|
// offer-answer exchange must be considered complete after
|
||||||
|
// receiving this.
|
||||||
|
kRollback // Resets any pending offers and sets signaling state back to
|
||||||
|
// stable.
|
||||||
};
|
};
|
||||||
|
|
||||||
// Returns the string form of the given SDP type. String forms are defined in
|
// Returns the string form of the given SDP type. String forms are defined in
|
||||||
@ -128,6 +131,7 @@ class RTC_EXPORT SessionDescriptionInterface {
|
|||||||
static const char kOffer[];
|
static const char kOffer[];
|
||||||
static const char kPrAnswer[];
|
static const char kPrAnswer[];
|
||||||
static const char kAnswer[];
|
static const char kAnswer[];
|
||||||
|
static const char kRollback[];
|
||||||
|
|
||||||
virtual ~SessionDescriptionInterface() {}
|
virtual ~SessionDescriptionInterface() {}
|
||||||
|
|
||||||
|
|||||||
@ -659,6 +659,9 @@ class RTC_EXPORT PeerConnectionInterface : public rtc::RefCountInterface {
|
|||||||
// logs with TURN server logs. It will not be added if it's an empty string.
|
// logs with TURN server logs. It will not be added if it's an empty string.
|
||||||
std::string turn_logging_id;
|
std::string turn_logging_id;
|
||||||
|
|
||||||
|
// Added to be able to control rollout of this feature.
|
||||||
|
bool enable_implicit_rollback = false;
|
||||||
|
|
||||||
//
|
//
|
||||||
// Don't forget to update operator== if adding something.
|
// Don't forget to update operator== if adding something.
|
||||||
//
|
//
|
||||||
|
|||||||
@ -152,8 +152,10 @@ std::unique_ptr<SessionDescriptionInterface> CreateSessionDescription(
|
|||||||
const std::string& sdp,
|
const std::string& sdp,
|
||||||
SdpParseError* error_out) {
|
SdpParseError* error_out) {
|
||||||
auto jsep_desc = std::make_unique<JsepSessionDescription>(type);
|
auto jsep_desc = std::make_unique<JsepSessionDescription>(type);
|
||||||
if (!SdpDeserialize(sdp, jsep_desc.get(), error_out)) {
|
if (type != SdpType::kRollback) {
|
||||||
return nullptr;
|
if (!SdpDeserialize(sdp, jsep_desc.get(), error_out)) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return std::move(jsep_desc);
|
return std::move(jsep_desc);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -471,6 +471,16 @@ void JsepTransportController::SetMediaTransportSettings(
|
|||||||
use_datagram_transport_for_data_channels_receive_only;
|
use_datagram_transport_for_data_channels_receive_only;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void JsepTransportController::RollbackTransportForMid(const std::string& mid) {
|
||||||
|
if (!network_thread_->IsCurrent()) {
|
||||||
|
network_thread_->Invoke<void>(RTC_FROM_HERE,
|
||||||
|
[=] { RollbackTransportForMid(mid); });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
RemoveTransportForMid(mid);
|
||||||
|
MaybeDestroyJsepTransport(mid);
|
||||||
|
}
|
||||||
|
|
||||||
std::unique_ptr<cricket::IceTransportInternal>
|
std::unique_ptr<cricket::IceTransportInternal>
|
||||||
JsepTransportController::CreateIceTransport(const std::string transport_name,
|
JsepTransportController::CreateIceTransport(const std::string transport_name,
|
||||||
bool rtcp) {
|
bool rtcp) {
|
||||||
|
|||||||
@ -239,6 +239,10 @@ class JsepTransportController : public sigslot::has_slots<> {
|
|||||||
bool use_datagram_transport_for_data_channels,
|
bool use_datagram_transport_for_data_channels,
|
||||||
bool use_datagram_transport_for_data_channels_receive_only);
|
bool use_datagram_transport_for_data_channels_receive_only);
|
||||||
|
|
||||||
|
// TODO(elrello): For now the rollback only removes mid to transport mapping
|
||||||
|
// and deletes unused transport, but doesn't consider anything more complex.
|
||||||
|
void RollbackTransportForMid(const std::string& mid);
|
||||||
|
|
||||||
// If media transport is present enabled and supported,
|
// If media transport is present enabled and supported,
|
||||||
// when this method is called, it creates a media transport and generates its
|
// when this method is called, it creates a media transport and generates its
|
||||||
// offer. The new offer is then returned, and the created media transport will
|
// offer. The new offer is then returned, and the created media transport will
|
||||||
|
|||||||
@ -782,6 +782,7 @@ bool PeerConnectionInterface::RTCConfiguration::operator==(
|
|||||||
absl::optional<CryptoOptions> crypto_options;
|
absl::optional<CryptoOptions> crypto_options;
|
||||||
bool offer_extmap_allow_mixed;
|
bool offer_extmap_allow_mixed;
|
||||||
std::string turn_logging_id;
|
std::string turn_logging_id;
|
||||||
|
bool enable_implicit_rollback;
|
||||||
};
|
};
|
||||||
static_assert(sizeof(stuff_being_tested_for_equality) == sizeof(*this),
|
static_assert(sizeof(stuff_being_tested_for_equality) == sizeof(*this),
|
||||||
"Did you add something to RTCConfiguration and forget to "
|
"Did you add something to RTCConfiguration and forget to "
|
||||||
@ -847,7 +848,8 @@ bool PeerConnectionInterface::RTCConfiguration::operator==(
|
|||||||
o.use_datagram_transport_for_data_channels_receive_only &&
|
o.use_datagram_transport_for_data_channels_receive_only &&
|
||||||
crypto_options == o.crypto_options &&
|
crypto_options == o.crypto_options &&
|
||||||
offer_extmap_allow_mixed == o.offer_extmap_allow_mixed &&
|
offer_extmap_allow_mixed == o.offer_extmap_allow_mixed &&
|
||||||
turn_logging_id == o.turn_logging_id;
|
turn_logging_id == o.turn_logging_id &&
|
||||||
|
enable_implicit_rollback == o.enable_implicit_rollback;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PeerConnectionInterface::RTCConfiguration::operator!=(
|
bool PeerConnectionInterface::RTCConfiguration::operator!=(
|
||||||
@ -2257,6 +2259,23 @@ void PeerConnection::SetLocalDescription(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// For SLD we support only explicit rollback.
|
||||||
|
if (desc->GetType() == SdpType::kRollback) {
|
||||||
|
if (IsUnifiedPlan()) {
|
||||||
|
RTCError error = Rollback();
|
||||||
|
if (error.ok()) {
|
||||||
|
PostSetSessionDescriptionSuccess(observer);
|
||||||
|
} else {
|
||||||
|
PostSetSessionDescriptionFailure(observer, std::move(error));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
PostSetSessionDescriptionFailure(
|
||||||
|
observer, RTCError(RTCErrorType::UNSUPPORTED_OPERATION,
|
||||||
|
"Rollback not supported in Plan B"));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
RTCError error = ValidateSessionDescription(desc.get(), cricket::CS_LOCAL);
|
RTCError error = ValidateSessionDescription(desc.get(), cricket::CS_LOCAL);
|
||||||
if (!error.ok()) {
|
if (!error.ok()) {
|
||||||
std::string error_message = GetSetDescriptionErrorMessage(
|
std::string error_message = GetSetDescriptionErrorMessage(
|
||||||
@ -2629,7 +2648,24 @@ void PeerConnection::SetRemoteDescription(
|
|||||||
RTCError(RTCErrorType::INTERNAL_ERROR, std::move(error_message)));
|
RTCError(RTCErrorType::INTERNAL_ERROR, std::move(error_message)));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (IsUnifiedPlan()) {
|
||||||
|
if (configuration_.enable_implicit_rollback) {
|
||||||
|
if (desc->GetType() == SdpType::kOffer &&
|
||||||
|
signaling_state() == kHaveLocalOffer) {
|
||||||
|
Rollback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Explicit rollback.
|
||||||
|
if (desc->GetType() == SdpType::kRollback) {
|
||||||
|
observer->OnSetRemoteDescriptionComplete(Rollback());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else if (desc->GetType() == SdpType::kRollback) {
|
||||||
|
observer->OnSetRemoteDescriptionComplete(
|
||||||
|
RTCError(RTCErrorType::UNSUPPORTED_OPERATION,
|
||||||
|
"Rollback not supported in Plan B"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (desc->GetType() == SdpType::kOffer) {
|
if (desc->GetType() == SdpType::kOffer) {
|
||||||
// Report to UMA the format of the received offer.
|
// Report to UMA the format of the received offer.
|
||||||
ReportSdpFormatReceived(*desc);
|
ReportSdpFormatReceived(*desc);
|
||||||
@ -3382,8 +3418,12 @@ PeerConnection::AssociateTransceiver(cricket::ContentSource source,
|
|||||||
transceiver = CreateAndAddTransceiver(sender, receiver);
|
transceiver = CreateAndAddTransceiver(sender, receiver);
|
||||||
transceiver->internal()->set_direction(
|
transceiver->internal()->set_direction(
|
||||||
RtpTransceiverDirection::kRecvOnly);
|
RtpTransceiverDirection::kRecvOnly);
|
||||||
|
if (type == SdpType::kOffer) {
|
||||||
|
transceiver_stable_states_by_transceivers_[transceiver] =
|
||||||
|
TransceiverStableState(RtpTransceiverDirection::kRecvOnly,
|
||||||
|
absl::nullopt, absl::nullopt, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the offer indicated simulcast but the answer rejected it.
|
// Check if the offer indicated simulcast but the answer rejected it.
|
||||||
// This can happen when simulcast is not supported on the remote party.
|
// This can happen when simulcast is not supported on the remote party.
|
||||||
if (SimulcastIsRejected(old_local_content, *media_desc)) {
|
if (SimulcastIsRejected(old_local_content, *media_desc)) {
|
||||||
@ -3416,6 +3456,20 @@ PeerConnection::AssociateTransceiver(cricket::ContentSource source,
|
|||||||
return std::move(error);
|
return std::move(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (type == SdpType::kOffer) {
|
||||||
|
// Make sure we don't overwrite existing stable states and that the
|
||||||
|
// state is really going to change when adding new record to the map.
|
||||||
|
auto it = transceiver_stable_states_by_transceivers_.find(transceiver);
|
||||||
|
if (it == transceiver_stable_states_by_transceivers_.end() &&
|
||||||
|
(transceiver->internal()->mid() != content.name ||
|
||||||
|
transceiver->internal()->mline_index() != mline_index)) {
|
||||||
|
transceiver_stable_states_by_transceivers_[transceiver] =
|
||||||
|
TransceiverStableState(transceiver->internal()->direction(),
|
||||||
|
transceiver->internal()->mid(),
|
||||||
|
transceiver->internal()->mline_index(), false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Associate the found or created RtpTransceiver with the m= section by
|
// Associate the found or created RtpTransceiver with the m= section by
|
||||||
// setting the value of the RtpTransceiver's mid property to the MID of the m=
|
// setting the value of the RtpTransceiver's mid property to the MID of the m=
|
||||||
// section, and establish a mapping between the transceiver and the index of
|
// section, and establish a mapping between the transceiver and the index of
|
||||||
@ -5837,6 +5891,7 @@ RTCError PeerConnection::UpdateSessionState(
|
|||||||
} else {
|
} else {
|
||||||
RTC_DCHECK(type == SdpType::kAnswer);
|
RTC_DCHECK(type == SdpType::kAnswer);
|
||||||
ChangeSignalingState(PeerConnectionInterface::kStable);
|
ChangeSignalingState(PeerConnectionInterface::kStable);
|
||||||
|
transceiver_stable_states_by_transceivers_.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update internal objects according to the session description's media
|
// Update internal objects according to the session description's media
|
||||||
@ -7550,4 +7605,51 @@ bool PeerConnection::CheckIfNegotiationIsNeeded() {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RTCError PeerConnection::Rollback() {
|
||||||
|
auto state = signaling_state();
|
||||||
|
if (state != PeerConnectionInterface::kHaveLocalOffer &&
|
||||||
|
state != PeerConnectionInterface::kHaveRemoteOffer) {
|
||||||
|
return RTCError(RTCErrorType::INVALID_STATE,
|
||||||
|
"Called in wrong signalingState: " +
|
||||||
|
GetSignalingStateString(signaling_state()));
|
||||||
|
}
|
||||||
|
RTC_DCHECK_RUN_ON(signaling_thread());
|
||||||
|
RTC_DCHECK(IsUnifiedPlan());
|
||||||
|
|
||||||
|
for (auto&& transceivers_stable_state_pair :
|
||||||
|
transceiver_stable_states_by_transceivers_) {
|
||||||
|
auto transceiver = transceivers_stable_state_pair.first;
|
||||||
|
auto state = transceivers_stable_state_pair.second;
|
||||||
|
RTC_DCHECK(transceiver->internal()->mid().has_value());
|
||||||
|
std::string mid = transceiver->internal()->mid().value();
|
||||||
|
transport_controller_->RollbackTransportForMid(mid);
|
||||||
|
DestroyTransceiverChannel(transceiver);
|
||||||
|
|
||||||
|
if (state.newly_created()) {
|
||||||
|
// Remove added transceivers with no added track.
|
||||||
|
if (transceiver->internal()->sender()->track()) {
|
||||||
|
transceiver->internal()->set_created_by_addtrack(true);
|
||||||
|
} else {
|
||||||
|
int remaining_transceiver_count = 0;
|
||||||
|
for (auto&& t : transceivers_) {
|
||||||
|
if (t != transceiver) {
|
||||||
|
transceivers_[remaining_transceiver_count++] = t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
transceivers_.resize(remaining_transceiver_count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
transceiver->internal()->sender_internal()->set_transport(nullptr);
|
||||||
|
transceiver->internal()->receiver_internal()->set_transport(nullptr);
|
||||||
|
transceiver->internal()->set_direction(state.direction());
|
||||||
|
transceiver->internal()->set_mid(state.mid());
|
||||||
|
transceiver->internal()->set_mline_index(state.mline_index());
|
||||||
|
}
|
||||||
|
transceiver_stable_states_by_transceivers_.clear();
|
||||||
|
pending_local_description_.reset();
|
||||||
|
pending_remote_description_.reset();
|
||||||
|
ChangeSignalingState(PeerConnectionInterface::kStable);
|
||||||
|
return RTCError::OK();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
|||||||
@ -391,6 +391,34 @@ class PeerConnection : public PeerConnectionInternal,
|
|||||||
FieldTrialFlag receive_only;
|
FieldTrialFlag receive_only;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Captures partial state to be used for rollback. Applicable only in
|
||||||
|
// Unified Plan.
|
||||||
|
class TransceiverStableState {
|
||||||
|
public:
|
||||||
|
TransceiverStableState() {}
|
||||||
|
TransceiverStableState(RtpTransceiverDirection direction,
|
||||||
|
absl::optional<std::string> mid,
|
||||||
|
absl::optional<size_t> mline_index,
|
||||||
|
bool newly_created)
|
||||||
|
: direction_(direction),
|
||||||
|
mid_(mid),
|
||||||
|
mline_index_(mline_index),
|
||||||
|
newly_created_(newly_created) {}
|
||||||
|
RtpTransceiverDirection direction() const { return direction_; }
|
||||||
|
absl::optional<std::string> mid() const { return mid_; }
|
||||||
|
absl::optional<size_t> mline_index() const { return mline_index_; }
|
||||||
|
bool newly_created() const { return newly_created_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
RtpTransceiverDirection direction_ = RtpTransceiverDirection::kRecvOnly;
|
||||||
|
absl::optional<std::string> mid_;
|
||||||
|
absl::optional<size_t> mline_index_;
|
||||||
|
// Indicates that the transceiver was created as part of applying a
|
||||||
|
// description to track potential need for removing transceiver during
|
||||||
|
// rollback.
|
||||||
|
bool newly_created_ = false;
|
||||||
|
};
|
||||||
|
|
||||||
// Implements MessageHandler.
|
// Implements MessageHandler.
|
||||||
void OnMessage(rtc::Message* msg) override;
|
void OnMessage(rtc::Message* msg) override;
|
||||||
|
|
||||||
@ -1165,6 +1193,7 @@ class PeerConnection : public PeerConnectionInternal,
|
|||||||
|
|
||||||
void UpdateNegotiationNeeded();
|
void UpdateNegotiationNeeded();
|
||||||
bool CheckIfNegotiationIsNeeded();
|
bool CheckIfNegotiationIsNeeded();
|
||||||
|
RTCError Rollback();
|
||||||
|
|
||||||
sigslot::signal1<DataChannel*> SignalDataChannelCreated_
|
sigslot::signal1<DataChannel*> SignalDataChannelCreated_
|
||||||
RTC_GUARDED_BY(signaling_thread());
|
RTC_GUARDED_BY(signaling_thread());
|
||||||
@ -1286,7 +1315,11 @@ class PeerConnection : public PeerConnectionInternal,
|
|||||||
RTC_GUARDED_BY(signaling_thread()); // A pointer is passed to senders_
|
RTC_GUARDED_BY(signaling_thread()); // A pointer is passed to senders_
|
||||||
rtc::scoped_refptr<RTCStatsCollector> stats_collector_
|
rtc::scoped_refptr<RTCStatsCollector> stats_collector_
|
||||||
RTC_GUARDED_BY(signaling_thread());
|
RTC_GUARDED_BY(signaling_thread());
|
||||||
|
// Holds changes made to transceivers during applying descriptors for
|
||||||
|
// potential rollback. Gets cleared once signaling state goes to stable.
|
||||||
|
std::map<rtc::scoped_refptr<RtpTransceiverProxyWithInternal<RtpTransceiver>>,
|
||||||
|
TransceiverStableState>
|
||||||
|
transceiver_stable_states_by_transceivers_;
|
||||||
std::vector<
|
std::vector<
|
||||||
rtc::scoped_refptr<RtpTransceiverProxyWithInternal<RtpTransceiver>>>
|
rtc::scoped_refptr<RtpTransceiverProxyWithInternal<RtpTransceiver>>>
|
||||||
transceivers_; // TODO(bugs.webrtc.org/9987): Accessed on both signaling
|
transceivers_; // TODO(bugs.webrtc.org/9987): Accessed on both signaling
|
||||||
|
|||||||
@ -230,7 +230,7 @@ class PeerConnectionWrapper : public webrtc::PeerConnectionObserver,
|
|||||||
// will set the whole offer/answer exchange in motion. Just need to wait for
|
// will set the whole offer/answer exchange in motion. Just need to wait for
|
||||||
// the signaling state to reach "stable".
|
// the signaling state to reach "stable".
|
||||||
void CreateAndSetAndSignalOffer() {
|
void CreateAndSetAndSignalOffer() {
|
||||||
auto offer = CreateOffer();
|
auto offer = CreateOfferAndWait();
|
||||||
ASSERT_NE(nullptr, offer);
|
ASSERT_NE(nullptr, offer);
|
||||||
EXPECT_TRUE(SetLocalDescriptionAndSendSdpMessage(std::move(offer)));
|
EXPECT_TRUE(SetLocalDescriptionAndSendSdpMessage(std::move(offer)));
|
||||||
}
|
}
|
||||||
@ -302,6 +302,13 @@ class PeerConnectionWrapper : public webrtc::PeerConnectionObserver,
|
|||||||
return ice_candidate_pair_change_history_;
|
return ice_candidate_pair_change_history_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Every PeerConnection signaling state in order that has been seen by the
|
||||||
|
// observer.
|
||||||
|
std::vector<PeerConnectionInterface::SignalingState>
|
||||||
|
peer_connection_signaling_state_history() const {
|
||||||
|
return peer_connection_signaling_state_history_;
|
||||||
|
}
|
||||||
|
|
||||||
void AddAudioVideoTracks() {
|
void AddAudioVideoTracks() {
|
||||||
AddAudioTrack();
|
AddAudioTrack();
|
||||||
AddVideoTrack();
|
AddVideoTrack();
|
||||||
@ -577,6 +584,14 @@ class PeerConnectionWrapper : public webrtc::PeerConnectionObserver,
|
|||||||
network_manager()->set_mdns_responder(std::move(mdns_responder));
|
network_manager()->set_mdns_responder(std::move(mdns_responder));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns null on failure.
|
||||||
|
std::unique_ptr<SessionDescriptionInterface> CreateOfferAndWait() {
|
||||||
|
rtc::scoped_refptr<MockCreateSessionDescriptionObserver> observer(
|
||||||
|
new rtc::RefCountedObject<MockCreateSessionDescriptionObserver>());
|
||||||
|
pc()->CreateOffer(observer, offer_answer_options_);
|
||||||
|
return WaitForDescriptionFromObserver(observer);
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
explicit PeerConnectionWrapper(const std::string& debug_name)
|
explicit PeerConnectionWrapper(const std::string& debug_name)
|
||||||
: debug_name_(debug_name) {}
|
: debug_name_(debug_name) {}
|
||||||
@ -731,14 +746,6 @@ class PeerConnectionWrapper : public webrtc::PeerConnectionObserver,
|
|||||||
ResetRtpReceiverObservers();
|
ResetRtpReceiverObservers();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns null on failure.
|
|
||||||
std::unique_ptr<SessionDescriptionInterface> CreateOffer() {
|
|
||||||
rtc::scoped_refptr<MockCreateSessionDescriptionObserver> observer(
|
|
||||||
new rtc::RefCountedObject<MockCreateSessionDescriptionObserver>());
|
|
||||||
pc()->CreateOffer(observer, offer_answer_options_);
|
|
||||||
return WaitForDescriptionFromObserver(observer);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns null on failure.
|
// Returns null on failure.
|
||||||
std::unique_ptr<SessionDescriptionInterface> CreateAnswer() {
|
std::unique_ptr<SessionDescriptionInterface> CreateAnswer() {
|
||||||
rtc::scoped_refptr<MockCreateSessionDescriptionObserver> observer(
|
rtc::scoped_refptr<MockCreateSessionDescriptionObserver> observer(
|
||||||
@ -894,6 +901,7 @@ class PeerConnectionWrapper : public webrtc::PeerConnectionObserver,
|
|||||||
void OnSignalingChange(
|
void OnSignalingChange(
|
||||||
webrtc::PeerConnectionInterface::SignalingState new_state) override {
|
webrtc::PeerConnectionInterface::SignalingState new_state) override {
|
||||||
EXPECT_EQ(pc()->signaling_state(), new_state);
|
EXPECT_EQ(pc()->signaling_state(), new_state);
|
||||||
|
peer_connection_signaling_state_history_.push_back(new_state);
|
||||||
}
|
}
|
||||||
void OnAddTrack(rtc::scoped_refptr<RtpReceiverInterface> receiver,
|
void OnAddTrack(rtc::scoped_refptr<RtpReceiverInterface> receiver,
|
||||||
const std::vector<rtc::scoped_refptr<MediaStreamInterface>>&
|
const std::vector<rtc::scoped_refptr<MediaStreamInterface>>&
|
||||||
@ -1037,7 +1045,8 @@ class PeerConnectionWrapper : public webrtc::PeerConnectionObserver,
|
|||||||
ice_gathering_state_history_;
|
ice_gathering_state_history_;
|
||||||
std::vector<cricket::CandidatePairChangeEvent>
|
std::vector<cricket::CandidatePairChangeEvent>
|
||||||
ice_candidate_pair_change_history_;
|
ice_candidate_pair_change_history_;
|
||||||
|
std::vector<PeerConnectionInterface::SignalingState>
|
||||||
|
peer_connection_signaling_state_history_;
|
||||||
webrtc::FakeRtcEventLogFactory* event_log_factory_;
|
webrtc::FakeRtcEventLogFactory* event_log_factory_;
|
||||||
|
|
||||||
rtc::AsyncInvoker invoker_;
|
rtc::AsyncInvoker invoker_;
|
||||||
@ -5991,6 +6000,61 @@ TEST_P(PeerConnectionIntegrationTest, OnIceCandidateError) {
|
|||||||
caller()->error_event().host_candidate.find(":"));
|
caller()->error_event().host_candidate.find(":"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(PeerConnectionIntegrationTestUnifiedPlan,
|
||||||
|
AudioKeepsFlowingAfterImplicitRollback) {
|
||||||
|
PeerConnectionInterface::RTCConfiguration config;
|
||||||
|
config.sdp_semantics = SdpSemantics::kUnifiedPlan;
|
||||||
|
config.enable_implicit_rollback = true;
|
||||||
|
ASSERT_TRUE(CreatePeerConnectionWrappersWithConfig(config, config));
|
||||||
|
ConnectFakeSignaling();
|
||||||
|
caller()->AddAudioTrack();
|
||||||
|
callee()->AddAudioTrack();
|
||||||
|
caller()->CreateAndSetAndSignalOffer();
|
||||||
|
ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
|
||||||
|
MediaExpectations media_expectations;
|
||||||
|
media_expectations.ExpectBidirectionalAudio();
|
||||||
|
ASSERT_TRUE(ExpectNewFrames(media_expectations));
|
||||||
|
SetSignalIceCandidates(false); // Workaround candidate outrace sdp.
|
||||||
|
caller()->AddVideoTrack();
|
||||||
|
callee()->AddVideoTrack();
|
||||||
|
rtc::scoped_refptr<MockSetSessionDescriptionObserver> observer(
|
||||||
|
new rtc::RefCountedObject<MockSetSessionDescriptionObserver>());
|
||||||
|
callee()->pc()->SetLocalDescription(observer,
|
||||||
|
callee()->CreateOfferAndWait().release());
|
||||||
|
EXPECT_TRUE_WAIT(observer->called(), kDefaultTimeout);
|
||||||
|
caller()->CreateAndSetAndSignalOffer(); // Implicit rollback.
|
||||||
|
ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
|
||||||
|
ASSERT_TRUE(ExpectNewFrames(media_expectations));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(PeerConnectionIntegrationTestUnifiedPlan,
|
||||||
|
ImplicitRollbackVisitsStableState) {
|
||||||
|
RTCConfiguration config;
|
||||||
|
config.sdp_semantics = SdpSemantics::kUnifiedPlan;
|
||||||
|
config.enable_implicit_rollback = true;
|
||||||
|
|
||||||
|
ASSERT_TRUE(CreatePeerConnectionWrappersWithConfig(config, config));
|
||||||
|
|
||||||
|
rtc::scoped_refptr<MockSetSessionDescriptionObserver> sld_observer(
|
||||||
|
new rtc::RefCountedObject<MockSetSessionDescriptionObserver>());
|
||||||
|
callee()->pc()->SetLocalDescription(sld_observer,
|
||||||
|
callee()->CreateOfferAndWait().release());
|
||||||
|
EXPECT_TRUE_WAIT(sld_observer->called(), kDefaultTimeout);
|
||||||
|
EXPECT_EQ(sld_observer->error(), "");
|
||||||
|
|
||||||
|
rtc::scoped_refptr<MockSetSessionDescriptionObserver> srd_observer(
|
||||||
|
new rtc::RefCountedObject<MockSetSessionDescriptionObserver>());
|
||||||
|
callee()->pc()->SetRemoteDescription(
|
||||||
|
srd_observer, caller()->CreateOfferAndWait().release());
|
||||||
|
EXPECT_TRUE_WAIT(srd_observer->called(), kDefaultTimeout);
|
||||||
|
EXPECT_EQ(srd_observer->error(), "");
|
||||||
|
|
||||||
|
EXPECT_THAT(callee()->peer_connection_signaling_state_history(),
|
||||||
|
ElementsAre(PeerConnectionInterface::kHaveLocalOffer,
|
||||||
|
PeerConnectionInterface::kStable,
|
||||||
|
PeerConnectionInterface::kHaveRemoteOffer));
|
||||||
|
}
|
||||||
|
|
||||||
INSTANTIATE_TEST_SUITE_P(PeerConnectionIntegrationTest,
|
INSTANTIATE_TEST_SUITE_P(PeerConnectionIntegrationTest,
|
||||||
PeerConnectionIntegrationTest,
|
PeerConnectionIntegrationTest,
|
||||||
Values(SdpSemantics::kPlanB,
|
Values(SdpSemantics::kPlanB,
|
||||||
|
|||||||
@ -1727,4 +1727,220 @@ TEST_F(PeerConnectionJsepTest, SetLocalDescriptionFailsMissingMid) {
|
|||||||
error);
|
error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(PeerConnectionJsepTest, RollbackSupportedInUnifiedPlan) {
|
||||||
|
RTCConfiguration config;
|
||||||
|
config.sdp_semantics = SdpSemantics::kUnifiedPlan;
|
||||||
|
config.enable_implicit_rollback = true;
|
||||||
|
auto caller = CreatePeerConnection(config);
|
||||||
|
auto callee = CreatePeerConnection(config);
|
||||||
|
EXPECT_TRUE(caller->CreateOfferAndSetAsLocal());
|
||||||
|
EXPECT_TRUE(caller->SetLocalDescription(caller->CreateRollback()));
|
||||||
|
EXPECT_TRUE(caller->CreateOfferAndSetAsLocal());
|
||||||
|
EXPECT_TRUE(caller->SetRemoteDescription(caller->CreateRollback()));
|
||||||
|
EXPECT_TRUE(caller->CreateOfferAndSetAsLocal());
|
||||||
|
EXPECT_TRUE(caller->SetRemoteDescription(callee->CreateOffer()));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(PeerConnectionJsepTest, RollbackNotSupportedInPlanB) {
|
||||||
|
RTCConfiguration config;
|
||||||
|
config.sdp_semantics = SdpSemantics::kPlanB;
|
||||||
|
config.enable_implicit_rollback = true;
|
||||||
|
auto caller = CreatePeerConnection(config);
|
||||||
|
auto callee = CreatePeerConnection(config);
|
||||||
|
EXPECT_TRUE(caller->CreateOfferAndSetAsLocal());
|
||||||
|
EXPECT_FALSE(caller->SetLocalDescription(caller->CreateRollback()));
|
||||||
|
EXPECT_FALSE(caller->SetRemoteDescription(caller->CreateRollback()));
|
||||||
|
EXPECT_FALSE(caller->SetRemoteDescription(callee->CreateOffer()));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(PeerConnectionJsepTest, RollbackFailsInStableState) {
|
||||||
|
auto caller = CreatePeerConnection();
|
||||||
|
EXPECT_FALSE(caller->SetLocalDescription(caller->CreateRollback()));
|
||||||
|
EXPECT_FALSE(caller->SetRemoteDescription(caller->CreateRollback()));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(PeerConnectionJsepTest, RollbackToStableStateAndClearLocalOffer) {
|
||||||
|
auto caller = CreatePeerConnection();
|
||||||
|
EXPECT_TRUE(caller->CreateOfferAndSetAsLocal());
|
||||||
|
EXPECT_TRUE(caller->SetLocalDescription(caller->CreateRollback()));
|
||||||
|
EXPECT_EQ(caller->signaling_state(), PeerConnectionInterface::kStable);
|
||||||
|
EXPECT_EQ(caller->pc()->pending_local_description(), nullptr);
|
||||||
|
|
||||||
|
EXPECT_TRUE(caller->CreateOfferAndSetAsLocal());
|
||||||
|
EXPECT_TRUE(caller->SetRemoteDescription(caller->CreateRollback()));
|
||||||
|
EXPECT_EQ(caller->signaling_state(), PeerConnectionInterface::kStable);
|
||||||
|
EXPECT_EQ(caller->pc()->pending_local_description(), nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(PeerConnectionJsepTest, RollbackToStableStateAndClearRemoteOffer) {
|
||||||
|
auto caller = CreatePeerConnection();
|
||||||
|
auto callee = CreatePeerConnection();
|
||||||
|
EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOffer()));
|
||||||
|
EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateRollback()));
|
||||||
|
EXPECT_EQ(callee->signaling_state(), PeerConnectionInterface::kStable);
|
||||||
|
EXPECT_EQ(callee->pc()->pending_remote_description(), nullptr);
|
||||||
|
|
||||||
|
EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOffer()));
|
||||||
|
EXPECT_TRUE(callee->SetLocalDescription(caller->CreateRollback()));
|
||||||
|
EXPECT_EQ(callee->signaling_state(), PeerConnectionInterface::kStable);
|
||||||
|
EXPECT_EQ(callee->pc()->pending_remote_description(), nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(PeerConnectionJsepTest, RollbackLocalOfferImplicitly) {
|
||||||
|
RTCConfiguration config;
|
||||||
|
config.sdp_semantics = SdpSemantics::kUnifiedPlan;
|
||||||
|
config.enable_implicit_rollback = true;
|
||||||
|
auto caller = CreatePeerConnection(config);
|
||||||
|
auto callee = CreatePeerConnection(config);
|
||||||
|
EXPECT_TRUE(callee->CreateOfferAndSetAsLocal());
|
||||||
|
EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOffer()));
|
||||||
|
EXPECT_EQ(callee->signaling_state(),
|
||||||
|
PeerConnectionInterface::kHaveRemoteOffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(PeerConnectionJsepTest, AttemptToRollbackLocalOfferImplicitly) {
|
||||||
|
RTCConfiguration config;
|
||||||
|
config.sdp_semantics = SdpSemantics::kUnifiedPlan;
|
||||||
|
config.enable_implicit_rollback = true;
|
||||||
|
auto caller = CreatePeerConnection(config);
|
||||||
|
auto callee = CreatePeerConnection(config);
|
||||||
|
EXPECT_TRUE(callee->CreateOfferAndSetAsLocal());
|
||||||
|
EXPECT_FALSE(callee->SetRemoteDescription(
|
||||||
|
CreateSessionDescription(SdpType::kOffer, "invalid sdp")));
|
||||||
|
EXPECT_EQ(callee->signaling_state(),
|
||||||
|
PeerConnectionInterface::kHaveLocalOffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(PeerConnectionJsepTest, RollbackRemovesTransceiver) {
|
||||||
|
auto caller = CreatePeerConnection();
|
||||||
|
caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
|
||||||
|
auto callee = CreatePeerConnection();
|
||||||
|
EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOffer()));
|
||||||
|
EXPECT_EQ(callee->pc()->GetTransceivers().size(), size_t{1});
|
||||||
|
EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateRollback()));
|
||||||
|
EXPECT_EQ(callee->pc()->GetTransceivers().size(), size_t{0});
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(PeerConnectionJsepTest, RollbackKeepsTransceiverAndClearsMid) {
|
||||||
|
auto caller = CreatePeerConnection();
|
||||||
|
caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
|
||||||
|
auto callee = CreatePeerConnection();
|
||||||
|
EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOffer()));
|
||||||
|
callee->AddAudioTrack("a");
|
||||||
|
EXPECT_EQ(callee->pc()->GetTransceivers().size(), size_t{1});
|
||||||
|
EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateRollback()));
|
||||||
|
// Transceiver can't be removed as track was added to it.
|
||||||
|
EXPECT_EQ(callee->pc()->GetTransceivers().size(), size_t{1});
|
||||||
|
// Mid got cleared to make it reusable.
|
||||||
|
EXPECT_EQ(callee->pc()->GetTransceivers()[0]->mid(), absl::nullopt);
|
||||||
|
// Transceiver should be counted as addTrack-created after rollback.
|
||||||
|
EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOffer()));
|
||||||
|
EXPECT_EQ(callee->pc()->GetTransceivers().size(), size_t{1});
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(PeerConnectionJsepTest, RollbackRestoresMid) {
|
||||||
|
auto caller = CreatePeerConnection();
|
||||||
|
caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
|
||||||
|
auto callee = CreatePeerConnection();
|
||||||
|
callee->AddAudioTrack("a");
|
||||||
|
auto offer = callee->CreateOffer();
|
||||||
|
EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOffer()));
|
||||||
|
EXPECT_EQ(callee->pc()->GetTransceivers().size(), size_t{1});
|
||||||
|
EXPECT_NE(callee->pc()->GetTransceivers()[0]->mid(), absl::nullopt);
|
||||||
|
EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateRollback()));
|
||||||
|
EXPECT_EQ(callee->pc()->GetTransceivers()[0]->mid(), absl::nullopt);
|
||||||
|
EXPECT_TRUE(callee->SetLocalDescription(std::move(offer)));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(PeerConnectionJsepTest, RollbackRestoresMidAndRemovesTransceiver) {
|
||||||
|
auto callee = CreatePeerConnection();
|
||||||
|
callee->AddVideoTrack("a");
|
||||||
|
auto offer = callee->CreateOffer();
|
||||||
|
auto caller = CreatePeerConnection();
|
||||||
|
caller->AddAudioTrack("b");
|
||||||
|
caller->AddVideoTrack("c");
|
||||||
|
auto mid = callee->pc()->GetTransceivers()[0]->mid();
|
||||||
|
EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOffer()));
|
||||||
|
EXPECT_EQ(callee->pc()->GetTransceivers().size(), size_t{2});
|
||||||
|
EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateRollback()));
|
||||||
|
EXPECT_EQ(callee->pc()->GetTransceivers().size(), size_t{1});
|
||||||
|
EXPECT_EQ(callee->pc()->GetTransceivers()[0]->mid(), mid);
|
||||||
|
EXPECT_EQ(callee->pc()->GetTransceivers()[0]->media_type(),
|
||||||
|
cricket::MEDIA_TYPE_VIDEO);
|
||||||
|
EXPECT_TRUE(callee->SetLocalDescription(std::move(offer)));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(PeerConnectionJsepTest, ImplicitlyRollbackTransceiversWithSameMids) {
|
||||||
|
RTCConfiguration config;
|
||||||
|
config.sdp_semantics = SdpSemantics::kUnifiedPlan;
|
||||||
|
config.enable_implicit_rollback = true;
|
||||||
|
auto caller = CreatePeerConnection(config);
|
||||||
|
caller->AddTransceiver(cricket::MEDIA_TYPE_VIDEO);
|
||||||
|
auto callee = CreatePeerConnection(config);
|
||||||
|
callee->AddTransceiver(cricket::MEDIA_TYPE_VIDEO);
|
||||||
|
EXPECT_TRUE(callee->CreateOfferAndSetAsLocal());
|
||||||
|
auto initial_mid = callee->pc()->GetTransceivers()[0]->mid();
|
||||||
|
EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
|
||||||
|
EXPECT_EQ(callee->pc()->GetTransceivers().size(), size_t{2});
|
||||||
|
EXPECT_EQ(callee->pc()->GetTransceivers()[0]->mid(), absl::nullopt);
|
||||||
|
EXPECT_EQ(callee->pc()->GetTransceivers()[1]->mid(),
|
||||||
|
caller->pc()->GetTransceivers()[0]->mid());
|
||||||
|
EXPECT_TRUE(callee->CreateAnswerAndSetAsLocal()); // Go to stable.
|
||||||
|
EXPECT_TRUE(callee->CreateOfferAndSetAsLocal());
|
||||||
|
EXPECT_NE(callee->pc()->GetTransceivers()[0]->mid(), initial_mid);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(PeerConnectionJsepTest, RollbackToNegotiatedStableState) {
|
||||||
|
RTCConfiguration config;
|
||||||
|
config.sdp_semantics = SdpSemantics::kUnifiedPlan;
|
||||||
|
config.bundle_policy = PeerConnectionInterface::kBundlePolicyMaxBundle;
|
||||||
|
auto caller = CreatePeerConnection(config);
|
||||||
|
caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
|
||||||
|
auto callee = CreatePeerConnection(config);
|
||||||
|
EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
|
||||||
|
EXPECT_TRUE(callee->CreateAnswerAndSetAsLocal());
|
||||||
|
caller->AddVideoTrack("a");
|
||||||
|
callee->AddVideoTrack("b");
|
||||||
|
EXPECT_TRUE(callee->CreateOfferAndSetAsLocal());
|
||||||
|
EXPECT_EQ(callee->pc()->GetTransceivers().size(), size_t{2});
|
||||||
|
auto audio_transport =
|
||||||
|
callee->pc()->GetTransceivers()[0]->sender()->dtls_transport();
|
||||||
|
EXPECT_EQ(callee->pc()->GetTransceivers()[0]->sender()->dtls_transport(),
|
||||||
|
callee->pc()->GetTransceivers()[1]->sender()->dtls_transport());
|
||||||
|
EXPECT_NE(callee->pc()->GetTransceivers()[1]->sender()->dtls_transport(),
|
||||||
|
nullptr);
|
||||||
|
EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateRollback()));
|
||||||
|
EXPECT_EQ(callee->pc()->GetTransceivers()[0]->sender()->dtls_transport(),
|
||||||
|
audio_transport); // Audio must remain working after rollback.
|
||||||
|
EXPECT_EQ(callee->pc()->GetTransceivers()[1]->sender()->dtls_transport(),
|
||||||
|
nullptr);
|
||||||
|
EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
|
||||||
|
|
||||||
|
EXPECT_EQ(callee->pc()->GetTransceivers()[0]->sender()->dtls_transport(),
|
||||||
|
audio_transport); // Audio transport is still the same.
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(PeerConnectionJsepTest, RollbackAfterMultipleSLD) {
|
||||||
|
auto callee = CreatePeerConnection();
|
||||||
|
callee->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
|
||||||
|
EXPECT_TRUE(callee->CreateOfferAndSetAsLocal());
|
||||||
|
callee->AddTransceiver(cricket::MEDIA_TYPE_VIDEO);
|
||||||
|
EXPECT_TRUE(callee->CreateOfferAndSetAsLocal());
|
||||||
|
EXPECT_TRUE(callee->SetRemoteDescription(callee->CreateRollback()));
|
||||||
|
EXPECT_EQ(callee->pc()->GetTransceivers().size(), size_t{2});
|
||||||
|
EXPECT_EQ(callee->pc()->GetTransceivers()[0]->mid(), absl::nullopt);
|
||||||
|
EXPECT_EQ(callee->pc()->GetTransceivers()[1]->mid(), absl::nullopt);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(PeerConnectionJsepTest, NoRollbackNeeded) {
|
||||||
|
auto caller = CreatePeerConnection();
|
||||||
|
caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
|
||||||
|
auto callee = CreatePeerConnection();
|
||||||
|
callee->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
|
||||||
|
EXPECT_TRUE(caller->CreateOfferAndSetAsLocal());
|
||||||
|
EXPECT_TRUE(caller->CreateOfferAndSetAsLocal());
|
||||||
|
EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOffer()));
|
||||||
|
EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOffer()));
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
|||||||
@ -125,6 +125,11 @@ PeerConnectionWrapper::CreateAnswerAndSetAsLocal(
|
|||||||
return answer;
|
return answer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<SessionDescriptionInterface>
|
||||||
|
PeerConnectionWrapper::CreateRollback() {
|
||||||
|
return CreateSessionDescription(SdpType::kRollback, "");
|
||||||
|
}
|
||||||
|
|
||||||
std::unique_ptr<SessionDescriptionInterface> PeerConnectionWrapper::CreateSdp(
|
std::unique_ptr<SessionDescriptionInterface> PeerConnectionWrapper::CreateSdp(
|
||||||
rtc::FunctionView<void(CreateSessionDescriptionObserver*)> fn,
|
rtc::FunctionView<void(CreateSessionDescriptionObserver*)> fn,
|
||||||
std::string* error_out) {
|
std::string* error_out) {
|
||||||
|
|||||||
@ -87,6 +87,7 @@ class PeerConnectionWrapper {
|
|||||||
const PeerConnectionInterface::RTCOfferAnswerOptions& options);
|
const PeerConnectionInterface::RTCOfferAnswerOptions& options);
|
||||||
// Calls CreateAnswerAndSetAsLocal with default options.
|
// Calls CreateAnswerAndSetAsLocal with default options.
|
||||||
std::unique_ptr<SessionDescriptionInterface> CreateAnswerAndSetAsLocal();
|
std::unique_ptr<SessionDescriptionInterface> CreateAnswerAndSetAsLocal();
|
||||||
|
std::unique_ptr<SessionDescriptionInterface> CreateRollback();
|
||||||
|
|
||||||
// Calls the underlying PeerConnection's SetLocalDescription method with the
|
// Calls the underlying PeerConnection's SetLocalDescription method with the
|
||||||
// given session description and waits for the success/failure response.
|
// given session description and waits for the success/failure response.
|
||||||
|
|||||||
@ -29,8 +29,10 @@ std::unique_ptr<SessionDescriptionInterface> CloneSessionDescriptionAsType(
|
|||||||
SdpType type) {
|
SdpType type) {
|
||||||
RTC_DCHECK(sdesc);
|
RTC_DCHECK(sdesc);
|
||||||
auto clone = std::make_unique<JsepSessionDescription>(type);
|
auto clone = std::make_unique<JsepSessionDescription>(type);
|
||||||
clone->Initialize(sdesc->description()->Clone(), sdesc->session_id(),
|
if (sdesc->description()) {
|
||||||
sdesc->session_version());
|
clone->Initialize(sdesc->description()->Clone(), sdesc->session_id(),
|
||||||
|
sdesc->session_version());
|
||||||
|
}
|
||||||
// As of writing, our version of GCC does not allow returning a unique_ptr of
|
// As of writing, our version of GCC does not allow returning a unique_ptr of
|
||||||
// a subclass as a unique_ptr of a base class. To get around this, we need to
|
// a subclass as a unique_ptr of a base class. To get around this, we need to
|
||||||
// std::move the return value.
|
// std::move the return value.
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user