From 696c9c6b64234a34631bb39c758996bb08449fe4 Mon Sep 17 00:00:00 2001 From: brandtr Date: Mon, 19 Dec 2016 05:47:28 -0800 Subject: [PATCH] Add multithreaded fake encoder and corresponding FlexFEC VideoSendStreamTest. This test would have found the issue that was fixed in https://codereview.webrtc.org/2562983002/. BUG=webrtc:5654 Review-Url: https://codereview.webrtc.org/2573453002 Cr-Commit-Position: refs/heads/master@{#15675} --- webrtc/test/fake_encoder.cc | 57 +++++++++++++ webrtc/test/fake_encoder.h | 26 ++++++ webrtc/video/BUILD.gn | 1 + webrtc/video/video_send_stream_tests.cc | 101 ++++++++++++++---------- 4 files changed, 142 insertions(+), 43 deletions(-) diff --git a/webrtc/test/fake_encoder.cc b/webrtc/test/fake_encoder.cc index ae1788bf6c..46b51b6aa4 100644 --- a/webrtc/test/fake_encoder.cc +++ b/webrtc/test/fake_encoder.cc @@ -12,6 +12,7 @@ #include +#include "webrtc/base/atomicops.h" #include "webrtc/base/checks.h" #include "webrtc/modules/video_coding/include/video_codec_interface.h" #include "webrtc/system_wrappers/include/sleep.h" @@ -225,5 +226,61 @@ int32_t DelayedEncoder::Encode(const VideoFrame& input_image, SleepMs(delay_ms); return FakeEncoder::Encode(input_image, codec_specific_info, frame_types); } + +MultiThreadedFakeH264Encoder::MultiThreadedFakeH264Encoder(Clock* clock) + : test::FakeH264Encoder(clock), + current_queue_(0), + queue1_("Queue 1"), + queue2_("Queue 2") {} + +MultiThreadedFakeH264Encoder::~MultiThreadedFakeH264Encoder() = default; + +class MultiThreadedFakeH264Encoder::EncodeTask : public rtc::QueuedTask { + public: + EncodeTask(MultiThreadedFakeH264Encoder* encoder, + const VideoFrame& input_image, + const CodecSpecificInfo* codec_specific_info, + const std::vector* frame_types) + : encoder_(encoder), + input_image_(input_image), + codec_specific_info_(), + frame_types_(*frame_types) { + if (codec_specific_info) + codec_specific_info_ = *codec_specific_info; + } + + private: + bool Run() override { + encoder_->EncodeCallback(input_image_, &codec_specific_info_, + &frame_types_); + return true; + } + + MultiThreadedFakeH264Encoder* const encoder_; + VideoFrame input_image_; + CodecSpecificInfo codec_specific_info_; + std::vector frame_types_; +}; + +int32_t MultiThreadedFakeH264Encoder::Encode( + const VideoFrame& input_image, + const CodecSpecificInfo* codec_specific_info, + const std::vector* frame_types) { + int current_queue = rtc::AtomicOps::Increment(¤t_queue_); + rtc::TaskQueue& queue = (current_queue % 2 == 0) ? queue1_ : queue2_; + + queue.PostTask(std::unique_ptr( + new EncodeTask(this, input_image, codec_specific_info, frame_types))); + + return 0; +} + +int32_t MultiThreadedFakeH264Encoder::EncodeCallback( + const VideoFrame& input_image, + const CodecSpecificInfo* codec_specific_info, + const std::vector* frame_types) { + return FakeH264Encoder::Encode(input_image, codec_specific_info, frame_types); +} + } // namespace test } // namespace webrtc diff --git a/webrtc/test/fake_encoder.h b/webrtc/test/fake_encoder.h index 57b39a4404..b81a04b43f 100644 --- a/webrtc/test/fake_encoder.h +++ b/webrtc/test/fake_encoder.h @@ -14,6 +14,7 @@ #include #include "webrtc/base/criticalsection.h" +#include "webrtc/base/task_queue.h" #include "webrtc/common_types.h" #include "webrtc/system_wrappers/include/clock.h" #include "webrtc/video_encoder.h" @@ -86,6 +87,31 @@ class DelayedEncoder : public test::FakeEncoder { rtc::CriticalSection lock_; int delay_ms_ GUARDED_BY(&lock_); }; + +// This class implements a multi-threaded fake encoder by posting +// FakeH264Encoder::Encode(.) tasks to |queue1_| and |queue2_|, in an +// alternating fashion. +class MultiThreadedFakeH264Encoder : public test::FakeH264Encoder { + public: + MultiThreadedFakeH264Encoder(Clock* clock); + virtual ~MultiThreadedFakeH264Encoder() override; + + int32_t Encode(const VideoFrame& input_image, + const CodecSpecificInfo* codec_specific_info, + const std::vector* frame_types) override; + + int32_t EncodeCallback(const VideoFrame& input_image, + const CodecSpecificInfo* codec_specific_info, + const std::vector* frame_types); + + protected: + class EncodeTask; + + int current_queue_; + rtc::TaskQueue queue1_; + rtc::TaskQueue queue2_; +}; + } // namespace test } // namespace webrtc diff --git a/webrtc/video/BUILD.gn b/webrtc/video/BUILD.gn index 0161479a6c..69756096de 100644 --- a/webrtc/video/BUILD.gn +++ b/webrtc/video/BUILD.gn @@ -84,6 +84,7 @@ if (rtc_include_tests) { "video_quality_test.h", ] deps = [ + "../base:rtc_task_queue", "../media:rtc_media_base", "../system_wrappers", "//testing/gtest", diff --git a/webrtc/video/video_send_stream_tests.cc b/webrtc/video/video_send_stream_tests.cc index 7a11ae1243..e0f9962c6e 100644 --- a/webrtc/video/video_send_stream_tests.cc +++ b/webrtc/video/video_send_stream_tests.cc @@ -356,25 +356,17 @@ class UlpfecObserver : public test::EndToEndTest { bool use_nack, bool expect_red, bool expect_ulpfec, - const std::string& codec) + const std::string& codec, + VideoEncoder* encoder) : EndToEndTest(VideoSendStreamTest::kDefaultTimeoutMs), + encoder_(encoder), payload_name_(codec), use_nack_(use_nack), expect_red_(expect_red), expect_ulpfec_(expect_ulpfec), sent_media_(false), sent_ulpfec_(false), - header_extensions_enabled_(header_extensions_enabled) { - if (codec == "H264") { - encoder_.reset(new test::FakeH264Encoder(Clock::GetRealTimeClock())); - } else if (codec == "VP8") { - encoder_.reset(VP8Encoder::Create()); - } else if (codec == "VP9") { - encoder_.reset(VP9Encoder::Create()); - } else { - RTC_NOTREACHED(); - } - } + header_extensions_enabled_(header_extensions_enabled) {} private: Action OnSendRtp(const uint8_t* packet, size_t length) override { @@ -462,7 +454,7 @@ class UlpfecObserver : public test::EndToEndTest { (*receive_configs)[0].rtp.nack.rtp_history_ms = VideoSendStreamTest::kNackRtpHistoryMs; } - send_config->encoder_settings.encoder = encoder_.get(); + send_config->encoder_settings.encoder = encoder_; send_config->encoder_settings.payload_name = payload_name_; send_config->rtp.ulpfec.red_payload_type = VideoSendStreamTest::kRedPayloadType; @@ -486,8 +478,8 @@ class UlpfecObserver : public test::EndToEndTest { } std::unique_ptr transport_adapter_; - std::unique_ptr encoder_; - const std::string payload_name_; + VideoEncoder* const encoder_; + std::string payload_name_; const bool use_nack_; const bool expect_red_; const bool expect_ulpfec_; @@ -498,12 +490,14 @@ class UlpfecObserver : public test::EndToEndTest { }; TEST_F(VideoSendStreamTest, SupportsUlpfecWithExtensions) { - UlpfecObserver test(true, false, true, true, "VP8"); + std::unique_ptr encoder(VP8Encoder::Create()); + UlpfecObserver test(true, false, true, true, "VP8", encoder.get()); RunBaseTest(&test); } TEST_F(VideoSendStreamTest, SupportsUlpfecWithoutExtensions) { - UlpfecObserver test(false, false, true, true, "VP8"); + std::unique_ptr encoder(VP8Encoder::Create()); + UlpfecObserver test(false, false, true, true, "VP8", encoder.get()); RunBaseTest(&test); } @@ -512,51 +506,56 @@ TEST_F(VideoSendStreamTest, SupportsUlpfecWithoutExtensions) { // bandwidth since the receiver has to wait for FEC retransmissions to determine // that the received state is actually decodable. TEST_F(VideoSendStreamTest, DoesNotUtilizeUlpfecForH264WithNackEnabled) { - UlpfecObserver test(false, true, true, false, "H264"); + std::unique_ptr encoder( + new test::FakeH264Encoder(Clock::GetRealTimeClock())); + UlpfecObserver test(false, true, true, false, "H264", encoder.get()); RunBaseTest(&test); } // Without retransmissions FEC for H264 is fine. TEST_F(VideoSendStreamTest, DoesUtilizeUlpfecForH264WithoutNackEnabled) { - UlpfecObserver test(false, false, true, true, "H264"); + std::unique_ptr encoder( + new test::FakeH264Encoder(Clock::GetRealTimeClock())); + UlpfecObserver test(false, false, true, true, "H264", encoder.get()); RunBaseTest(&test); } TEST_F(VideoSendStreamTest, DoesUtilizeUlpfecForVp8WithNackEnabled) { - UlpfecObserver test(false, true, true, true, "VP8"); + std::unique_ptr encoder(VP8Encoder::Create()); + UlpfecObserver test(false, true, true, true, "VP8", encoder.get()); RunBaseTest(&test); } #if !defined(RTC_DISABLE_VP9) TEST_F(VideoSendStreamTest, DoesUtilizeUlpfecForVp9WithNackEnabled) { - UlpfecObserver test(false, true, true, true, "VP9"); + std::unique_ptr encoder(VP9Encoder::Create()); + UlpfecObserver test(false, true, true, true, "VP9", encoder.get()); RunBaseTest(&test); } #endif // !defined(RTC_DISABLE_VP9) +TEST_F(VideoSendStreamTest, SupportsUlpfecWithMultiThreadedH264) { + std::unique_ptr encoder( + new test::MultiThreadedFakeH264Encoder(Clock::GetRealTimeClock())); + UlpfecObserver test(false, false, true, true, "H264", encoder.get()); + RunBaseTest(&test); +} + // TODO(brandtr): Move these FlexFEC tests when we have created // FlexfecSendStream. class FlexfecObserver : public test::EndToEndTest { public: FlexfecObserver(bool header_extensions_enabled, bool use_nack, - const std::string& codec) + const std::string& codec, + VideoEncoder* encoder) : EndToEndTest(VideoSendStreamTest::kDefaultTimeoutMs), + encoder_(encoder), payload_name_(codec), use_nack_(use_nack), sent_media_(false), sent_flexfec_(false), - header_extensions_enabled_(header_extensions_enabled) { - if (codec == "H264") { - encoder_.reset(new test::FakeH264Encoder(Clock::GetRealTimeClock())); - } else if (codec == "VP8") { - encoder_.reset(VP8Encoder::Create()); - } else if (codec == "VP9") { - encoder_.reset(VP9Encoder::Create()); - } else { - RTC_NOTREACHED(); - } - } + header_extensions_enabled_(header_extensions_enabled) {} size_t GetNumFlexfecStreams() const override { return 1; } @@ -611,7 +610,7 @@ class FlexfecObserver : public test::EndToEndTest { (*receive_configs)[0].rtp.nack.rtp_history_ms = VideoSendStreamTest::kNackRtpHistoryMs; } - send_config->encoder_settings.encoder = encoder_.get(); + send_config->encoder_settings.encoder = encoder_; send_config->encoder_settings.payload_name = payload_name_; if (header_extensions_enabled_) { send_config->rtp.extensions.push_back(RtpExtension( @@ -630,8 +629,8 @@ class FlexfecObserver : public test::EndToEndTest { } std::unique_ptr transport_adapter_; - std::unique_ptr encoder_; - const std::string payload_name_; + VideoEncoder* const encoder_; + std::string payload_name_; const bool use_nack_; bool sent_media_; bool sent_flexfec_; @@ -639,39 +638,55 @@ class FlexfecObserver : public test::EndToEndTest { }; TEST_F(VideoSendStreamTest, SupportsFlexfecVp8) { - FlexfecObserver test(false, false, "VP8"); + std::unique_ptr encoder(VP8Encoder::Create()); + FlexfecObserver test(false, false, "VP8", encoder.get()); RunBaseTest(&test); } TEST_F(VideoSendStreamTest, SupportsFlexfecWithNackVp8) { - FlexfecObserver test(false, true, "VP8"); + std::unique_ptr encoder(VP8Encoder::Create()); + FlexfecObserver test(false, true, "VP8", encoder.get()); RunBaseTest(&test); } TEST_F(VideoSendStreamTest, SupportsFlexfecWithRtpExtensionsVp8) { - FlexfecObserver test(true, false, "VP8"); + std::unique_ptr encoder(VP8Encoder::Create()); + FlexfecObserver test(true, false, "VP8", encoder.get()); RunBaseTest(&test); } #if !defined(RTC_DISABLE_VP9) TEST_F(VideoSendStreamTest, SupportsFlexfecVp9) { - FlexfecObserver test(false, false, "VP9"); + std::unique_ptr encoder(VP9Encoder::Create()); + FlexfecObserver test(false, false, "VP9", encoder.get()); RunBaseTest(&test); } TEST_F(VideoSendStreamTest, SupportsFlexfecWithNackVp9) { - FlexfecObserver test(false, true, "VP9"); + std::unique_ptr encoder(VP9Encoder::Create()); + FlexfecObserver test(false, true, "VP9", encoder.get()); RunBaseTest(&test); } #endif // defined(RTC_DISABLE_VP9) TEST_F(VideoSendStreamTest, SupportsFlexfecH264) { - FlexfecObserver test(false, false, "H264"); + std::unique_ptr encoder( + new test::FakeH264Encoder(Clock::GetRealTimeClock())); + FlexfecObserver test(false, false, "H264", encoder.get()); RunBaseTest(&test); } TEST_F(VideoSendStreamTest, SupportsFlexfecWithNackH264) { - FlexfecObserver test(false, true, "H264"); + std::unique_ptr encoder( + new test::FakeH264Encoder(Clock::GetRealTimeClock())); + FlexfecObserver test(false, true, "H264", encoder.get()); + RunBaseTest(&test); +} + +TEST_F(VideoSendStreamTest, SupportsFlexfecWithMultiThreadedH264) { + std::unique_ptr encoder( + new test::MultiThreadedFakeH264Encoder(Clock::GetRealTimeClock())); + FlexfecObserver test(false, false, "H264", encoder.get()); RunBaseTest(&test); }