red: modify the encoder to send RFC 2198

modifies the RED encoder to send the actual RFC 2198 format
described in
  https://tools.ietf.org/html/rfc2198
Decoding is handled in neteq, see red_payload_splitter.h

BUG=webrtc:11640

Change-Id: Ib3005882a3ceee49d2b05c43357f552432a984ac
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/176371
Commit-Queue: Harald Alvestrand <hta@webrtc.org>
Reviewed-by: Henrik Lundin <henrik.lundin@webrtc.org>
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#31560}
diff --git a/AUTHORS b/AUTHORS
index 90b6cb7..188503e 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -92,6 +92,8 @@
 Stephan Hartmann <stha09@googlemail.com>
 
 &yet LLC <*@andyet.com>
+8x8 Inc. <*@sip-communicator.org>
+8x8 Inc. <*@8x8.com>
 Agora IO <*@agora.io>
 ARM Holdings <*@arm.com>
 BroadSoft Inc. <*@broadsoft.com>
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 e75806a..8d028c9 100644
--- a/modules/audio_coding/codecs/red/audio_encoder_copy_red.cc
+++ b/modules/audio_coding/codecs/red/audio_encoder_copy_red.cc
@@ -15,6 +15,7 @@
 #include <utility>
 #include <vector>
 
+#include "rtc_base/byte_order.h"
 #include "rtc_base/checks.h"
 
 namespace webrtc {
@@ -59,32 +60,62 @@
     uint32_t rtp_timestamp,
     rtc::ArrayView<const int16_t> audio,
     rtc::Buffer* encoded) {
-  const size_t primary_offset = encoded->size();
+  // Allocate room for RFC 2198 header if there is redundant data.
+  // Otherwise this will send the primary payload type without
+  // wrapping in RED.
+  const size_t header_length_bytes = secondary_info_.encoded_bytes > 0 ? 5 : 0;
+  size_t secondary_length_bytes = 0;
+
+  if (secondary_info_.encoded_bytes > 0) {
+    encoded->SetSize(header_length_bytes);
+    encoded->AppendData(secondary_encoded_);
+    secondary_length_bytes = secondary_info_.encoded_bytes;
+  }
   EncodedInfo info = speech_encoder_->Encode(rtp_timestamp, audio, encoded);
 
-  RTC_CHECK(info.redundant.empty()) << "Cannot use nested redundant encoders.";
-  RTC_DCHECK_EQ(encoded->size() - primary_offset, info.encoded_bytes);
-
-  if (info.encoded_bytes > 0) {
-    // |info| will be implicitly cast to an EncodedInfoLeaf struct, effectively
-    // discarding the (empty) vector of redundant information. This is
-    // intentional.
-    info.redundant.push_back(info);
-    RTC_DCHECK_EQ(info.redundant.size(), 1);
-    if (secondary_info_.encoded_bytes > 0) {
-      encoded->AppendData(secondary_encoded_);
-      info.redundant.push_back(secondary_info_);
-      RTC_DCHECK_EQ(info.redundant.size(), 2);
-    }
-    // Save primary to secondary.
-    secondary_encoded_.SetData(encoded->data() + primary_offset,
-                               info.encoded_bytes);
-    secondary_info_ = info;
-    RTC_DCHECK_EQ(info.speech, info.redundant[0].speech);
+  if (info.encoded_bytes == 0) {
+    encoded->Clear();
+    return info;
   }
+
+  // Actually construct the RFC 2198 header.
+  if (secondary_info_.encoded_bytes > 0) {
+    const uint32_t timestamp_delta =
+        info.encoded_timestamp - secondary_info_.encoded_timestamp;
+
+    encoded->data()[0] = secondary_info_.payload_type | 0x80;
+    RTC_DCHECK_LT(secondary_info_.encoded_bytes, 1 << 10);
+    rtc::SetBE16(static_cast<uint8_t*>(encoded->data()) + 1,
+                 (timestamp_delta << 2) | (secondary_info_.encoded_bytes >> 8));
+    encoded->data()[3] = secondary_info_.encoded_bytes & 0xff;
+    encoded->data()[4] = info.payload_type;
+  }
+
+  RTC_CHECK(info.redundant.empty()) << "Cannot use nested redundant encoders.";
+  RTC_DCHECK_EQ(encoded->size() - header_length_bytes - secondary_length_bytes,
+                info.encoded_bytes);
+
+  // |info| will be implicitly cast to an EncodedInfoLeaf struct, effectively
+  // discarding the (empty) vector of redundant information. This is
+  // intentional.
+  info.redundant.push_back(info);
+  RTC_DCHECK_EQ(info.redundant.size(), 1);
+  if (secondary_info_.encoded_bytes > 0) {
+    info.redundant.push_back(secondary_info_);
+    RTC_DCHECK_EQ(info.redundant.size(), 2);
+  }
+  // Save primary to secondary.
+  secondary_encoded_.SetData(
+      &encoded->data()[header_length_bytes + secondary_info_.encoded_bytes],
+      info.encoded_bytes);
+  secondary_info_ = info;
+  RTC_DCHECK_EQ(info.speech, info.redundant[0].speech);
+
   // Update main EncodedInfo.
-  info.payload_type = red_payload_type_;
-  info.encoded_bytes = 0;
+  if (header_length_bytes > 0) {
+    info.payload_type = red_payload_type_;
+  }
+  info.encoded_bytes = header_length_bytes;
   for (std::vector<EncodedInfoLeaf>::const_iterator it = info.redundant.begin();
        it != info.redundant.end(); ++it) {
     info.encoded_bytes += it->encoded_bytes;
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 e20515a..720acb4 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
@@ -139,6 +139,7 @@
 // new data, even if the RED codec is loaded with a secondary encoding.
 TEST_F(AudioEncoderCopyRedTest, CheckNoOutput) {
   static const size_t kEncodedSize = 17;
+  static const size_t kHeaderLenBytes = 5;
   {
     InSequence s;
     EXPECT_CALL(*mock_encoder_, EncodeImpl(_, _, _))
@@ -160,7 +161,7 @@
 
   // Final call to the speech encoder will produce output.
   Encode();
-  EXPECT_EQ(2 * kEncodedSize, encoded_info_.encoded_bytes);
+  EXPECT_EQ(2 * kEncodedSize + kHeaderLenBytes, encoded_info_.encoded_bytes);
   ASSERT_EQ(2u, encoded_info_.redundant.size());
 }
 
@@ -187,7 +188,7 @@
     ASSERT_EQ(2u, encoded_info_.redundant.size());
     EXPECT_EQ(i, encoded_info_.redundant[0].encoded_bytes);
     EXPECT_EQ(i - 1, encoded_info_.redundant[1].encoded_bytes);
-    EXPECT_EQ(i + i - 1, encoded_info_.encoded_bytes);
+    EXPECT_EQ(5 + i + i - 1, encoded_info_.encoded_bytes);
   }
 }
 
@@ -224,6 +225,7 @@
   // Let the mock encoder write payloads with increasing values. The first
   // payload will have values 0, 1, 2, ..., kPayloadLenBytes - 1.
   static const size_t kPayloadLenBytes = 5;
+  static const size_t kHeaderLenBytes = 5;
   uint8_t payload[kPayloadLenBytes];
   for (uint8_t i = 0; i < kPayloadLenBytes; ++i) {
     payload[i] = i;
@@ -239,7 +241,7 @@
     EXPECT_EQ(i, encoded_.data()[i]);
   }
 
-  for (int j = 0; j < 5; ++j) {
+  for (int j = 0; j < 1; ++j) {
     // Increment all values of the payload by 10.
     for (size_t i = 0; i < kPayloadLenBytes; ++i)
       payload[i] += 10;
@@ -249,16 +251,17 @@
     EXPECT_EQ(kPayloadLenBytes, encoded_info_.redundant[0].encoded_bytes);
     EXPECT_EQ(kPayloadLenBytes, encoded_info_.redundant[1].encoded_bytes);
     for (size_t i = 0; i < kPayloadLenBytes; ++i) {
-      // Check primary payload.
-      EXPECT_EQ((j + 1) * 10 + i, encoded_.data()[i]);
       // Check secondary payload.
-      EXPECT_EQ(j * 10 + i, encoded_.data()[i + kPayloadLenBytes]);
+      EXPECT_EQ(j * 10 + i, encoded_.data()[kHeaderLenBytes + i]);
+
+      // Check primary payload.
+      EXPECT_EQ((j + 1) * 10 + i,
+                encoded_.data()[kHeaderLenBytes + i + kPayloadLenBytes]);
     }
   }
 }
 
 // Checks correct propagation of payload type.
-// Checks that the correct timestamps are returned.
 TEST_F(AudioEncoderCopyRedTest, CheckPayloadType) {
   const int primary_payload_type = red_payload_type_ + 1;
   AudioEncoder::EncodedInfo info;
@@ -272,7 +275,7 @@
   Encode();
   ASSERT_EQ(1u, encoded_info_.redundant.size());
   EXPECT_EQ(primary_payload_type, encoded_info_.redundant[0].payload_type);
-  EXPECT_EQ(red_payload_type_, encoded_info_.payload_type);
+  EXPECT_EQ(primary_payload_type, encoded_info_.payload_type);
 
   const int secondary_payload_type = red_payload_type_ + 2;
   info.payload_type = secondary_payload_type;
@@ -286,6 +289,36 @@
   EXPECT_EQ(red_payload_type_, encoded_info_.payload_type);
 }
 
+TEST_F(AudioEncoderCopyRedTest, CheckRFC2198Header) {
+  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 produce a redundant encoding.
+
+  EXPECT_EQ(encoded_.size(),
+            5u + 2 * 10u);  // header size + two encoded payloads.
+  EXPECT_EQ(encoded_[0], primary_payload_type | 0x80);
+
+  uint32_t timestamp_delta = encoded_info_.encoded_timestamp -
+                             encoded_info_.redundant[1].encoded_timestamp;
+  // Timestamp delta is encoded as a 14 bit value.
+  EXPECT_EQ(encoded_[1], timestamp_delta >> 6);
+  EXPECT_EQ(static_cast<uint8_t>(encoded_[2] >> 2), timestamp_delta & 0x3f);
+  // Redundant length is encoded as 10 bit value.
+  EXPECT_EQ(encoded_[2] & 0x3u, encoded_info_.redundant[1].encoded_bytes >> 8);
+  EXPECT_EQ(encoded_[3], encoded_info_.redundant[1].encoded_bytes & 0xff);
+  EXPECT_EQ(encoded_[4], primary_payload_type);
+}
+
 #if GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID)
 
 // This test fixture tests various error conditions that makes the