negotiate RED codec for audio

negotiates the RED codec for opus audio behind a field trial
  WebRTC-Audio-Redundancy
This adds the following line to the SDP:
  a=rtpmap:someid RED/48000/2

To test start Chrome with
  --force-fieldtrials=WebRTC-Audio-Red-For-Opus/Enabled

BUG=webrtc:11640

Change-Id: I8fa9fb07d03db5f90cdb08765baaa03d3d0458cc
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/176372
Reviewed-by: Henrik Lundin <henrik.lundin@webrtc.org>
Reviewed-by: Karl Wiberg <kwiberg@webrtc.org>
Commit-Queue: Harald Alvestrand <hta@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#31562}
This commit is contained in:
Philipp Hancke 2020-06-24 12:52:42 +02:00 committed by Commit Bot
parent afdbf8e6f8
commit edcd9665b8
7 changed files with 97 additions and 1 deletions

View File

@ -71,6 +71,7 @@ rtc_library("audio") {
"../modules/audio_coding:audio_coding_module_typedefs",
"../modules/audio_coding:audio_encoder_cng",
"../modules/audio_coding:audio_network_adaptor_config",
"../modules/audio_coding:red",
"../modules/audio_device",
"../modules/audio_processing",
"../modules/audio_processing:api",

View File

@ -31,6 +31,7 @@
#include "logging/rtc_event_log/events/rtc_event_audio_send_stream_config.h"
#include "logging/rtc_event_log/rtc_stream_config.h"
#include "modules/audio_coding/codecs/cng/audio_encoder_cng.h"
#include "modules/audio_coding/codecs/red/audio_encoder_copy_red.h"
#include "modules/audio_processing/include/audio_processing.h"
#include "modules/rtp_rtcp/source/rtp_header_extensions.h"
#include "rtc_base/checks.h"
@ -659,6 +660,14 @@ bool AudioSendStream::SetupSendCodec(const Config& new_config) {
new_config.send_codec_spec->format.clockrate_hz);
}
// Wrap the encoder in a RED encoder, if RED is enabled.
if (spec.red_payload_type) {
AudioEncoderCopyRed::Config red_config;
red_config.payload_type = *spec.red_payload_type;
red_config.speech_encoder = std::move(encoder);
encoder = std::make_unique<AudioEncoderCopyRed>(std::move(red_config));
}
// Set currently known overhead (used in ANA, opus only).
// If overhead changes later, it will be updated in UpdateOverheadForEncoder.
{

View File

@ -371,6 +371,7 @@ TEST(AudioSendStreamTest, ConfigToString) {
config.send_codec_spec->nack_enabled = true;
config.send_codec_spec->transport_cc_enabled = false;
config.send_codec_spec->cng_payload_type = 42;
config.send_codec_spec->red_payload_type = 43;
config.encoder_factory = MockAudioEncoderFactory::CreateUnusedFactory();
config.rtp.extmap_allow_mixed = true;
config.rtp.extensions.push_back(
@ -383,7 +384,7 @@ TEST(AudioSendStreamTest, ConfigToString) {
"send_transport: null, "
"min_bitrate_bps: 12000, max_bitrate_bps: 34000, "
"send_codec_spec: {nack_enabled: true, transport_cc_enabled: false, "
"cng_payload_type: 42, payload_type: 103, "
"cng_payload_type: 42, red_payload_type: 43, payload_type: 103, "
"format: {name: isac, clockrate_hz: 16000, num_channels: 1, "
"parameters: {}}}}",
config.ToString());

View File

@ -75,6 +75,8 @@ std::string AudioSendStream::Config::SendCodecSpec::ToString() const {
ss << ", transport_cc_enabled: " << (transport_cc_enabled ? "true" : "false");
ss << ", cng_payload_type: "
<< (cng_payload_type ? rtc::ToString(*cng_payload_type) : "<unset>");
ss << ", red_payload_type: "
<< (red_payload_type ? rtc::ToString(*red_payload_type) : "<unset>");
ss << ", payload_type: " << payload_type;
ss << ", format: " << rtc::ToString(format);
ss << '}';

View File

@ -140,6 +140,7 @@ class AudioSendStream : public AudioSender {
bool nack_enabled = false;
bool transport_cc_enabled = false;
absl::optional<int> cng_payload_type;
absl::optional<int> red_payload_type;
// If unset, use the encoder's default target bitrate.
absl::optional<int> target_bitrate_bps;
};

View File

@ -99,6 +99,12 @@ std::string ToString(const AudioCodec& codec) {
return ss.Release();
}
// If this field trial is enabled, we will negotiate and use RFC 2198
// redundancy for opus audio.
bool IsAudioRedForOpusFieldTrialEnabled() {
return webrtc::field_trial::IsEnabled("WebRTC-Audio-Red-For-Opus");
}
bool IsCodec(const AudioCodec& codec, const char* ref_name) {
return absl::EqualsIgnoreCase(codec.name, ref_name);
}
@ -682,6 +688,11 @@ std::vector<AudioCodec> WebRtcVoiceEngine::CollectCodecs(
}
}
// Add red codec.
if (IsAudioRedForOpusFieldTrialEnabled()) {
map_format({kRedCodecName, 48000, 2}, &out);
}
// Add telephone-event codecs last.
for (const auto& dtmf : generate_dtmf) {
if (dtmf.second) {
@ -1542,6 +1553,8 @@ bool WebRtcVoiceMediaChannel::SetRecvCodecs(
}
auto format = AudioCodecToSdpAudioFormat(codec);
if (!IsCodec(codec, kCnCodecName) && !IsCodec(codec, kDtmfCodecName) &&
(!IsAudioRedForOpusFieldTrialEnabled() ||
!IsCodec(codec, kRedCodecName)) &&
!engine()->decoder_factory_->IsSupportedDecoder(format)) {
RTC_LOG(LS_ERROR) << "Unsupported codec: " << rtc::ToString(format);
return false;
@ -1692,6 +1705,19 @@ bool WebRtcVoiceMediaChannel::SetSendCodecs(
}
}
if (IsAudioRedForOpusFieldTrialEnabled()) {
// Loop through the codecs to find the RED codec that matches opus
// with respect to clockrate and number of channels.
for (const AudioCodec& red_codec : codecs) {
if (IsCodec(red_codec, kRedCodecName) &&
red_codec.clockrate == send_codec_spec->format.clockrate_hz &&
red_codec.channels == send_codec_spec->format.num_channels) {
send_codec_spec->red_payload_type = red_codec.id;
break;
}
}
}
if (send_codec_spec_ != send_codec_spec) {
send_codec_spec_ = std::move(send_codec_spec);
// Apply new settings to all streams.

View File

@ -59,6 +59,7 @@ const cricket::AudioCodec kG722CodecVoE(9, "G722", 16000, 64000, 1);
const cricket::AudioCodec kG722CodecSdp(9, "G722", 8000, 64000, 1);
const cricket::AudioCodec kCn8000Codec(13, "CN", 8000, 0, 1);
const cricket::AudioCodec kCn16000Codec(105, "CN", 16000, 0, 1);
const cricket::AudioCodec kRed48000Codec(112, "RED", 48000, 32000, 2);
const cricket::AudioCodec kTelephoneEventCodec1(106,
"telephone-event",
8000,
@ -1031,6 +1032,30 @@ TEST_P(WebRtcVoiceEngineTestFake, ChangeRecvCodecPayloadType) {
EXPECT_TRUE(channel_->SetRecvParameters(parameters));
}
// Test that we set Opus/Red under the field trial.
TEST_P(WebRtcVoiceEngineTestFake, RecvRed) {
webrtc::test::ScopedFieldTrials override_field_trials(
"WebRTC-Audio-Red-For-Opus/Enabled/");
EXPECT_TRUE(SetupRecvStream());
cricket::AudioRecvParameters parameters;
parameters.codecs.push_back(kOpusCodec);
parameters.codecs.push_back(kRed48000Codec);
EXPECT_TRUE(channel_->SetRecvParameters(parameters));
EXPECT_THAT(GetRecvStreamConfig(kSsrcX).decoder_map,
(ContainerEq<std::map<int, webrtc::SdpAudioFormat>>(
{{111, {"opus", 48000, 2}}, {112, {"red", 48000, 2}}})));
}
// Test that we do not allow setting Opus/Red by default.
TEST_P(WebRtcVoiceEngineTestFake, RecvRedDefault) {
EXPECT_TRUE(SetupRecvStream());
cricket::AudioRecvParameters parameters;
parameters.codecs.push_back(kOpusCodec);
parameters.codecs.push_back(kRed48000Codec);
EXPECT_FALSE(channel_->SetRecvParameters(parameters));
}
TEST_P(WebRtcVoiceEngineTestFake, SetSendBandwidthAuto) {
EXPECT_TRUE(SetupSendStream());
@ -1442,6 +1467,37 @@ TEST_P(WebRtcVoiceEngineTestFake, SetSendCodecs) {
EXPECT_FALSE(channel_->CanInsertDtmf());
}
// Test that we set Opus/Red under the field trial.
TEST_P(WebRtcVoiceEngineTestFake, SetSendCodecsRed) {
webrtc::test::ScopedFieldTrials override_field_trials(
"WebRTC-Audio-Red-For-Opus/Enabled/");
EXPECT_TRUE(SetupSendStream());
cricket::AudioSendParameters parameters;
parameters.codecs.push_back(kOpusCodec);
parameters.codecs.push_back(kRed48000Codec);
parameters.codecs[0].id = 96;
SetSendParameters(parameters);
const auto& send_codec_spec = *GetSendStreamConfig(kSsrcX).send_codec_spec;
EXPECT_EQ(96, send_codec_spec.payload_type);
EXPECT_STRCASEEQ("opus", send_codec_spec.format.name.c_str());
EXPECT_EQ(112, send_codec_spec.red_payload_type);
}
// Test that we set do not interpret Opus/Red by default.
TEST_P(WebRtcVoiceEngineTestFake, SetSendCodecsRedDefault) {
EXPECT_TRUE(SetupSendStream());
cricket::AudioSendParameters parameters;
parameters.codecs.push_back(kOpusCodec);
parameters.codecs.push_back(kRed48000Codec);
parameters.codecs[0].id = 96;
SetSendParameters(parameters);
const auto& send_codec_spec = *GetSendStreamConfig(kSsrcX).send_codec_spec;
EXPECT_EQ(96, send_codec_spec.payload_type);
EXPECT_STRCASEEQ("opus", send_codec_spec.format.name.c_str());
EXPECT_EQ(absl::nullopt, send_codec_spec.red_payload_type);
}
// Test that WebRtcVoiceEngine reconfigures, rather than recreates its
// AudioSendStream.
TEST_P(WebRtcVoiceEngineTestFake, DontRecreateSendStream) {