blob: 24686e813e727aae07367c8ae911b76e6ed5a8be [file] [log] [blame]
/*
* 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 <array>
#include <memory>
#include <vector>
#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_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/gmock.h"
#include "test/gtest.h"
using ::testing::_;
using ::testing::Return;
using EncoderInfo = webrtc::VideoEncoder::EncoderInfo;
using FramerateFractions =
absl::InlinedVector<uint8_t, webrtc::kMaxTemporalStreams>;
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<SimulcastTestFixture> CreateSpecificSimulcastTestFixture(
VideoEncoderFactory* internal_encoder_factory) {
std::unique_ptr<VideoEncoderFactory> encoder_factory =
std::make_unique<FunctionVideoEncoderFactory>(
[internal_encoder_factory]() {
return std::make_unique<SimulcastEncoderAdapter>(
internal_encoder_factory,
SdpVideoFormat(cricket::kVp8CodecName));
});
std::unique_ptr<VideoDecoderFactory> decoder_factory =
std::make_unique<FunctionVideoDecoderFactory>(
[]() { 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<SdpVideoFormat> GetSupportedFormats() const override;
std::unique_ptr<VideoEncoder> CreateVideoEncoder(
const SdpVideoFormat& format) override;
const std::vector<MockVideoEncoder*>& encoders() const;
void SetEncoderNames(const std::vector<const char*>& encoder_names);
void set_init_encode_return_value(int32_t value);
void set_requested_resolution_alignments(
std::vector<int> 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<MockVideoEncoder*> encoders_;
std::vector<const char*> encoder_names_;
// Keep number of entries in sync with |kMaxSimulcastStreams|.
std::vector<int> 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<VideoFrameType>* 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<SdpVideoFormat> MockVideoEncoderFactory::GetSupportedFormats()
const {
std::vector<SdpVideoFormat> formats = {SdpVideoFormat("VP8")};
return formats;
}
std::unique_ptr<VideoEncoder> MockVideoEncoderFactory::CreateVideoEncoder(
const SdpVideoFormat& format) {
auto encoder = std::make_unique<::testing::NiceMock<MockVideoEncoder>>(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<MockVideoEncoder*>& MockVideoEncoderFactory::encoders()
const {
return encoders_;
}
void MockVideoEncoderFactory::SetEncoderNames(
const std::vector<const char*>& 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<MockVideoEncoderFactory> primary_factory_;
std::unique_ptr<MockVideoEncoderFactory> 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_ = std::make_unique<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;
}
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() {
SimulcastTestFixtureImpl::DefaultSettings(
&codec_, static_cast<const int*>(kTestTemporalLayerProfile),
kVideoCodecVP8);
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<TestSimulcastEncoderAdapterFakeHelper> helper_;
std::unique_ptr<VideoEncoder> adapter_;
VideoCodec codec_;
int last_encoded_image_width_;
int last_encoded_image_height_;
int last_encoded_image_simulcast_index_;
std::unique_ptr<SimulcastRateAllocator> 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<MockVideoEncoder*> 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);
EXPECT_EQ(0, 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<const int*>(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<VideoFrameBuffer> 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<VideoFrameType> 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<MockVideoEncoder*> 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<MockVideoEncoder*> 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<MockVideoEncoder*> encoders = helper_->factory()->encoders();
ASSERT_EQ(3u, encoders.size());
std::array<VideoCodec, 3> 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<MockVideoEncoder*> 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(0, 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(0, 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<const int*>(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<const int*>(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<const int*>(kTestTemporalLayerProfile),
kVideoCodecVP8);
std::vector<const char*> 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<const int*>(kTestTemporalLayerProfile),
kVideoCodecVP8);
std::vector<const char*> 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<const int*>(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<I420BufferInterface> 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<const int*>(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<VideoFrameBuffer> buffer(
new rtc::RefCountedObject<FakeNativeBufferI420>(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<VideoFrameType> frame_types(3, VideoFrameType::kVideoFrameKey);
EXPECT_EQ(0, adapter_->Encode(input_frame, &frame_types));
}
TEST_F(TestSimulcastEncoderAdapterFake, NativeHandleForwardingOnlyIfSupported) {
SimulcastTestFixtureImpl::DefaultSettings(
&codec_, static_cast<const int*>(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<VideoFrameBuffer> buffer(
new rtc::RefCountedObject<FakeNativeBufferI420>(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<VideoFrameType>* frame_types) {
EXPECT_EQ(frame.video_frame_buffer()->type(),
VideoFrameBuffer::Type::kI420);
return 0;
});
std::vector<VideoFrameType> frame_types(3, VideoFrameType::kVideoFrameKey);
EXPECT_EQ(0, adapter_->Encode(input_frame, &frame_types));
}
TEST_F(TestSimulcastEncoderAdapterFake, TestFailureReturnCodesFromEncodeCalls) {
SimulcastTestFixtureImpl::DefaultSettings(
&codec_, static_cast<const int*>(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<I420Buffer> 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<VideoFrameType> 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<const int*>(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<const int*>(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<const int*>(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<const int*>(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<VideoFrameBuffer> 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<MockVideoEncoder*> 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<VideoFrameType> 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<const int*>(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<VideoFrameBuffer> 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<MockVideoEncoder*> 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<const int*>(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<const int*>(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<const int*>(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, ReportsInternalSource) {
SimulcastTestFixtureImpl::DefaultSettings(
&codec_, static_cast<const int*>(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<const int*>(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<const int*>(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<MockVideoEncoder*> 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<size_t>(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<const int*>(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<MockVideoEncoder*> 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<const int*>(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<VideoFrameBuffer> 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<VideoFrameType> 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<MockVideoEncoder*> 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<MockVideoEncoder*> primary_encoders =
helper_->factory()->encoders();
std::vector<MockVideoEncoder*> 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<VideoFrameBuffer> 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<VideoFrameType> 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<const int*>(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);
}
} // namespace test
} // namespace webrtc