diff --git a/api/rtp_sender_interface.cc b/api/rtp_sender_interface.cc index d23fd1844c..2b27b7489c 100644 --- a/api/rtp_sender_interface.cc +++ b/api/rtp_sender_interface.cc @@ -30,4 +30,16 @@ rtc::scoped_refptr RtpSenderInterface::dtls_transport() return nullptr; } +// TODO(amithi): Fix downstream dependencies and make GetParameters pure +// virtual. +RtpParameters RtpSenderInterface::GetParameters() { + const RtpSenderInterface* interface = this; + return interface->GetParameters(); +} + +RtpParameters RtpSenderInterface::GetParameters() const { + RtpSenderInterface* interface = const_cast(this); + return interface->GetParameters(); +} + } // namespace webrtc diff --git a/api/rtp_sender_interface.h b/api/rtp_sender_interface.h index c04ce69158..83cbe7a9da 100644 --- a/api/rtp_sender_interface.h +++ b/api/rtp_sender_interface.h @@ -67,7 +67,9 @@ class RtpSenderInterface : public rtc::RefCountInterface { // TODO(orphis): Make it pure virtual once Chrome has updated virtual std::vector init_send_encodings() const; - virtual RtpParameters GetParameters() = 0; + // TODO(amithi): Fix downstream dependecies and remove the non-const method. + virtual RtpParameters GetParameters(); + virtual RtpParameters GetParameters() const; // Note that only a subset of the parameters can currently be changed. See // rtpparameters.h // The encodings are in increasing quality order for simulcast. @@ -104,7 +106,7 @@ PROXY_CONSTMETHOD0(cricket::MediaType, media_type) PROXY_CONSTMETHOD0(std::string, id) PROXY_CONSTMETHOD0(std::vector, stream_ids) PROXY_CONSTMETHOD0(std::vector, init_send_encodings) -PROXY_METHOD0(RtpParameters, GetParameters); +PROXY_CONSTMETHOD0(RtpParameters, GetParameters); PROXY_METHOD1(RTCError, SetParameters, const RtpParameters&) PROXY_CONSTMETHOD0(rtc::scoped_refptr, GetDtmfSender); PROXY_METHOD1(void, diff --git a/api/test/mock_rtpsender.h b/api/test/mock_rtpsender.h index dca718f145..6a656ea56e 100644 --- a/api/test/mock_rtpsender.h +++ b/api/test/mock_rtpsender.h @@ -28,7 +28,7 @@ class MockRtpSender : public rtc::RefCountedObject { MOCK_CONST_METHOD0(id, std::string()); MOCK_CONST_METHOD0(stream_ids, std::vector()); MOCK_CONST_METHOD0(init_send_encodings, std::vector()); - MOCK_METHOD0(GetParameters, RtpParameters()); + MOCK_CONST_METHOD0(GetParameters, RtpParameters()); MOCK_METHOD1(SetParameters, RTCError(const RtpParameters&)); MOCK_CONST_METHOD0(GetDtmfSender, rtc::scoped_refptr()); }; diff --git a/media/base/media_engine.cc b/media/base/media_engine.cc index 62c3cec076..43929116ac 100644 --- a/media/base/media_engine.cc +++ b/media/base/media_engine.cc @@ -15,6 +15,7 @@ #include #include +#include "absl/algorithm/container.h" #include "api/video/video_bitrate_allocation.h" #include "rtc_base/checks.h" #include "rtc_base/string_encode.h" @@ -40,6 +41,13 @@ webrtc::RtpParameters CreateRtpParametersWithEncodings(StreamParams sp) { for (size_t i = 0; i < encodings.size(); ++i) { encodings[i].ssrc = primary_ssrcs[i]; } + + const std::vector& rids = sp.rids(); + RTC_DCHECK(rids.size() == 0 || rids.size() == encoding_count); + for (size_t i = 0; i < rids.size(); ++i) { + encodings[i].rid = rids[i].rid; + } + webrtc::RtpParameters parameters; parameters.encodings = encodings; parameters.rtcp.cname = sp.cname; @@ -115,13 +123,21 @@ webrtc::RTCError CheckRtpParametersInvalidModificationAndValues( RTCErrorType::INVALID_MODIFICATION, "Attempted to set RtpParameters with modified header extensions"); } - - for (size_t i = 0; i < rtp_parameters.encodings.size(); ++i) { - if (rtp_parameters.encodings[i].ssrc != - old_rtp_parameters.encodings[i].ssrc) { - LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_MODIFICATION, - "Attempted to set RtpParameters with modified SSRC"); - } + if (!absl::c_equal(old_rtp_parameters.encodings, rtp_parameters.encodings, + [](const webrtc::RtpEncodingParameters& encoding1, + const webrtc::RtpEncodingParameters& encoding2) { + return encoding1.rid == encoding2.rid; + })) { + LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_MODIFICATION, + "Attempted to change RID values in the encodings."); + } + if (!absl::c_equal(old_rtp_parameters.encodings, rtp_parameters.encodings, + [](const webrtc::RtpEncodingParameters& encoding1, + const webrtc::RtpEncodingParameters& encoding2) { + return encoding1.ssrc == encoding2.ssrc; + })) { + LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_MODIFICATION, + "Attempted to set RtpParameters with modified SSRC"); } return CheckRtpParametersValues(rtp_parameters); diff --git a/pc/peer_connection.cc b/pc/peer_connection.cc index 10aa21e902..12117a566c 100644 --- a/pc/peer_connection.cc +++ b/pc/peer_connection.cc @@ -3004,9 +3004,10 @@ static std::vector GetSendEncodingsFromRemoteDescription( static RTCError UpdateSimulcastLayerStatusInSender( const std::vector& layers, - RtpSenderInterface* sender) { + rtc::scoped_refptr sender) { RTC_DCHECK(sender); RtpParameters parameters = sender->GetParameters(); + std::vector disabled_layers; // The simulcast envelope cannot be changed, only the status of the streams. // So we will iterate over the send encodings rather than the layers. @@ -3016,29 +3017,36 @@ static RTCError UpdateSimulcastLayerStatusInSender( return layer.rid == encoding.rid; }); // A layer that cannot be found may have been removed by the remote party. - encoding.active = iter != layers.end() && !iter->is_paused; + if (iter == layers.end()) { + disabled_layers.push_back(encoding.rid); + continue; + } + + encoding.active = !iter->is_paused; } - return sender->SetParameters(parameters); + RTCError result = sender->SetParameters(parameters); + if (result.ok()) { + result = sender->DisableEncodingLayers(disabled_layers); + } + + return result; } -static RTCError DisableSimulcastInSender(RtpSenderInterface* sender) { +static RTCError DisableSimulcastInSender( + rtc::scoped_refptr sender) { RTC_DCHECK(sender); RtpParameters parameters = sender->GetParameters(); - if (parameters.encodings.empty()) { + if (parameters.encodings.size() <= 1) { return RTCError::OK(); } - bool first_layer_status = parameters.encodings[0].active; - for (RtpEncodingParameters& encoding : parameters.encodings) { - // TODO(bugs.webrtc.org/10251): Active does not really disable the layer. - // The user might enable it at a later point in time even though it was - // rejected. - encoding.active = false; - } - parameters.encodings[0].active = first_layer_status; - - return sender->SetParameters(parameters); + std::vector disabled_layers; + std::transform( + parameters.encodings.begin() + 1, parameters.encodings.end(), + std::back_inserter(disabled_layers), + [](const RtpEncodingParameters& encoding) { return encoding.rid; }); + return sender->DisableEncodingLayers(disabled_layers); } RTCErrorOr>> @@ -3127,7 +3135,7 @@ PeerConnection::AssociateTransceiver(cricket::ContentSource source, old_local_content->media_description()->HasSimulcast() && !media_desc->HasSimulcast()) { RTCError error = - DisableSimulcastInSender(transceiver->internal()->sender()); + DisableSimulcastInSender(transceiver->internal()->sender_internal()); if (!error.ok()) { RTC_LOG(LS_ERROR) << "Failed to remove rejected simulcast."; return std::move(error); @@ -3148,7 +3156,7 @@ PeerConnection::AssociateTransceiver(cricket::ContentSource source, .receive_layers() .GetAllLayers(); RTCError error = UpdateSimulcastLayerStatusInSender( - layers, transceiver->internal()->sender()); + layers, transceiver->internal()->sender_internal()); if (!error.ok()) { RTC_LOG(LS_ERROR) << "Failed updating status for simulcast layers."; return std::move(error); diff --git a/pc/peer_connection_simulcast_unittest.cc b/pc/peer_connection_simulcast_unittest.cc index f9e3d932e2..695d249cd9 100644 --- a/pc/peer_connection_simulcast_unittest.cc +++ b/pc/peer_connection_simulcast_unittest.cc @@ -174,7 +174,7 @@ TEST_F(PeerConnectionSimulcastTests, CanCreateTransceiverWithRid) { auto parameters = transceiver->sender()->GetParameters(); // Single RID should be removed. EXPECT_THAT(parameters.encodings, - ElementsAre(Field(&RtpEncodingParameters::rid, Eq("")))); + ElementsAre(Field("rid", &RtpEncodingParameters::rid, Eq("")))); } TEST_F(PeerConnectionSimulcastTests, CanCreateTransceiverWithSimulcast) { @@ -195,7 +195,7 @@ TEST_F(PeerConnectionSimulcastTests, RidsAreAutogeneratedIfNotProvided) { auto parameters = transceiver->sender()->GetParameters(); ASSERT_EQ(3u, parameters.encodings.size()); EXPECT_THAT(parameters.encodings, - Each(Field(&RtpEncodingParameters::rid, Ne("")))); + Each(Field("rid", &RtpEncodingParameters::rid, Ne("")))); } // Validates that an error is returned when there is a mix of supplied and not @@ -211,8 +211,7 @@ TEST_F(PeerConnectionSimulcastTests, MustSupplyAllOrNoRidsInSimulcast) { EXPECT_EQ(RTCErrorType::INVALID_PARAMETER, error.error().type()); } -// Validates that a single RID does not get negotiated. -// This test is currently disabled because a single RID is not supported. +// 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)); @@ -323,8 +322,8 @@ TEST_F(PeerConnectionSimulcastTests, PausedSimulcastLayersAreDisabledInSender) { } // Checks that when Simulcast is not supported by the remote party, then all -// the layers (except the first) are marked as disabled. -TEST_F(PeerConnectionSimulcastTests, SimulcastRejectedDisablesExtraLayers) { +// 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); @@ -340,15 +339,19 @@ TEST_F(PeerConnectionSimulcastTests, SimulcastRejectedDisablesExtraLayers) { EXPECT_TRUE(remote->SetRemoteDescription(std::move(offer), &error)) << error; auto answer = remote->CreateAnswerAndSetAsLocal(); EXPECT_TRUE(local->SetRemoteDescription(std::move(answer), &error)) << error; - EXPECT_TRUE(ValidateTransceiverParameters(transceiver, expected_layers)); + 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 layer is -// rejected, then only that layer is marked as disabled. +// 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)); @@ -368,8 +371,7 @@ TEST_F(PeerConnectionSimulcastTests, RejectedSimulcastLayersAreDeactivated) { } ASSERT_TRUE(mcd_answer->HasSimulcast()); EXPECT_TRUE(local->SetRemoteDescription(std::move(answer), &error)) << error; - layers[0].is_paused = true; - EXPECT_TRUE(ValidateTransceiverParameters(transceiver, layers)); + EXPECT_TRUE(ValidateTransceiverParameters(transceiver, expected_layers)); } // Checks that simulcast is set up correctly when the server sends an offer diff --git a/pc/rtp_sender.cc b/pc/rtp_sender.cc index b9a269a208..c6e5b28345 100644 --- a/pc/rtp_sender.cc +++ b/pc/rtp_sender.cc @@ -82,6 +82,36 @@ void MaybeAttachFrameEncryptorToMediaChannel( } } +void RemoveEncodingLayers(const std::vector& rids, + std::vector* encodings) { + RTC_DCHECK(encodings); + encodings->erase( + std::remove_if(encodings->begin(), encodings->end(), + [&rids](const RtpEncodingParameters& encoding) { + return absl::c_linear_search(rids, encoding.rid); + }), + encodings->end()); +} + +RtpParameters RestoreEncodingLayers( + const RtpParameters& parameters, + const std::vector& removed_rids, + const std::vector& all_layers) { + RTC_DCHECK_EQ(parameters.encodings.size() + removed_rids.size(), + all_layers.size()); + RtpParameters result(parameters); + result.encodings.clear(); + size_t index = 0; + for (const RtpEncodingParameters& encoding : all_layers) { + if (absl::c_linear_search(removed_rids, encoding.rid)) { + result.encodings.push_back(encoding); + continue; + } + result.encodings.push_back(parameters.encodings[index++]); + } + return result; +} + } // namespace // Returns true if any RtpParameters member that isn't implemented contains a @@ -246,7 +276,7 @@ bool AudioRtpSender::SetTrack(MediaStreamTrackInterface* track) { return true; } -RtpParameters AudioRtpSender::GetParameters() { +RtpParameters AudioRtpSender::GetParameters() const { if (stopped_) { return RtpParameters(); } @@ -440,6 +470,13 @@ void AudioRtpSender::ClearAudioSend() { } } +RTCError AudioRtpSender::DisableEncodingLayers( + const std::vector& rids) { + // Multiple encoding layers (and simulcast) are not supported in audio. + return rids.empty() ? RTCError::OK() + : RTCError(RTCErrorType::UNSUPPORTED_OPERATION); +} + VideoRtpSender::VideoRtpSender(rtc::Thread* worker_thread, const std::string& id) : worker_thread_(worker_thread), id_(id) { @@ -501,7 +538,7 @@ bool VideoRtpSender::SetTrack(MediaStreamTrackInterface* track) { return true; } -RtpParameters VideoRtpSender::GetParameters() { +RtpParameters VideoRtpSender::GetParameters() const { if (stopped_) { return RtpParameters(); } @@ -513,6 +550,7 @@ RtpParameters VideoRtpSender::GetParameters() { } return worker_thread_->Invoke(RTC_FROM_HERE, [&] { RtpParameters result = media_channel_->GetRtpSendParameters(ssrc_); + RemoveEncodingLayers(disabled_rids_, &result.encodings); last_transaction_id_ = rtc::CreateRandomUuid(); result.transaction_id = last_transaction_id_.value(); return result; @@ -551,7 +589,16 @@ RTCError VideoRtpSender::SetParameters(const RtpParameters& parameters) { return result; } return worker_thread_->Invoke(RTC_FROM_HERE, [&] { - RTCError result = media_channel_->SetRtpSendParameters(ssrc_, parameters); + RtpParameters rtp_parameters = parameters; + if (!disabled_rids_.empty()) { + // Need to add the inactive layers. + RtpParameters old_parameters = + media_channel_->GetRtpSendParameters(ssrc_); + rtp_parameters = RestoreEncodingLayers(parameters, disabled_rids_, + old_parameters.encodings); + } + RTCError result = + media_channel_->SetRtpSendParameters(ssrc_, rtp_parameters); last_transaction_id_.reset(); return result; }); @@ -605,6 +652,7 @@ void VideoRtpSender::SetSsrc(uint32_t ssrc) { for (size_t i = 0; i < init_parameters_.encodings.size(); ++i) { init_parameters_.encodings[i].ssrc = current_parameters.encodings[i].ssrc; + init_parameters_.encodings[i].rid = current_parameters.encodings[i].rid; current_parameters.encodings[i] = init_parameters_.encodings[i]; } current_parameters.degradation_preference = @@ -684,4 +732,36 @@ void VideoRtpSender::ClearVideoSend() { }); } +RTCError VideoRtpSender::DisableEncodingLayers( + const std::vector& rids) { + if (stopped_) { + return RTCError(RTCErrorType::INVALID_STATE); + } + + if (!media_channel_ || !ssrc_) { + RemoveEncodingLayers(rids, &init_parameters_.encodings); + return RTCError::OK(); + } + + // Check that all the specified layers exist and disable them in the channel. + RtpParameters parameters = GetParameters(); + for (const std::string& rid : rids) { + auto iter = absl::c_find_if(parameters.encodings, + [&rid](const RtpEncodingParameters& encoding) { + return encoding.rid == rid; + }); + if (iter == parameters.encodings.end()) { + return RTCError(RTCErrorType::INVALID_PARAMETER, + "RID: " + rid + " does not refer to a valid layer."); + } + iter->active = false; + } + + RTCError result = SetParameters(parameters); + if (result.ok()) { + disabled_rids_.insert(disabled_rids_.end(), rids.begin(), rids.end()); + } + return result; +} + } // namespace webrtc diff --git a/pc/rtp_sender.h b/pc/rtp_sender.h index fd8980ceac..ac498c99d8 100644 --- a/pc/rtp_sender.h +++ b/pc/rtp_sender.h @@ -59,6 +59,11 @@ class RtpSenderInternal : public RtpSenderInterface { // otherwise remains constant. Used to generate IDs for stats. // The special value zero means that no track is attached. virtual int AttachmentId() const = 0; + + // Disables the layers identified by the specified RIDs. + // If the specified list is empty, this is a no-op. + virtual RTCError DisableEncodingLayers( + const std::vector& rid) = 0; }; // LocalAudioSinkAdapter receives data callback as a sink to the local @@ -128,7 +133,7 @@ class AudioRtpSender : public DtmfProviderInterface, std::vector stream_ids() const override { return stream_ids_; } - RtpParameters GetParameters() override; + RtpParameters GetParameters() const override; RTCError SetParameters(const RtpParameters& parameters) override; rtc::scoped_refptr GetDtmfSender() const override; @@ -163,6 +168,8 @@ class AudioRtpSender : public DtmfProviderInterface, void SetMediaChannel(cricket::MediaChannel* media_channel) override; + RTCError DisableEncodingLayers(const std::vector& rids) override; + private: // TODO(nisse): Since SSRC == 0 is technically valid, figure out // some other way to test if we have a valid SSRC. @@ -184,7 +191,12 @@ class AudioRtpSender : public DtmfProviderInterface, rtc::scoped_refptr track_; rtc::scoped_refptr dtls_transport_; rtc::scoped_refptr dtmf_sender_proxy_; - absl::optional last_transaction_id_; + // |last_transaction_id_| is used to verify that |SetParameters| is receiving + // the parameters object that was last returned from |GetParameters|. + // As such, it is used for internal verification and is not observable by the + // the client. It is marked as mutable to enable |GetParameters| to be a + // const method. + mutable absl::optional last_transaction_id_; uint32_t ssrc_ = 0; bool cached_track_enabled_ = false; bool stopped_ = false; @@ -239,7 +251,7 @@ class VideoRtpSender : public ObserverInterface, dtls_transport_ = dtls_transport; } - RtpParameters GetParameters() override; + RtpParameters GetParameters() const override; RTCError SetParameters(const RtpParameters& parameters) override; rtc::scoped_refptr GetDtmfSender() const override; @@ -262,6 +274,8 @@ class VideoRtpSender : public ObserverInterface, void SetMediaChannel(cricket::MediaChannel* media_channel) override; + RTCError DisableEncodingLayers(const std::vector& rids) override; + private: bool can_send_track() const { return track_ && ssrc_; } // Helper function to construct options for @@ -276,7 +290,12 @@ class VideoRtpSender : public ObserverInterface, RtpParameters init_parameters_; cricket::VideoMediaChannel* media_channel_ = nullptr; rtc::scoped_refptr track_; - absl::optional last_transaction_id_; + // |last_transaction_id_| is used to verify that |SetParameters| is receiving + // the parameters object that was last returned from |GetParameters|. + // As such, it is used for internal verification and is not observable by the + // the client. It is marked as mutable to enable |GetParameters| to be a + // const method. + mutable absl::optional last_transaction_id_; uint32_t ssrc_ = 0; VideoTrackInterface::ContentHint cached_track_content_hint_ = VideoTrackInterface::ContentHint::kNone; @@ -284,6 +303,7 @@ class VideoRtpSender : public ObserverInterface, int attachment_id_ = 0; rtc::scoped_refptr frame_encryptor_; rtc::scoped_refptr dtls_transport_; + std::vector disabled_rids_; }; } // namespace webrtc diff --git a/pc/rtp_sender_receiver_unittest.cc b/pc/rtp_sender_receiver_unittest.cc index 1f015f6699..482fc55b23 100644 --- a/pc/rtp_sender_receiver_unittest.cc +++ b/pc/rtp_sender_receiver_unittest.cc @@ -15,6 +15,7 @@ #include #include +#include "absl/algorithm/container.h" #include "absl/memory/memory.h" #include "absl/types/optional.h" #include "api/audio_options.h" @@ -63,9 +64,11 @@ #include "test/gtest.h" using ::testing::_; +using ::testing::ContainerEq; using ::testing::Exactly; using ::testing::InvokeWithoutArgs; using ::testing::Return; +using RidList = std::vector; namespace { @@ -83,8 +86,10 @@ static const int kDefaultTimeout = 10000; // 10 seconds. namespace webrtc { -class RtpSenderReceiverTest : public testing::Test, - public sigslot::has_slots<> { +class RtpSenderReceiverTest + : public testing::Test, + public testing::WithParamInterface>, + public sigslot::has_slots<> { public: RtpSenderReceiverTest() : network_thread_(rtc::Thread::Current()), @@ -203,17 +208,38 @@ class RtpSenderReceiverTest : public testing::Test, void CreateVideoRtpSender() { CreateVideoRtpSender(false); } - void CreateVideoRtpSenderWithSimulcast( - int num_layers = kVideoSimulcastLayerCount) { + cricket::StreamParams CreateSimulcastStreamParams(int num_layers) { std::vector ssrcs; ssrcs.reserve(num_layers); - for (int i = 0; i < num_layers; ++i) + for (int i = 0; i < num_layers; ++i) { ssrcs.push_back(kVideoSsrcSimulcast + i); - cricket::StreamParams stream_params = - cricket::CreateSimStreamParams("cname", ssrcs); + } + return cricket::CreateSimStreamParams("cname", ssrcs); + } + + uint32_t CreateVideoRtpSender(const cricket::StreamParams& stream_params) { video_media_channel_->AddSendStream(stream_params); uint32_t primary_ssrc = stream_params.first_ssrc(); CreateVideoRtpSender(primary_ssrc); + return primary_ssrc; + } + + uint32_t CreateVideoRtpSenderWithSimulcast( + int num_layers = kVideoSimulcastLayerCount) { + return CreateVideoRtpSender(CreateSimulcastStreamParams(num_layers)); + } + + uint32_t CreateVideoRtpSenderWithSimulcast( + const std::vector& rids) { + cricket::StreamParams stream_params = + CreateSimulcastStreamParams(rids.size()); + std::vector rid_descriptions; + absl::c_transform( + rids, std::back_inserter(rid_descriptions), [](const std::string& rid) { + return cricket::RidDescription(rid, cricket::RidDirection::kSend); + }); + stream_params.set_rids(rid_descriptions); + return CreateVideoRtpSender(stream_params); } void CreateVideoRtpSender(bool is_screencast, uint32_t ssrc = kVideoSsrc) { @@ -343,6 +369,91 @@ class RtpSenderReceiverTest : public testing::Test, EXPECT_FALSE(video_media_channel_->HasSink(kVideoSsrc)); } + // Verifies that the encoding layers contain the specified RIDs. + bool VerifyEncodingLayers(const VideoRtpSender& sender, + const std::vector& rids) { + bool has_failure = HasFailure(); + RtpParameters parameters = sender.GetParameters(); + std::vector encoding_rids; + absl::c_transform( + parameters.encodings, std::back_inserter(encoding_rids), + [](const RtpEncodingParameters& encoding) { return encoding.rid; }); + EXPECT_THAT(rids, ContainerEq(encoding_rids)); + return has_failure || !HasFailure(); + } + + // Runs a test for disabling the encoding layers on the specified sender. + void RunDisableEncodingLayersTest( + const std::vector& all_layers, + const std::vector& disabled_layers, + VideoRtpSender* sender) { + std::vector expected; + absl::c_copy_if(all_layers, std::back_inserter(expected), + [&disabled_layers](const std::string& rid) { + return !absl::c_linear_search(disabled_layers, rid); + }); + + EXPECT_TRUE(VerifyEncodingLayers(*sender, all_layers)); + sender->DisableEncodingLayers(disabled_layers); + EXPECT_TRUE(VerifyEncodingLayers(*sender, expected)); + } + + // Runs a test for setting an encoding layer as inactive. + // This test assumes that some layers have already been disabled. + void RunSetLastLayerAsInactiveTest(VideoRtpSender* sender) { + auto parameters = sender->GetParameters(); + if (parameters.encodings.size() == 0) { + return; + } + + RtpEncodingParameters& encoding = parameters.encodings.back(); + auto rid = encoding.rid; + EXPECT_TRUE(encoding.active); + encoding.active = false; + auto error = sender->SetParameters(parameters); + ASSERT_TRUE(error.ok()); + parameters = sender->GetParameters(); + RtpEncodingParameters& result_encoding = parameters.encodings.back(); + EXPECT_EQ(rid, result_encoding.rid); + EXPECT_FALSE(result_encoding.active); + } + + // Runs a test for disabling the encoding layers on a sender without a media + // channel. + void RunDisableSimulcastLayersWithoutMediaEngineTest( + const std::vector& all_layers, + const std::vector& disabled_layers) { + VideoRtpSender sender(rtc::Thread::Current(), "1"); + RtpParameters parameters; + parameters.encodings.resize(all_layers.size()); + for (size_t i = 0; i < all_layers.size(); ++i) { + parameters.encodings[i].rid = all_layers[i]; + } + sender.set_init_send_encodings(parameters.encodings); + RunDisableEncodingLayersTest(all_layers, disabled_layers, &sender); + RunSetLastLayerAsInactiveTest(&sender); + } + + // Runs a test for disabling the encoding layers on a sender with a media + // channel. + void RunDisableSimulcastLayersWithMediaEngineTest( + const std::vector& all_layers, + const std::vector& disabled_layers) { + uint32_t ssrc = CreateVideoRtpSenderWithSimulcast(all_layers); + RunDisableEncodingLayersTest(all_layers, disabled_layers, + video_rtp_sender_.get()); + + auto channel_parameters = video_media_channel_->GetRtpSendParameters(ssrc); + ASSERT_EQ(channel_parameters.encodings.size(), all_layers.size()); + for (size_t i = 0; i < all_layers.size(); ++i) { + EXPECT_EQ(all_layers[i], channel_parameters.encodings[i].rid); + bool is_active = !absl::c_linear_search(disabled_layers, all_layers[i]); + EXPECT_EQ(is_active, channel_parameters.encodings[i].active); + } + + RunSetLastLayerAsInactiveTest(video_rtp_sender_.get()); + } + protected: rtc::Thread* const network_thread_; rtc::Thread* const worker_thread_; @@ -1210,20 +1321,6 @@ TEST_F(RtpSenderReceiverTest, DestroyVideoRtpSender(); } -TEST_F(RtpSenderReceiverTest, VideoSenderCanSetRid) { - CreateVideoRtpSender(); - RtpParameters params = video_rtp_sender_->GetParameters(); - EXPECT_EQ(1u, params.encodings.size()); - const std::string rid = "dummy_rid"; - params.encodings[0].rid = rid; - EXPECT_TRUE(video_rtp_sender_->SetParameters(params).ok()); - params = video_rtp_sender_->GetParameters(); - EXPECT_EQ(1u, params.encodings.size()); - EXPECT_EQ(rid, params.encodings[0].rid); - - DestroyVideoRtpSender(); -} - TEST_F(RtpSenderReceiverTest, VideoSenderCanSetScaleResolutionDownBy) { CreateVideoRtpSender(); @@ -1669,4 +1766,44 @@ TEST_F(RtpSenderReceiverTest, VideoReceiverCannotSetFrameDecryptorAfterStop) { // TODO(webrtc:9926) - Validate media channel not set once fakes updated. } +// Helper method for syntactic sugar for accepting a vector with '{}' notation. +std::pair CreatePairOfRidVectors( + const std::vector& first, + const std::vector& second) { + return std::make_pair(first, second); +} + +// These parameters are used to test disabling simulcast layers. +const std::pair kDisableSimulcastLayersParameters[] = { + // Tests removing the first layer. This is a special case because + // the first layer's SSRC is also the 'primary' SSRC used to associate the + // parameters to the media channel. + CreatePairOfRidVectors({"1", "2", "3", "4"}, {"1"}), + // Tests removing some layers. + CreatePairOfRidVectors({"1", "2", "3", "4"}, {"2", "4"}), + // Tests simulcast rejected scenario all layers except first are rejected. + CreatePairOfRidVectors({"1", "2", "3", "4"}, {"2", "3", "4"}), + // Tests removing all layers. + CreatePairOfRidVectors({"1", "2", "3", "4"}, {"1", "2", "3", "4"}), +}; + +// Runs test for disabling layers on a sender without a media engine set. +TEST_P(RtpSenderReceiverTest, DisableSimulcastLayersWithoutMediaEngine) { + auto parameter = GetParam(); + RunDisableSimulcastLayersWithoutMediaEngineTest(parameter.first, + parameter.second); +} + +// Runs test for disabling layers on a sender with a media engine set. +TEST_P(RtpSenderReceiverTest, DisableSimulcastLayersWithMediaEngine) { + auto parameter = GetParam(); + RunDisableSimulcastLayersWithMediaEngineTest(parameter.first, + parameter.second); +} + +INSTANTIATE_TEST_SUITE_P( + DisableSimulcastLayersInSender, + RtpSenderReceiverTest, + ::testing::ValuesIn(kDisableSimulcastLayersParameters)); + } // namespace webrtc diff --git a/pc/test/mock_rtp_sender_internal.h b/pc/test/mock_rtp_sender_internal.h index fff81f4b50..a497cb5c3c 100644 --- a/pc/test/mock_rtp_sender_internal.h +++ b/pc/test/mock_rtp_sender_internal.h @@ -33,7 +33,7 @@ class MockRtpSenderInternal : public RtpSenderInternal { MOCK_CONST_METHOD0(stream_ids, std::vector()); MOCK_CONST_METHOD0(init_send_encodings, std::vector()); MOCK_METHOD1(set_transport, void(rtc::scoped_refptr)); - MOCK_METHOD0(GetParameters, RtpParameters()); + MOCK_CONST_METHOD0(GetParameters, RtpParameters()); MOCK_METHOD1(SetParameters, RTCError(const RtpParameters&)); MOCK_CONST_METHOD0(GetDtmfSender, rtc::scoped_refptr()); MOCK_METHOD1(SetFrameEncryptor, @@ -49,6 +49,8 @@ class MockRtpSenderInternal : public RtpSenderInternal { void(const std::vector&)); MOCK_METHOD0(Stop, void()); MOCK_CONST_METHOD0(AttachmentId, int()); + MOCK_METHOD1(DisableEncodingLayers, + RTCError(const std::vector&)); }; } // namespace webrtc