Added a flag to AudioCodecSpec to indicate adaptive bitrate support.

It's currently only used to ensure transport-cc is enabled for the format in question. It might be used to toggle more things in future.

BUG=webrtc:5806

Review-Url: https://codereview.webrtc.org/2669583002
Cr-Commit-Position: refs/heads/master@{#16514}
This commit is contained in:
ossu 2017-02-09 05:14:32 -08:00 committed by Commit bot
parent 0289364abc
commit 9def800b70
5 changed files with 135 additions and 32 deletions

View File

@ -1166,26 +1166,30 @@ AudioCodecs WebRtcVoiceEngine::CollectRecvCodecs() const {
{ 32000, false },
{ 48000, false }};
auto map_format = [&mapper, &out] (const webrtc::SdpAudioFormat& format) {
auto map_format = [&mapper](const webrtc::SdpAudioFormat& format,
AudioCodecs* out) {
rtc::Optional<AudioCodec> opt_codec = mapper.ToAudioCodec(format);
if (!opt_codec) {
if (opt_codec) {
if (out) {
out->push_back(*opt_codec);
}
} else {
LOG(LS_ERROR) << "Unable to assign payload type to format: " << format;
return false;
}
auto& codec = *opt_codec;
if (IsCodec(codec, kOpusCodecName)) {
// TODO(ossu): Set this specifically for Opus for now, until we have a
// better way of dealing with rtcp-fb parameters.
codec.AddFeedbackParam(
FeedbackParam(kRtcpFbParamTransportCc, kParamValueEmpty));
}
out.push_back(codec);
return true;
return opt_codec;
};
for (const auto& spec : specs) {
if (map_format(spec.format)) {
// We need to do some extra stuff before adding the main codecs to out.
rtc::Optional<AudioCodec> opt_codec = map_format(spec.format, nullptr);
if (opt_codec) {
AudioCodec& codec = *opt_codec;
if (spec.supports_network_adaption) {
codec.AddFeedbackParam(
FeedbackParam(kRtcpFbParamTransportCc, kParamValueEmpty));
}
if (spec.allow_comfort_noise) {
// Generate a CN entry if the decoder allows it and we support the
// clockrate.
@ -1200,20 +1204,22 @@ AudioCodecs WebRtcVoiceEngine::CollectRecvCodecs() const {
if (dtmf != generate_dtmf.end()) {
dtmf->second = true;
}
out.push_back(codec);
}
}
// Add CN codecs after "proper" audio codecs.
for (const auto& cn : generate_cn) {
if (cn.second) {
map_format({kCnCodecName, cn.first, 1});
map_format({kCnCodecName, cn.first, 1}, &out);
}
}
// Add telephone-event codecs last.
for (const auto& dtmf : generate_dtmf) {
if (dtmf.second) {
map_format({kDtmfCodecName, dtmf.first, 1});
map_format({kDtmfCodecName, dtmf.first, 1}, &out);
}
}

View File

@ -3680,3 +3680,73 @@ TEST(WebRtcVoiceEngineTest, SetRecvCodecs) {
parameters.codecs = engine.recv_codecs();
EXPECT_TRUE(channel.SetRecvParameters(parameters));
}
TEST(WebRtcVoiceEngineTest, CollectRecvCodecs) {
std::vector<webrtc::AudioCodecSpec> specs;
webrtc::AudioCodecSpec spec1({"codec1", 48000, 2, {{"param1", "value1"}}});
spec1.allow_comfort_noise = false;
spec1.supports_network_adaption = true;
specs.push_back(spec1);
webrtc::AudioCodecSpec spec2({"codec2", 32000, 1});
spec2.allow_comfort_noise = false;
specs.push_back(spec2);
specs.push_back(webrtc::AudioCodecSpec({"codec3", 16000, 1,
{{"param1", "value1b"},
{"param2", "value2"}}}));
specs.push_back(webrtc::AudioCodecSpec({"codec4", 8000, 1}));
specs.push_back(webrtc::AudioCodecSpec({"codec5", 8000, 2}));
rtc::scoped_refptr<webrtc::MockAudioDecoderFactory> mock_factory =
new rtc::RefCountedObject<webrtc::MockAudioDecoderFactory>;
EXPECT_CALL(*mock_factory.get(), GetSupportedDecoders())
.WillOnce(Return(specs));
cricket::WebRtcVoiceEngine engine(nullptr, mock_factory, nullptr);
auto codecs = engine.recv_codecs();
EXPECT_EQ(11, codecs.size());
// Rather than just ASSERTing that there are enough codecs, ensure that we can
// check the actual values safely, to provide better test results.
auto get_codec =
[&codecs](size_t index) -> const cricket::AudioCodec& {
static const cricket::AudioCodec missing_codec(0, "<missing>", 0, 0, 0);
if (codecs.size() > index)
return codecs[index];
return missing_codec;
};
// Ensure the general codecs are generated first and in order.
for (size_t i = 0; i != specs.size(); ++i) {
EXPECT_EQ(specs[i].format.name, get_codec(i).name);
EXPECT_EQ(specs[i].format.clockrate_hz, get_codec(i).clockrate);
EXPECT_EQ(specs[i].format.num_channels, get_codec(i).channels);
EXPECT_EQ(specs[i].format.parameters, get_codec(i).params);
}
// Find the index of a codec, or -1 if not found, so that we can easily check
// supplementary codecs are orderd after the general codecs.
auto find_codec =
[&codecs](const webrtc::SdpAudioFormat& format) -> int {
for (size_t i = 0; i != codecs.size(); ++i) {
const cricket::AudioCodec& codec = codecs[i];
if (STR_CASE_CMP(codec.name.c_str(), format.name.c_str()) == 0 &&
codec.clockrate == format.clockrate_hz &&
codec.channels == format.num_channels) {
return static_cast<int>(i);
}
}
return -1;
};
// Ensure all supplementary codecs are generated last. Their internal ordering
// is not important.
// Without this cast, the comparison turned unsigned and, thus, failed for -1.
const int num_specs = static_cast<int>(specs.size());
EXPECT_GE(find_codec({"cn", 8000, 1}), num_specs);
EXPECT_GE(find_codec({"cn", 16000, 1}), num_specs);
EXPECT_EQ(find_codec({"cn", 32000, 1}), -1);
EXPECT_GE(find_codec({"telephone-event", 8000, 1}), num_specs);
EXPECT_GE(find_codec({"telephone-event", 16000, 1}), num_specs);
EXPECT_GE(find_codec({"telephone-event", 32000, 1}), num_specs);
EXPECT_GE(find_codec({"telephone-event", 48000, 1}), num_specs);
}

View File

@ -77,4 +77,10 @@ std::ostream& operator<<(std::ostream& os, const SdpAudioFormat& saf) {
return os;
}
AudioCodecSpec::AudioCodecSpec(const SdpAudioFormat& format)
: format(format) {}
AudioCodecSpec::AudioCodecSpec(SdpAudioFormat&& format)
: format(std::move(format)) {}
} // namespace webrtc

View File

@ -54,10 +54,26 @@ struct SdpAudioFormat {
void swap(SdpAudioFormat& a, SdpAudioFormat& b);
std::ostream& operator<<(std::ostream& os, const SdpAudioFormat& saf);
// To avoid API breakage, and make the code clearer, AudioCodecSpec should not
// be directly initializable with any flags indicating optional support. If it
// were, these initializers would break any time a new flag was added. It's also
// more difficult to understand:
// AudioCodecSpec spec{{"format", 8000, 1}, true, false, false, true, true};
// than
// AudioCodecSpec spec({"format", 8000, 1});
// spec.allow_comfort_noise = true;
// spec.future_flag_b = true;
// spec.future_flag_c = true;
struct AudioCodecSpec {
explicit AudioCodecSpec(const SdpAudioFormat& format);
explicit AudioCodecSpec(SdpAudioFormat&& format);
~AudioCodecSpec() = default;
SdpAudioFormat format;
bool allow_comfort_noise; // This encoder can be used with an external
// comfort noise generator.
bool allow_comfort_noise = true; // This codec can be used with an external
// comfort noise generator.
bool supports_network_adaption = false; // This codec can adapt to varying
// network conditions.
};
} // namespace webrtc

View File

@ -174,31 +174,36 @@ NamedDecoderConstructor decoder_constructors[] = {
class BuiltinAudioDecoderFactory : public AudioDecoderFactory {
public:
std::vector<AudioCodecSpec> GetSupportedDecoders() override {
static std::vector<AudioCodecSpec> specs = {
// Although this looks a bit strange, it means specs need only be initalized
// once, and that that initialization is thread-safe.
static std::vector<AudioCodecSpec> specs =
[]{
std::vector<AudioCodecSpec> specs;
#ifdef WEBRTC_CODEC_OPUS
{ { "opus", 48000, 2, {
{"minptime", "10" },
{"useinbandfec", "1" }
}
}, false
},
AudioCodecSpec opus({"opus", 48000, 2, {
{"minptime", "10"},
{"useinbandfec", "1"}
}});
opus.allow_comfort_noise = false;
opus.supports_network_adaption = true;
specs.push_back(opus);
#endif
#if (defined(WEBRTC_CODEC_ISAC) || defined(WEBRTC_CODEC_ISACFX))
{{"isac", 16000, 1}, true},
specs.push_back(AudioCodecSpec({"isac", 16000, 1}));
#endif
#if (defined(WEBRTC_CODEC_ISAC))
{{"isac", 32000, 1}, true},
specs.push_back(AudioCodecSpec({"isac", 32000, 1}));
#endif
#ifdef WEBRTC_CODEC_G722
{{"G722", 8000, 1}, true},
specs.push_back(AudioCodecSpec({"G722", 8000, 1}));
#endif
#ifdef WEBRTC_CODEC_ILBC
{{"iLBC", 8000, 1}, true},
specs.push_back(AudioCodecSpec({"iLBC", 8000, 1}));
#endif
{{"PCMU", 8000, 1}, true},
{{"PCMA", 8000, 1}, true}
};
specs.push_back(AudioCodecSpec({"PCMU", 8000, 1}));
specs.push_back(AudioCodecSpec({"PCMA", 8000, 1}));
return specs;
}();
return specs;
}