SEA discards inactive encoders in implementation name
Inactive encoders are included in the string when they are paused due to bitrate allocation being 0 for that simulcast layer. #rtc_ktlo Bug: webrtc:376804631 Change-Id: I4234b452b60fee58981907380df41962fda5bf40 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/367660 Reviewed-by: Henrik Boström <hbos@webrtc.org> Commit-Queue: Evan Shrubsole <eshr@google.com> Reviewed-by: Erik Språng <sprang@webrtc.org> Cr-Commit-Position: refs/heads/main@{#43367}
This commit is contained in:
parent
a5676c17b3
commit
e6f0c2fd23
@ -473,6 +473,7 @@ rtc_library("rtc_simulcast_encoder_adapter") {
|
||||
"../modules/video_coding:video_coding_utility",
|
||||
"../rtc_base:checks",
|
||||
"../rtc_base:logging",
|
||||
"../rtc_base:stringutils",
|
||||
"../rtc_base/experiments:encoder_info_settings",
|
||||
"../rtc_base/experiments:rate_control_settings",
|
||||
"../rtc_base/system:no_unique_address",
|
||||
@ -952,12 +953,14 @@ if (rtc_include_tests) {
|
||||
"../api/transport:bitrate_settings",
|
||||
"../api/transport:field_trial_based_config",
|
||||
"../api/transport/rtp:rtp_source",
|
||||
"../api/units:data_rate",
|
||||
"../api/units:time_delta",
|
||||
"../api/units:timestamp",
|
||||
"../api/video:builtin_video_bitrate_allocator_factory",
|
||||
"../api/video:recordable_encoded_frame",
|
||||
"../api/video:resolution",
|
||||
"../api/video:video_bitrate_allocation",
|
||||
"../api/video:video_bitrate_allocator",
|
||||
"../api/video:video_bitrate_allocator_factory",
|
||||
"../api/video:video_codec_constants",
|
||||
"../api/video:video_frame",
|
||||
|
||||
@ -58,6 +58,8 @@
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/experiments/rate_control_settings.h"
|
||||
#include "rtc_base/logging.h"
|
||||
#include "rtc_base/strings/str_join.h"
|
||||
#include "rtc_base/strings/string_builder.h"
|
||||
|
||||
namespace webrtc {
|
||||
namespace {
|
||||
@ -393,6 +395,9 @@ int SimulcastEncoderAdapter::InitEncode(
|
||||
<< WebRtcVideoCodecErrorToString(ret);
|
||||
return ret;
|
||||
}
|
||||
RTC_LOG(LS_WARNING) << "[SEA] InitEncode: failed with error code: "
|
||||
<< WebRtcVideoCodecErrorToString(ret)
|
||||
<< ". Falling back to multi-encoder mode.";
|
||||
}
|
||||
|
||||
// Multi-encoder simulcast or singlecast (deactivated layers).
|
||||
@ -945,15 +950,17 @@ VideoEncoder::EncoderInfo SimulcastEncoderAdapter::GetEncoderInfo() const {
|
||||
}
|
||||
|
||||
encoder_info.scaling_settings = VideoEncoder::ScalingSettings::kOff;
|
||||
std::vector<std::string> encoder_names;
|
||||
|
||||
for (size_t i = 0; i < stream_contexts_.size(); ++i) {
|
||||
VideoEncoder::EncoderInfo encoder_impl_info =
|
||||
stream_contexts_[i].encoder().GetEncoderInfo();
|
||||
if (i == 0) {
|
||||
// Encoder name indicates names of all sub-encoders.
|
||||
encoder_info.implementation_name += " (";
|
||||
encoder_info.implementation_name += encoder_impl_info.implementation_name;
|
||||
|
||||
// Encoder name indicates names of all active sub-encoders.
|
||||
if (!stream_contexts_[i].is_paused()) {
|
||||
encoder_names.push_back(encoder_impl_info.implementation_name);
|
||||
}
|
||||
if (i == 0) {
|
||||
encoder_info.supports_native_handle =
|
||||
encoder_impl_info.supports_native_handle;
|
||||
encoder_info.has_trusted_rate_controller =
|
||||
@ -962,9 +969,6 @@ VideoEncoder::EncoderInfo SimulcastEncoderAdapter::GetEncoderInfo() const {
|
||||
encoder_impl_info.is_hardware_accelerated;
|
||||
encoder_info.is_qp_trusted = encoder_impl_info.is_qp_trusted;
|
||||
} else {
|
||||
encoder_info.implementation_name += ", ";
|
||||
encoder_info.implementation_name += encoder_impl_info.implementation_name;
|
||||
|
||||
// Native handle supported if any encoder supports it.
|
||||
encoder_info.supports_native_handle |=
|
||||
encoder_impl_info.supports_native_handle;
|
||||
@ -999,7 +1003,13 @@ VideoEncoder::EncoderInfo SimulcastEncoderAdapter::GetEncoderInfo() const {
|
||||
encoder_info.apply_alignment_to_all_simulcast_layers = true;
|
||||
}
|
||||
}
|
||||
encoder_info.implementation_name += ")";
|
||||
|
||||
if (!encoder_names.empty()) {
|
||||
rtc::StringBuilder implementation_name_builder(" (");
|
||||
implementation_name_builder << StrJoin(encoder_names, ", ");
|
||||
implementation_name_builder << ")";
|
||||
encoder_info.implementation_name += implementation_name_builder.Release();
|
||||
}
|
||||
|
||||
OverrideFromFieldTrial(&encoder_info);
|
||||
|
||||
|
||||
@ -21,7 +21,10 @@
|
||||
#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/units/data_rate.h"
|
||||
#include "api/video/video_bitrate_allocator.h"
|
||||
#include "api/video/video_codec_constants.h"
|
||||
#include "api/video_codecs/scalability_mode.h"
|
||||
#include "api/video_codecs/sdp_video_format.h"
|
||||
#include "api/video_codecs/video_encoder.h"
|
||||
#include "api/video_codecs/video_encoder_factory.h"
|
||||
@ -992,15 +995,15 @@ TEST_F(TestSimulcastEncoderAdapterFake, SupportsImplementationName) {
|
||||
SimulcastTestFixtureImpl::DefaultSettings(
|
||||
&codec_, static_cast<const int*>(kTestTemporalLayerProfile),
|
||||
kVideoCodecVP8);
|
||||
codec_.numberOfSimulcastStreams = 2;
|
||||
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("SimulcastEncoderAdapter",
|
||||
adapter_->GetEncoderInfo().implementation_name);
|
||||
EXPECT_EQ(0, adapter_->InitEncode(&codec_, kSettings));
|
||||
EXPECT_EQ("SimulcastEncoderAdapter (codec1, codec2, codec3)",
|
||||
EXPECT_EQ("SimulcastEncoderAdapter (codec1, codec2)",
|
||||
adapter_->GetEncoderInfo().implementation_name);
|
||||
|
||||
// Single streams should not expose "SimulcastEncoderAdapter" in name.
|
||||
@ -1022,13 +1025,59 @@ TEST_F(TestSimulcastEncoderAdapterFake, RuntimeEncoderInfoUpdate) {
|
||||
encoder_names.push_back("codec3");
|
||||
helper_->factory()->SetEncoderNames(encoder_names);
|
||||
EXPECT_EQ(0, adapter_->InitEncode(&codec_, kSettings));
|
||||
EXPECT_EQ("SimulcastEncoderAdapter (codec1, codec2, codec3)",
|
||||
EXPECT_EQ("SimulcastEncoderAdapter (codec1, codec2)",
|
||||
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)",
|
||||
EXPECT_EQ("SimulcastEncoderAdapter (fallback1, codec2)",
|
||||
adapter_->GetEncoderInfo().implementation_name);
|
||||
}
|
||||
|
||||
TEST_F(TestSimulcastEncoderAdapterFake, EncoderInfoDeactiveLayersUpdatesName) {
|
||||
SimulcastTestFixtureImpl::DefaultSettings(
|
||||
&codec_, static_cast<const int*>(kTestTemporalLayerProfile),
|
||||
kVideoCodecVP8);
|
||||
const DataRate target_bitrate =
|
||||
DataRate::KilobitsPerSec(codec_.simulcastStream[0].targetBitrate +
|
||||
codec_.simulcastStream[1].targetBitrate +
|
||||
codec_.simulcastStream[2].targetBitrate);
|
||||
const DataRate bandwidth_allocation =
|
||||
target_bitrate + DataRate::KilobitsPerSec(600);
|
||||
const DataRate target_bitrate_without_layer3 =
|
||||
target_bitrate -
|
||||
DataRate::KilobitsPerSec(codec_.simulcastStream[2].targetBitrate);
|
||||
const DataRate bandwidth_allocation_without_layer3 =
|
||||
target_bitrate + DataRate::KilobitsPerSec(300);
|
||||
|
||||
std::vector<const char*> encoder_names = {"codec1", "codec2", "codec3"};
|
||||
helper_->factory()->SetEncoderNames(encoder_names);
|
||||
rate_allocator_ = std::make_unique<SimulcastRateAllocator>(env_, codec_);
|
||||
|
||||
EXPECT_EQ(0, adapter_->InitEncode(&codec_, kSettings));
|
||||
adapter_->SetRates(VideoEncoder::RateControlParameters(
|
||||
rate_allocator_->Allocate(
|
||||
VideoBitrateAllocationParameters(target_bitrate, 30)),
|
||||
30.0, bandwidth_allocation));
|
||||
EXPECT_EQ("SimulcastEncoderAdapter (codec1, codec2, codec3)",
|
||||
adapter_->GetEncoderInfo().implementation_name);
|
||||
|
||||
// Disable the third encoder using bitrate allocation.
|
||||
adapter_->SetRates(VideoEncoder::RateControlParameters(
|
||||
rate_allocator_->Allocate(
|
||||
VideoBitrateAllocationParameters(target_bitrate_without_layer3, 30)),
|
||||
30.0, bandwidth_allocation_without_layer3));
|
||||
EXPECT_EQ("SimulcastEncoderAdapter (codec1, codec2)",
|
||||
adapter_->GetEncoderInfo().implementation_name);
|
||||
|
||||
// Enable the third encoder again using bitrate allocation.
|
||||
rate_allocator_ = std::make_unique<SimulcastRateAllocator>(env_, codec_);
|
||||
adapter_->SetRates(VideoEncoder::RateControlParameters(
|
||||
rate_allocator_->Allocate(
|
||||
VideoBitrateAllocationParameters(target_bitrate, 30)),
|
||||
30.0, bandwidth_allocation));
|
||||
EXPECT_EQ("SimulcastEncoderAdapter (codec1, codec2, codec3)",
|
||||
adapter_->GetEncoderInfo().implementation_name);
|
||||
}
|
||||
|
||||
|
||||
@ -246,12 +246,19 @@ void SimulcastTestFixtureImpl::DefaultSettings(
|
||||
&settings->simulcastStream[layer_order[2]],
|
||||
temporal_layer_profile[2]);
|
||||
settings->SetFrameDropEnabled(true);
|
||||
if (codec_type == kVideoCodecVP8) {
|
||||
switch (codec_type) {
|
||||
case kVideoCodecVP8:
|
||||
settings->VP8()->denoisingOn = true;
|
||||
settings->VP8()->automaticResizeOn = false;
|
||||
settings->VP8()->keyFrameInterval = 3000;
|
||||
} else {
|
||||
break;
|
||||
case kVideoCodecH264:
|
||||
settings->H264()->keyFrameInterval = 3000;
|
||||
break;
|
||||
case kVideoCodecVP9:
|
||||
break;
|
||||
default:
|
||||
RTC_CHECK_NOTREACHED();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -11,6 +11,7 @@
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <set>
|
||||
@ -55,6 +56,7 @@
|
||||
#include "test/scoped_key_value_config.h"
|
||||
|
||||
using ::testing::Eq;
|
||||
using ::testing::Field;
|
||||
using ::testing::Optional;
|
||||
using ::testing::SizeIs;
|
||||
using ::testing::StrCaseEq;
|
||||
@ -78,6 +80,22 @@ constexpr TimeDelta kLongTimeoutForRampingUp = TimeDelta::Minutes(1);
|
||||
constexpr DataRate kVp9ExpectedMaxBitrateForL1T3 =
|
||||
DataRate::KilobitsPerSec(1500);
|
||||
|
||||
auto HasEncoderImplementation(absl::string_view impl) {
|
||||
return Field("encoder_implementation",
|
||||
&RTCOutboundRtpStreamStats::encoder_implementation,
|
||||
Optional(StrEq(impl)));
|
||||
}
|
||||
|
||||
auto HasScalabilityMode(absl::string_view mode) {
|
||||
return Field("scalability_mode", &RTCOutboundRtpStreamStats::scalability_mode,
|
||||
Optional(StrEq(mode)));
|
||||
}
|
||||
|
||||
auto HasEmptyScalabilityMode() {
|
||||
return Field("scalability_mode", &RTCOutboundRtpStreamStats::scalability_mode,
|
||||
Eq(std::nullopt));
|
||||
}
|
||||
|
||||
struct StringParamToString {
|
||||
std::string operator()(const ::testing::TestParamInfo<std::string>& info) {
|
||||
return info.param;
|
||||
@ -320,6 +338,9 @@ class PeerConnectionEncodingsIntegrationTest : public ::testing::Test {
|
||||
report->GetStatsOfType<RTCOutboundRtpStreamStats>();
|
||||
for (const auto* outbound_rtp : outbound_rtps) {
|
||||
if (outbound_rtp->rid.value_or("") == rid) {
|
||||
RTC_LOG(LS_INFO) << "Encoding " << rid << " is "
|
||||
<< (outbound_rtp->active.value_or(false) ? "active"
|
||||
: "inactive");
|
||||
return *outbound_rtp->active;
|
||||
}
|
||||
}
|
||||
@ -327,6 +348,19 @@ class PeerConnectionEncodingsIntegrationTest : public ::testing::Test {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string GetEncoderImplementationName(
|
||||
rtc::scoped_refptr<PeerConnectionTestWrapper> pc_wrapper) {
|
||||
rtc::scoped_refptr<const RTCStatsReport> report = GetStats(pc_wrapper);
|
||||
std::vector<const RTCOutboundRtpStreamStats*> outbound_rtps =
|
||||
report->GetStatsOfType<RTCOutboundRtpStreamStats>();
|
||||
for (const auto* outbound_rtp : outbound_rtps) {
|
||||
if (outbound_rtp->encoder_implementation.has_value()) {
|
||||
return *outbound_rtp->encoder_implementation;
|
||||
}
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
Resolution GetEncodingResolution(
|
||||
rtc::scoped_refptr<PeerConnectionTestWrapper> pc_wrapper,
|
||||
std::string_view rid = "") {
|
||||
@ -828,7 +862,7 @@ TEST_F(PeerConnectionEncodingsIntegrationTest,
|
||||
}
|
||||
|
||||
// Exercise common path where `scalability_mode` is not specified until after
|
||||
// negotiation, requring us to recreate the stream when the number of streams
|
||||
// negotiation, requiring us to recreate the stream when the number of streams
|
||||
// changes from 1 (legacy SVC) to 3 (standard simulcast).
|
||||
TEST_F(PeerConnectionEncodingsIntegrationTest,
|
||||
VP9_SwitchFromLegacySvcToStandardSingleActiveEncodingSvc) {
|
||||
@ -885,6 +919,98 @@ TEST_F(PeerConnectionEncodingsIntegrationTest,
|
||||
EXPECT_FALSE(parameters.encodings[2].scalability_mode.has_value());
|
||||
}
|
||||
|
||||
TEST_F(PeerConnectionEncodingsIntegrationTest,
|
||||
VP9_SimulcastDeactiveActiveLayer_StandardSvc) {
|
||||
rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
|
||||
rtc::scoped_refptr<PeerConnectionTestWrapper> remote_pc_wrapper = CreatePc();
|
||||
ExchangeIceCandidates(local_pc_wrapper, remote_pc_wrapper);
|
||||
|
||||
std::vector<cricket::SimulcastLayer> layers =
|
||||
CreateLayers({"q", "h", "f"}, /*active=*/true);
|
||||
rtc::scoped_refptr<RtpTransceiverInterface> transceiver =
|
||||
AddTransceiverWithSimulcastLayers(local_pc_wrapper, remote_pc_wrapper,
|
||||
layers);
|
||||
constexpr absl::string_view kCodec = "VP9";
|
||||
std::vector<RtpCodecCapability> codecs =
|
||||
GetCapabilitiesAndRestrictToCodec(remote_pc_wrapper, kCodec);
|
||||
transceiver->SetCodecPreferences(codecs);
|
||||
|
||||
// Switch to the standard mode. Despite only having a single active stream in
|
||||
// both cases, this internally reconfigures from 1 stream to 3 streams.
|
||||
// Test coverage for https://crbug.com/webrtc/15016.
|
||||
rtc::scoped_refptr<RtpSenderInterface> sender = transceiver->sender();
|
||||
RtpParameters parameters = sender->GetParameters();
|
||||
ASSERT_THAT(parameters.encodings, SizeIs(3));
|
||||
parameters.encodings[0].active = true;
|
||||
parameters.encodings[0].scalability_mode = "L1T3";
|
||||
parameters.encodings[0].scale_resolution_down_by = 4.0;
|
||||
parameters.encodings[1].active = true;
|
||||
parameters.encodings[1].scalability_mode = "L1T1";
|
||||
parameters.encodings[1].scale_resolution_down_by = 2.0;
|
||||
parameters.encodings[2].active = true;
|
||||
parameters.encodings[2].scalability_mode = "L1T1";
|
||||
parameters.encodings[2].scale_resolution_down_by = 1.0;
|
||||
EXPECT_TRUE(sender->SetParameters(parameters).ok());
|
||||
|
||||
// The original negotiation triggers legacy SVC because we didn't specify
|
||||
// any scalability mode.
|
||||
NegotiateWithSimulcastTweaks(local_pc_wrapper, remote_pc_wrapper);
|
||||
local_pc_wrapper->WaitForConnection();
|
||||
remote_pc_wrapper->WaitForConnection();
|
||||
|
||||
// Since the standard API is configuring simulcast we get three outbound-rtps,
|
||||
// and two are active.
|
||||
ASSERT_TRUE_WAIT(HasOutboundRtpBytesSent(local_pc_wrapper, /*num_layers=*/3u,
|
||||
/*num_active_layers=*/3u),
|
||||
kDefaultTimeout.ms());
|
||||
// Wait until scalability mode is reported and expected resolution reached.
|
||||
// Ramp up time may be significant.
|
||||
EXPECT_TRUE_WAIT(OutboundRtpResolutionsAreLessThanOrEqualToExpectations(
|
||||
local_pc_wrapper,
|
||||
{{"q", 320, 180}, {"h", 640, 360}, {"f", 1280, 720}}),
|
||||
kLongTimeoutForRampingUp.ms() / 2);
|
||||
rtc::scoped_refptr<const RTCStatsReport> report = GetStats(local_pc_wrapper);
|
||||
ASSERT_TRUE(report);
|
||||
std::vector<const RTCOutboundRtpStreamStats*> outbound_rtps =
|
||||
report->GetStatsOfType<RTCOutboundRtpStreamStats>();
|
||||
EXPECT_THAT(outbound_rtps,
|
||||
Each(HasEncoderImplementation(
|
||||
"SimulcastEncoderAdapter (libvpx, libvpx, libvpx)")));
|
||||
|
||||
// GetParameters() does not report any fallback.
|
||||
parameters = sender->GetParameters();
|
||||
ASSERT_THAT(parameters.encodings, SizeIs(3));
|
||||
EXPECT_THAT(parameters.encodings[0].scalability_mode,
|
||||
Optional(StrEq("L1T3")));
|
||||
EXPECT_THAT(parameters.encodings[1].scalability_mode,
|
||||
Optional(StrEq("L1T1")));
|
||||
EXPECT_THAT(parameters.encodings[2].scalability_mode,
|
||||
Optional(StrEq("L1T1")));
|
||||
EXPECT_THAT(parameters.encodings[2].scale_resolution_down_by, Eq(1.0));
|
||||
EXPECT_THAT(parameters.encodings[1].scale_resolution_down_by, Eq(2.0));
|
||||
EXPECT_THAT(parameters.encodings[0].scale_resolution_down_by, Eq(4.0));
|
||||
|
||||
// Deactivate the active layer.
|
||||
parameters.encodings[2].active = false;
|
||||
EXPECT_TRUE(sender->SetParameters(parameters).ok());
|
||||
// Ensure that we are getting VGA at L1T1 from the "f" rid.
|
||||
ASSERT_TRUE_WAIT(!EncodingIsActive(local_pc_wrapper, "f"),
|
||||
kDefaultTimeout.ms());
|
||||
|
||||
EXPECT_EQ_WAIT(GetEncoderImplementationName(local_pc_wrapper),
|
||||
"SimulcastEncoderAdapter (libvpx, libvpx)",
|
||||
kDefaultTimeout.ms());
|
||||
|
||||
report = GetStats(local_pc_wrapper);
|
||||
ASSERT_TRUE(report);
|
||||
outbound_rtps = report->GetStatsOfType<RTCOutboundRtpStreamStats>();
|
||||
EXPECT_THAT(outbound_rtps, Each(HasEncoderImplementation(
|
||||
"SimulcastEncoderAdapter (libvpx, libvpx)")));
|
||||
EXPECT_THAT(outbound_rtps, UnorderedElementsAre(HasScalabilityMode("L1T3"),
|
||||
HasScalabilityMode("L1T1"),
|
||||
HasEmptyScalabilityMode()));
|
||||
}
|
||||
|
||||
TEST_F(PeerConnectionEncodingsIntegrationTest,
|
||||
VP9_SimulcastMultiplLayersActive_StandardSvc) {
|
||||
rtc::scoped_refptr<PeerConnectionTestWrapper> local_pc_wrapper = CreatePc();
|
||||
@ -935,6 +1061,12 @@ TEST_F(PeerConnectionEncodingsIntegrationTest,
|
||||
ASSERT_TRUE_WAIT(HasOutboundRtpWithRidAndScalabilityMode(
|
||||
local_pc_wrapper, "h", "L1T1", 720 / 2),
|
||||
kLongTimeoutForRampingUp.ms() / 2);
|
||||
rtc::scoped_refptr<const RTCStatsReport> report = GetStats(local_pc_wrapper);
|
||||
ASSERT_TRUE(report);
|
||||
std::vector<const RTCOutboundRtpStreamStats*> outbound_rtps =
|
||||
report->GetStatsOfType<RTCOutboundRtpStreamStats>();
|
||||
EXPECT_THAT(outbound_rtps, Each(HasEncoderImplementation(
|
||||
"SimulcastEncoderAdapter (libvpx, libvpx)")));
|
||||
|
||||
// GetParameters() does not report any fallback.
|
||||
parameters = sender->GetParameters();
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user