aboutsummaryrefslogtreecommitdiff
path: root/modules/audio_coding
diff options
context:
space:
mode:
authorPhilipp Hancke <fippo@sip-communicator.org>2020-06-11 10:57:58 +0200
committerCommit Bot <commit-bot@chromium.org>2020-06-24 21:25:05 +0000
commit603cc3a31e832565829a578b05036bd9def54796 (patch)
tree8af55284fb8a7227eb8fc19de16499b98a028d1f /modules/audio_coding
parent1b485322085d941a9ec08acc6da3a05e6f5fb5e5 (diff)
downloadwebrtc-603cc3a31e832565829a578b05036bd9def54796.tar.gz
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}
Diffstat (limited to 'modules/audio_coding')
-rw-r--r--modules/audio_coding/codecs/red/audio_encoder_copy_red.cc73
-rw-r--r--modules/audio_coding/codecs/red/audio_encoder_copy_red_unittest.cc49
2 files changed, 93 insertions, 29 deletions
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 e75806af10..8d028c9b9a 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 @@ AudioEncoder::EncodedInfo AudioEncoderCopyRed::EncodeImpl(
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);
+ 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() - 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);
+ 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 e20515a165..720acb4f87 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 @@ TEST_F(AudioEncoderCopyRedTest, CheckImmediateEncode) {
// 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 @@ TEST_F(AudioEncoderCopyRedTest, CheckNoOutput) {
// 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 @@ TEST_F(AudioEncoderCopyRedTest, CheckPayloadSizes) {
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 @@ TEST_F(AudioEncoderCopyRedTest, CheckPayloads) {
// 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 @@ TEST_F(AudioEncoderCopyRedTest, CheckPayloads) {
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 @@ TEST_F(AudioEncoderCopyRedTest, CheckPayloads) {
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 @@ TEST_F(AudioEncoderCopyRedTest, CheckPayloadType) {
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 @@ TEST_F(AudioEncoderCopyRedTest, CheckPayloadType) {
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