red: handle 0-redundancy encoding case

which is a "degenerated" case that just adds a one-byte header.

BUG=webrtc:11640

Change-Id: I173f4cb56270e0090219e4450b036bbe1b51756a
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/231696
Commit-Queue: Henrik Lundin <henrik.lundin@webrtc.org>
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Reviewed-by: Henrik Lundin <henrik.lundin@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#34976}
diff --git a/modules/audio_coding/codecs/red/audio_encoder_copy_red.cc b/modules/audio_coding/codecs/red/audio_encoder_copy_red.cc
index 0667962..0e9e100 100644
--- a/modules/audio_coding/codecs/red/audio_encoder_copy_red.cc
+++ b/modules/audio_coding/codecs/red/audio_encoder_copy_red.cc
@@ -43,7 +43,7 @@
       webrtc::field_trial::FindFullName("WebRTC-Audio-Red-For-Opus");
   size_t redundancy = 0;
   if (sscanf(red_trial.c_str(), "Enabled-%zu", &redundancy) != 1 ||
-      redundancy < 1 || redundancy > 9) {
+      redundancy > 9) {
     return kRedNumberOfRedundantEncodings;
   }
   return redundancy;
@@ -161,8 +161,10 @@
     rit->second.SetData(next->second);
   }
   it = redundant_encodings_.begin();
-  it->first = info;
-  it->second.SetData(primary_encoded_);
+  if (it != redundant_encodings_.end()) {
+    it->first = info;
+    it->second.SetData(primary_encoded_);
+  }
 
   // Update main EncodedInfo.
   info.payload_type = red_payload_type_;
diff --git a/modules/audio_coding/codecs/red/audio_encoder_copy_red_unittest.cc b/modules/audio_coding/codecs/red/audio_encoder_copy_red_unittest.cc
index f8e3437..168ac3a 100644
--- a/modules/audio_coding/codecs/red/audio_encoder_copy_red_unittest.cc
+++ b/modules/audio_coding/codecs/red/audio_encoder_copy_red_unittest.cc
@@ -196,6 +196,32 @@
 }
 
 // Checks that the correct payload sizes are populated into the redundancy
+// information for a redundancy level of 0.
+TEST_F(AudioEncoderCopyRedTest, CheckPayloadSizes0) {
+  webrtc::test::ScopedFieldTrials field_trials(
+      "WebRTC-Audio-Red-For-Opus/Enabled-0/");
+  // Recreate the RED encoder to take the new field trial setting into account.
+  AudioEncoderCopyRed::Config config;
+  config.payload_type = red_payload_type_;
+  config.speech_encoder = std::move(red_->ReclaimContainedEncoders()[0]);
+  red_.reset(new AudioEncoderCopyRed(std::move(config)));
+
+  // Let the mock encoder return payload sizes 1, 2, 3, ..., 10 for the sequence
+  // of calls.
+  static const int kNumPackets = 10;
+  InSequence s;
+  for (int encode_size = 1; encode_size <= kNumPackets; ++encode_size) {
+    EXPECT_CALL(*mock_encoder_, EncodeImpl(_, _, _))
+        .WillOnce(Invoke(MockAudioEncoder::FakeEncoding(encode_size)));
+  }
+
+  for (size_t i = 1; i <= kNumPackets; ++i) {
+    Encode();
+    ASSERT_EQ(0u, encoded_info_.redundant.size());
+    EXPECT_EQ(1 + i, encoded_info_.encoded_bytes);
+  }
+}
+// Checks that the correct payload sizes are populated into the redundancy
 // information for a redundancy level of 2.
 TEST_F(AudioEncoderCopyRedTest, CheckPayloadSizes2) {
   webrtc::test::ScopedFieldTrials field_trials(
@@ -435,6 +461,34 @@
                     encoded_info_.redundant[1].encoded_timestamp;
 }
 
+// Variant with a redundancy of 0.
+TEST_F(AudioEncoderCopyRedTest, CheckRFC2198Header0) {
+  webrtc::test::ScopedFieldTrials field_trials(
+      "WebRTC-Audio-Red-For-Opus/Enabled-0/");
+  // Recreate the RED encoder to take the new field trial setting into account.
+  AudioEncoderCopyRed::Config config;
+  config.payload_type = red_payload_type_;
+  config.speech_encoder = std::move(red_->ReclaimContainedEncoders()[0]);
+  red_.reset(new AudioEncoderCopyRed(std::move(config)));
+
+  const int primary_payload_type = red_payload_type_ + 1;
+  AudioEncoder::EncodedInfo info;
+  info.encoded_bytes = 10;
+  info.encoded_timestamp = timestamp_;
+  info.payload_type = primary_payload_type;
+
+  EXPECT_CALL(*mock_encoder_, EncodeImpl(_, _, _))
+      .WillOnce(Invoke(MockAudioEncoder::FakeEncoding(info)));
+  Encode();
+  info.encoded_timestamp = timestamp_;  // update timestamp.
+  EXPECT_CALL(*mock_encoder_, EncodeImpl(_, _, _))
+      .WillOnce(Invoke(MockAudioEncoder::FakeEncoding(info)));
+  Encode();  // Second call will not produce a redundant encoding.
+
+  EXPECT_EQ(encoded_.size(),
+            1u + 1 * 10u);  // header size + one encoded payloads.
+  EXPECT_EQ(encoded_[0], primary_payload_type);
+}
 // Variant with a redundancy of 2.
 TEST_F(AudioEncoderCopyRedTest, CheckRFC2198Header2) {
   webrtc::test::ScopedFieldTrials field_trials(