diff --git a/webrtc/api/audio_codecs/BUILD.gn b/webrtc/api/audio_codecs/BUILD.gn index ca0cf95b42..416ccbbf3f 100644 --- a/webrtc/api/audio_codecs/BUILD.gn +++ b/webrtc/api/audio_codecs/BUILD.gn @@ -17,6 +17,7 @@ rtc_source_set("audio_codecs_api") { "audio_decoder.cc", "audio_decoder.h", "audio_decoder_factory.h", + "audio_decoder_factory_template.h", "audio_encoder.cc", "audio_encoder.h", "audio_encoder_factory.h", diff --git a/webrtc/api/audio_codecs/audio_decoder_factory_template.h b/webrtc/api/audio_codecs/audio_decoder_factory_template.h new file mode 100644 index 0000000000..f79ead7469 --- /dev/null +++ b/webrtc/api/audio_codecs/audio_decoder_factory_template.h @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2017 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. + */ + +#ifndef WEBRTC_API_AUDIO_CODECS_AUDIO_DECODER_FACTORY_TEMPLATE_H_ +#define WEBRTC_API_AUDIO_CODECS_AUDIO_DECODER_FACTORY_TEMPLATE_H_ + +#include +#include + +#include "webrtc/api/audio_codecs/audio_decoder_factory.h" +#include "webrtc/base/scoped_ref_ptr.h" + +namespace webrtc { + +namespace audio_decoder_factory_template_impl { + +template +struct Helper; + +// Base case: 0 template parameters. +template <> +struct Helper<> { + static void AppendSupportedDecoders(std::vector* specs) {} + static bool IsSupportedDecoder(const SdpAudioFormat& format) { return false; } + static std::unique_ptr MakeAudioDecoder( + const SdpAudioFormat& format) { + return nullptr; + } +}; + +// Inductive case: Called with n + 1 template parameters; calls subroutines +// with n template parameters. +template +struct Helper { + static void AppendSupportedDecoders(std::vector* specs) { + T::AppendSupportedDecoders(specs); + Helper::AppendSupportedDecoders(specs); + } + static bool IsSupportedDecoder(const SdpAudioFormat& format) { + auto opt_config = T::SdpToConfig(format); + return opt_config ? true : Helper::IsSupportedDecoder(format); + } + static std::unique_ptr MakeAudioDecoder( + const SdpAudioFormat& format) { + auto opt_config = T::SdpToConfig(format); + return opt_config ? T::MakeAudioDecoder(*opt_config) + : Helper::MakeAudioDecoder(format); + } +}; + +template +class AudioDecoderFactoryT : public AudioDecoderFactory { + public: + std::vector GetSupportedDecoders() override { + std::vector specs; + Helper::AppendSupportedDecoders(&specs); + return specs; + } + + bool IsSupportedDecoder(const SdpAudioFormat& format) override { + return Helper::IsSupportedDecoder(format); + } + + std::unique_ptr MakeAudioDecoder( + const SdpAudioFormat& format) override { + return Helper::MakeAudioDecoder(format); + } +}; + +} // namespace audio_decoder_factory_template_impl + +// Make an AudioDecoderFactory that can create instances of the given decoders. +// +// Each decoder type is given as a template argument to the function; it should +// be a struct with the following static member functions: +// +// // Converts |audio_format| to a ConfigType instance. Returns an empty +// // optional if |audio_format| doesn't correctly specify an decoder of our +// // type. +// rtc::Optional SdpToConfig(const SdpAudioFormat& audio_format); +// +// // Appends zero or more AudioCodecSpecs to the list that will be returned +// // by AudioDecoderFactory::GetSupportedDecoders(). +// void AppendSupportedDecoders(std::vector* specs); +// +// // Creates an AudioDecoder for the specified format. Used to implement +// // AudioDecoderFactory::MakeAudioDecoder(). +// std::unique_ptr MakeAudioDecoder(const ConfigType& config); +// +// ConfigType should be a type that encapsulates all the settings needed to +// create an AudioDecoder. +// +// Whenever it tries to do something, the new factory will try each of the +// decoder types in the order they were specified in the template argument +// list, stopping at the first one that claims to be able to do the job. +// +// NOTE: This function is still under development and may change without notice. +// +// TODO(kwiberg): Point at CreateBuiltinAudioDecoderFactory() for an example of +// how it is used. +template +rtc::scoped_refptr CreateAudioDecoderFactory() { + // There's no technical reason we couldn't allow zero template parameters, + // but such a factory couldn't create any decoders, and callers can do this + // by mistake by simply forgetting the <> altogether. So we forbid it in + // order to prevent caller foot-shooting. + static_assert(sizeof...(Ts) >= 1, + "Caller must give at least one template parameter"); + + return rtc::scoped_refptr( + new rtc::RefCountedObject< + audio_decoder_factory_template_impl::AudioDecoderFactoryT>()); +} + +} // namespace webrtc + +#endif // WEBRTC_API_AUDIO_CODECS_AUDIO_DECODER_FACTORY_TEMPLATE_H_ diff --git a/webrtc/api/audio_codecs/test/BUILD.gn b/webrtc/api/audio_codecs/test/BUILD.gn index 7f2aa399d4..22b3f4e8e9 100644 --- a/webrtc/api/audio_codecs/test/BUILD.gn +++ b/webrtc/api/audio_codecs/test/BUILD.gn @@ -16,6 +16,7 @@ if (rtc_include_tests) { rtc_source_set("audio_codecs_api_unittests") { testonly = true sources = [ + "audio_decoder_factory_template_unittest.cc", "audio_encoder_factory_template_unittest.cc", ] deps = [ diff --git a/webrtc/api/audio_codecs/test/audio_decoder_factory_template_unittest.cc b/webrtc/api/audio_codecs/test/audio_decoder_factory_template_unittest.cc new file mode 100644 index 0000000000..170bba79bf --- /dev/null +++ b/webrtc/api/audio_codecs/test/audio_decoder_factory_template_unittest.cc @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2017 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 "webrtc/api/audio_codecs/audio_decoder_factory_template.h" +#include "webrtc/base/ptr_util.h" +#include "webrtc/test/gmock.h" +#include "webrtc/test/gtest.h" +#include "webrtc/test/mock_audio_decoder.h" + +namespace webrtc { + +namespace { + +struct BogusParams { + static SdpAudioFormat AudioFormat() { return {"bogus", 8000, 1}; } + static AudioCodecInfo CodecInfo() { return {8000, 1, 12345}; } +}; + +struct ShamParams { + static SdpAudioFormat AudioFormat() { + return {"sham", 16000, 2, {{"param", "value"}}}; + } + static AudioCodecInfo CodecInfo() { return {16000, 2, 23456}; } +}; + +struct MyLittleConfig { + SdpAudioFormat audio_format; +}; + +template +struct AudioDecoderFakeApi { + static rtc::Optional SdpToConfig( + const SdpAudioFormat& audio_format) { + if (Params::AudioFormat() == audio_format) { + MyLittleConfig config = {audio_format}; + return rtc::Optional(config); + } else { + return rtc::Optional(); + } + } + + static void AppendSupportedDecoders(std::vector* specs) { + specs->push_back({Params::AudioFormat(), Params::CodecInfo()}); + } + + static AudioCodecInfo QueryAudioDecoder(const MyLittleConfig&) { + return Params::CodecInfo(); + } + + static std::unique_ptr MakeAudioDecoder(const MyLittleConfig&) { + auto dec = rtc::MakeUnique>(); + EXPECT_CALL(*dec, SampleRateHz()) + .WillOnce(testing::Return(Params::CodecInfo().sample_rate_hz)); + EXPECT_CALL(*dec, Die()); + return std::move(dec); + } +}; + +} // namespace + +TEST(AudioDecoderFactoryTemplateTest, NoDecoderTypes) { + rtc::scoped_refptr factory( + new rtc::RefCountedObject< + audio_decoder_factory_template_impl::AudioDecoderFactoryT<>>()); + EXPECT_THAT(factory->GetSupportedDecoders(), testing::IsEmpty()); + EXPECT_FALSE(factory->IsSupportedDecoder({"foo", 8000, 1})); + EXPECT_EQ(nullptr, factory->MakeAudioDecoder({"bar", 16000, 1})); +} + +TEST(AudioDecoderFactoryTemplateTest, OneDecoderType) { + auto factory = CreateAudioDecoderFactory>(); + EXPECT_THAT(factory->GetSupportedDecoders(), + testing::ElementsAre( + AudioCodecSpec{{"bogus", 8000, 1}, {8000, 1, 12345}})); + EXPECT_FALSE(factory->IsSupportedDecoder({"foo", 8000, 1})); + EXPECT_TRUE(factory->IsSupportedDecoder({"bogus", 8000, 1})); + EXPECT_EQ(nullptr, factory->MakeAudioDecoder({"bar", 16000, 1})); + auto dec = factory->MakeAudioDecoder({"bogus", 8000, 1}); + ASSERT_NE(nullptr, dec); + EXPECT_EQ(8000, dec->SampleRateHz()); +} + +TEST(AudioDecoderFactoryTemplateTest, TwoDecoderTypes) { + auto factory = CreateAudioDecoderFactory, + AudioDecoderFakeApi>(); + EXPECT_THAT(factory->GetSupportedDecoders(), + testing::ElementsAre( + AudioCodecSpec{{"bogus", 8000, 1}, {8000, 1, 12345}}, + AudioCodecSpec{{"sham", 16000, 2, {{"param", "value"}}}, + {16000, 2, 23456}})); + EXPECT_FALSE(factory->IsSupportedDecoder({"foo", 8000, 1})); + EXPECT_TRUE(factory->IsSupportedDecoder({"bogus", 8000, 1})); + EXPECT_TRUE( + factory->IsSupportedDecoder({"sham", 16000, 2, {{"param", "value"}}})); + EXPECT_EQ(nullptr, factory->MakeAudioDecoder({"bar", 16000, 1})); + auto dec1 = factory->MakeAudioDecoder({"bogus", 8000, 1}); + ASSERT_NE(nullptr, dec1); + EXPECT_EQ(8000, dec1->SampleRateHz()); + EXPECT_EQ(nullptr, factory->MakeAudioDecoder({"sham", 16000, 2})); + auto dec2 = + factory->MakeAudioDecoder({"sham", 16000, 2, {{"param", "value"}}}); + ASSERT_NE(nullptr, dec2); + EXPECT_EQ(16000, dec2->SampleRateHz()); +} + +} // namespace webrtc