/* * Copyright (c) 2014 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 "media/engine/simulcast_encoder_adapter.h" #include #include #include #include "api/test/create_simulcast_test_fixture.h" #include "api/test/simulcast_test_fixture.h" #include "api/test/video/function_video_decoder_factory.h" #include "api/test/video/function_video_encoder_factory.h" #include "api/video/video_codec_constants.h" #include "api/video_codecs/sdp_video_format.h" #include "api/video_codecs/video_encoder.h" #include "api/video_codecs/video_encoder_factory.h" #include "common_video/include/video_frame_buffer.h" #include "media/base/media_constants.h" #include "media/engine/internal_encoder_factory.h" #include "modules/video_coding/codecs/vp8/include/vp8.h" #include "modules/video_coding/include/video_codec_interface.h" #include "modules/video_coding/utility/simulcast_test_fixture_impl.h" #include "rtc_base/checks.h" #include "test/field_trial.h" #include "test/gmock.h" #include "test/gtest.h" using ::testing::_; using ::testing::Return; using EncoderInfo = webrtc::VideoEncoder::EncoderInfo; using FramerateFractions = absl::InlinedVector; namespace webrtc { namespace test { namespace { constexpr int kDefaultWidth = 1280; constexpr int kDefaultHeight = 720; const VideoEncoder::Capabilities kCapabilities(false); const VideoEncoder::Settings kSettings(kCapabilities, 1, 1200); std::unique_ptr CreateSpecificSimulcastTestFixture( VideoEncoderFactory* internal_encoder_factory) { std::unique_ptr encoder_factory = std::make_unique( [internal_encoder_factory]() { return std::make_unique( internal_encoder_factory, SdpVideoFormat(cricket::kVp8CodecName)); }); std::unique_ptr decoder_factory = std::make_unique( []() { return VP8Decoder::Create(); }); return CreateSimulcastTestFixture(std::move(encoder_factory), std::move(decoder_factory), SdpVideoFormat(cricket::kVp8CodecName)); } } // namespace TEST(SimulcastEncoderAdapterSimulcastTest, TestKeyFrameRequestsOnAllStreams) { InternalEncoderFactory internal_encoder_factory; auto fixture = CreateSpecificSimulcastTestFixture(&internal_encoder_factory); fixture->TestKeyFrameRequestsOnAllStreams(); } TEST(SimulcastEncoderAdapterSimulcastTest, TestPaddingAllStreams) { InternalEncoderFactory internal_encoder_factory; auto fixture = CreateSpecificSimulcastTestFixture(&internal_encoder_factory); fixture->TestPaddingAllStreams(); } TEST(SimulcastEncoderAdapterSimulcastTest, TestPaddingTwoStreams) { InternalEncoderFactory internal_encoder_factory; auto fixture = CreateSpecificSimulcastTestFixture(&internal_encoder_factory); fixture->TestPaddingTwoStreams(); } TEST(SimulcastEncoderAdapterSimulcastTest, TestPaddingTwoStreamsOneMaxedOut) { InternalEncoderFactory internal_encoder_factory; auto fixture = CreateSpecificSimulcastTestFixture(&internal_encoder_factory); fixture->TestPaddingTwoStreamsOneMaxedOut(); } TEST(SimulcastEncoderAdapterSimulcastTest, TestPaddingOneStream) { InternalEncoderFactory internal_encoder_factory; auto fixture = CreateSpecificSimulcastTestFixture(&internal_encoder_factory); fixture->TestPaddingOneStream(); } TEST(SimulcastEncoderAdapterSimulcastTest, TestPaddingOneStreamTwoMaxedOut) { InternalEncoderFactory internal_encoder_factory; auto fixture = CreateSpecificSimulcastTestFixture(&internal_encoder_factory); fixture->TestPaddingOneStreamTwoMaxedOut(); } TEST(SimulcastEncoderAdapterSimulcastTest, TestSendAllStreams) { InternalEncoderFactory internal_encoder_factory; auto fixture = CreateSpecificSimulcastTestFixture(&internal_encoder_factory); fixture->TestSendAllStreams(); } TEST(SimulcastEncoderAdapterSimulcastTest, TestDisablingStreams) { InternalEncoderFactory internal_encoder_factory; auto fixture = CreateSpecificSimulcastTestFixture(&internal_encoder_factory); fixture->TestDisablingStreams(); } TEST(SimulcastEncoderAdapterSimulcastTest, TestActiveStreams) { InternalEncoderFactory internal_encoder_factory; auto fixture = CreateSpecificSimulcastTestFixture(&internal_encoder_factory); fixture->TestActiveStreams(); } TEST(SimulcastEncoderAdapterSimulcastTest, TestSwitchingToOneStream) { InternalEncoderFactory internal_encoder_factory; auto fixture = CreateSpecificSimulcastTestFixture(&internal_encoder_factory); fixture->TestSwitchingToOneStream(); } TEST(SimulcastEncoderAdapterSimulcastTest, TestSwitchingToOneOddStream) { InternalEncoderFactory internal_encoder_factory; auto fixture = CreateSpecificSimulcastTestFixture(&internal_encoder_factory); fixture->TestSwitchingToOneOddStream(); } TEST(SimulcastEncoderAdapterSimulcastTest, TestStrideEncodeDecode) { InternalEncoderFactory internal_encoder_factory; auto fixture = CreateSpecificSimulcastTestFixture(&internal_encoder_factory); fixture->TestStrideEncodeDecode(); } TEST(SimulcastEncoderAdapterSimulcastTest, TestSpatioTemporalLayers333PatternEncoder) { InternalEncoderFactory internal_encoder_factory; auto fixture = CreateSpecificSimulcastTestFixture(&internal_encoder_factory); fixture->TestSpatioTemporalLayers333PatternEncoder(); } TEST(SimulcastEncoderAdapterSimulcastTest, TestSpatioTemporalLayers321PatternEncoder) { InternalEncoderFactory internal_encoder_factory; auto fixture = CreateSpecificSimulcastTestFixture(&internal_encoder_factory); fixture->TestSpatioTemporalLayers321PatternEncoder(); } TEST(SimulcastEncoderAdapterSimulcastTest, TestDecodeWidthHeightSet) { InternalEncoderFactory internal_encoder_factory; auto fixture = CreateSpecificSimulcastTestFixture(&internal_encoder_factory); fixture->TestDecodeWidthHeightSet(); } class MockVideoEncoder; class MockVideoEncoderFactory : public VideoEncoderFactory { public: std::vector GetSupportedFormats() const override; std::unique_ptr CreateVideoEncoder( const SdpVideoFormat& format) override; const std::vector& encoders() const; void SetEncoderNames(const std::vector& encoder_names); void set_init_encode_return_value(int32_t value); void set_requested_resolution_alignments( std::vector requested_resolution_alignments) { requested_resolution_alignments_ = requested_resolution_alignments; } void set_supports_simulcast(bool supports_simulcast) { supports_simulcast_ = supports_simulcast; } void DestroyVideoEncoder(VideoEncoder* encoder); private: int32_t init_encode_return_value_ = 0; std::vector encoders_; std::vector encoder_names_; // Keep number of entries in sync with |kMaxSimulcastStreams|. std::vector requested_resolution_alignments_ = {1, 1, 1}; bool supports_simulcast_ = false; }; class MockVideoEncoder : public VideoEncoder { public: explicit MockVideoEncoder(MockVideoEncoderFactory* factory) : factory_(factory), scaling_settings_(VideoEncoder::ScalingSettings::kOff), video_format_("unknown"), callback_(nullptr) {} MOCK_METHOD(void, SetFecControllerOverride, (FecControllerOverride * fec_controller_override), (override)); int32_t InitEncode(const VideoCodec* codecSettings, const VideoEncoder::Settings& settings) override { codec_ = *codecSettings; return init_encode_return_value_; } MOCK_METHOD(int32_t, Encode, (const VideoFrame& inputImage, const std::vector* frame_types), (override)); int32_t RegisterEncodeCompleteCallback( EncodedImageCallback* callback) override { callback_ = callback; return 0; } MOCK_METHOD(int32_t, Release, (), (override)); void SetRates(const RateControlParameters& parameters) { last_set_rates_ = parameters; } EncoderInfo GetEncoderInfo() const override { EncoderInfo info; info.supports_native_handle = supports_native_handle_; info.implementation_name = implementation_name_; info.scaling_settings = scaling_settings_; info.requested_resolution_alignment = requested_resolution_alignment_; info.apply_alignment_to_all_simulcast_layers = apply_alignment_to_all_simulcast_layers_; info.has_trusted_rate_controller = has_trusted_rate_controller_; info.is_hardware_accelerated = is_hardware_accelerated_; info.has_internal_source = has_internal_source_; info.fps_allocation[0] = fps_allocation_; info.supports_simulcast = supports_simulcast_; return info; } virtual ~MockVideoEncoder() { factory_->DestroyVideoEncoder(this); } const VideoCodec& codec() const { return codec_; } void SendEncodedImage(int width, int height) { // Sends a fake image of the given width/height. EncodedImage image; image._encodedWidth = width; image._encodedHeight = height; CodecSpecificInfo codec_specific_info; codec_specific_info.codecType = webrtc::kVideoCodecVP8; callback_->OnEncodedImage(image, &codec_specific_info); } void set_supports_native_handle(bool enabled) { supports_native_handle_ = enabled; } void set_implementation_name(const std::string& name) { implementation_name_ = name; } void set_init_encode_return_value(int32_t value) { init_encode_return_value_ = value; } void set_scaling_settings(const VideoEncoder::ScalingSettings& settings) { scaling_settings_ = settings; } void set_requested_resolution_alignment(int requested_resolution_alignment) { requested_resolution_alignment_ = requested_resolution_alignment; } void set_apply_alignment_to_all_simulcast_layers(bool apply) { apply_alignment_to_all_simulcast_layers_ = apply; } void set_has_trusted_rate_controller(bool trusted) { has_trusted_rate_controller_ = trusted; } void set_is_hardware_accelerated(bool is_hardware_accelerated) { is_hardware_accelerated_ = is_hardware_accelerated; } void set_has_internal_source(bool has_internal_source) { has_internal_source_ = has_internal_source; } void set_fps_allocation(const FramerateFractions& fps_allocation) { fps_allocation_ = fps_allocation; } RateControlParameters last_set_rates() const { return last_set_rates_; } void set_supports_simulcast(bool supports_simulcast) { supports_simulcast_ = supports_simulcast; } void set_video_format(const SdpVideoFormat& video_format) { video_format_ = video_format; } bool supports_simulcast() const { return supports_simulcast_; } SdpVideoFormat video_format() const { return video_format_; } private: MockVideoEncoderFactory* const factory_; bool supports_native_handle_ = false; std::string implementation_name_ = "unknown"; VideoEncoder::ScalingSettings scaling_settings_; int requested_resolution_alignment_ = 1; bool apply_alignment_to_all_simulcast_layers_ = false; bool has_trusted_rate_controller_ = false; bool is_hardware_accelerated_ = false; bool has_internal_source_ = false; int32_t init_encode_return_value_ = 0; VideoEncoder::RateControlParameters last_set_rates_; FramerateFractions fps_allocation_; bool supports_simulcast_ = false; SdpVideoFormat video_format_; VideoCodec codec_; EncodedImageCallback* callback_; }; std::vector MockVideoEncoderFactory::GetSupportedFormats() const { std::vector formats = {SdpVideoFormat("VP8")}; return formats; } std::unique_ptr MockVideoEncoderFactory::CreateVideoEncoder( const SdpVideoFormat& format) { auto encoder = std::make_unique<::testing::NiceMock>(this); encoder->set_init_encode_return_value(init_encode_return_value_); const char* encoder_name = encoder_names_.empty() ? "codec_implementation_name" : encoder_names_[encoders_.size()]; encoder->set_implementation_name(encoder_name); RTC_CHECK_LT(encoders_.size(), requested_resolution_alignments_.size()); encoder->set_requested_resolution_alignment( requested_resolution_alignments_[encoders_.size()]); encoder->set_supports_simulcast(supports_simulcast_); encoder->set_video_format(format); encoders_.push_back(encoder.get()); return encoder; } void MockVideoEncoderFactory::DestroyVideoEncoder(VideoEncoder* encoder) { for (size_t i = 0; i < encoders_.size(); ++i) { if (encoders_[i] == encoder) { encoders_.erase(encoders_.begin() + i); break; } } } const std::vector& MockVideoEncoderFactory::encoders() const { return encoders_; } void MockVideoEncoderFactory::SetEncoderNames( const std::vector& encoder_names) { encoder_names_ = encoder_names; } void MockVideoEncoderFactory::set_init_encode_return_value(int32_t value) { init_encode_return_value_ = value; } class TestSimulcastEncoderAdapterFakeHelper { public: explicit TestSimulcastEncoderAdapterFakeHelper( bool use_fallback_factory, const SdpVideoFormat& video_format) : primary_factory_(new MockVideoEncoderFactory()), fallback_factory_(use_fallback_factory ? new MockVideoEncoderFactory() : nullptr), video_format_(video_format) {} // Can only be called once as the SimulcastEncoderAdapter will take the // ownership of |factory_|. VideoEncoder* CreateMockEncoderAdapter() { return new SimulcastEncoderAdapter(primary_factory_.get(), fallback_factory_.get(), video_format_); } MockVideoEncoderFactory* factory() { return primary_factory_.get(); } MockVideoEncoderFactory* fallback_factory() { return fallback_factory_.get(); } private: std::unique_ptr primary_factory_; std::unique_ptr fallback_factory_; SdpVideoFormat video_format_; }; static const int kTestTemporalLayerProfile[3] = {3, 2, 1}; class TestSimulcastEncoderAdapterFake : public ::testing::Test, public EncodedImageCallback { public: TestSimulcastEncoderAdapterFake() : last_encoded_image_width_(-1), last_encoded_image_height_(-1), last_encoded_image_simulcast_index_(-1), use_fallback_factory_(false) {} virtual ~TestSimulcastEncoderAdapterFake() { if (adapter_) { adapter_->Release(); } } void SetUp() override { helper_.reset(new TestSimulcastEncoderAdapterFakeHelper( use_fallback_factory_, SdpVideoFormat("VP8", sdp_video_parameters_))); adapter_.reset(helper_->CreateMockEncoderAdapter()); last_encoded_image_width_ = -1; last_encoded_image_height_ = -1; last_encoded_image_simulcast_index_ = -1; } void ReSetUp() { if (adapter_) { adapter_->Release(); // |helper_| owns factories which |adapter_| needs to destroy encoders. // Release |adapter_| before |helper_| (released in SetUp()). adapter_.reset(); } SetUp(); } Result OnEncodedImage(const EncodedImage& encoded_image, const CodecSpecificInfo* codec_specific_info) override { last_encoded_image_width_ = encoded_image._encodedWidth; last_encoded_image_height_ = encoded_image._encodedHeight; last_encoded_image_simulcast_index_ = encoded_image.SpatialIndex().value_or(-1); return Result(Result::OK, encoded_image.Timestamp()); } bool GetLastEncodedImageInfo(int* out_width, int* out_height, int* out_simulcast_index) { if (last_encoded_image_width_ == -1) { return false; } *out_width = last_encoded_image_width_; *out_height = last_encoded_image_height_; *out_simulcast_index = last_encoded_image_simulcast_index_; return true; } void SetupCodec() { SetupCodec(/*active_streams=*/{true, true, true}); } void SetupCodec(std::vector active_streams) { SimulcastTestFixtureImpl::DefaultSettings( &codec_, static_cast(kTestTemporalLayerProfile), kVideoCodecVP8); ASSERT_LE(active_streams.size(), codec_.numberOfSimulcastStreams); codec_.numberOfSimulcastStreams = active_streams.size(); for (size_t stream_idx = 0; stream_idx < kMaxSimulcastStreams; ++stream_idx) { if (stream_idx >= codec_.numberOfSimulcastStreams) { // Reset parameters of unspecified stream. codec_.simulcastStream[stream_idx] = {0}; } else { codec_.simulcastStream[stream_idx].active = active_streams[stream_idx]; } } rate_allocator_.reset(new SimulcastRateAllocator(codec_)); EXPECT_EQ(0, adapter_->InitEncode(&codec_, kSettings)); adapter_->RegisterEncodeCompleteCallback(this); } void VerifyCodec(const VideoCodec& ref, int stream_index) { const VideoCodec& target = helper_->factory()->encoders()[stream_index]->codec(); EXPECT_EQ(ref.codecType, target.codecType); EXPECT_EQ(ref.width, target.width); EXPECT_EQ(ref.height, target.height); EXPECT_EQ(ref.startBitrate, target.startBitrate); EXPECT_EQ(ref.maxBitrate, target.maxBitrate); EXPECT_EQ(ref.minBitrate, target.minBitrate); EXPECT_EQ(ref.maxFramerate, target.maxFramerate); EXPECT_EQ(ref.VP8().complexity, target.VP8().complexity); EXPECT_EQ(ref.VP8().numberOfTemporalLayers, target.VP8().numberOfTemporalLayers); EXPECT_EQ(ref.VP8().denoisingOn, target.VP8().denoisingOn); EXPECT_EQ(ref.VP8().automaticResizeOn, target.VP8().automaticResizeOn); EXPECT_EQ(ref.VP8().frameDroppingOn, target.VP8().frameDroppingOn); EXPECT_EQ(ref.VP8().keyFrameInterval, target.VP8().keyFrameInterval); EXPECT_EQ(ref.qpMax, target.qpMax); EXPECT_EQ(0, target.numberOfSimulcastStreams); EXPECT_EQ(ref.mode, target.mode); // No need to compare simulcastStream as numberOfSimulcastStreams should // always be 0. } void InitRefCodec(int stream_index, VideoCodec* ref_codec, bool reverse_layer_order = false) { *ref_codec = codec_; ref_codec->VP8()->numberOfTemporalLayers = kTestTemporalLayerProfile[reverse_layer_order ? 2 - stream_index : stream_index]; ref_codec->width = codec_.simulcastStream[stream_index].width; ref_codec->height = codec_.simulcastStream[stream_index].height; ref_codec->maxBitrate = codec_.simulcastStream[stream_index].maxBitrate; ref_codec->minBitrate = codec_.simulcastStream[stream_index].minBitrate; ref_codec->qpMax = codec_.simulcastStream[stream_index].qpMax; } void VerifyCodecSettings() { EXPECT_EQ(3u, helper_->factory()->encoders().size()); VideoCodec ref_codec; // stream 0, the lowest resolution stream. InitRefCodec(0, &ref_codec); ref_codec.qpMax = 45; ref_codec.VP8()->complexity = webrtc::VideoCodecComplexity::kComplexityHigher; ref_codec.VP8()->denoisingOn = false; ref_codec.startBitrate = 100; // Should equal to the target bitrate. VerifyCodec(ref_codec, 0); // stream 1 InitRefCodec(1, &ref_codec); ref_codec.VP8()->denoisingOn = false; // The start bitrate (300kbit) minus what we have for the lower layers // (100kbit). ref_codec.startBitrate = 200; VerifyCodec(ref_codec, 1); // stream 2, the biggest resolution stream. InitRefCodec(2, &ref_codec); // We don't have enough bits to send this, so the adapter should have // configured it to use the min bitrate for this layer (600kbit) but turn // off sending. ref_codec.startBitrate = 600; VerifyCodec(ref_codec, 2); } protected: std::unique_ptr helper_; std::unique_ptr adapter_; VideoCodec codec_; int last_encoded_image_width_; int last_encoded_image_height_; int last_encoded_image_simulcast_index_; std::unique_ptr rate_allocator_; bool use_fallback_factory_; SdpVideoFormat::Parameters sdp_video_parameters_; }; TEST_F(TestSimulcastEncoderAdapterFake, InitEncode) { SetupCodec(); VerifyCodecSettings(); } TEST_F(TestSimulcastEncoderAdapterFake, ReleaseWithoutInitEncode) { EXPECT_EQ(0, adapter_->Release()); } TEST_F(TestSimulcastEncoderAdapterFake, Reinit) { SetupCodec(); EXPECT_EQ(0, adapter_->Release()); EXPECT_EQ(0, adapter_->InitEncode(&codec_, kSettings)); } TEST_F(TestSimulcastEncoderAdapterFake, EncodedCallbackForDifferentEncoders) { SetupCodec(); // Set bitrates so that we send all layers. adapter_->SetRates(VideoEncoder::RateControlParameters( rate_allocator_->Allocate(VideoBitrateAllocationParameters(1200, 30)), 30.0)); // At this point, the simulcast encoder adapter should have 3 streams: HD, // quarter HD, and quarter quarter HD. We're going to mostly ignore the exact // resolutions, to test that the adapter forwards on the correct resolution // and simulcast index values, going only off the encoder that generates the // image. std::vector encoders = helper_->factory()->encoders(); ASSERT_EQ(3u, encoders.size()); encoders[0]->SendEncodedImage(1152, 704); int width; int height; int simulcast_index; EXPECT_TRUE(GetLastEncodedImageInfo(&width, &height, &simulcast_index)); EXPECT_EQ(1152, width); EXPECT_EQ(704, height); // SEA doesn't intercept frame encode complete callback for the lowest stream. EXPECT_EQ(-1, simulcast_index); encoders[1]->SendEncodedImage(300, 620); EXPECT_TRUE(GetLastEncodedImageInfo(&width, &height, &simulcast_index)); EXPECT_EQ(300, width); EXPECT_EQ(620, height); EXPECT_EQ(1, simulcast_index); encoders[2]->SendEncodedImage(120, 240); EXPECT_TRUE(GetLastEncodedImageInfo(&width, &height, &simulcast_index)); EXPECT_EQ(120, width); EXPECT_EQ(240, height); EXPECT_EQ(2, simulcast_index); } // This test verifies that the underlying encoders are reused, when the adapter // is reinited with different number of simulcast streams. It further checks // that the allocated encoders are reused in the same order as before, starting // with the lowest stream. TEST_F(TestSimulcastEncoderAdapterFake, ReusesEncodersInOrder) { // Set up common settings for three streams. SimulcastTestFixtureImpl::DefaultSettings( &codec_, static_cast(kTestTemporalLayerProfile), kVideoCodecVP8); rate_allocator_.reset(new SimulcastRateAllocator(codec_)); adapter_->RegisterEncodeCompleteCallback(this); const uint32_t target_bitrate = 1000 * (codec_.simulcastStream[0].targetBitrate + codec_.simulcastStream[1].targetBitrate + codec_.simulcastStream[2].minBitrate); // Input data. rtc::scoped_refptr buffer(I420Buffer::Create(1280, 720)); VideoFrame input_frame = VideoFrame::Builder() .set_video_frame_buffer(buffer) .set_timestamp_rtp(100) .set_timestamp_ms(1000) .set_rotation(kVideoRotation_180) .build(); std::vector frame_types; // Encode with three streams. EXPECT_EQ(0, adapter_->InitEncode(&codec_, kSettings)); VerifyCodecSettings(); adapter_->SetRates(VideoEncoder::RateControlParameters( rate_allocator_->Allocate( VideoBitrateAllocationParameters(target_bitrate, 30)), 30.0)); std::vector original_encoders = helper_->factory()->encoders(); ASSERT_EQ(3u, original_encoders.size()); EXPECT_CALL(*original_encoders[0], Encode(_, _)) .WillOnce(Return(WEBRTC_VIDEO_CODEC_OK)); EXPECT_CALL(*original_encoders[1], Encode(_, _)) .WillOnce(Return(WEBRTC_VIDEO_CODEC_OK)); EXPECT_CALL(*original_encoders[2], Encode(_, _)) .WillOnce(Return(WEBRTC_VIDEO_CODEC_OK)); frame_types.resize(3, VideoFrameType::kVideoFrameKey); EXPECT_EQ(0, adapter_->Encode(input_frame, &frame_types)); EXPECT_CALL(*original_encoders[0], Release()) .WillOnce(Return(WEBRTC_VIDEO_CODEC_OK)); EXPECT_CALL(*original_encoders[1], Release()) .WillOnce(Return(WEBRTC_VIDEO_CODEC_OK)); EXPECT_CALL(*original_encoders[2], Release()) .WillOnce(Return(WEBRTC_VIDEO_CODEC_OK)); EXPECT_EQ(0, adapter_->Release()); // Encode with two streams. codec_.width /= 2; codec_.height /= 2; codec_.numberOfSimulcastStreams = 2; EXPECT_EQ(0, adapter_->InitEncode(&codec_, kSettings)); adapter_->SetRates(VideoEncoder::RateControlParameters( rate_allocator_->Allocate( VideoBitrateAllocationParameters(target_bitrate, 30)), 30.0)); std::vector new_encoders = helper_->factory()->encoders(); ASSERT_EQ(2u, new_encoders.size()); ASSERT_EQ(original_encoders[0], new_encoders[0]); EXPECT_CALL(*original_encoders[0], Encode(_, _)) .WillOnce(Return(WEBRTC_VIDEO_CODEC_OK)); ASSERT_EQ(original_encoders[1], new_encoders[1]); EXPECT_CALL(*original_encoders[1], Encode(_, _)) .WillOnce(Return(WEBRTC_VIDEO_CODEC_OK)); frame_types.resize(2, VideoFrameType::kVideoFrameKey); EXPECT_EQ(0, adapter_->Encode(input_frame, &frame_types)); EXPECT_CALL(*original_encoders[0], Release()) .WillOnce(Return(WEBRTC_VIDEO_CODEC_OK)); EXPECT_CALL(*original_encoders[1], Release()) .WillOnce(Return(WEBRTC_VIDEO_CODEC_OK)); EXPECT_EQ(0, adapter_->Release()); // Encode with single stream. codec_.width /= 2; codec_.height /= 2; codec_.numberOfSimulcastStreams = 1; EXPECT_EQ(0, adapter_->InitEncode(&codec_, kSettings)); adapter_->SetRates(VideoEncoder::RateControlParameters( rate_allocator_->Allocate( VideoBitrateAllocationParameters(target_bitrate, 30)), 30.0)); new_encoders = helper_->factory()->encoders(); ASSERT_EQ(1u, new_encoders.size()); ASSERT_EQ(original_encoders[0], new_encoders[0]); EXPECT_CALL(*original_encoders[0], Encode(_, _)) .WillOnce(Return(WEBRTC_VIDEO_CODEC_OK)); frame_types.resize(1, VideoFrameType::kVideoFrameKey); EXPECT_EQ(0, adapter_->Encode(input_frame, &frame_types)); EXPECT_CALL(*original_encoders[0], Release()) .WillOnce(Return(WEBRTC_VIDEO_CODEC_OK)); EXPECT_EQ(0, adapter_->Release()); // Encode with three streams, again. codec_.width *= 4; codec_.height *= 4; codec_.numberOfSimulcastStreams = 3; EXPECT_EQ(0, adapter_->InitEncode(&codec_, kSettings)); adapter_->SetRates(VideoEncoder::RateControlParameters( rate_allocator_->Allocate( VideoBitrateAllocationParameters(target_bitrate, 30)), 30.0)); new_encoders = helper_->factory()->encoders(); ASSERT_EQ(3u, new_encoders.size()); // The first encoder is reused. ASSERT_EQ(original_encoders[0], new_encoders[0]); EXPECT_CALL(*original_encoders[0], Encode(_, _)) .WillOnce(Return(WEBRTC_VIDEO_CODEC_OK)); // The second and third encoders are new. EXPECT_CALL(*new_encoders[1], Encode(_, _)) .WillOnce(Return(WEBRTC_VIDEO_CODEC_OK)); EXPECT_CALL(*new_encoders[2], Encode(_, _)) .WillOnce(Return(WEBRTC_VIDEO_CODEC_OK)); frame_types.resize(3, VideoFrameType::kVideoFrameKey); EXPECT_EQ(0, adapter_->Encode(input_frame, &frame_types)); EXPECT_CALL(*original_encoders[0], Release()) .WillOnce(Return(WEBRTC_VIDEO_CODEC_OK)); EXPECT_CALL(*new_encoders[1], Release()) .WillOnce(Return(WEBRTC_VIDEO_CODEC_OK)); EXPECT_CALL(*new_encoders[2], Release()) .WillOnce(Return(WEBRTC_VIDEO_CODEC_OK)); EXPECT_EQ(0, adapter_->Release()); } TEST_F(TestSimulcastEncoderAdapterFake, DoesNotLeakEncoders) { SetupCodec(); VerifyCodecSettings(); EXPECT_EQ(3u, helper_->factory()->encoders().size()); // The adapter should destroy all encoders it has allocated. Since // |helper_->factory()| is owned by |adapter_|, however, we need to rely on // lsan to find leaks here. EXPECT_EQ(0, adapter_->Release()); adapter_.reset(); } // This test verifies that an adapter reinit with the same codec settings as // before does not change the underlying encoder codec settings. TEST_F(TestSimulcastEncoderAdapterFake, ReinitDoesNotReorderEncoderSettings) { SetupCodec(); VerifyCodecSettings(); // Capture current codec settings. std::vector encoders = helper_->factory()->encoders(); ASSERT_EQ(3u, encoders.size()); std::array codecs_before; for (int i = 0; i < 3; ++i) { codecs_before[i] = encoders[i]->codec(); } // Reinitialize and verify that the new codec settings are the same. EXPECT_EQ(0, adapter_->Release()); EXPECT_EQ(0, adapter_->InitEncode(&codec_, kSettings)); for (int i = 0; i < 3; ++i) { const VideoCodec& codec_before = codecs_before[i]; const VideoCodec& codec_after = encoders[i]->codec(); // webrtc::VideoCodec does not implement operator==. EXPECT_EQ(codec_before.codecType, codec_after.codecType); EXPECT_EQ(codec_before.width, codec_after.width); EXPECT_EQ(codec_before.height, codec_after.height); EXPECT_EQ(codec_before.startBitrate, codec_after.startBitrate); EXPECT_EQ(codec_before.maxBitrate, codec_after.maxBitrate); EXPECT_EQ(codec_before.minBitrate, codec_after.minBitrate); EXPECT_EQ(codec_before.maxFramerate, codec_after.maxFramerate); EXPECT_EQ(codec_before.qpMax, codec_after.qpMax); EXPECT_EQ(codec_before.numberOfSimulcastStreams, codec_after.numberOfSimulcastStreams); EXPECT_EQ(codec_before.mode, codec_after.mode); EXPECT_EQ(codec_before.expect_encode_from_texture, codec_after.expect_encode_from_texture); } } // This test is similar to the one above, except that it tests the simulcastIdx // from the CodecSpecificInfo that is connected to an encoded frame. The // PayloadRouter demuxes the incoming encoded frames on different RTP modules // using the simulcastIdx, so it's important that there is no corresponding // encoder reordering in between adapter reinits as this would lead to PictureID // discontinuities. TEST_F(TestSimulcastEncoderAdapterFake, ReinitDoesNotReorderFrameSimulcastIdx) { SetupCodec(); adapter_->SetRates(VideoEncoder::RateControlParameters( rate_allocator_->Allocate(VideoBitrateAllocationParameters(1200, 30)), 30.0)); VerifyCodecSettings(); // Send frames on all streams. std::vector encoders = helper_->factory()->encoders(); ASSERT_EQ(3u, encoders.size()); encoders[0]->SendEncodedImage(1152, 704); int width; int height; int simulcast_index; EXPECT_TRUE(GetLastEncodedImageInfo(&width, &height, &simulcast_index)); // SEA doesn't intercept frame encode complete callback for the lowest stream. EXPECT_EQ(-1, simulcast_index); encoders[1]->SendEncodedImage(300, 620); EXPECT_TRUE(GetLastEncodedImageInfo(&width, &height, &simulcast_index)); EXPECT_EQ(1, simulcast_index); encoders[2]->SendEncodedImage(120, 240); EXPECT_TRUE(GetLastEncodedImageInfo(&width, &height, &simulcast_index)); EXPECT_EQ(2, simulcast_index); // Reinitialize. EXPECT_EQ(0, adapter_->Release()); EXPECT_EQ(0, adapter_->InitEncode(&codec_, kSettings)); adapter_->SetRates(VideoEncoder::RateControlParameters( rate_allocator_->Allocate(VideoBitrateAllocationParameters(1200, 30)), 30.0)); // Verify that the same encoder sends out frames on the same simulcast index. encoders[0]->SendEncodedImage(1152, 704); EXPECT_TRUE(GetLastEncodedImageInfo(&width, &height, &simulcast_index)); EXPECT_EQ(-1, simulcast_index); encoders[1]->SendEncodedImage(300, 620); EXPECT_TRUE(GetLastEncodedImageInfo(&width, &height, &simulcast_index)); EXPECT_EQ(1, simulcast_index); encoders[2]->SendEncodedImage(120, 240); EXPECT_TRUE(GetLastEncodedImageInfo(&width, &height, &simulcast_index)); EXPECT_EQ(2, simulcast_index); } TEST_F(TestSimulcastEncoderAdapterFake, SupportsNativeHandleForSingleStreams) { SimulcastTestFixtureImpl::DefaultSettings( &codec_, static_cast(kTestTemporalLayerProfile), kVideoCodecVP8); codec_.numberOfSimulcastStreams = 1; EXPECT_EQ(0, adapter_->InitEncode(&codec_, kSettings)); adapter_->RegisterEncodeCompleteCallback(this); ASSERT_EQ(1u, helper_->factory()->encoders().size()); helper_->factory()->encoders()[0]->set_supports_native_handle(true); EXPECT_EQ(0, adapter_->InitEncode(&codec_, kSettings)); EXPECT_TRUE(adapter_->GetEncoderInfo().supports_native_handle); helper_->factory()->encoders()[0]->set_supports_native_handle(false); EXPECT_EQ(0, adapter_->InitEncode(&codec_, kSettings)); EXPECT_FALSE(adapter_->GetEncoderInfo().supports_native_handle); } TEST_F(TestSimulcastEncoderAdapterFake, SetRatesUnderMinBitrate) { SimulcastTestFixtureImpl::DefaultSettings( &codec_, static_cast(kTestTemporalLayerProfile), kVideoCodecVP8); codec_.minBitrate = 50; codec_.numberOfSimulcastStreams = 1; EXPECT_EQ(0, adapter_->InitEncode(&codec_, kSettings)); rate_allocator_.reset(new SimulcastRateAllocator(codec_)); // Above min should be respected. VideoBitrateAllocation target_bitrate = rate_allocator_->Allocate( VideoBitrateAllocationParameters(codec_.minBitrate * 1000, 30)); adapter_->SetRates(VideoEncoder::RateControlParameters(target_bitrate, 30.0)); EXPECT_EQ(target_bitrate, helper_->factory()->encoders()[0]->last_set_rates().bitrate); // Below min but non-zero should be replaced with the min bitrate. VideoBitrateAllocation too_low_bitrate = rate_allocator_->Allocate( VideoBitrateAllocationParameters((codec_.minBitrate - 1) * 1000, 30)); adapter_->SetRates( VideoEncoder::RateControlParameters(too_low_bitrate, 30.0)); EXPECT_EQ(target_bitrate, helper_->factory()->encoders()[0]->last_set_rates().bitrate); // Zero should be passed on as is, since it means "pause". adapter_->SetRates( VideoEncoder::RateControlParameters(VideoBitrateAllocation(), 30.0)); EXPECT_EQ(VideoBitrateAllocation(), helper_->factory()->encoders()[0]->last_set_rates().bitrate); } TEST_F(TestSimulcastEncoderAdapterFake, SupportsImplementationName) { EXPECT_EQ("SimulcastEncoderAdapter", adapter_->GetEncoderInfo().implementation_name); SimulcastTestFixtureImpl::DefaultSettings( &codec_, static_cast(kTestTemporalLayerProfile), kVideoCodecVP8); std::vector encoder_names; encoder_names.push_back("codec1"); encoder_names.push_back("codec2"); encoder_names.push_back("codec3"); helper_->factory()->SetEncoderNames(encoder_names); EXPECT_EQ(0, adapter_->InitEncode(&codec_, kSettings)); EXPECT_EQ("SimulcastEncoderAdapter (codec1, codec2, codec3)", adapter_->GetEncoderInfo().implementation_name); // Single streams should not expose "SimulcastEncoderAdapter" in name. EXPECT_EQ(0, adapter_->Release()); codec_.numberOfSimulcastStreams = 1; EXPECT_EQ(0, adapter_->InitEncode(&codec_, kSettings)); adapter_->RegisterEncodeCompleteCallback(this); ASSERT_EQ(1u, helper_->factory()->encoders().size()); EXPECT_EQ("codec1", adapter_->GetEncoderInfo().implementation_name); } TEST_F(TestSimulcastEncoderAdapterFake, RuntimeEncoderInfoUpdate) { SimulcastTestFixtureImpl::DefaultSettings( &codec_, static_cast(kTestTemporalLayerProfile), kVideoCodecVP8); std::vector encoder_names; encoder_names.push_back("codec1"); encoder_names.push_back("codec2"); encoder_names.push_back("codec3"); helper_->factory()->SetEncoderNames(encoder_names); EXPECT_EQ(0, adapter_->InitEncode(&codec_, kSettings)); EXPECT_EQ("SimulcastEncoderAdapter (codec1, codec2, codec3)", adapter_->GetEncoderInfo().implementation_name); // Change name of first encoder to indicate it has done a fallback to another // implementation. helper_->factory()->encoders().front()->set_implementation_name("fallback1"); EXPECT_EQ("SimulcastEncoderAdapter (fallback1, codec2, codec3)", adapter_->GetEncoderInfo().implementation_name); } TEST_F(TestSimulcastEncoderAdapterFake, SupportsNativeHandleForMultipleStreams) { SimulcastTestFixtureImpl::DefaultSettings( &codec_, static_cast(kTestTemporalLayerProfile), kVideoCodecVP8); codec_.numberOfSimulcastStreams = 3; EXPECT_EQ(0, adapter_->InitEncode(&codec_, kSettings)); adapter_->RegisterEncodeCompleteCallback(this); ASSERT_EQ(3u, helper_->factory()->encoders().size()); for (MockVideoEncoder* encoder : helper_->factory()->encoders()) encoder->set_supports_native_handle(true); // As long as one encoder supports native handle, it's enabled. helper_->factory()->encoders()[0]->set_supports_native_handle(false); EXPECT_TRUE(adapter_->GetEncoderInfo().supports_native_handle); // Once none do, then the adapter claims no support. helper_->factory()->encoders()[1]->set_supports_native_handle(false); helper_->factory()->encoders()[2]->set_supports_native_handle(false); EXPECT_EQ(0, adapter_->InitEncode(&codec_, kSettings)); EXPECT_FALSE(adapter_->GetEncoderInfo().supports_native_handle); } // TODO(nisse): Reuse definition in webrtc/test/fake_texture_handle.h. class FakeNativeBufferI420 : public VideoFrameBuffer { public: FakeNativeBufferI420(int width, int height, bool allow_to_i420) : width_(width), height_(height), allow_to_i420_(allow_to_i420) {} Type type() const override { return Type::kNative; } int width() const override { return width_; } int height() const override { return height_; } rtc::scoped_refptr ToI420() override { if (allow_to_i420_) { return I420Buffer::Create(width_, height_); } else { RTC_NOTREACHED(); } return nullptr; } private: const int width_; const int height_; const bool allow_to_i420_; }; TEST_F(TestSimulcastEncoderAdapterFake, NativeHandleForwardingForMultipleStreams) { SimulcastTestFixtureImpl::DefaultSettings( &codec_, static_cast(kTestTemporalLayerProfile), kVideoCodecVP8); codec_.numberOfSimulcastStreams = 3; // High start bitrate, so all streams are enabled. codec_.startBitrate = 3000; EXPECT_EQ(0, adapter_->InitEncode(&codec_, kSettings)); adapter_->RegisterEncodeCompleteCallback(this); ASSERT_EQ(3u, helper_->factory()->encoders().size()); for (MockVideoEncoder* encoder : helper_->factory()->encoders()) encoder->set_supports_native_handle(true); EXPECT_EQ(0, adapter_->InitEncode(&codec_, kSettings)); EXPECT_TRUE(adapter_->GetEncoderInfo().supports_native_handle); rtc::scoped_refptr buffer( rtc::make_ref_counted(1280, 720, /*allow_to_i420=*/false)); VideoFrame input_frame = VideoFrame::Builder() .set_video_frame_buffer(buffer) .set_timestamp_rtp(100) .set_timestamp_ms(1000) .set_rotation(kVideoRotation_180) .build(); // Expect calls with the given video frame verbatim, since it's a texture // frame and can't otherwise be modified/resized. for (MockVideoEncoder* encoder : helper_->factory()->encoders()) EXPECT_CALL(*encoder, Encode(::testing::Ref(input_frame), _)).Times(1); std::vector frame_types(3, VideoFrameType::kVideoFrameKey); EXPECT_EQ(0, adapter_->Encode(input_frame, &frame_types)); } TEST_F(TestSimulcastEncoderAdapterFake, NativeHandleForwardingOnlyIfSupported) { SimulcastTestFixtureImpl::DefaultSettings( &codec_, static_cast(kTestTemporalLayerProfile), kVideoCodecVP8); codec_.numberOfSimulcastStreams = 3; // High start bitrate, so all streams are enabled. codec_.startBitrate = 3000; EXPECT_EQ(0, adapter_->InitEncode(&codec_, kSettings)); adapter_->RegisterEncodeCompleteCallback(this); ASSERT_EQ(3u, helper_->factory()->encoders().size()); // QVGA encoders has fallen back to software. auto& encoders = helper_->factory()->encoders(); encoders[0]->set_supports_native_handle(false); encoders[1]->set_supports_native_handle(true); encoders[2]->set_supports_native_handle(true); EXPECT_EQ(0, adapter_->InitEncode(&codec_, kSettings)); EXPECT_TRUE(adapter_->GetEncoderInfo().supports_native_handle); rtc::scoped_refptr buffer( rtc::make_ref_counted(1280, 720, /*allow_to_i420=*/true)); VideoFrame input_frame = VideoFrame::Builder() .set_video_frame_buffer(buffer) .set_timestamp_rtp(100) .set_timestamp_ms(1000) .set_rotation(kVideoRotation_180) .build(); // Expect calls with the given video frame verbatim, since it's a texture // frame and can't otherwise be modified/resized, but only on the two // streams supporting it... EXPECT_CALL(*encoders[1], Encode(::testing::Ref(input_frame), _)).Times(1); EXPECT_CALL(*encoders[2], Encode(::testing::Ref(input_frame), _)).Times(1); // ...the lowest one gets a software buffer. EXPECT_CALL(*encoders[0], Encode) .WillOnce([&](const VideoFrame& frame, const std::vector* frame_types) { EXPECT_EQ(frame.video_frame_buffer()->type(), VideoFrameBuffer::Type::kI420); return 0; }); std::vector frame_types(3, VideoFrameType::kVideoFrameKey); EXPECT_EQ(0, adapter_->Encode(input_frame, &frame_types)); } TEST_F(TestSimulcastEncoderAdapterFake, TestFailureReturnCodesFromEncodeCalls) { SimulcastTestFixtureImpl::DefaultSettings( &codec_, static_cast(kTestTemporalLayerProfile), kVideoCodecVP8); codec_.numberOfSimulcastStreams = 3; EXPECT_EQ(0, adapter_->InitEncode(&codec_, kSettings)); adapter_->RegisterEncodeCompleteCallback(this); ASSERT_EQ(3u, helper_->factory()->encoders().size()); // Tell the 2nd encoder to request software fallback. EXPECT_CALL(*helper_->factory()->encoders()[1], Encode(_, _)) .WillOnce(Return(WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE)); // Send a fake frame and assert the return is software fallback. rtc::scoped_refptr input_buffer = I420Buffer::Create(kDefaultWidth, kDefaultHeight); input_buffer->InitializeData(); VideoFrame input_frame = VideoFrame::Builder() .set_video_frame_buffer(input_buffer) .set_timestamp_rtp(0) .set_timestamp_us(0) .set_rotation(kVideoRotation_0) .build(); std::vector frame_types(3, VideoFrameType::kVideoFrameKey); EXPECT_EQ(WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE, adapter_->Encode(input_frame, &frame_types)); } TEST_F(TestSimulcastEncoderAdapterFake, TestInitFailureCleansUpEncoders) { SimulcastTestFixtureImpl::DefaultSettings( &codec_, static_cast(kTestTemporalLayerProfile), kVideoCodecVP8); codec_.numberOfSimulcastStreams = 3; helper_->factory()->set_init_encode_return_value( WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE); EXPECT_EQ(WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE, adapter_->InitEncode(&codec_, kSettings)); EXPECT_TRUE(helper_->factory()->encoders().empty()); } TEST_F(TestSimulcastEncoderAdapterFake, DoesNotAlterMaxQpForScreenshare) { const int kHighMaxQp = 56; const int kLowMaxQp = 46; SimulcastTestFixtureImpl::DefaultSettings( &codec_, static_cast(kTestTemporalLayerProfile), kVideoCodecVP8); codec_.numberOfSimulcastStreams = 3; codec_.simulcastStream[0].qpMax = kHighMaxQp; codec_.mode = VideoCodecMode::kScreensharing; EXPECT_EQ(0, adapter_->InitEncode(&codec_, kSettings)); EXPECT_EQ(3u, helper_->factory()->encoders().size()); // Just check the lowest stream, which is the one that where the adapter // might alter the max qp setting. VideoCodec ref_codec; InitRefCodec(0, &ref_codec); ref_codec.qpMax = kHighMaxQp; ref_codec.VP8()->complexity = webrtc::VideoCodecComplexity::kComplexityHigher; ref_codec.VP8()->denoisingOn = false; ref_codec.startBitrate = 100; // Should equal to the target bitrate. VerifyCodec(ref_codec, 0); // Change the max qp and try again. codec_.simulcastStream[0].qpMax = kLowMaxQp; EXPECT_EQ(0, adapter_->InitEncode(&codec_, kSettings)); EXPECT_EQ(3u, helper_->factory()->encoders().size()); ref_codec.qpMax = kLowMaxQp; VerifyCodec(ref_codec, 0); } TEST_F(TestSimulcastEncoderAdapterFake, DoesNotAlterMaxQpForScreenshareReversedLayer) { const int kHighMaxQp = 56; const int kLowMaxQp = 46; SimulcastTestFixtureImpl::DefaultSettings( &codec_, static_cast(kTestTemporalLayerProfile), kVideoCodecVP8, true /* reverse_layer_order */); codec_.numberOfSimulcastStreams = 3; codec_.simulcastStream[2].qpMax = kHighMaxQp; codec_.mode = VideoCodecMode::kScreensharing; EXPECT_EQ(0, adapter_->InitEncode(&codec_, kSettings)); EXPECT_EQ(3u, helper_->factory()->encoders().size()); // Just check the lowest stream, which is the one that where the adapter // might alter the max qp setting. VideoCodec ref_codec; InitRefCodec(2, &ref_codec, true /* reverse_layer_order */); ref_codec.qpMax = kHighMaxQp; ref_codec.VP8()->complexity = webrtc::VideoCodecComplexity::kComplexityHigher; ref_codec.VP8()->denoisingOn = false; ref_codec.startBitrate = 100; // Should equal to the target bitrate. VerifyCodec(ref_codec, 2); // Change the max qp and try again. codec_.simulcastStream[2].qpMax = kLowMaxQp; EXPECT_EQ(0, adapter_->InitEncode(&codec_, kSettings)); EXPECT_EQ(3u, helper_->factory()->encoders().size()); ref_codec.qpMax = kLowMaxQp; VerifyCodec(ref_codec, 2); } TEST_F(TestSimulcastEncoderAdapterFake, ActivatesCorrectStreamsInInitEncode) { // Set up common settings for three streams. SimulcastTestFixtureImpl::DefaultSettings( &codec_, static_cast(kTestTemporalLayerProfile), kVideoCodecVP8); rate_allocator_.reset(new SimulcastRateAllocator(codec_)); adapter_->RegisterEncodeCompleteCallback(this); // Only enough start bitrate for the lowest stream. ASSERT_EQ(3u, codec_.numberOfSimulcastStreams); codec_.startBitrate = codec_.simulcastStream[0].targetBitrate + codec_.simulcastStream[1].minBitrate - 1; // Input data. rtc::scoped_refptr buffer(I420Buffer::Create(1280, 720)); VideoFrame input_frame = VideoFrame::Builder() .set_video_frame_buffer(buffer) .set_timestamp_rtp(100) .set_timestamp_ms(1000) .set_rotation(kVideoRotation_180) .build(); // Encode with three streams. EXPECT_EQ(0, adapter_->InitEncode(&codec_, kSettings)); std::vector original_encoders = helper_->factory()->encoders(); ASSERT_EQ(3u, original_encoders.size()); // Only first encoder will be active and called. EXPECT_CALL(*original_encoders[0], Encode(_, _)) .WillOnce(Return(WEBRTC_VIDEO_CODEC_OK)); EXPECT_CALL(*original_encoders[1], Encode(_, _)).Times(0); EXPECT_CALL(*original_encoders[2], Encode(_, _)).Times(0); std::vector frame_types; frame_types.resize(3, VideoFrameType::kVideoFrameKey); EXPECT_EQ(0, adapter_->Encode(input_frame, &frame_types)); } TEST_F(TestSimulcastEncoderAdapterFake, TrustedRateControl) { // Set up common settings for three streams. SimulcastTestFixtureImpl::DefaultSettings( &codec_, static_cast(kTestTemporalLayerProfile), kVideoCodecVP8); rate_allocator_.reset(new SimulcastRateAllocator(codec_)); adapter_->RegisterEncodeCompleteCallback(this); // Only enough start bitrate for the lowest stream. ASSERT_EQ(3u, codec_.numberOfSimulcastStreams); codec_.startBitrate = codec_.simulcastStream[0].targetBitrate + codec_.simulcastStream[1].minBitrate - 1; // Input data. rtc::scoped_refptr buffer(I420Buffer::Create(1280, 720)); VideoFrame input_frame = VideoFrame::Builder() .set_video_frame_buffer(buffer) .set_timestamp_rtp(100) .set_timestamp_ms(1000) .set_rotation(kVideoRotation_180) .build(); // No encoder trusted, so simulcast adapter should not be either. EXPECT_EQ(0, adapter_->InitEncode(&codec_, kSettings)); EXPECT_FALSE(adapter_->GetEncoderInfo().has_trusted_rate_controller); // Encode with three streams. std::vector original_encoders = helper_->factory()->encoders(); // All encoders are trusted, so simulcast adapter should be too. original_encoders[0]->set_has_trusted_rate_controller(true); original_encoders[1]->set_has_trusted_rate_controller(true); original_encoders[2]->set_has_trusted_rate_controller(true); EXPECT_EQ(0, adapter_->InitEncode(&codec_, kSettings)); EXPECT_TRUE(adapter_->GetEncoderInfo().has_trusted_rate_controller); // One encoder not trusted, so simulcast adapter should not be either. original_encoders[2]->set_has_trusted_rate_controller(false); EXPECT_EQ(0, adapter_->InitEncode(&codec_, kSettings)); EXPECT_FALSE(adapter_->GetEncoderInfo().has_trusted_rate_controller); // No encoder trusted, so simulcast adapter should not be either. original_encoders[0]->set_has_trusted_rate_controller(false); original_encoders[1]->set_has_trusted_rate_controller(false); EXPECT_EQ(0, adapter_->InitEncode(&codec_, kSettings)); EXPECT_FALSE(adapter_->GetEncoderInfo().has_trusted_rate_controller); } TEST_F(TestSimulcastEncoderAdapterFake, ReportsHardwareAccelerated) { SimulcastTestFixtureImpl::DefaultSettings( &codec_, static_cast(kTestTemporalLayerProfile), kVideoCodecVP8); codec_.numberOfSimulcastStreams = 3; adapter_->RegisterEncodeCompleteCallback(this); EXPECT_EQ(0, adapter_->InitEncode(&codec_, kSettings)); ASSERT_EQ(3u, helper_->factory()->encoders().size()); // None of the encoders uses HW support, so simulcast adapter reports false. for (MockVideoEncoder* encoder : helper_->factory()->encoders()) { encoder->set_is_hardware_accelerated(false); } EXPECT_EQ(0, adapter_->InitEncode(&codec_, kSettings)); EXPECT_FALSE(adapter_->GetEncoderInfo().is_hardware_accelerated); // One encoder uses HW support, so simulcast adapter reports true. helper_->factory()->encoders()[2]->set_is_hardware_accelerated(true); EXPECT_EQ(0, adapter_->InitEncode(&codec_, kSettings)); EXPECT_TRUE(adapter_->GetEncoderInfo().is_hardware_accelerated); } TEST_F(TestSimulcastEncoderAdapterFake, ReportsLeastCommonMultipleOfRequestedResolutionAlignments) { SimulcastTestFixtureImpl::DefaultSettings( &codec_, static_cast(kTestTemporalLayerProfile), kVideoCodecVP8); codec_.numberOfSimulcastStreams = 3; helper_->factory()->set_requested_resolution_alignments({2, 4, 7}); EXPECT_EQ(0, adapter_->InitEncode(&codec_, kSettings)); EXPECT_EQ(adapter_->GetEncoderInfo().requested_resolution_alignment, 28); } TEST_F(TestSimulcastEncoderAdapterFake, ReportsApplyAlignmentToSimulcastLayers) { SimulcastTestFixtureImpl::DefaultSettings( &codec_, static_cast(kTestTemporalLayerProfile), kVideoCodecVP8); codec_.numberOfSimulcastStreams = 3; // No encoder has apply_alignment_to_all_simulcast_layers, report false. EXPECT_EQ(0, adapter_->InitEncode(&codec_, kSettings)); ASSERT_EQ(3u, helper_->factory()->encoders().size()); for (MockVideoEncoder* encoder : helper_->factory()->encoders()) { encoder->set_apply_alignment_to_all_simulcast_layers(false); } EXPECT_FALSE( adapter_->GetEncoderInfo().apply_alignment_to_all_simulcast_layers); // One encoder has apply_alignment_to_all_simulcast_layers, report true. helper_->factory() ->encoders()[1] ->set_apply_alignment_to_all_simulcast_layers(true); EXPECT_EQ(0, adapter_->InitEncode(&codec_, kSettings)); EXPECT_TRUE( adapter_->GetEncoderInfo().apply_alignment_to_all_simulcast_layers); } TEST_F(TestSimulcastEncoderAdapterFake, EncoderInfoFromFieldTrial) { test::ScopedFieldTrials field_trials( "WebRTC-SimulcastEncoderAdapter-GetEncoderInfoOverride/" "requested_resolution_alignment:8," "apply_alignment_to_all_simulcast_layers/"); SetUp(); SimulcastTestFixtureImpl::DefaultSettings( &codec_, static_cast(kTestTemporalLayerProfile), kVideoCodecVP8); codec_.numberOfSimulcastStreams = 3; EXPECT_EQ(0, adapter_->InitEncode(&codec_, kSettings)); ASSERT_EQ(3u, helper_->factory()->encoders().size()); EXPECT_EQ(8, adapter_->GetEncoderInfo().requested_resolution_alignment); EXPECT_TRUE( adapter_->GetEncoderInfo().apply_alignment_to_all_simulcast_layers); EXPECT_TRUE(adapter_->GetEncoderInfo().resolution_bitrate_limits.empty()); } TEST_F(TestSimulcastEncoderAdapterFake, EncoderInfoFromFieldTrialForSingleStream) { test::ScopedFieldTrials field_trials( "WebRTC-SimulcastEncoderAdapter-GetEncoderInfoOverride/" "requested_resolution_alignment:9," "frame_size_pixels:123|456|789," "min_start_bitrate_bps:11000|22000|33000," "min_bitrate_bps:44000|55000|66000," "max_bitrate_bps:77000|88000|99000/"); SetUp(); SimulcastTestFixtureImpl::DefaultSettings( &codec_, static_cast(kTestTemporalLayerProfile), kVideoCodecVP8); codec_.numberOfSimulcastStreams = 1; EXPECT_EQ(0, adapter_->InitEncode(&codec_, kSettings)); ASSERT_EQ(1u, helper_->factory()->encoders().size()); EXPECT_EQ(9, adapter_->GetEncoderInfo().requested_resolution_alignment); EXPECT_FALSE( adapter_->GetEncoderInfo().apply_alignment_to_all_simulcast_layers); EXPECT_THAT( adapter_->GetEncoderInfo().resolution_bitrate_limits, ::testing::ElementsAre( VideoEncoder::ResolutionBitrateLimits{123, 11000, 44000, 77000}, VideoEncoder::ResolutionBitrateLimits{456, 22000, 55000, 88000}, VideoEncoder::ResolutionBitrateLimits{789, 33000, 66000, 99000})); } TEST_F(TestSimulcastEncoderAdapterFake, ReportsInternalSource) { SimulcastTestFixtureImpl::DefaultSettings( &codec_, static_cast(kTestTemporalLayerProfile), kVideoCodecVP8); codec_.numberOfSimulcastStreams = 3; adapter_->RegisterEncodeCompleteCallback(this); EXPECT_EQ(0, adapter_->InitEncode(&codec_, kSettings)); ASSERT_EQ(3u, helper_->factory()->encoders().size()); // All encoders have internal source, simulcast adapter reports true. for (MockVideoEncoder* encoder : helper_->factory()->encoders()) { encoder->set_has_internal_source(true); } EXPECT_EQ(0, adapter_->InitEncode(&codec_, kSettings)); EXPECT_TRUE(adapter_->GetEncoderInfo().has_internal_source); // One encoder does not have internal source, simulcast adapter reports false. helper_->factory()->encoders()[2]->set_has_internal_source(false); EXPECT_EQ(0, adapter_->InitEncode(&codec_, kSettings)); EXPECT_FALSE(adapter_->GetEncoderInfo().has_internal_source); } TEST_F(TestSimulcastEncoderAdapterFake, ReportsFpsAllocation) { SimulcastTestFixtureImpl::DefaultSettings( &codec_, static_cast(kTestTemporalLayerProfile), kVideoCodecVP8); codec_.numberOfSimulcastStreams = 3; adapter_->RegisterEncodeCompleteCallback(this); EXPECT_EQ(0, adapter_->InitEncode(&codec_, kSettings)); ASSERT_EQ(3u, helper_->factory()->encoders().size()); // Combination of three different supported mode: // Simulcast stream 0 has undefined fps behavior. // Simulcast stream 1 has three temporal layers. // Simulcast stream 2 has 1 temporal layer. FramerateFractions expected_fps_allocation[kMaxSpatialLayers]; expected_fps_allocation[1].push_back(EncoderInfo::kMaxFramerateFraction / 4); expected_fps_allocation[1].push_back(EncoderInfo::kMaxFramerateFraction / 2); expected_fps_allocation[1].push_back(EncoderInfo::kMaxFramerateFraction); expected_fps_allocation[2].push_back(EncoderInfo::kMaxFramerateFraction); // All encoders have internal source, simulcast adapter reports true. for (size_t i = 0; i < codec_.numberOfSimulcastStreams; ++i) { MockVideoEncoder* encoder = helper_->factory()->encoders()[i]; encoder->set_fps_allocation(expected_fps_allocation[i]); } EXPECT_EQ(0, adapter_->InitEncode(&codec_, kSettings)); EXPECT_THAT(adapter_->GetEncoderInfo().fps_allocation, ::testing::ElementsAreArray(expected_fps_allocation)); } TEST_F(TestSimulcastEncoderAdapterFake, SetRateDistributesBandwithAllocation) { SimulcastTestFixtureImpl::DefaultSettings( &codec_, static_cast(kTestTemporalLayerProfile), kVideoCodecVP8); codec_.numberOfSimulcastStreams = 3; const DataRate target_bitrate = DataRate::KilobitsPerSec(codec_.simulcastStream[0].targetBitrate + codec_.simulcastStream[1].targetBitrate + codec_.simulcastStream[2].minBitrate); const DataRate bandwidth_allocation = target_bitrate + DataRate::KilobitsPerSec(600); rate_allocator_.reset(new SimulcastRateAllocator(codec_)); EXPECT_EQ(0, adapter_->InitEncode(&codec_, kSettings)); adapter_->RegisterEncodeCompleteCallback(this); // Set bitrates so that we send all layers. adapter_->SetRates(VideoEncoder::RateControlParameters( rate_allocator_->Allocate( VideoBitrateAllocationParameters(target_bitrate.bps(), 30)), 30.0, bandwidth_allocation)); std::vector encoders = helper_->factory()->encoders(); ASSERT_EQ(3u, encoders.size()); for (size_t i = 0; i < 3; ++i) { const uint32_t layer_bitrate_bps = (i < static_cast(codec_.numberOfSimulcastStreams) - 1 ? codec_.simulcastStream[i].targetBitrate : codec_.simulcastStream[i].minBitrate) * 1000; EXPECT_EQ(layer_bitrate_bps, encoders[i]->last_set_rates().bitrate.get_sum_bps()) << i; EXPECT_EQ( (layer_bitrate_bps * bandwidth_allocation.bps()) / target_bitrate.bps(), encoders[i]->last_set_rates().bandwidth_allocation.bps()) << i; } } TEST_F(TestSimulcastEncoderAdapterFake, CanSetZeroBitrateWithHeadroom) { SimulcastTestFixtureImpl::DefaultSettings( &codec_, static_cast(kTestTemporalLayerProfile), kVideoCodecVP8); codec_.numberOfSimulcastStreams = 3; rate_allocator_.reset(new SimulcastRateAllocator(codec_)); EXPECT_EQ(0, adapter_->InitEncode(&codec_, kSettings)); adapter_->RegisterEncodeCompleteCallback(this); // Set allocated bitrate to 0, but keep (network) bandwidth allocation. VideoEncoder::RateControlParameters rate_params; rate_params.framerate_fps = 30; rate_params.bandwidth_allocation = DataRate::KilobitsPerSec(600); adapter_->SetRates(rate_params); std::vector encoders = helper_->factory()->encoders(); ASSERT_EQ(3u, encoders.size()); for (size_t i = 0; i < 3; ++i) { EXPECT_EQ(0u, encoders[i]->last_set_rates().bitrate.get_sum_bps()); } } TEST_F(TestSimulcastEncoderAdapterFake, SupportsSimulcast) { SimulcastTestFixtureImpl::DefaultSettings( &codec_, static_cast(kTestTemporalLayerProfile), kVideoCodecVP8); codec_.numberOfSimulcastStreams = 3; // Indicate that mock encoders internally support simulcast. helper_->factory()->set_supports_simulcast(true); adapter_->RegisterEncodeCompleteCallback(this); EXPECT_EQ(0, adapter_->InitEncode(&codec_, kSettings)); // Only one encoder should have been produced. ASSERT_EQ(1u, helper_->factory()->encoders().size()); rtc::scoped_refptr buffer(I420Buffer::Create(1280, 720)); VideoFrame input_frame = VideoFrame::Builder() .set_video_frame_buffer(buffer) .set_timestamp_rtp(100) .set_timestamp_ms(1000) .set_rotation(kVideoRotation_180) .build(); EXPECT_CALL(*helper_->factory()->encoders()[0], Encode) .WillOnce(Return(WEBRTC_VIDEO_CODEC_OK)); std::vector frame_types(3, VideoFrameType::kVideoFrameKey); EXPECT_EQ(0, adapter_->Encode(input_frame, &frame_types)); } TEST_F(TestSimulcastEncoderAdapterFake, PassesSdpVideoFormatToEncoder) { sdp_video_parameters_ = {{"test_param", "test_value"}}; SetUp(); SetupCodec(); std::vector encoders = helper_->factory()->encoders(); ASSERT_GT(encoders.size(), 0u); EXPECT_EQ(encoders[0]->video_format(), SdpVideoFormat("VP8", sdp_video_parameters_)); } TEST_F(TestSimulcastEncoderAdapterFake, SupportsFallback) { // Enable support for fallback encoder factory and re-setup. use_fallback_factory_ = true; SetUp(); SetupCodec(); // Make sure we have bitrate for all layers. DataRate max_bitrate = DataRate::Zero(); for (int i = 0; i < 3; ++i) { max_bitrate += DataRate::KilobitsPerSec(codec_.simulcastStream[i].maxBitrate); } const auto rate_settings = VideoEncoder::RateControlParameters( rate_allocator_->Allocate( VideoBitrateAllocationParameters(max_bitrate.bps(), 30)), 30.0, max_bitrate); adapter_->SetRates(rate_settings); std::vector primary_encoders = helper_->factory()->encoders(); std::vector fallback_encoders = helper_->fallback_factory()->encoders(); ASSERT_EQ(3u, primary_encoders.size()); ASSERT_EQ(3u, fallback_encoders.size()); // Create frame to test with. rtc::scoped_refptr buffer(I420Buffer::Create(1280, 720)); VideoFrame input_frame = VideoFrame::Builder() .set_video_frame_buffer(buffer) .set_timestamp_rtp(100) .set_timestamp_ms(1000) .set_rotation(kVideoRotation_180) .build(); std::vector frame_types(3, VideoFrameType::kVideoFrameKey); // All primary encoders used. for (auto codec : primary_encoders) { EXPECT_CALL(*codec, Encode).WillOnce(Return(WEBRTC_VIDEO_CODEC_OK)); } EXPECT_EQ(0, adapter_->Encode(input_frame, &frame_types)); // Trigger fallback on first encoder. primary_encoders[0]->set_init_encode_return_value( WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE); EXPECT_EQ(0, adapter_->InitEncode(&codec_, kSettings)); adapter_->SetRates(rate_settings); EXPECT_CALL(*fallback_encoders[0], Encode) .WillOnce(Return(WEBRTC_VIDEO_CODEC_OK)); EXPECT_CALL(*primary_encoders[1], Encode) .WillOnce(Return(WEBRTC_VIDEO_CODEC_OK)); EXPECT_CALL(*primary_encoders[2], Encode) .WillOnce(Return(WEBRTC_VIDEO_CODEC_OK)); EXPECT_EQ(0, adapter_->Encode(input_frame, &frame_types)); // Trigger fallback on all encoder. primary_encoders[1]->set_init_encode_return_value( WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE); primary_encoders[2]->set_init_encode_return_value( WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE); EXPECT_EQ(0, adapter_->InitEncode(&codec_, kSettings)); adapter_->SetRates(rate_settings); EXPECT_CALL(*fallback_encoders[0], Encode) .WillOnce(Return(WEBRTC_VIDEO_CODEC_OK)); EXPECT_CALL(*fallback_encoders[1], Encode) .WillOnce(Return(WEBRTC_VIDEO_CODEC_OK)); EXPECT_CALL(*fallback_encoders[2], Encode) .WillOnce(Return(WEBRTC_VIDEO_CODEC_OK)); EXPECT_EQ(0, adapter_->Encode(input_frame, &frame_types)); // Return to primary encoders on all streams. for (int i = 0; i < 3; ++i) { primary_encoders[i]->set_init_encode_return_value(WEBRTC_VIDEO_CODEC_OK); } EXPECT_EQ(0, adapter_->InitEncode(&codec_, kSettings)); adapter_->SetRates(rate_settings); for (auto codec : primary_encoders) { EXPECT_CALL(*codec, Encode).WillOnce(Return(WEBRTC_VIDEO_CODEC_OK)); } EXPECT_EQ(0, adapter_->Encode(input_frame, &frame_types)); } TEST_F(TestSimulcastEncoderAdapterFake, SupportsPerSimulcastLayerMaxFramerate) { SimulcastTestFixtureImpl::DefaultSettings( &codec_, static_cast(kTestTemporalLayerProfile), kVideoCodecVP8); codec_.numberOfSimulcastStreams = 3; codec_.simulcastStream[0].maxFramerate = 60; codec_.simulcastStream[1].maxFramerate = 30; codec_.simulcastStream[2].maxFramerate = 10; EXPECT_EQ(0, adapter_->InitEncode(&codec_, kSettings)); ASSERT_EQ(3u, helper_->factory()->encoders().size()); EXPECT_EQ(60u, helper_->factory()->encoders()[0]->codec().maxFramerate); EXPECT_EQ(30u, helper_->factory()->encoders()[1]->codec().maxFramerate); EXPECT_EQ(10u, helper_->factory()->encoders()[2]->codec().maxFramerate); } TEST_F(TestSimulcastEncoderAdapterFake, CreatesEncoderOnlyIfStreamIsActive) { // Legacy singlecast SetupCodec(/*active_streams=*/{}); EXPECT_EQ(1u, helper_->factory()->encoders().size()); // Simulcast-capable underlaying encoder ReSetUp(); helper_->factory()->set_supports_simulcast(true); SetupCodec(/*active_streams=*/{true, true}); EXPECT_EQ(1u, helper_->factory()->encoders().size()); // Muti-encoder simulcast ReSetUp(); helper_->factory()->set_supports_simulcast(false); SetupCodec(/*active_streams=*/{true, true}); EXPECT_EQ(2u, helper_->factory()->encoders().size()); // Singlecast via layers deactivation. Lowest layer is active. ReSetUp(); helper_->factory()->set_supports_simulcast(false); SetupCodec(/*active_streams=*/{true, false}); EXPECT_EQ(1u, helper_->factory()->encoders().size()); // Singlecast via layers deactivation. Highest layer is active. ReSetUp(); helper_->factory()->set_supports_simulcast(false); SetupCodec(/*active_streams=*/{false, true}); EXPECT_EQ(1u, helper_->factory()->encoders().size()); } TEST_F(TestSimulcastEncoderAdapterFake, RecreateEncoderIfPreferTemporalSupportIsEnabled) { // Normally SEA reuses encoders. But, when TL-based SW fallback is enabled, // the encoder which served the lowest stream should be recreated before it // can be used to process an upper layer and vice-versa. test::ScopedFieldTrials field_trials( "WebRTC-Video-PreferTemporalSupportOnBaseLayer/Enabled/"); use_fallback_factory_ = true; ReSetUp(); // Legacy singlecast SetupCodec(/*active_streams=*/{}); ASSERT_EQ(1u, helper_->factory()->encoders().size()); // Singlecast, the lowest stream is active. Encoder should be reused. MockVideoEncoder* prev_encoder = helper_->factory()->encoders()[0]; SetupCodec(/*active_streams=*/{true, false}); ASSERT_EQ(1u, helper_->factory()->encoders().size()); EXPECT_EQ(helper_->factory()->encoders()[0], prev_encoder); // Singlecast, an upper stream is active. Encoder should be recreated. EXPECT_CALL(*prev_encoder, Release()).Times(1); SetupCodec(/*active_streams=*/{false, true}); ASSERT_EQ(1u, helper_->factory()->encoders().size()); EXPECT_NE(helper_->factory()->encoders()[0], prev_encoder); // Singlecast, the lowest stream is active. Encoder should be recreated. prev_encoder = helper_->factory()->encoders()[0]; EXPECT_CALL(*prev_encoder, Release()).Times(1); SetupCodec(/*active_streams=*/{true, false}); ASSERT_EQ(1u, helper_->factory()->encoders().size()); EXPECT_NE(helper_->factory()->encoders()[0], prev_encoder); } } // namespace test } // namespace webrtc