Extend AudioEncoderFactoryTemplate to pass Environment to AudioEncoder factory traits

Bug: webrtc:343086059
Change-Id: I1b8f066708ad630b9911bbcaacdc34542dd247ee
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/355480
Commit-Queue: Danil Chapovalov <danilchap@webrtc.org>
Reviewed-by: Jakob Ivarsson‎ <jakobi@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#42531}
This commit is contained in:
Danil Chapovalov 2024-06-25 12:26:50 +02:00 committed by WebRTC LUCI CQ
parent d03ce76147
commit eaea3e26d7
2 changed files with 196 additions and 7 deletions

View File

@ -55,6 +55,65 @@ struct Helper<> {
}
};
// Use ranked overloads (abseil.io/tips/229) for dispatching.
struct Rank0 {};
struct Rank1 : Rank0 {};
template <typename Trait,
typename = std::enable_if_t<std::is_convertible_v<
decltype(Trait::MakeAudioEncoder(
std::declval<Environment>(),
std::declval<typename Trait::Config>(),
std::declval<AudioEncoderFactory::Options>())),
std::unique_ptr<AudioEncoder>>>>
absl::Nullable<std::unique_ptr<AudioEncoder>> CreateEncoder(
Rank1,
const Environment& env,
const typename Trait::Config& config,
const AudioEncoderFactory::Options& options) {
return Trait::MakeAudioEncoder(env, config, options);
}
template <typename Trait,
typename = std::enable_if_t<std::is_convertible_v<
decltype(Trait::MakeAudioEncoder(
std::declval<typename Trait::Config>(),
int{},
std::declval<absl::optional<AudioCodecPairId>>())),
std::unique_ptr<AudioEncoder>>>>
absl::Nullable<std::unique_ptr<AudioEncoder>> CreateEncoder(
Rank0,
const Environment& env,
const typename Trait::Config& config,
const AudioEncoderFactory::Options& options) {
return Trait::MakeAudioEncoder(config, options.payload_type,
options.codec_pair_id);
}
template <typename Trait,
typename = std::enable_if_t<std::is_convertible_v<
decltype(Trait::MakeAudioEncoder(
std::declval<typename Trait::Config>(),
int{},
std::declval<absl::optional<AudioCodecPairId>>())),
std::unique_ptr<AudioEncoder>>>>
absl::Nullable<std::unique_ptr<AudioEncoder>> LegacyCreateEncoder(
Rank1,
const typename Trait::Config& config,
int payload_type,
absl::optional<AudioCodecPairId> codec_pair_ids) {
return Trait::MakeAudioEncoder(config, payload_type, codec_pair_ids);
}
template <typename Trait>
absl::Nullable<std::unique_ptr<AudioEncoder>> LegacyCreateEncoder(
Rank0,
const typename Trait::Config& config,
int payload_type,
absl::optional<AudioCodecPairId> codec_pair_ids) {
RTC_CHECK_NOTREACHED();
}
// Inductive case: Called with n + 1 template parameters; calls subroutines
// with n template parameters.
template <typename T, typename... Ts>
@ -80,7 +139,8 @@ struct Helper<T, Ts...> {
absl::optional<AudioCodecPairId> codec_pair_id) {
auto opt_config = T::SdpToConfig(format);
if (opt_config) {
return T::MakeAudioEncoder(*opt_config, payload_type, codec_pair_id);
return LegacyCreateEncoder<T>(Rank1{}, *opt_config, payload_type,
codec_pair_id);
} else {
return Helper<Ts...>::MakeAudioEncoder(payload_type, format,
codec_pair_id);
@ -92,10 +152,7 @@ struct Helper<T, Ts...> {
const SdpAudioFormat& format,
const AudioEncoderFactory::Options& options) {
if (auto opt_config = T::SdpToConfig(format); opt_config.has_value()) {
// TODO: bugs.webrtc.org/343086059 - propagate `env` to the individual
// encoder factory functions.
return T::MakeAudioEncoder(*opt_config, options.payload_type,
options.codec_pair_id);
return CreateEncoder<T>(Rank1{}, env, *opt_config, options);
}
return Helper<Ts...>::CreateAudioEncoder(env, format, options);
}
@ -151,8 +208,13 @@ class AudioEncoderFactoryT : public AudioEncoderFactory {
// AudioCodecInfo QueryAudioEncoder(const ConfigType& config);
//
// // Creates an AudioEncoder for the specified format. Used to implement
// // AudioEncoderFactory::MakeAudioEncoder().
// std::unique_ptr<AudioDecoder> MakeAudioEncoder(
// // AudioEncoderFactory::Create.
// std::unique_ptr<AudioEncoder> MakeAudioEncoder(
// const Environment& env,
// const ConfigType& config,
// const AudioEncoderFactory::Options& options);
// or
// std::unique_ptr<AudioEncoder> MakeAudioEncoder(
// const ConfigType& config,
// int payload_type,
// absl::optional<AudioCodecPairId> codec_pair_id);
@ -160,6 +222,12 @@ class AudioEncoderFactoryT : public AudioEncoderFactory {
// ConfigType should be a type that encapsulates all the settings needed to
// create an AudioEncoder. T::Config (where T is the encoder struct) should
// either be the config type, or an alias for it.
// When both MakeAudioEncoder signatures are present, 1st one is preferred.
//
// Note: AudioEncoderFactory::MakeAudioEncoder factory can't use 1st signature,
// and thus uses 2nd signature when available, crashes otherwise.
// TODO: bugs.webrtc.org/343086059 - Remove the note above when MakeAudioEncoder
// is removed.
//
// Whenever it tries to do something, the new factory will try each of the
// encoders in the order they were specified in the template argument list,

View File

@ -37,8 +37,10 @@ namespace webrtc {
namespace {
using ::testing::IsNull;
using ::testing::NiceMock;
using ::testing::Pointer;
using ::testing::Property;
using ::testing::Return;
struct BogusParams {
static SdpAudioFormat AudioFormat() { return {"bogus", 8000, 1}; }
@ -87,6 +89,125 @@ struct AudioEncoderFakeApi {
}
};
// Trait to pass as template parameter to `CreateAudioEncoderFactory` with
// all the functions except the functions to create the audio encoder.
struct BaseAudioEncoderApi {
// Create Encoders with different sample rates depending if it is created
// through V1 or V2 method so that a test may detect which method was used.
static constexpr int kV1SameRate = 10'000;
static constexpr int kV2SameRate = 20'000;
struct Config {};
static SdpAudioFormat AudioFormat() { return {"fake", 16'000, 2, {}}; }
static AudioCodecInfo CodecInfo() { return {16'000, 2, 23456}; }
static absl::optional<Config> SdpToConfig(
const SdpAudioFormat& audio_format) {
return Config();
}
static void AppendSupportedEncoders(std::vector<AudioCodecSpec>* specs) {
specs->push_back({AudioFormat(), CodecInfo()});
}
static AudioCodecInfo QueryAudioEncoder(const Config&) { return CodecInfo(); }
};
struct AudioEncoderApiWithV1Make : BaseAudioEncoderApi {
static std::unique_ptr<AudioEncoder> MakeAudioEncoder(
const Config&,
int payload_type,
absl::optional<AudioCodecPairId> codec_pair_id) {
auto encoder = std::make_unique<NiceMock<MockAudioEncoder>>();
ON_CALL(*encoder, SampleRateHz).WillByDefault(Return(kV1SameRate));
return encoder;
}
};
struct AudioEncoderApiWithV2Make : BaseAudioEncoderApi {
static std::unique_ptr<AudioEncoder> MakeAudioEncoder(
const Environment& env,
const Config& config,
const AudioEncoderFactory::Options& options) {
auto encoder = std::make_unique<NiceMock<MockAudioEncoder>>();
ON_CALL(*encoder, SampleRateHz).WillByDefault(Return(kV2SameRate));
return encoder;
}
};
struct AudioEncoderApiWithBothV1AndV2Make : BaseAudioEncoderApi {
static std::unique_ptr<AudioEncoder> MakeAudioEncoder(
const Config&,
int payload_type,
absl::optional<AudioCodecPairId> codec_pair_id) {
auto encoder = std::make_unique<NiceMock<MockAudioEncoder>>();
ON_CALL(*encoder, SampleRateHz).WillByDefault(Return(kV1SameRate));
return encoder;
}
static std::unique_ptr<AudioEncoder> MakeAudioEncoder(
const Environment& env,
const Config& config,
const AudioEncoderFactory::Options& options) {
auto encoder = std::make_unique<NiceMock<MockAudioEncoder>>();
ON_CALL(*encoder, SampleRateHz).WillByDefault(Return(kV2SameRate));
return encoder;
}
};
TEST(AudioEncoderFactoryTemplateTest,
UsesV1MakeAudioEncoderWhenV2IsNotAvailable) {
const Environment env = CreateEnvironment();
auto factory = CreateAudioEncoderFactory<AudioEncoderApiWithV1Make>();
EXPECT_THAT(
factory->MakeAudioEncoder(17, BaseAudioEncoderApi::AudioFormat(), {}),
Pointer(Property(&AudioEncoder::SampleRateHz,
BaseAudioEncoderApi::kV1SameRate)));
EXPECT_THAT(factory->Create(env, BaseAudioEncoderApi::AudioFormat(), {}),
Pointer(Property(&AudioEncoder::SampleRateHz,
BaseAudioEncoderApi::kV1SameRate)));
}
TEST(AudioEncoderFactoryTemplateTest,
PreferV2MakeAudioEncoderWhenBothAreAvailable) {
const Environment env = CreateEnvironment();
auto factory =
CreateAudioEncoderFactory<AudioEncoderApiWithBothV1AndV2Make>();
EXPECT_THAT(factory->Create(env, BaseAudioEncoderApi::AudioFormat(), {}),
Pointer(Property(&AudioEncoder::SampleRateHz,
BaseAudioEncoderApi::kV2SameRate)));
// For backward compatibility legacy AudioEncoderFactory::MakeAudioEncoder
// still can be used, and uses older signature of the Trait::MakeAudioEncoder.
EXPECT_THAT(
factory->MakeAudioEncoder(17, BaseAudioEncoderApi::AudioFormat(), {}),
Pointer(Property(&AudioEncoder::SampleRateHz,
BaseAudioEncoderApi::kV1SameRate)));
}
TEST(AudioEncoderFactoryTemplateTest, CanUseTraitWithOnlyV2MakeAudioEncoder) {
const Environment env = CreateEnvironment();
auto factory = CreateAudioEncoderFactory<AudioEncoderApiWithV2Make>();
EXPECT_THAT(factory->Create(env, BaseAudioEncoderApi::AudioFormat(), {}),
Pointer(Property(&AudioEncoder::SampleRateHz,
BaseAudioEncoderApi::kV2SameRate)));
}
#if GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID)
TEST(AudioEncoderFactoryTemplateTest, CrashesWhenV2OnlyTraitUsedWithOlderApi) {
auto factory = CreateAudioEncoderFactory<AudioEncoderApiWithV2Make>();
// V2 signature requires Environment that
// AudioEncoderFactory::MakeAudioEncoder doesn't provide.
EXPECT_DEATH(
factory->MakeAudioEncoder(17, BaseAudioEncoderApi::AudioFormat(), {}),
"");
}
#endif
TEST(AudioEncoderFactoryTemplateTest, NoEncoderTypes) {
test::ScopedKeyValueConfig field_trials;
const Environment env = CreateEnvironment(&field_trials);