|  | /* | 
|  | *  Copyright 2020 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 <memory> | 
|  | #include <optional> | 
|  |  | 
|  | #include "api/adaptation/resource.h" | 
|  | #include "api/audio_codecs/builtin_audio_decoder_factory.h" | 
|  | #include "api/audio_codecs/builtin_audio_encoder_factory.h" | 
|  | #include "api/environment/environment.h" | 
|  | #include "api/make_ref_counted.h" | 
|  | #include "api/media_stream_interface.h" | 
|  | #include "api/peer_connection_interface.h" | 
|  | #include "api/rtc_error.h" | 
|  | #include "api/rtp_parameters.h" | 
|  | #include "api/rtp_sender_interface.h" | 
|  | #include "api/scoped_refptr.h" | 
|  | #include "api/test/rtc_error_matchers.h" | 
|  | #include "api/video/video_source_interface.h" | 
|  | #include "call/adaptation/test/fake_resource.h" | 
|  | #include "pc/test/fake_periodic_video_source.h" | 
|  | #include "pc/test/fake_periodic_video_track_source.h" | 
|  | #include "pc/test/peer_connection_test_wrapper.h" | 
|  | #include "rtc_base/checks.h" | 
|  | #include "rtc_base/thread.h" | 
|  | #include "rtc_base/virtual_socket_server.h" | 
|  | #include "system_wrappers/include/clock.h" | 
|  | #include "test/create_test_environment.h" | 
|  | #include "test/gmock.h" | 
|  | #include "test/gtest.h" | 
|  | #include "test/wait_until.h" | 
|  |  | 
|  | namespace webrtc { | 
|  |  | 
|  | struct TrackWithPeriodicSource { | 
|  | scoped_refptr<VideoTrackInterface> track; | 
|  | scoped_refptr<FakePeriodicVideoTrackSource> periodic_track_source; | 
|  | }; | 
|  |  | 
|  | // Performs an O/A exchange and waits until the signaling state is stable again. | 
|  | void Negotiate(scoped_refptr<PeerConnectionTestWrapper> caller, | 
|  | scoped_refptr<PeerConnectionTestWrapper> callee) { | 
|  | // Wire up callbacks and listeners such that a full O/A is performed in | 
|  | // response to CreateOffer(). | 
|  | PeerConnectionTestWrapper::Connect(caller.get(), callee.get()); | 
|  | caller->CreateOffer(PeerConnectionInterface::RTCOfferAnswerOptions()); | 
|  | caller->WaitForNegotiation(); | 
|  | } | 
|  |  | 
|  | TrackWithPeriodicSource CreateTrackWithPeriodicSource( | 
|  | Clock& clock, | 
|  | scoped_refptr<PeerConnectionFactoryInterface> factory) { | 
|  | FakePeriodicVideoSource::Config periodic_track_source_config; | 
|  | periodic_track_source_config.frame_interval_ms = 100; | 
|  | periodic_track_source_config.timestamp_offset_ms = clock.TimeInMilliseconds(); | 
|  | scoped_refptr<FakePeriodicVideoTrackSource> periodic_track_source = | 
|  | make_ref_counted<FakePeriodicVideoTrackSource>( | 
|  | periodic_track_source_config, /* remote */ false); | 
|  | TrackWithPeriodicSource track_with_source; | 
|  | track_with_source.track = | 
|  | factory->CreateVideoTrack(periodic_track_source, "PeriodicTrack"); | 
|  | track_with_source.periodic_track_source = periodic_track_source; | 
|  | return track_with_source; | 
|  | } | 
|  |  | 
|  | // Triggers overuse and obtains VideoSinkWants. Adaptation processing happens in | 
|  | // parallel and this function makes no guarantee that the returned | 
|  | // VideoSinkWants have yet to reflect the overuse signal. Used together with | 
|  | // EXPECT_TRUE_WAIT to "spam overuse until a change is observed". | 
|  | VideoSinkWants TriggerOveruseAndGetSinkWants( | 
|  | scoped_refptr<FakeResource> fake_resource, | 
|  | const FakePeriodicVideoSource& source) { | 
|  | fake_resource->SetUsageState(ResourceUsageState::kOveruse); | 
|  | return source.wants(); | 
|  | } | 
|  |  | 
|  | class PeerConnectionAdaptationIntegrationTest : public ::testing::Test { | 
|  | public: | 
|  | PeerConnectionAdaptationIntegrationTest() | 
|  | : env_(CreateTestEnvironment()), | 
|  | virtual_socket_server_(), | 
|  | network_thread_(new Thread(&virtual_socket_server_)), | 
|  | worker_thread_(Thread::Create()) { | 
|  | RTC_CHECK(network_thread_->Start()); | 
|  | RTC_CHECK(worker_thread_->Start()); | 
|  | } | 
|  |  | 
|  | scoped_refptr<PeerConnectionTestWrapper> CreatePcWrapper(const char* name) { | 
|  | scoped_refptr<PeerConnectionTestWrapper> pc_wrapper = | 
|  | make_ref_counted<PeerConnectionTestWrapper>( | 
|  | name, env_, &virtual_socket_server_, network_thread_.get(), | 
|  | worker_thread_.get()); | 
|  | PeerConnectionInterface::RTCConfiguration config; | 
|  | config.sdp_semantics = SdpSemantics::kUnifiedPlan; | 
|  | EXPECT_TRUE(pc_wrapper->CreatePc(config, CreateBuiltinAudioEncoderFactory(), | 
|  | CreateBuiltinAudioDecoderFactory())); | 
|  | return pc_wrapper; | 
|  | } | 
|  |  | 
|  | protected: | 
|  | const Environment env_; | 
|  | VirtualSocketServer virtual_socket_server_; | 
|  | std::unique_ptr<Thread> network_thread_; | 
|  | std::unique_ptr<Thread> worker_thread_; | 
|  | }; | 
|  |  | 
|  | TEST_F(PeerConnectionAdaptationIntegrationTest, | 
|  | ResouceInjectedAfterNegotiationCausesReductionInResolution) { | 
|  | auto caller_wrapper = CreatePcWrapper("caller"); | 
|  | auto caller = caller_wrapper->pc(); | 
|  | auto callee_wrapper = CreatePcWrapper("callee"); | 
|  |  | 
|  | // Adding a track and negotiating ensures that a VideoSendStream exists. | 
|  | TrackWithPeriodicSource track_with_source = | 
|  | CreateTrackWithPeriodicSource(env_.clock(), caller_wrapper->pc_factory()); | 
|  | auto sender = caller->AddTrack(track_with_source.track, {}).value(); | 
|  | Negotiate(caller_wrapper, callee_wrapper); | 
|  | // Prefer degrading resolution. | 
|  | auto parameters = sender->GetParameters(); | 
|  | parameters.degradation_preference = DegradationPreference::MAINTAIN_FRAMERATE; | 
|  | sender->SetParameters(parameters); | 
|  |  | 
|  | const auto& source = | 
|  | track_with_source.periodic_track_source->fake_periodic_source(); | 
|  | int pixel_count_before_overuse = source.wants().max_pixel_count; | 
|  |  | 
|  | // Inject a fake resource and spam kOveruse until resolution becomes limited. | 
|  | auto fake_resource = FakeResource::Create("FakeResource"); | 
|  | caller->AddAdaptationResource(fake_resource); | 
|  | EXPECT_THAT(WaitUntil( | 
|  | [&] { | 
|  | return TriggerOveruseAndGetSinkWants(fake_resource, source) | 
|  | .max_pixel_count; | 
|  | }, | 
|  | ::testing::Lt(pixel_count_before_overuse)), | 
|  | IsRtcOk()); | 
|  | } | 
|  |  | 
|  | TEST_F(PeerConnectionAdaptationIntegrationTest, | 
|  | ResouceInjectedBeforeNegotiationCausesReductionInResolution) { | 
|  | auto caller_wrapper = CreatePcWrapper("caller"); | 
|  | auto caller = caller_wrapper->pc(); | 
|  | auto callee_wrapper = CreatePcWrapper("callee"); | 
|  |  | 
|  | // Inject a fake resource before adding any tracks or negotiating. | 
|  | auto fake_resource = FakeResource::Create("FakeResource"); | 
|  | caller->AddAdaptationResource(fake_resource); | 
|  |  | 
|  | // Adding a track and negotiating ensures that a VideoSendStream exists. | 
|  | TrackWithPeriodicSource track_with_source = | 
|  | CreateTrackWithPeriodicSource(env_.clock(), caller_wrapper->pc_factory()); | 
|  | auto sender = caller->AddTrack(track_with_source.track, {}).value(); | 
|  | Negotiate(caller_wrapper, callee_wrapper); | 
|  | // Prefer degrading resolution. | 
|  | auto parameters = sender->GetParameters(); | 
|  | parameters.degradation_preference = DegradationPreference::MAINTAIN_FRAMERATE; | 
|  | sender->SetParameters(parameters); | 
|  |  | 
|  | const auto& source = | 
|  | track_with_source.periodic_track_source->fake_periodic_source(); | 
|  | int pixel_count_before_overuse = source.wants().max_pixel_count; | 
|  |  | 
|  | // Spam kOveruse until resolution becomes limited. | 
|  | EXPECT_THAT(WaitUntil( | 
|  | [&] { | 
|  | return TriggerOveruseAndGetSinkWants(fake_resource, source) | 
|  | .max_pixel_count; | 
|  | }, | 
|  | ::testing::Lt(pixel_count_before_overuse)), | 
|  | IsRtcOk()); | 
|  | } | 
|  |  | 
|  | }  // namespace webrtc |