mirror of
https://github.com/mollyim/webrtc.git
synced 2025-05-17 15:47:53 +01:00

This member of the CodecInfo struct was set in several places, but not used for anything. To aid deletion, this cl defines a default implementation of VideoEncoderFactory::QueryVideoEncoder. The next step is to delete almost all downstream implementations of that method, since the only classes that have to implement it are the few factories that produce "internal source" encoders, e.g., for Chromium remoting. Bug: None Change-Id: I1f0dbf0d302933004ebdc779460cb2cb3a894e02 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/179520 Reviewed-by: Kári Helgason <kthelgason@webrtc.org> Reviewed-by: Sami Kalliomäki <sakal@webrtc.org> Reviewed-by: Sebastian Jansson <srte@webrtc.org> Commit-Queue: Niels Moller <nisse@webrtc.org> Cr-Commit-Position: refs/heads/master@{#31844}
1520 lines
60 KiB
C++
1520 lines
60 KiB
C++
/*
|
|
* 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.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, nullptr);
|
|
}
|
|
|
|
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_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 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,
|
|
const RTPFragmentationHeader* fragmentation) 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.plType, target.plType);
|
|
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.plType, codec_after.plType);
|
|
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, 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
|