mirror of
https://github.com/mollyim/webrtc.git
synced 2025-05-20 09:07:52 +01:00

RtpSender uses a transactional model when getting and setting RtpParameters. One must call GetParameters() and then can use the returned object in a subsequent call to SetParameters(). PeerConnection was calling GetParameters() and SetParameters() during negotiation in SetLocalDescription and SetRemoteDescription effectively invalidating any parameters that the client previously held. This change introduces an internal way for the platform to modify parameters without invalidating the transactional model, provided that the modification is not severe. Ex. removing simulcast layers is a severe modification and will invalidate any outstanding parameters. Bug: webrtc:10339 Change-Id: I362e8ca4d9556e04a1aa7a3e74e2c275f8d16fbc Reviewed-on: https://webrtc-review.googlesource.com/c/124504 Reviewed-by: Seth Hampson <shampson@webrtc.org> Reviewed-by: Steve Anton <steveanton@webrtc.org> Commit-Queue: Amit Hilbuch <amithi@webrtc.org> Cr-Commit-Position: refs/heads/master@{#26864}
464 lines
20 KiB
C++
464 lines
20 KiB
C++
/*
|
|
* Copyright 2019 The WebRTC project authors. All Rights Reserved.
|
|
*
|
|
* Use of this source code is governed by a BSD-style license
|
|
* that can be found in the LICENSE file in the root of the source
|
|
* tree. An additional intellectual property rights grant can be found
|
|
* in the file PATENTS. All contributing project authors may
|
|
* be found in the AUTHORS file in the root of the source tree.
|
|
*/
|
|
|
|
#include <ostream> // no-presubmit-check TODO(webrtc:8982)
|
|
|
|
#include "absl/algorithm/container.h"
|
|
#include "absl/memory/memory.h"
|
|
#include "api/audio_codecs/builtin_audio_decoder_factory.h"
|
|
#include "api/audio_codecs/builtin_audio_encoder_factory.h"
|
|
#include "api/create_peerconnection_factory.h"
|
|
#include "api/rtp_transceiver_interface.h"
|
|
#include "api/video_codecs/builtin_video_decoder_factory.h"
|
|
#include "api/video_codecs/builtin_video_encoder_factory.h"
|
|
#include "pc/peer_connection.h"
|
|
#include "pc/peer_connection_wrapper.h"
|
|
#include "pc/test/fake_audio_capture_module.h"
|
|
#include "pc/test/mock_peer_connection_observers.h"
|
|
#include "rtc_base/gunit.h"
|
|
#include "test/gmock.h"
|
|
|
|
using ::testing::Contains;
|
|
using ::testing::Each;
|
|
using ::testing::ElementsAre;
|
|
using ::testing::ElementsAreArray;
|
|
using ::testing::Eq;
|
|
using ::testing::Field;
|
|
using ::testing::IsEmpty;
|
|
using ::testing::Ne;
|
|
using ::testing::Property;
|
|
using ::testing::SizeIs;
|
|
|
|
using cricket::MediaContentDescription;
|
|
using cricket::RidDescription;
|
|
using cricket::SimulcastDescription;
|
|
using cricket::SimulcastLayer;
|
|
using cricket::StreamParams;
|
|
|
|
namespace cricket {
|
|
|
|
std::ostream& operator<<( // no-presubmit-check TODO(webrtc:8982)
|
|
std::ostream& os, // no-presubmit-check TODO(webrtc:8982)
|
|
const SimulcastLayer& layer) {
|
|
if (layer.is_paused) {
|
|
os << "~";
|
|
}
|
|
return os << layer.rid;
|
|
}
|
|
|
|
} // namespace cricket
|
|
|
|
namespace {
|
|
|
|
std::vector<SimulcastLayer> CreateLayers(const std::vector<std::string>& rids,
|
|
const std::vector<bool>& active) {
|
|
RTC_DCHECK_EQ(rids.size(), active.size());
|
|
std::vector<SimulcastLayer> result;
|
|
absl::c_transform(rids, active, std::back_inserter(result),
|
|
[](const std::string& rid, bool is_active) {
|
|
return SimulcastLayer(rid, !is_active);
|
|
});
|
|
return result;
|
|
}
|
|
std::vector<SimulcastLayer> CreateLayers(const std::vector<std::string>& rids,
|
|
bool active) {
|
|
return CreateLayers(rids, std::vector<bool>(rids.size(), active));
|
|
}
|
|
|
|
} // namespace
|
|
namespace webrtc {
|
|
|
|
class PeerConnectionSimulcastTests : public testing::Test {
|
|
public:
|
|
PeerConnectionSimulcastTests()
|
|
: pc_factory_(
|
|
CreatePeerConnectionFactory(rtc::Thread::Current(),
|
|
rtc::Thread::Current(),
|
|
rtc::Thread::Current(),
|
|
FakeAudioCaptureModule::Create(),
|
|
CreateBuiltinAudioEncoderFactory(),
|
|
CreateBuiltinAudioDecoderFactory(),
|
|
CreateBuiltinVideoEncoderFactory(),
|
|
CreateBuiltinVideoDecoderFactory(),
|
|
nullptr,
|
|
nullptr)) {}
|
|
|
|
rtc::scoped_refptr<PeerConnectionInterface> CreatePeerConnection(
|
|
MockPeerConnectionObserver* observer) {
|
|
PeerConnectionInterface::RTCConfiguration config;
|
|
config.sdp_semantics = SdpSemantics::kUnifiedPlan;
|
|
PeerConnectionDependencies pcd(observer);
|
|
auto pc = pc_factory_->CreatePeerConnection(config, std::move(pcd));
|
|
EXPECT_TRUE(pc);
|
|
observer->SetPeerConnectionInterface(pc);
|
|
return pc;
|
|
}
|
|
|
|
std::unique_ptr<PeerConnectionWrapper> CreatePeerConnectionWrapper() {
|
|
auto observer = absl::make_unique<MockPeerConnectionObserver>();
|
|
auto pc = CreatePeerConnection(observer.get());
|
|
return absl::make_unique<PeerConnectionWrapper>(pc_factory_, pc,
|
|
std::move(observer));
|
|
}
|
|
|
|
void ExchangeOfferAnswer(PeerConnectionWrapper* local,
|
|
PeerConnectionWrapper* remote,
|
|
const std::vector<SimulcastLayer>& answer_layers) {
|
|
auto offer = local->CreateOfferAndSetAsLocal();
|
|
// Remove simulcast as the second peer connection won't support it.
|
|
auto removed_simulcast = RemoveSimulcast(offer.get());
|
|
std::string err;
|
|
EXPECT_TRUE(remote->SetRemoteDescription(std::move(offer), &err)) << err;
|
|
auto answer = remote->CreateAnswerAndSetAsLocal();
|
|
// Setup the answer to look like a server response.
|
|
auto mcd_answer = answer->description()->contents()[0].media_description();
|
|
auto& receive_layers = mcd_answer->simulcast_description().receive_layers();
|
|
for (const SimulcastLayer& layer : answer_layers) {
|
|
receive_layers.AddLayer(layer);
|
|
}
|
|
EXPECT_TRUE(local->SetRemoteDescription(std::move(answer), &err)) << err;
|
|
}
|
|
|
|
RtpTransceiverInit CreateTransceiverInit(
|
|
const std::vector<SimulcastLayer>& layers) {
|
|
RtpTransceiverInit init;
|
|
for (const SimulcastLayer& layer : layers) {
|
|
RtpEncodingParameters encoding;
|
|
encoding.rid = layer.rid;
|
|
encoding.active = !layer.is_paused;
|
|
init.send_encodings.push_back(encoding);
|
|
}
|
|
return init;
|
|
}
|
|
|
|
rtc::scoped_refptr<RtpTransceiverInterface> AddTransceiver(
|
|
PeerConnectionWrapper* pc,
|
|
const std::vector<SimulcastLayer>& layers) {
|
|
auto init = CreateTransceiverInit(layers);
|
|
return pc->AddTransceiver(cricket::MEDIA_TYPE_VIDEO, init);
|
|
}
|
|
|
|
SimulcastDescription RemoveSimulcast(SessionDescriptionInterface* sd) {
|
|
auto mcd = sd->description()->contents()[0].media_description();
|
|
auto result = mcd->simulcast_description();
|
|
mcd->set_simulcast_description(SimulcastDescription());
|
|
return result;
|
|
}
|
|
|
|
void AddRequestToReceiveSimulcast(const std::vector<SimulcastLayer>& layers,
|
|
SessionDescriptionInterface* sd) {
|
|
auto mcd = sd->description()->contents()[0].media_description();
|
|
SimulcastDescription simulcast;
|
|
auto& receive_layers = simulcast.receive_layers();
|
|
for (const SimulcastLayer& layer : layers) {
|
|
receive_layers.AddLayer(layer);
|
|
}
|
|
mcd->set_simulcast_description(simulcast);
|
|
}
|
|
|
|
bool ValidateTransceiverParameters(
|
|
rtc::scoped_refptr<RtpTransceiverInterface> transceiver,
|
|
const std::vector<SimulcastLayer>& layers) {
|
|
bool errors_before = ::testing::Test::HasFailure();
|
|
auto parameters = transceiver->sender()->GetParameters();
|
|
std::vector<SimulcastLayer> result_layers;
|
|
absl::c_transform(parameters.encodings, std::back_inserter(result_layers),
|
|
[](const RtpEncodingParameters& encoding) {
|
|
return SimulcastLayer(encoding.rid, !encoding.active);
|
|
});
|
|
EXPECT_THAT(result_layers, ElementsAreArray(layers));
|
|
// If there were errors before this code ran, we cannot tell if this
|
|
// validation succeeded or not.
|
|
return errors_before || !::testing::Test::HasFailure();
|
|
}
|
|
|
|
private:
|
|
rtc::scoped_refptr<PeerConnectionFactoryInterface> pc_factory_;
|
|
};
|
|
|
|
// Validates that RIDs are supported arguments when adding a transceiver.
|
|
TEST_F(PeerConnectionSimulcastTests, CanCreateTransceiverWithRid) {
|
|
auto pc = CreatePeerConnectionWrapper();
|
|
auto layers = CreateLayers({"f"}, true);
|
|
auto transceiver = AddTransceiver(pc.get(), layers);
|
|
ASSERT_TRUE(transceiver);
|
|
auto parameters = transceiver->sender()->GetParameters();
|
|
// Single RID should be removed.
|
|
EXPECT_THAT(parameters.encodings,
|
|
ElementsAre(Field("rid", &RtpEncodingParameters::rid, Eq(""))));
|
|
}
|
|
|
|
TEST_F(PeerConnectionSimulcastTests, CanCreateTransceiverWithSimulcast) {
|
|
auto pc = CreatePeerConnectionWrapper();
|
|
auto layers = CreateLayers({"f", "h", "q"}, true);
|
|
auto transceiver = AddTransceiver(pc.get(), layers);
|
|
ASSERT_TRUE(transceiver);
|
|
EXPECT_TRUE(ValidateTransceiverParameters(transceiver, layers));
|
|
}
|
|
|
|
TEST_F(PeerConnectionSimulcastTests, RidsAreAutogeneratedIfNotProvided) {
|
|
auto pc = CreatePeerConnectionWrapper();
|
|
auto init = CreateTransceiverInit(CreateLayers({"f", "h", "q"}, true));
|
|
for (RtpEncodingParameters& parameters : init.send_encodings) {
|
|
parameters.rid = "";
|
|
}
|
|
auto transceiver = pc->AddTransceiver(cricket::MEDIA_TYPE_VIDEO, init);
|
|
auto parameters = transceiver->sender()->GetParameters();
|
|
ASSERT_EQ(3u, parameters.encodings.size());
|
|
EXPECT_THAT(parameters.encodings,
|
|
Each(Field("rid", &RtpEncodingParameters::rid, Ne(""))));
|
|
}
|
|
|
|
// Validates that an error is returned when there is a mix of supplied and not
|
|
// supplied RIDs in a call to AddTransceiver.
|
|
TEST_F(PeerConnectionSimulcastTests, MustSupplyAllOrNoRidsInSimulcast) {
|
|
auto pc_wrapper = CreatePeerConnectionWrapper();
|
|
auto pc = pc_wrapper->pc();
|
|
// Cannot create a layer with empty RID. Remove the RID after init is created.
|
|
auto layers = CreateLayers({"f", "h", "remove"}, true);
|
|
auto init = CreateTransceiverInit(layers);
|
|
init.send_encodings[2].rid = "";
|
|
auto error = pc->AddTransceiver(cricket::MEDIA_TYPE_VIDEO, init);
|
|
EXPECT_EQ(RTCErrorType::INVALID_PARAMETER, error.error().type());
|
|
}
|
|
|
|
// Validates that a single RID is removed from the encoding layer.
|
|
TEST_F(PeerConnectionSimulcastTests, SingleRidIsRemovedFromSessionDescription) {
|
|
auto pc = CreatePeerConnectionWrapper();
|
|
auto transceiver = AddTransceiver(pc.get(), CreateLayers({"1"}, true));
|
|
auto offer = pc->CreateOfferAndSetAsLocal();
|
|
ASSERT_TRUE(offer);
|
|
auto contents = offer->description()->contents();
|
|
ASSERT_EQ(1u, contents.size());
|
|
EXPECT_THAT(contents[0].media_description()->streams(),
|
|
ElementsAre(Property(&StreamParams::has_rids, false)));
|
|
}
|
|
|
|
TEST_F(PeerConnectionSimulcastTests, SimulcastLayersRemovedFromTail) {
|
|
static_assert(
|
|
kMaxSimulcastStreams < 8,
|
|
"Test assumes that the platform does not allow 8 simulcast layers");
|
|
auto pc = CreatePeerConnectionWrapper();
|
|
auto layers = CreateLayers({"1", "2", "3", "4", "5", "6", "7", "8"}, true);
|
|
std::vector<SimulcastLayer> expected_layers;
|
|
std::copy_n(layers.begin(), kMaxSimulcastStreams,
|
|
std::back_inserter(expected_layers));
|
|
auto transceiver = AddTransceiver(pc.get(), layers);
|
|
EXPECT_TRUE(ValidateTransceiverParameters(transceiver, expected_layers));
|
|
}
|
|
|
|
// Checks that an offfer to send simulcast contains a SimulcastDescription.
|
|
TEST_F(PeerConnectionSimulcastTests, SimulcastAppearsInSessionDescription) {
|
|
auto pc = CreatePeerConnectionWrapper();
|
|
std::vector<std::string> rids({"f", "h", "q"});
|
|
auto layers = CreateLayers(rids, true);
|
|
auto transceiver = AddTransceiver(pc.get(), layers);
|
|
auto offer = pc->CreateOffer();
|
|
ASSERT_TRUE(offer);
|
|
auto contents = offer->description()->contents();
|
|
ASSERT_EQ(1u, contents.size());
|
|
auto content = contents[0];
|
|
auto mcd = content.media_description();
|
|
ASSERT_TRUE(mcd->HasSimulcast());
|
|
auto simulcast = mcd->simulcast_description();
|
|
EXPECT_THAT(simulcast.receive_layers(), IsEmpty());
|
|
// The size is validated separately because GetAllLayers() flattens the list.
|
|
EXPECT_THAT(simulcast.send_layers(), SizeIs(3));
|
|
std::vector<SimulcastLayer> result = simulcast.send_layers().GetAllLayers();
|
|
EXPECT_THAT(result, ElementsAreArray(layers));
|
|
auto streams = mcd->streams();
|
|
ASSERT_EQ(1u, streams.size());
|
|
auto stream = streams[0];
|
|
EXPECT_FALSE(stream.has_ssrcs());
|
|
EXPECT_TRUE(stream.has_rids());
|
|
std::vector<std::string> result_rids;
|
|
absl::c_transform(stream.rids(), std::back_inserter(result_rids),
|
|
[](const RidDescription& rid) { return rid.rid; });
|
|
EXPECT_THAT(result_rids, ElementsAreArray(rids));
|
|
}
|
|
|
|
// Checks that Simulcast layers propagate to the sender parameters.
|
|
TEST_F(PeerConnectionSimulcastTests, SimulcastLayersAreSetInSender) {
|
|
auto local = CreatePeerConnectionWrapper();
|
|
auto remote = CreatePeerConnectionWrapper();
|
|
auto layers = CreateLayers({"f", "h", "q"}, true);
|
|
auto transceiver = AddTransceiver(local.get(), layers);
|
|
auto offer = local->CreateOfferAndSetAsLocal();
|
|
EXPECT_TRUE(ValidateTransceiverParameters(transceiver, layers));
|
|
|
|
// Remove simulcast as the second peer connection won't support it.
|
|
auto simulcast = RemoveSimulcast(offer.get());
|
|
std::string error;
|
|
EXPECT_TRUE(remote->SetRemoteDescription(std::move(offer), &error)) << error;
|
|
auto answer = remote->CreateAnswerAndSetAsLocal();
|
|
|
|
// Setup an answer that mimics a server accepting simulcast.
|
|
auto mcd_answer = answer->description()->contents()[0].media_description();
|
|
mcd_answer->mutable_streams().clear();
|
|
auto simulcast_layers = simulcast.send_layers().GetAllLayers();
|
|
auto& receive_layers = mcd_answer->simulcast_description().receive_layers();
|
|
for (const auto& layer : simulcast_layers) {
|
|
receive_layers.AddLayer(layer);
|
|
}
|
|
EXPECT_TRUE(local->SetRemoteDescription(std::move(answer), &error)) << error;
|
|
EXPECT_TRUE(ValidateTransceiverParameters(transceiver, layers));
|
|
}
|
|
|
|
// Checks that paused Simulcast layers propagate to the sender parameters.
|
|
TEST_F(PeerConnectionSimulcastTests, PausedSimulcastLayersAreDisabledInSender) {
|
|
auto local = CreatePeerConnectionWrapper();
|
|
auto remote = CreatePeerConnectionWrapper();
|
|
auto layers = CreateLayers({"f", "h", "q"}, {true, false, true});
|
|
auto server_layers = CreateLayers({"f", "h", "q"}, {true, false, false});
|
|
RTC_DCHECK_EQ(layers.size(), server_layers.size());
|
|
auto transceiver = AddTransceiver(local.get(), layers);
|
|
auto offer = local->CreateOfferAndSetAsLocal();
|
|
EXPECT_TRUE(ValidateTransceiverParameters(transceiver, layers));
|
|
|
|
// Remove simulcast as the second peer connection won't support it.
|
|
RemoveSimulcast(offer.get());
|
|
std::string error;
|
|
EXPECT_TRUE(remote->SetRemoteDescription(std::move(offer), &error)) << error;
|
|
auto answer = remote->CreateAnswerAndSetAsLocal();
|
|
|
|
// Setup an answer that mimics a server accepting simulcast.
|
|
auto mcd_answer = answer->description()->contents()[0].media_description();
|
|
mcd_answer->mutable_streams().clear();
|
|
auto& receive_layers = mcd_answer->simulcast_description().receive_layers();
|
|
for (const SimulcastLayer& layer : server_layers) {
|
|
receive_layers.AddLayer(layer);
|
|
}
|
|
EXPECT_TRUE(local->SetRemoteDescription(std::move(answer), &error)) << error;
|
|
EXPECT_TRUE(ValidateTransceiverParameters(transceiver, server_layers));
|
|
}
|
|
|
|
// Checks that when Simulcast is not supported by the remote party, then all
|
|
// the layers (except the first) are removed.
|
|
TEST_F(PeerConnectionSimulcastTests, SimulcastRejectedRemovesExtraLayers) {
|
|
auto local = CreatePeerConnectionWrapper();
|
|
auto remote = CreatePeerConnectionWrapper();
|
|
auto layers = CreateLayers({"1", "2", "3", "4"}, true);
|
|
auto transceiver = AddTransceiver(local.get(), layers);
|
|
ExchangeOfferAnswer(local.get(), remote.get(), {});
|
|
auto parameters = transceiver->sender()->GetParameters();
|
|
// Should only have the first layer.
|
|
EXPECT_THAT(parameters.encodings,
|
|
ElementsAre(Field("rid", &RtpEncodingParameters::rid, Eq("1"))));
|
|
}
|
|
|
|
// Checks that if Simulcast is supported by remote party, but some layers are
|
|
// rejected, then only rejected layers are removed from the sender.
|
|
TEST_F(PeerConnectionSimulcastTests, RejectedSimulcastLayersAreDeactivated) {
|
|
auto local = CreatePeerConnectionWrapper();
|
|
auto remote = CreatePeerConnectionWrapper();
|
|
auto layers = CreateLayers({"1", "2", "3", "4"}, true);
|
|
auto expected_layers = CreateLayers({"2", "3", "4"}, true);
|
|
auto transceiver = AddTransceiver(local.get(), layers);
|
|
auto offer = local->CreateOfferAndSetAsLocal();
|
|
EXPECT_TRUE(ValidateTransceiverParameters(transceiver, layers));
|
|
// Remove simulcast as the second peer connection won't support it.
|
|
auto removed_simulcast = RemoveSimulcast(offer.get());
|
|
std::string error;
|
|
EXPECT_TRUE(remote->SetRemoteDescription(std::move(offer), &error)) << error;
|
|
auto answer = remote->CreateAnswerAndSetAsLocal();
|
|
auto mcd_answer = answer->description()->contents()[0].media_description();
|
|
// Setup the answer to look like a server response.
|
|
// Remove one of the layers to reject it in the answer.
|
|
auto simulcast_layers = removed_simulcast.send_layers().GetAllLayers();
|
|
simulcast_layers.erase(simulcast_layers.begin());
|
|
auto& receive_layers = mcd_answer->simulcast_description().receive_layers();
|
|
for (const auto& layer : simulcast_layers) {
|
|
receive_layers.AddLayer(layer);
|
|
}
|
|
ASSERT_TRUE(mcd_answer->HasSimulcast());
|
|
EXPECT_TRUE(local->SetRemoteDescription(std::move(answer), &error)) << error;
|
|
EXPECT_TRUE(ValidateTransceiverParameters(transceiver, expected_layers));
|
|
}
|
|
|
|
// Checks that simulcast is set up correctly when the server sends an offer
|
|
// requesting to receive simulcast.
|
|
TEST_F(PeerConnectionSimulcastTests, ServerSendsOfferToReceiveSimulcast) {
|
|
auto local = CreatePeerConnectionWrapper();
|
|
auto remote = CreatePeerConnectionWrapper();
|
|
auto layers = CreateLayers({"f", "h", "q"}, true);
|
|
AddTransceiver(local.get(), layers);
|
|
auto offer = local->CreateOfferAndSetAsLocal();
|
|
// Remove simulcast as a sender and set it up as a receiver.
|
|
RemoveSimulcast(offer.get());
|
|
AddRequestToReceiveSimulcast(layers, offer.get());
|
|
std::string error;
|
|
EXPECT_TRUE(remote->SetRemoteDescription(std::move(offer), &error)) << error;
|
|
auto transceiver = remote->pc()->GetTransceivers()[0];
|
|
transceiver->SetDirection(RtpTransceiverDirection::kSendRecv);
|
|
EXPECT_TRUE(remote->CreateAnswerAndSetAsLocal());
|
|
EXPECT_TRUE(ValidateTransceiverParameters(transceiver, layers));
|
|
}
|
|
|
|
// Checks that SetRemoteDescription doesn't attempt to associate a transceiver
|
|
// when simulcast is requested by the server.
|
|
TEST_F(PeerConnectionSimulcastTests, TransceiverIsNotRecycledWithSimulcast) {
|
|
auto local = CreatePeerConnectionWrapper();
|
|
auto remote = CreatePeerConnectionWrapper();
|
|
auto layers = CreateLayers({"f", "h", "q"}, true);
|
|
AddTransceiver(local.get(), layers);
|
|
auto offer = local->CreateOfferAndSetAsLocal();
|
|
// Remove simulcast as a sender and set it up as a receiver.
|
|
RemoveSimulcast(offer.get());
|
|
AddRequestToReceiveSimulcast(layers, offer.get());
|
|
// Call AddTrack so that a transceiver is created.
|
|
remote->AddVideoTrack("fake_track");
|
|
std::string error;
|
|
EXPECT_TRUE(remote->SetRemoteDescription(std::move(offer), &error)) << error;
|
|
auto transceivers = remote->pc()->GetTransceivers();
|
|
ASSERT_EQ(2u, transceivers.size());
|
|
auto transceiver = transceivers[1];
|
|
transceiver->SetDirection(RtpTransceiverDirection::kSendRecv);
|
|
EXPECT_TRUE(remote->CreateAnswerAndSetAsLocal());
|
|
EXPECT_TRUE(ValidateTransceiverParameters(transceiver, layers));
|
|
}
|
|
|
|
// Checks that if the number of layers changes during negotiation, then any
|
|
// outstanding get/set parameters transaction is invalidated.
|
|
TEST_F(PeerConnectionSimulcastTests, ParametersAreInvalidatedWhenLayersChange) {
|
|
auto local = CreatePeerConnectionWrapper();
|
|
auto remote = CreatePeerConnectionWrapper();
|
|
auto layers = CreateLayers({"1", "2", "3"}, true);
|
|
auto transceiver = AddTransceiver(local.get(), layers);
|
|
auto parameters = transceiver->sender()->GetParameters();
|
|
ASSERT_EQ(3u, parameters.encodings.size());
|
|
// Response will reject simulcast altogether.
|
|
ExchangeOfferAnswer(local.get(), remote.get(), {});
|
|
auto result = transceiver->sender()->SetParameters(parameters);
|
|
EXPECT_EQ(RTCErrorType::INVALID_STATE, result.type());
|
|
}
|
|
|
|
// Checks that even though negotiation modifies the sender's parameters, an
|
|
// outstanding get/set parameters transaction is not invalidated.
|
|
// This test negotiates twice because initial parameters before negotiation
|
|
// is missing critical information and cannot be set on the sender.
|
|
TEST_F(PeerConnectionSimulcastTests,
|
|
NegotiationDoesNotInvalidateParameterTransactions) {
|
|
auto local = CreatePeerConnectionWrapper();
|
|
auto remote = CreatePeerConnectionWrapper();
|
|
auto layers = CreateLayers({"1", "2", "3"}, true);
|
|
auto expected_layers = CreateLayers({"1", "2", "3"}, false);
|
|
auto transceiver = AddTransceiver(local.get(), layers);
|
|
ExchangeOfferAnswer(local.get(), remote.get(), expected_layers);
|
|
|
|
// Verify that negotiation does not invalidate the parameters.
|
|
auto parameters = transceiver->sender()->GetParameters();
|
|
ExchangeOfferAnswer(local.get(), remote.get(), expected_layers);
|
|
|
|
auto result = transceiver->sender()->SetParameters(parameters);
|
|
EXPECT_TRUE(result.ok());
|
|
EXPECT_TRUE(ValidateTransceiverParameters(transceiver, expected_layers));
|
|
}
|
|
|
|
} // namespace webrtc
|