diff options
author | Philipp Hancke <fippo@sip-communicator.org> | 2020-06-11 10:57:58 +0200 |
---|---|---|
committer | Commit Bot <commit-bot@chromium.org> | 2020-06-24 21:25:05 +0000 |
commit | 603cc3a31e832565829a578b05036bd9def54796 (patch) | |
tree | 8af55284fb8a7227eb8fc19de16499b98a028d1f /modules/audio_coding | |
parent | 1b485322085d941a9ec08acc6da3a05e6f5fb5e5 (diff) | |
download | webrtc-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.cc | 73 | ||||
-rw-r--r-- | modules/audio_coding/codecs/red/audio_encoder_copy_red_unittest.cc | 49 |
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 |