/* * libjingle * Copyright 2010 Google Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ // If we don't have a WebRtcVideoFrame, just skip all of these tests. #if defined(HAVE_WEBRTC_VIDEO) #include // For INT_MAX #include #include #include "talk/media/base/mediachannel.h" #include "talk/media/base/testutils.h" #include "talk/media/base/videoadapter.h" #include "talk/media/devices/filevideocapturer.h" #include "talk/media/webrtc/webrtcvideoframe.h" #include "webrtc/base/gunit.h" #include "webrtc/base/logging.h" #include "webrtc/base/sigslot.h" namespace cricket { namespace { static const uint32 kWaitTimeout = 3000U; // 3 seconds. static const uint32 kShortWaitTimeout = 1000U; // 1 second. void UpdateCpuLoad(CoordinatedVideoAdapter* adapter, int current_cpus, int max_cpus, float process_load, float system_load) { adapter->set_cpu_load_min_samples(1); adapter->OnCpuLoadUpdated(current_cpus, max_cpus, process_load, system_load); } } class VideoAdapterTest : public testing::Test { public: virtual void SetUp() { capturer_.reset(new FileVideoCapturer); EXPECT_TRUE(capturer_->Init(GetTestFilePath( "captured-320x240-2s-48.frames"))); capture_format_ = capturer_->GetSupportedFormats()->at(0); capture_format_.interval = VideoFormat::FpsToInterval(50); adapter_.reset(new VideoAdapter()); adapter_->SetInputFormat(capture_format_); listener_.reset(new VideoCapturerListener(adapter_.get())); capturer_->SignalFrameCaptured.connect( listener_.get(), &VideoCapturerListener::OnFrameCaptured); } virtual void TearDown() { // Explicitly disconnect the VideoCapturer before to avoid data races // (frames delivered to VideoCapturerListener while it's being destructed). capturer_->SignalFrameCaptured.disconnect_all(); } protected: class VideoCapturerListener: public sigslot::has_slots<> { public: struct Stats { int captured_frames; int dropped_frames; bool last_adapt_was_no_op; int adapted_width; int adapted_height; }; explicit VideoCapturerListener(VideoAdapter* adapter) : video_adapter_(adapter), adapted_frame_(NULL), copied_output_frame_(), captured_frames_(0), dropped_frames_(0), last_adapt_was_no_op_(false) { } void OnFrameCaptured(VideoCapturer* capturer, const CapturedFrame* captured_frame) { WebRtcVideoFrame temp_i420; EXPECT_TRUE(temp_i420.Init(captured_frame, captured_frame->width, abs(captured_frame->height))); VideoFrame* out_frame = NULL; rtc::CritScope lock(&crit_); EXPECT_TRUE(video_adapter_->AdaptFrame(&temp_i420, &out_frame)); if (out_frame) { if (out_frame == &temp_i420) { last_adapt_was_no_op_ = true; copied_output_frame_.reset(temp_i420.Copy()); adapted_frame_ = copied_output_frame_.get(); } else { last_adapt_was_no_op_ = false; adapted_frame_ = out_frame; copied_output_frame_.reset(); } } else { ++dropped_frames_; } ++captured_frames_; } Stats GetStats() { rtc::CritScope lock(&crit_); Stats stats; stats.captured_frames = captured_frames_; stats.dropped_frames = dropped_frames_; stats.last_adapt_was_no_op = last_adapt_was_no_op_; if (adapted_frame_ != NULL) { stats.adapted_width = static_cast(adapted_frame_->GetWidth()); stats.adapted_height = static_cast(adapted_frame_->GetHeight()); } else { stats.adapted_width = stats.adapted_height = -1; } return stats; } VideoFrame* CopyAdaptedFrame() { rtc::CritScope lock(&crit_); if (adapted_frame_ == NULL) { return NULL; } return adapted_frame_->Copy(); } private: rtc::CriticalSection crit_; VideoAdapter* video_adapter_; const VideoFrame* adapted_frame_; rtc::scoped_ptr copied_output_frame_; int captured_frames_; int dropped_frames_; bool last_adapt_was_no_op_; }; class CpuAdapterListener: public sigslot::has_slots<> { public: CpuAdapterListener() : received_cpu_signal_(false) {} void OnCpuAdaptationSignalled() { received_cpu_signal_ = true; } bool received_cpu_signal() { return received_cpu_signal_; } private: bool received_cpu_signal_; }; void VerifyAdaptedResolution(const VideoCapturerListener::Stats& stats, int width, int height) { EXPECT_EQ(width, stats.adapted_width); EXPECT_EQ(height, stats.adapted_height); } rtc::scoped_ptr capturer_; rtc::scoped_ptr adapter_; rtc::scoped_ptr listener_; VideoFormat capture_format_; }; // Test adapter remembers exact pixel count TEST_F(VideoAdapterTest, AdaptNumPixels) { adapter_->SetOutputNumPixels(123456); EXPECT_EQ(123456, adapter_->GetOutputNumPixels()); } // Test adapter is constructed but not activated. Expect no frame drop and no // resolution change. TEST_F(VideoAdapterTest, AdaptInactive) { // Output resolution is not set. EXPECT_EQ(INT_MAX, adapter_->GetOutputNumPixels()); // Call Adapter with some frames. EXPECT_EQ(CS_RUNNING, capturer_->Start(capture_format_)); EXPECT_TRUE_WAIT(!capturer_->IsRunning() || listener_->GetStats().captured_frames >= 10, kWaitTimeout); // Verify no frame drop and no resolution change. VideoCapturerListener::Stats stats = listener_->GetStats(); EXPECT_GE(stats.captured_frames, 10); EXPECT_EQ(0, stats.dropped_frames); VerifyAdaptedResolution(stats, capture_format_.width, capture_format_.height); } // Do not adapt the frame rate or the resolution. Expect no frame drop and no // resolution change. TEST_F(VideoAdapterTest, AdaptNothing) { adapter_->SetOutputFormat(capture_format_); EXPECT_EQ(CS_RUNNING, capturer_->Start(capture_format_)); EXPECT_TRUE_WAIT(!capturer_->IsRunning() || listener_->GetStats().captured_frames >= 10, kWaitTimeout); // Verify no frame drop and no resolution change. VideoCapturerListener::Stats stats = listener_->GetStats(); EXPECT_GE(stats.captured_frames, 10); EXPECT_EQ(0, stats.dropped_frames); VerifyAdaptedResolution(stats, capture_format_.width, capture_format_.height); EXPECT_TRUE(stats.last_adapt_was_no_op); } TEST_F(VideoAdapterTest, AdaptZeroInterval) { VideoFormat format = capturer_->GetSupportedFormats()->at(0); format.interval = 0; adapter_->SetInputFormat(format); adapter_->SetOutputFormat(format); EXPECT_EQ(CS_RUNNING, capturer_->Start(capture_format_)); EXPECT_TRUE_WAIT(!capturer_->IsRunning() || listener_->GetStats().captured_frames >= 10, kWaitTimeout); // Verify no crash and that frames aren't dropped. VideoCapturerListener::Stats stats = listener_->GetStats(); EXPECT_GE(stats.captured_frames, 10); EXPECT_EQ(0, stats.dropped_frames); VerifyAdaptedResolution(stats, capture_format_.width, capture_format_.height); } // Adapt the frame rate to be half of the capture rate at the beginning. Expect // the number of dropped frames to be half of the number the captured frames. TEST_F(VideoAdapterTest, AdaptFramerate) { VideoFormat request_format = capture_format_; request_format.interval *= 2; adapter_->SetOutputFormat(request_format); EXPECT_EQ(CS_RUNNING, capturer_->Start(capture_format_)); EXPECT_TRUE_WAIT(!capturer_->IsRunning() || listener_->GetStats().captured_frames >= 10, kWaitTimeout); // Verify frame drop and no resolution change. VideoCapturerListener::Stats stats = listener_->GetStats(); EXPECT_GE(stats.captured_frames, 10); EXPECT_EQ(stats.captured_frames / 2, stats.dropped_frames); VerifyAdaptedResolution(stats, capture_format_.width, capture_format_.height); } // Adapt the frame rate to be half of the capture rate at the beginning. Expect // the number of dropped frames to be half of the number the captured frames. TEST_F(VideoAdapterTest, AdaptFramerateVariable) { VideoFormat request_format = capture_format_; request_format.interval = request_format.interval * 3 / 2; adapter_->SetOutputFormat(request_format); EXPECT_EQ(CS_RUNNING, capturer_->Start(capture_format_)); EXPECT_TRUE_WAIT(!capturer_->IsRunning() || listener_->GetStats().captured_frames >= 30, kWaitTimeout); // Verify frame drop and no resolution change. VideoCapturerListener::Stats stats = listener_->GetStats(); EXPECT_GE(stats.captured_frames, 30); // Verify 2 / 3 kept (20) and 1 / 3 dropped (10). EXPECT_EQ(stats.captured_frames * 1 / 3, stats.dropped_frames); VerifyAdaptedResolution(stats, capture_format_.width, capture_format_.height); } // Adapt the frame rate to be half of the capture rate after capturing no less // than 10 frames. Expect no frame dropped before adaptation and frame dropped // after adaptation. TEST_F(VideoAdapterTest, AdaptFramerateOntheFly) { VideoFormat request_format = capture_format_; adapter_->SetOutputFormat(request_format); EXPECT_EQ(CS_RUNNING, capturer_->Start(capture_format_)); EXPECT_TRUE_WAIT(!capturer_->IsRunning() || listener_->GetStats().captured_frames >= 10, kWaitTimeout); // Verify no frame drop before adaptation. EXPECT_EQ(0, listener_->GetStats().dropped_frames); // Adapat the frame rate. request_format.interval *= 2; adapter_->SetOutputFormat(request_format); EXPECT_TRUE_WAIT(!capturer_->IsRunning() || listener_->GetStats().captured_frames >= 20, kWaitTimeout); // Verify frame drop after adaptation. EXPECT_GT(listener_->GetStats().dropped_frames, 0); } // Adapt the frame resolution to be a quarter of the capture resolution at the // beginning. Expect resolution change. TEST_F(VideoAdapterTest, AdaptResolution) { VideoFormat request_format = capture_format_; request_format.width /= 2; request_format.height /= 2; adapter_->SetOutputFormat(request_format); EXPECT_EQ(CS_RUNNING, capturer_->Start(capture_format_)); EXPECT_TRUE_WAIT(!capturer_->IsRunning() || listener_->GetStats().captured_frames >= 10, kWaitTimeout); // Verify no frame drop and resolution change. VideoCapturerListener::Stats stats = listener_->GetStats(); EXPECT_EQ(0, stats.dropped_frames); VerifyAdaptedResolution(stats, request_format.width, request_format.height); } // Adapt the frame resolution to half width. Expect resolution change. TEST_F(VideoAdapterTest, AdaptResolutionNarrow) { VideoFormat request_format = capture_format_; request_format.width /= 2; adapter_->set_scale_third(true); adapter_->SetOutputFormat(request_format); EXPECT_EQ(CS_RUNNING, capturer_->Start(capture_format_)); EXPECT_TRUE_WAIT(!capturer_->IsRunning() || listener_->GetStats().captured_frames >= 10, kWaitTimeout); // Verify resolution change. VerifyAdaptedResolution(listener_->GetStats(), 213, 160); } // Adapt the frame resolution to half height. Expect resolution change. TEST_F(VideoAdapterTest, AdaptResolutionWide) { VideoFormat request_format = capture_format_; request_format.height /= 2; adapter_->set_scale_third(true); adapter_->SetOutputFormat(request_format); EXPECT_EQ(CS_RUNNING, capturer_->Start(capture_format_)); EXPECT_TRUE_WAIT(!capturer_->IsRunning() || listener_->GetStats().captured_frames >= 10, kWaitTimeout); // Verify resolution change. VerifyAdaptedResolution(listener_->GetStats(), 213, 160); } // Adapt the frame resolution to be a quarter of the capture resolution after // capturing no less than 10 frames. Expect no resolution change before // adaptation and resolution change after adaptation. TEST_F(VideoAdapterTest, AdaptResolutionOnTheFly) { VideoFormat request_format = capture_format_; adapter_->SetOutputFormat(request_format); EXPECT_EQ(CS_RUNNING, capturer_->Start(capture_format_)); EXPECT_TRUE_WAIT(!capturer_->IsRunning() || listener_->GetStats().captured_frames >= 10, kWaitTimeout); // Verify no resolution change before adaptation. VerifyAdaptedResolution( listener_->GetStats(), request_format.width, request_format.height); // Adapt the frame resolution. request_format.width /= 2; request_format.height /= 2; adapter_->SetOutputFormat(request_format); int captured_frames = listener_->GetStats().captured_frames; EXPECT_TRUE_WAIT( !capturer_->IsRunning() || listener_->GetStats().captured_frames >= captured_frames + 10, kWaitTimeout); // Verify resolution change after adaptation. VerifyAdaptedResolution( listener_->GetStats(), request_format.width, request_format.height); } // Black the output frame. TEST_F(VideoAdapterTest, BlackOutput) { adapter_->SetOutputFormat(capture_format_); EXPECT_EQ(CS_RUNNING, capturer_->Start(capture_format_)); EXPECT_TRUE_WAIT(!capturer_->IsRunning() || listener_->GetStats().captured_frames >= 10, kWaitTimeout); // Verify that the output frame is not black. rtc::scoped_ptr adapted_frame(listener_->CopyAdaptedFrame()); EXPECT_NE(16, *adapted_frame->GetYPlane()); EXPECT_NE(128, *adapted_frame->GetUPlane()); EXPECT_NE(128, *adapted_frame->GetVPlane()); adapter_->SetBlackOutput(true); int captured_frames = listener_->GetStats().captured_frames; EXPECT_TRUE_WAIT( !capturer_->IsRunning() || listener_->GetStats().captured_frames >= captured_frames + 10, kWaitTimeout); // Verify that the output frame is black. adapted_frame.reset(listener_->CopyAdaptedFrame()); EXPECT_EQ(16, *adapted_frame->GetYPlane()); EXPECT_EQ(128, *adapted_frame->GetUPlane()); EXPECT_EQ(128, *adapted_frame->GetVPlane()); // Verify that the elapsed time and timestamp of the black frame increase. int64 elapsed_time = adapted_frame->GetElapsedTime(); int64 timestamp = adapted_frame->GetTimeStamp(); captured_frames = listener_->GetStats().captured_frames; EXPECT_TRUE_WAIT( !capturer_->IsRunning() || listener_->GetStats().captured_frames >= captured_frames + 10, kWaitTimeout); adapted_frame.reset(listener_->CopyAdaptedFrame()); EXPECT_GT(adapted_frame->GetElapsedTime(), elapsed_time); EXPECT_GT(adapted_frame->GetTimeStamp(), timestamp); // Change the output size VideoFormat request_format = capture_format_; request_format.width /= 2; request_format.height /= 2; adapter_->SetOutputFormat(request_format); captured_frames = listener_->GetStats().captured_frames; EXPECT_TRUE_WAIT( !capturer_->IsRunning() || listener_->GetStats().captured_frames >= captured_frames + 10, kWaitTimeout); // Verify resolution change after adaptation. VerifyAdaptedResolution( listener_->GetStats(), request_format.width, request_format.height); // Verify that the output frame is black. adapted_frame.reset(listener_->CopyAdaptedFrame()); EXPECT_EQ(16, *adapted_frame->GetYPlane()); EXPECT_EQ(128, *adapted_frame->GetUPlane()); EXPECT_EQ(128, *adapted_frame->GetVPlane()); } // Drop all frames. TEST_F(VideoAdapterTest, DropAllFrames) { VideoFormat format; // with resolution 0x0. adapter_->SetOutputFormat(format); EXPECT_EQ(CS_RUNNING, capturer_->Start(capture_format_)); EXPECT_TRUE_WAIT(!capturer_->IsRunning() || listener_->GetStats().captured_frames >= 10, kWaitTimeout); // Verify all frames are dropped. VideoCapturerListener::Stats stats = listener_->GetStats(); EXPECT_GE(stats.captured_frames, 10); EXPECT_EQ(stats.captured_frames, stats.dropped_frames); } TEST(CoordinatedVideoAdapterTest, TestCoordinatedWithoutCpuAdaptation) { CoordinatedVideoAdapter adapter; adapter.set_cpu_adaptation(false); VideoFormat format(640, 400, VideoFormat::FpsToInterval(30), FOURCC_I420); adapter.SetInputFormat(format); adapter.set_scale_third(true); EXPECT_EQ(format, adapter.input_format()); EXPECT_TRUE(adapter.output_format().IsSize0x0()); // Server format request 640x400. format.height = 400; adapter.OnOutputFormatRequest(format); EXPECT_EQ(640, adapter.output_format().width); EXPECT_EQ(400, adapter.output_format().height); // Server format request 1280x720, higher than input. Adapt nothing. format.width = 1280; format.height = 720; adapter.OnOutputFormatRequest(format); EXPECT_EQ(640, adapter.output_format().width); EXPECT_EQ(400, adapter.output_format().height); // Cpu load is high, but cpu adaptation is disabled. Adapt nothing. adapter.OnCpuLoadUpdated(1, 1, 0.99f, 0.99f); EXPECT_EQ(640, adapter.output_format().width); EXPECT_EQ(400, adapter.output_format().height); // Encoder resolution request: downgrade with different size. Adapt nothing. adapter.OnEncoderResolutionRequest(320, 200, CoordinatedVideoAdapter::DOWNGRADE); EXPECT_EQ(640, adapter.output_format().width); EXPECT_EQ(400, adapter.output_format().height); // Encoder resolution request: downgrade. adapter.OnEncoderResolutionRequest(640, 400, CoordinatedVideoAdapter::DOWNGRADE); EXPECT_EQ(480, adapter.output_format().width); EXPECT_EQ(300, adapter.output_format().height); // Encoder resolution request: downgrade. But GD off. Adapt nothing. adapter.set_gd_adaptation(false); adapter.OnEncoderResolutionRequest(480, 300, CoordinatedVideoAdapter::DOWNGRADE); EXPECT_EQ(480, adapter.output_format().width); EXPECT_EQ(300, adapter.output_format().height); adapter.set_gd_adaptation(true); // Encoder resolution request: downgrade. adapter.OnEncoderResolutionRequest(480, 300, CoordinatedVideoAdapter::DOWNGRADE); EXPECT_EQ(320, adapter.output_format().width); EXPECT_EQ(200, adapter.output_format().height); // Encoder resolution request: keep. Adapt nothing. adapter.OnEncoderResolutionRequest(320, 200, CoordinatedVideoAdapter::KEEP); EXPECT_EQ(320, adapter.output_format().width); EXPECT_EQ(200, adapter.output_format().height); // Encoder resolution request: upgrade. adapter.OnEncoderResolutionRequest(320, 200, CoordinatedVideoAdapter::UPGRADE); EXPECT_EQ(480, adapter.output_format().width); EXPECT_EQ(300, adapter.output_format().height); // Server format request 0x0. format.width = 0; format.height = 0; adapter.OnOutputFormatRequest(format); EXPECT_TRUE(adapter.output_format().IsSize0x0()); // Server format request 320x200. format.width = 320; format.height = 200; adapter.OnOutputFormatRequest(format); EXPECT_EQ(320, adapter.output_format().width); EXPECT_EQ(200, adapter.output_format().height); // Server format request 160x100. But view disabled. Adapt nothing. adapter.set_view_adaptation(false); format.width = 160; format.height = 100; adapter.OnOutputFormatRequest(format); EXPECT_EQ(320, adapter.output_format().width); EXPECT_EQ(200, adapter.output_format().height); adapter.set_view_adaptation(true); // Enable View Switch. Expect adapt down. adapter.set_view_switch(true); format.width = 160; format.height = 100; adapter.OnOutputFormatRequest(format); EXPECT_EQ(160, adapter.output_format().width); EXPECT_EQ(100, adapter.output_format().height); // Encoder resolution request: upgrade. Adapt nothing. adapter.OnEncoderResolutionRequest(160, 100, CoordinatedVideoAdapter::UPGRADE); EXPECT_EQ(160, adapter.output_format().width); EXPECT_EQ(100, adapter.output_format().height); // Request View of 2 / 3. Expect adapt down. adapter.set_view_switch(true); format.width = (640 * 2 + 1) / 3; format.height = (400 * 2 + 1) / 3; adapter.OnOutputFormatRequest(format); EXPECT_EQ((640 * 2 + 1) / 3, adapter.output_format().width); EXPECT_EQ((400 * 2 + 1) / 3, adapter.output_format().height); // Request View of 3 / 8. Expect adapt down. adapter.set_view_switch(true); format.width = 640 * 3 / 8; format.height = 400 * 3 / 8; adapter.OnOutputFormatRequest(format); EXPECT_EQ(640 * 3 / 8, adapter.output_format().width); EXPECT_EQ(400 * 3 / 8, adapter.output_format().height); // View Switch back up. Expect adapt. format.width = 320; format.height = 200; adapter.OnOutputFormatRequest(format); EXPECT_EQ(320, adapter.output_format().width); EXPECT_EQ(200, adapter.output_format().height); adapter.set_view_switch(false); // Encoder resolution request: upgrade. Constrained by server request. adapter.OnEncoderResolutionRequest(320, 200, CoordinatedVideoAdapter::UPGRADE); EXPECT_EQ(320, adapter.output_format().width); EXPECT_EQ(200, adapter.output_format().height); // Server format request 480x300. format.width = 480; format.height = 300; adapter.OnOutputFormatRequest(format); EXPECT_EQ(480, adapter.output_format().width); EXPECT_EQ(300, adapter.output_format().height); } TEST(CoordinatedVideoAdapterTest, TestCoordinatedWithCpuAdaptation) { CoordinatedVideoAdapter adapter; adapter.set_cpu_adaptation(true); EXPECT_FALSE(adapter.cpu_smoothing()); VideoFormat format(640, 400, VideoFormat::FpsToInterval(30), FOURCC_I420); adapter.SetInputFormat(format); // Server format request 640x400. format.height = 400; adapter.OnOutputFormatRequest(format); EXPECT_EQ(640, adapter.output_format().width); EXPECT_EQ(400, adapter.output_format().height); // Process load is medium, but system load is high. Downgrade. UpdateCpuLoad(&adapter, 1, 1, 0.55f, 0.98f); EXPECT_EQ(480, adapter.output_format().width); EXPECT_EQ(300, adapter.output_format().height); // CPU high, but cpu adaptation disabled. Adapt nothing. adapter.set_cpu_adaptation(false); adapter.OnCpuLoadUpdated(1, 1, 0.55f, 0.98f); EXPECT_EQ(480, adapter.output_format().width); EXPECT_EQ(300, adapter.output_format().height); adapter.set_cpu_adaptation(true); // System load is high, but time has not elaspsed. Adapt nothing. adapter.set_cpu_load_min_samples(2); adapter.OnCpuLoadUpdated(1, 1, 0.55f, 0.98f); EXPECT_EQ(480, adapter.output_format().width); EXPECT_EQ(300, adapter.output_format().height); // Process load is medium, but system load is high. Downgrade. UpdateCpuLoad(&adapter, 1, 1, 0.55f, 0.98f); EXPECT_EQ(320, adapter.output_format().width); EXPECT_EQ(200, adapter.output_format().height); // Test reason for adapting is CPU. EXPECT_EQ(CoordinatedVideoAdapter::ADAPTREASON_CPU, adapter.adapt_reason()); // Server format request 320x200. Same as CPU. Do nothing. format.width = 320; format.height = 200; adapter.OnOutputFormatRequest(format); EXPECT_EQ(320, adapter.output_format().width); EXPECT_EQ(200, adapter.output_format().height); // Test reason for adapting is CPU and VIEW. EXPECT_EQ(CoordinatedVideoAdapter::ADAPTREASON_CPU + CoordinatedVideoAdapter::ADAPTREASON_VIEW, adapter.adapt_reason()); // Process load and system load are normal. Adapt nothing. UpdateCpuLoad(&adapter, 1, 1, 0.5f, 0.8f); EXPECT_EQ(320, adapter.output_format().width); EXPECT_EQ(200, adapter.output_format().height); // Process load and system load are low, but view is still low. Adapt nothing. UpdateCpuLoad(&adapter, 1, 1, 0.2f, 0.3f); EXPECT_EQ(320, adapter.output_format().width); EXPECT_EQ(200, adapter.output_format().height); // Test reason for adapting is VIEW. EXPECT_EQ(CoordinatedVideoAdapter::ADAPTREASON_VIEW, adapter.adapt_reason()); // Server format request 640x400. Cpu is still low. Upgrade. format.width = 640; format.height = 400; adapter.OnOutputFormatRequest(format); EXPECT_EQ(480, adapter.output_format().width); EXPECT_EQ(300, adapter.output_format().height); // Test reason for adapting is CPU. EXPECT_EQ(CoordinatedVideoAdapter::ADAPTREASON_CPU, adapter.adapt_reason()); // Encoder resolution request: downgrade. adapter.OnEncoderResolutionRequest(480, 300, CoordinatedVideoAdapter::DOWNGRADE); EXPECT_EQ(320, adapter.output_format().width); EXPECT_EQ(200, adapter.output_format().height); // Test reason for adapting is BANDWIDTH. EXPECT_EQ(CoordinatedVideoAdapter::ADAPTREASON_BANDWIDTH, adapter.adapt_reason()); // Process load and system load are low. Constrained by GD. Adapt nothing adapter.OnCpuLoadUpdated(1, 1, 0.2f, 0.3f); EXPECT_EQ(320, adapter.output_format().width); EXPECT_EQ(200, adapter.output_format().height); // Encoder resolution request: upgrade. adapter.OnEncoderResolutionRequest(320, 200, CoordinatedVideoAdapter::UPGRADE); EXPECT_EQ(480, adapter.output_format().width); EXPECT_EQ(300, adapter.output_format().height); // Encoder resolution request: upgrade. Constrained by CPU. adapter.OnEncoderResolutionRequest(480, 300, CoordinatedVideoAdapter::UPGRADE); EXPECT_EQ(480, adapter.output_format().width); EXPECT_EQ(300, adapter.output_format().height); // Server format request 640x400. Constrained by CPU. format.width = 640; format.height = 400; adapter.OnOutputFormatRequest(format); EXPECT_EQ(480, adapter.output_format().width); EXPECT_EQ(300, adapter.output_format().height); } TEST(CoordinatedVideoAdapterTest, TestCoordinatedWithCpuRequest) { CoordinatedVideoAdapter adapter; adapter.set_cpu_adaptation(true); EXPECT_FALSE(adapter.cpu_smoothing()); VideoFormat format(640, 400, VideoFormat::FpsToInterval(30), FOURCC_I420); adapter.SetInputFormat(format); // Server format request 640x400. format.height = 400; adapter.OnOutputFormatRequest(format); EXPECT_EQ(640, adapter.output_format().width); EXPECT_EQ(400, adapter.output_format().height); // CPU resolution request: downgrade. Adapt down. adapter.OnCpuResolutionRequest(CoordinatedVideoAdapter::DOWNGRADE); EXPECT_EQ(480, adapter.output_format().width); EXPECT_EQ(300, adapter.output_format().height); // CPU resolution request: keep. Do nothing. adapter.OnCpuResolutionRequest(CoordinatedVideoAdapter::KEEP); EXPECT_EQ(480, adapter.output_format().width); EXPECT_EQ(300, adapter.output_format().height); // CPU resolution request: downgrade, but cpu adaptation disabled. // Adapt nothing. adapter.set_cpu_adaptation(false); adapter.OnCpuResolutionRequest(CoordinatedVideoAdapter::DOWNGRADE); EXPECT_EQ(480, adapter.output_format().width); EXPECT_EQ(300, adapter.output_format().height); // CPU resolution request: downgrade. Adapt down. adapter.set_cpu_adaptation(true); adapter.OnCpuResolutionRequest(CoordinatedVideoAdapter::DOWNGRADE); EXPECT_EQ(320, adapter.output_format().width); EXPECT_EQ(200, adapter.output_format().height); // Test reason for adapting is CPU. EXPECT_EQ(CoordinatedVideoAdapter::ADAPTREASON_CPU, adapter.adapt_reason()); // CPU resolution request: downgrade, but already at minimum. Do nothing. adapter.OnCpuResolutionRequest(CoordinatedVideoAdapter::DOWNGRADE); EXPECT_EQ(320, adapter.output_format().width); EXPECT_EQ(200, adapter.output_format().height); // Server format request 320x200. Same as CPU. Do nothing. format.width = 320; format.height = 200; adapter.OnOutputFormatRequest(format); EXPECT_EQ(320, adapter.output_format().width); EXPECT_EQ(200, adapter.output_format().height); // Test reason for adapting is CPU and VIEW. EXPECT_EQ(CoordinatedVideoAdapter::ADAPTREASON_CPU + CoordinatedVideoAdapter::ADAPTREASON_VIEW, adapter.adapt_reason()); // CPU resolution request: upgrade, but view request still low. Do nothing. adapter.OnCpuResolutionRequest(CoordinatedVideoAdapter::UPGRADE); EXPECT_EQ(320, adapter.output_format().width); EXPECT_EQ(200, adapter.output_format().height); // Test reason for adapting is VIEW. EXPECT_EQ(CoordinatedVideoAdapter::ADAPTREASON_VIEW, adapter.adapt_reason()); // Server format request 640x400. Cpu is still low. Upgrade. format.width = 640; format.height = 400; adapter.OnOutputFormatRequest(format); EXPECT_EQ(480, adapter.output_format().width); EXPECT_EQ(300, adapter.output_format().height); // Test reason for adapting is CPU. EXPECT_EQ(CoordinatedVideoAdapter::ADAPTREASON_CPU, adapter.adapt_reason()); // Encoder resolution request: downgrade. adapter.OnEncoderResolutionRequest(480, 300, CoordinatedVideoAdapter::DOWNGRADE); EXPECT_EQ(320, adapter.output_format().width); EXPECT_EQ(200, adapter.output_format().height); // Test reason for adapting is BANDWIDTH. EXPECT_EQ(CoordinatedVideoAdapter::ADAPTREASON_BANDWIDTH, adapter.adapt_reason()); // Process load and system load are low. Constrained by GD. Adapt nothing adapter.OnCpuLoadUpdated(1, 1, 0.2f, 0.3f); EXPECT_EQ(320, adapter.output_format().width); EXPECT_EQ(200, adapter.output_format().height); // Encoder resolution request: upgrade. adapter.OnEncoderResolutionRequest(320, 200, CoordinatedVideoAdapter::UPGRADE); EXPECT_EQ(480, adapter.output_format().width); EXPECT_EQ(300, adapter.output_format().height); // Encoder resolution request: upgrade. Constrained by CPU. adapter.OnEncoderResolutionRequest(480, 300, CoordinatedVideoAdapter::UPGRADE); EXPECT_EQ(480, adapter.output_format().width); EXPECT_EQ(300, adapter.output_format().height); // Server format request 640x400. Constrained by CPU. format.width = 640; format.height = 400; adapter.OnOutputFormatRequest(format); EXPECT_EQ(480, adapter.output_format().width); EXPECT_EQ(300, adapter.output_format().height); } TEST(CoordinatedVideoAdapterTest, TestViewRequestPlusCameraSwitch) { CoordinatedVideoAdapter adapter; adapter.set_view_switch(true); // Start at HD. VideoFormat format(1280, 720, VideoFormat::FpsToInterval(30), FOURCC_I420); adapter.SetInputFormat(format); EXPECT_EQ(format, adapter.input_format()); EXPECT_TRUE(adapter.output_format().IsSize0x0()); // View request for VGA. format.width = 640; format.height = 360; adapter.OnOutputFormatRequest(format); EXPECT_EQ(640, adapter.output_format().width); EXPECT_EQ(360, adapter.output_format().height); EXPECT_EQ(CoordinatedVideoAdapter::ADAPTREASON_VIEW, adapter.adapt_reason()); // Now, the camera reopens at VGA. // Both the frame and the output format should be 640x360. WebRtcVideoFrame in_frame; in_frame.InitToBlack(640, 360, 1, 1, 33, 33); VideoFrame* out_frame; adapter.AdaptFrame(&in_frame, &out_frame); EXPECT_EQ(640u, out_frame->GetWidth()); EXPECT_EQ(360u, out_frame->GetHeight()); // At this point, the view is no longer adapted, since the input has resized // small enough to fit the last view request. EXPECT_EQ(0, adapter.adapt_reason()); // And another view request comes in for 640x360, which should have no // real impact. adapter.OnOutputFormatRequest(format); EXPECT_EQ(640, adapter.output_format().width); EXPECT_EQ(360, adapter.output_format().height); EXPECT_EQ(0, adapter.adapt_reason()); } TEST(CoordinatedVideoAdapterTest, TestVGAWidth) { CoordinatedVideoAdapter adapter; adapter.set_view_switch(true); // Start at 640x480, for cameras that don't support 640x360. VideoFormat format(640, 480, VideoFormat::FpsToInterval(30), FOURCC_I420); adapter.SetInputFormat(format); EXPECT_EQ(format, adapter.input_format()); EXPECT_TRUE(adapter.output_format().IsSize0x0()); // Output format is 640x360, though. format.width = 640; format.height = 360; adapter.SetOutputFormat(format); // And also a view request comes for 640x360. adapter.OnOutputFormatRequest(format); // At this point, we have to adapt down to something lower. EXPECT_EQ(480, adapter.output_format().width); EXPECT_EQ(360, adapter.output_format().height); // But if frames come in at 640x360, we shouldn't adapt them down. // Fake a 640x360 frame. WebRtcVideoFrame in_frame; in_frame.InitToBlack(640, 360, 1, 1, 33, 33); VideoFrame* out_frame; adapter.AdaptFrame(&in_frame, &out_frame); EXPECT_EQ(640u, out_frame->GetWidth()); EXPECT_EQ(360u, out_frame->GetHeight()); // Similarly, no-op adapt requests for other reasons shouldn't change // adaptation state (before a previous bug, the previous EXPECTs would // fail and the following would succeed, as the no-op CPU request would // fix the adaptation state). adapter.set_cpu_adaptation(true); UpdateCpuLoad(&adapter, 1, 1, 0.7f, 0.7f); adapter.AdaptFrame(&in_frame, &out_frame); EXPECT_EQ(640u, out_frame->GetWidth()); EXPECT_EQ(360u, out_frame->GetHeight()); } // When adapting resolution for CPU or GD, the quantity of pixels that the // request is based on is reduced to half or double, and then an actual // resolution is snapped to, rounding to the closest actual resolution. // This works well for some tolerance to 3/4, odd widths and aspect ratios // that dont exactly match, but is not best behavior for ViewRequests which // need to be be strictly respected to avoid going over the resolution budget // given to the codec - 854x480 total pixels. // ViewRequest must find a lower resolution. TEST(CoordinatedVideoAdapterTest, TestCoordinatedViewRequestDown) { CoordinatedVideoAdapter adapter; adapter.set_cpu_adaptation(false); VideoFormat format(960, 540, VideoFormat::FpsToInterval(30), FOURCC_I420); adapter.SetInputFormat(format); adapter.set_scale_third(true); EXPECT_EQ(format, adapter.input_format()); EXPECT_TRUE(adapter.output_format().IsSize0x0()); // Server format request 640x400. Expect HVGA. format.width = 640; format.height = 400; adapter.OnOutputFormatRequest(format); EXPECT_EQ(640, adapter.output_format().width); EXPECT_EQ(360, adapter.output_format().height); // Test reason for adapting is VIEW. EXPECT_EQ(CoordinatedVideoAdapter::ADAPTREASON_VIEW, adapter.adapt_reason()); } // Test that we downgrade video for cpu up to two times. TEST(CoordinatedVideoAdapterTest, TestCpuDowngradeTimes) { CoordinatedVideoAdapter adapter; adapter.set_cpu_adaptation(true); EXPECT_FALSE(adapter.cpu_smoothing()); VideoFormat format(640, 400, VideoFormat::FpsToInterval(30), FOURCC_I420); adapter.SetInputFormat(format); // Server format request 640x400. format.height = 400; adapter.OnOutputFormatRequest(format); EXPECT_EQ(640, adapter.output_format().width); EXPECT_EQ(400, adapter.output_format().height); // Process load and system load are low. Do not change the cpu desired format // and do not adapt. adapter.OnCpuLoadUpdated(1, 1, 0.2f, 0.3f); EXPECT_EQ(640, adapter.output_format().width); EXPECT_EQ(400, adapter.output_format().height); // System load is high. Downgrade. UpdateCpuLoad(&adapter, 1, 1, 0.95f, 0.95f); EXPECT_EQ(480, adapter.output_format().width); EXPECT_EQ(300, adapter.output_format().height); // System load is high. Downgrade again. UpdateCpuLoad(&adapter, 1, 1, 0.95f, 0.95f); EXPECT_EQ(320, adapter.output_format().width); EXPECT_EQ(200, adapter.output_format().height); // System load is still high. Do not downgrade any more. UpdateCpuLoad(&adapter, 1, 1, 0.95f, 0.95f); EXPECT_EQ(320, adapter.output_format().width); EXPECT_EQ(200, adapter.output_format().height); // Process load and system load are low. Upgrade. UpdateCpuLoad(&adapter, 1, 1, 0.2f, 0.3f); EXPECT_EQ(480, adapter.output_format().width); EXPECT_EQ(300, adapter.output_format().height); // System load is high. Downgrade. UpdateCpuLoad(&adapter, 1, 1, 0.95f, 0.95f); EXPECT_EQ(320, adapter.output_format().width); EXPECT_EQ(200, adapter.output_format().height); // System load is still high. Do not downgrade any more. UpdateCpuLoad(&adapter, 1, 1, 0.95f, 0.95f); EXPECT_EQ(320, adapter.output_format().width); EXPECT_EQ(200, adapter.output_format().height); } // Test that we respect CPU adapter threshold values. TEST(CoordinatedVideoAdapterTest, TestAdapterCpuThreshold) { CoordinatedVideoAdapter adapter; adapter.set_cpu_adaptation(true); EXPECT_FALSE(adapter.cpu_smoothing()); VideoFormat format(640, 400, VideoFormat::FpsToInterval(30), FOURCC_I420); adapter.SetInputFormat(format); // Server format request 640x400. format.height = 400; adapter.OnOutputFormatRequest(format); EXPECT_EQ(640, adapter.output_format().width); EXPECT_EQ(400, adapter.output_format().height); // Process load and system load are low. Do not change the cpu desired format // and do not adapt. adapter.OnCpuLoadUpdated(1, 1, 0.2f, 0.3f); EXPECT_EQ(640, adapter.output_format().width); EXPECT_EQ(400, adapter.output_format().height); // System load is high. Downgrade. UpdateCpuLoad(&adapter, 1, 1, 0.95f, 0.95f); EXPECT_EQ(480, adapter.output_format().width); EXPECT_EQ(300, adapter.output_format().height); // Test reason for adapting is CPU. EXPECT_EQ(CoordinatedVideoAdapter::ADAPTREASON_CPU, adapter.adapt_reason()); // System load is high. Normally downgrade but threshold is high. Do nothing. adapter.set_high_system_threshold(0.98f); // Set threshold high. UpdateCpuLoad(&adapter, 1, 1, 0.95f, 0.95f); EXPECT_EQ(480, adapter.output_format().width); EXPECT_EQ(300, adapter.output_format().height); // System load is medium. Normally do nothing, threshold is low. Adapt down. adapter.set_high_system_threshold(0.75f); // Set threshold low. UpdateCpuLoad(&adapter, 1, 1, 0.8f, 0.8f); EXPECT_EQ(320, adapter.output_format().width); EXPECT_EQ(200, adapter.output_format().height); } // Test that for an upgrade cpu request, we actually upgrade the desired format; // for a downgrade request, we downgrade from the output format. TEST(CoordinatedVideoAdapterTest, TestRealCpuUpgrade) { CoordinatedVideoAdapter adapter; adapter.set_cpu_adaptation(true); adapter.set_cpu_smoothing(true); VideoFormat format(640, 400, VideoFormat::FpsToInterval(30), FOURCC_I420); adapter.SetInputFormat(format); // Server format request 640x400. format.width = 640; format.height = 400; adapter.OnOutputFormatRequest(format); EXPECT_EQ(640, adapter.output_format().width); EXPECT_EQ(400, adapter.output_format().height); // Process load and system load are low. Do not change the cpu desired format // and do not adapt. UpdateCpuLoad(&adapter, 1, 1, 0.2f, 0.3f); EXPECT_EQ(640, adapter.output_format().width); EXPECT_EQ(400, adapter.output_format().height); // Server format request 320x200. format.width = 320; format.height = 200; adapter.OnOutputFormatRequest(format); EXPECT_EQ(320, adapter.output_format().width); EXPECT_EQ(200, adapter.output_format().height); // Process load and system load are low. Do not change the cpu desired format // and do not adapt. UpdateCpuLoad(&adapter, 1, 1, 0.2f, 0.3f); EXPECT_EQ(320, adapter.output_format().width); EXPECT_EQ(200, adapter.output_format().height); // Server format request 640x400. Set to 640x400 immediately. format.width = 640; format.height = 400; adapter.OnOutputFormatRequest(format); EXPECT_EQ(640, adapter.output_format().width); EXPECT_EQ(400, adapter.output_format().height); // Server format request 320x200. format.width = 320; format.height = 200; adapter.OnOutputFormatRequest(format); EXPECT_EQ(320, adapter.output_format().width); EXPECT_EQ(200, adapter.output_format().height); // Process load is high, but system is not. Do not change the cpu desired // format and do not adapt. for (size_t i = 0; i < 10; ++i) { UpdateCpuLoad(&adapter, 1, 1, 0.75f, 0.8f); } EXPECT_EQ(320, adapter.output_format().width); EXPECT_EQ(200, adapter.output_format().height); } // Test that for an upgrade encoder request, we actually upgrade the desired // format; for a downgrade request, we downgrade from the output format. TEST(CoordinatedVideoAdapterTest, TestRealEncoderUpgrade) { CoordinatedVideoAdapter adapter; adapter.set_cpu_adaptation(true); adapter.set_cpu_smoothing(true); VideoFormat format(640, 400, VideoFormat::FpsToInterval(30), FOURCC_I420); adapter.SetInputFormat(format); // Server format request 640x400. format.width = 640; format.height = 400; adapter.OnOutputFormatRequest(format); EXPECT_EQ(640, adapter.output_format().width); EXPECT_EQ(400, adapter.output_format().height); // Encoder resolution request. Do not change the encoder desired format and // do not adapt. adapter.OnEncoderResolutionRequest(640, 400, CoordinatedVideoAdapter::UPGRADE); EXPECT_EQ(640, adapter.output_format().width); EXPECT_EQ(400, adapter.output_format().height); // Server format request 320x200. format.width = 320; format.height = 200; adapter.OnOutputFormatRequest(format); EXPECT_EQ(320, adapter.output_format().width); EXPECT_EQ(200, adapter.output_format().height); // Encoder resolution request. Do not change the encoder desired format and // do not adapt. adapter.OnEncoderResolutionRequest(320, 200, CoordinatedVideoAdapter::UPGRADE); EXPECT_EQ(320, adapter.output_format().width); EXPECT_EQ(200, adapter.output_format().height); // Server format request 640x400. Set to 640x400 immediately. format.width = 640; format.height = 400; adapter.OnOutputFormatRequest(format); EXPECT_EQ(480, adapter.output_format().width); EXPECT_EQ(300, adapter.output_format().height); // Test reason for adapting is BANDWIDTH. EXPECT_EQ(CoordinatedVideoAdapter::ADAPTREASON_BANDWIDTH, adapter.adapt_reason()); // Server format request 320x200. format.width = 320; format.height = 200; adapter.OnOutputFormatRequest(format); EXPECT_EQ(320, adapter.output_format().width); EXPECT_EQ(200, adapter.output_format().height); // Encoder resolution request. Downgrade from 320x200. adapter.OnEncoderResolutionRequest(320, 200, CoordinatedVideoAdapter::DOWNGRADE); EXPECT_EQ(240, adapter.output_format().width); EXPECT_EQ(150, adapter.output_format().height); } TEST(CoordinatedVideoAdapterTest, TestNormalizeOutputFormat) { CoordinatedVideoAdapter adapter; // The input format is 640x360 and the output is limited to 16:9. VideoFormat format(640, 360, VideoFormat::FpsToInterval(30), FOURCC_I420); adapter.SetInputFormat(format); format.width = 320; format.height = 180; format.interval = VideoFormat::FpsToInterval(15); adapter.OnOutputFormatRequest(format); EXPECT_EQ(320, adapter.output_format().width); EXPECT_EQ(180, adapter.output_format().height); EXPECT_EQ(VideoFormat::FpsToInterval(15), adapter.output_format().interval); format.width = 320; format.height = 200; format.interval = VideoFormat::FpsToInterval(40); adapter.OnOutputFormatRequest(format); EXPECT_EQ(320, adapter.output_format().width); EXPECT_EQ(180, adapter.output_format().height); EXPECT_EQ(VideoFormat::FpsToInterval(30), adapter.output_format().interval); // Test reason for adapting is VIEW. Should work even with normalization. EXPECT_EQ(CoordinatedVideoAdapter::ADAPTREASON_VIEW, adapter.adapt_reason()); format.width = 320; format.height = 240; adapter.OnOutputFormatRequest(format); EXPECT_EQ(320, adapter.output_format().width); EXPECT_EQ(180, adapter.output_format().height); // The input format is 640x480 and the output will be 4:3. format.width = 640; format.height = 480; adapter.SetInputFormat(format); EXPECT_EQ(320, adapter.output_format().width); EXPECT_EQ(240, adapter.output_format().height); format.width = 320; format.height = 240; adapter.OnOutputFormatRequest(format); EXPECT_EQ(320, adapter.output_format().width); EXPECT_EQ(240, adapter.output_format().height); // The input format is initialized after the output. At that time, the output // height is adjusted. format.width = 0; format.height = 0; adapter.SetInputFormat(format); format.width = 320; format.height = 240; format.interval = VideoFormat::FpsToInterval(30); adapter.OnOutputFormatRequest(format); EXPECT_EQ(320, adapter.output_format().width); EXPECT_EQ(240, adapter.output_format().height); EXPECT_EQ(VideoFormat::FpsToInterval(30), adapter.output_format().interval); format.width = 640; format.height = 480; format.interval = VideoFormat::FpsToInterval(15); adapter.SetInputFormat(format); EXPECT_EQ(320, adapter.output_format().width); EXPECT_EQ(240, adapter.output_format().height); EXPECT_EQ(VideoFormat::FpsToInterval(15), adapter.output_format().interval); } // Test that we downgrade video for cpu up to two times. TEST_F(VideoAdapterTest, CpuDowngradeAndSignal) { CoordinatedVideoAdapter adapter; CpuAdapterListener cpu_listener; adapter.SignalCpuAdaptationUnable.connect( &cpu_listener, &CpuAdapterListener::OnCpuAdaptationSignalled); adapter.set_cpu_adaptation(true); EXPECT_FALSE(adapter.cpu_smoothing()); VideoFormat format(640, 360, VideoFormat::FpsToInterval(30), FOURCC_I420); adapter.SetInputFormat(format); adapter.OnOutputFormatRequest(format); // System load is high. Downgrade. UpdateCpuLoad(&adapter, 1, 1, 0.95f, 0.95f); // System load is high. Downgrade again. UpdateCpuLoad(&adapter, 1, 1, 0.95f, 0.95f); // System load is still high. Do not downgrade any more. Ensure we have not // signalled until after the cpu warning though. EXPECT_TRUE(!cpu_listener.received_cpu_signal()); UpdateCpuLoad(&adapter, 1, 1, 0.95f, 0.95f); EXPECT_TRUE_WAIT(cpu_listener.received_cpu_signal(), kWaitTimeout); } // Test that we downgrade video for cpu up to two times. TEST_F(VideoAdapterTest, CpuDowngradeAndDontSignal) { CoordinatedVideoAdapter adapter; CpuAdapterListener cpu_listener; adapter.SignalCpuAdaptationUnable.connect( &cpu_listener, &CpuAdapterListener::OnCpuAdaptationSignalled); adapter.set_cpu_adaptation(true); adapter.set_cpu_smoothing(true); VideoFormat format(640, 360, VideoFormat::FpsToInterval(30), FOURCC_I420); adapter.SetInputFormat(format); adapter.OnOutputFormatRequest(format); // System load is high. Downgrade. UpdateCpuLoad(&adapter, 1, 1, 0.95f, 0.95f); // System load is high, process is not, Do not downgrade again. UpdateCpuLoad(&adapter, 1, 1, 0.25f, 0.95f); // System load is high, process is not, Do not downgrade again and do not // signal. adapter.set_cpu_adaptation(false); UpdateCpuLoad(&adapter, 1, 1, 0.95f, 0.95f); rtc::Thread::Current()->ProcessMessages(kShortWaitTimeout); EXPECT_TRUE(!cpu_listener.received_cpu_signal()); adapter.set_cpu_adaptation(true); } // Test that we require enough time before we downgrade. TEST_F(VideoAdapterTest, CpuMinTimeRequirement) { CoordinatedVideoAdapter adapter; CpuAdapterListener cpu_listener; adapter.SignalCpuAdaptationUnable.connect( &cpu_listener, &CpuAdapterListener::OnCpuAdaptationSignalled); adapter.set_cpu_adaptation(true); adapter.set_cpu_smoothing(true); VideoFormat format(640, 360, VideoFormat::FpsToInterval(30), FOURCC_I420); adapter.SetInputFormat(format); adapter.OnOutputFormatRequest(format); EXPECT_EQ(3, adapter.cpu_load_min_samples()); adapter.set_cpu_load_min_samples(5); for (size_t i = 0; i < 4; ++i) { adapter.OnCpuLoadUpdated(1, 1, 1.0f, 1.0f); EXPECT_EQ(640, adapter.output_format().width); EXPECT_EQ(360, adapter.output_format().height); } // The computed cpu load should now be around 93.5%, with the coefficient of // 0.4 and a seed value of 0.5. That should be high enough to adapt, but it // isn't enough samples, so we shouldn't have adapted on any of the previous // samples. // One more sample is enough, though, once enough time has passed. adapter.OnCpuLoadUpdated(1, 1, 1.0f, 1.0f); EXPECT_EQ(480, adapter.output_format().width); EXPECT_EQ(270, adapter.output_format().height); // Now the cpu is lower, but we still need enough samples to upgrade. for (size_t i = 0; i < 4; ++i) { adapter.OnCpuLoadUpdated(1, 1, 0.1f, 0.1f); EXPECT_EQ(480, adapter.output_format().width); EXPECT_EQ(270, adapter.output_format().height); } // One more sample is enough, once time has elapsed. adapter.OnCpuLoadUpdated(1, 1, 1.0f, 1.0f); EXPECT_EQ(640, adapter.output_format().width); EXPECT_EQ(360, adapter.output_format().height); } TEST_F(VideoAdapterTest, CpuIgnoresSpikes) { CoordinatedVideoAdapter adapter; CpuAdapterListener cpu_listener; adapter.SignalCpuAdaptationUnable.connect( &cpu_listener, &CpuAdapterListener::OnCpuAdaptationSignalled); adapter.set_cpu_adaptation(true); adapter.set_cpu_smoothing(true); VideoFormat format(640, 360, VideoFormat::FpsToInterval(30), FOURCC_I420); adapter.SetInputFormat(format); adapter.OnOutputFormatRequest(format); // System load is high. Downgrade. for (size_t i = 0; i < 5; ++i) { UpdateCpuLoad(&adapter, 1, 1, 0.95f, 0.95f); } EXPECT_EQ(480, adapter.output_format().width); EXPECT_EQ(270, adapter.output_format().height); // Now we're in a state where we could upgrade or downgrade, so get to a // steady state of about 75% cpu usage. for (size_t i = 0; i < 5; ++i) { UpdateCpuLoad(&adapter, 1, 1, 0.75f, 0.75f); EXPECT_EQ(480, adapter.output_format().width); EXPECT_EQ(270, adapter.output_format().height); } // Now, the cpu spikes for two samples, but then goes back to // normal. This shouldn't cause adaptation. UpdateCpuLoad(&adapter, 1, 1, 0.90f, 0.90f); UpdateCpuLoad(&adapter, 1, 1, 0.90f, 0.90f); EXPECT_EQ(480, adapter.output_format().width); EXPECT_EQ(270, adapter.output_format().height); // Back to the steady state for awhile. for (size_t i = 0; i < 5; ++i) { UpdateCpuLoad(&adapter, 1, 1, 0.75, 0.75); EXPECT_EQ(480, adapter.output_format().width); EXPECT_EQ(270, adapter.output_format().height); } // Now, system cpu usage is starting to drop down. But it takes a bit before // it gets all the way there. for (size_t i = 0; i < 10; ++i) { UpdateCpuLoad(&adapter, 1, 1, 0.5f, 0.5f); } EXPECT_EQ(640, adapter.output_format().width); EXPECT_EQ(360, adapter.output_format().height); } } // namespace cricket #endif // HAVE_WEBRTC_VIDEO