aboutsummaryrefslogtreecommitdiff
path: root/webrtc/modules/audio_coding
diff options
context:
space:
mode:
Diffstat (limited to 'webrtc/modules/audio_coding')
-rw-r--r--webrtc/modules/audio_coding/BUILD.gn57
-rw-r--r--webrtc/modules/audio_coding/codecs/audio_encoder.cc21
-rw-r--r--webrtc/modules/audio_coding/codecs/audio_encoder.h25
-rw-r--r--webrtc/modules/audio_coding/codecs/cng/cng.gypi1
-rw-r--r--webrtc/modules/audio_coding/codecs/g711/g711.gypi3
-rw-r--r--webrtc/modules/audio_coding/codecs/g722/g722.gypi3
-rw-r--r--webrtc/modules/audio_coding/codecs/ilbc/ilbc.gypi1
-rw-r--r--webrtc/modules/audio_coding/codecs/interfaces.gypi9
-rw-r--r--webrtc/modules/audio_coding/codecs/isac/main/source/isac.gypi1
-rw-r--r--webrtc/modules/audio_coding/codecs/opus/opus.gypi3
-rw-r--r--webrtc/modules/audio_coding/codecs/pcm16b/pcm16b.gypi1
-rw-r--r--webrtc/modules/audio_coding/codecs/red/audio_encoder_copy_red.cc88
-rw-r--r--webrtc/modules/audio_coding/codecs/red/audio_encoder_copy_red.h59
-rw-r--r--webrtc/modules/audio_coding/codecs/red/audio_encoder_copy_red_unittest.cc321
-rw-r--r--webrtc/modules/audio_coding/codecs/red/red.gypi33
-rw-r--r--webrtc/modules/audio_coding/main/acm2/audio_coding_module.gypi1
16 files changed, 621 insertions, 6 deletions
diff --git a/webrtc/modules/audio_coding/BUILD.gn b/webrtc/modules/audio_coding/BUILD.gn
index 97b432d32a..994ff32afd 100644
--- a/webrtc/modules/audio_coding/BUILD.gn
+++ b/webrtc/modules/audio_coding/BUILD.gn
@@ -101,6 +101,7 @@ source_set("audio_coding") {
":isacfix",
":neteq",
":pcm16b",
+ ":red",
"../../common_audio",
"../../system_wrappers",
]
@@ -120,6 +121,15 @@ source_set("audio_decoder_interface") {
public_configs = [ "../..:common_inherited_config" ]
}
+source_set("audio_encoder_interface") {
+ sources = [
+ "codecs/audio_encoder.cc",
+ "codecs/audio_encoder.h",
+ ]
+ configs += [ "../..:common_config" ]
+ public_configs = [ "../..:common_inherited_config" ]
+}
+
config("cng_config") {
include_dirs = [
"../../..",
@@ -144,7 +154,35 @@ source_set("cng") {
":cng_config",
]
- deps = [ "../../common_audio" ]
+ deps = [
+ "../../common_audio",
+ ":audio_encoder_interface",
+ ]
+}
+
+config("red_config") {
+ include_dirs = [
+ "codecs/red",
+ ]
+}
+
+source_set("red") {
+ sources = [
+ "codecs/red/audio_encoder_copy_red.cc",
+ "codecs/red/audio_encoder_copy_red.h",
+ ]
+
+ configs += [ "../..:common_config" ]
+
+ public_configs = [
+ "../..:common_inherited_config",
+ ":red_config",
+ ]
+
+ deps = [
+ "../../common_audio",
+ ":audio_encoder_interface",
+ ]
}
config("g711_config") {
@@ -170,6 +208,8 @@ source_set("g711") {
"../..:common_inherited_config",
":g711_config",
]
+
+ deps = [ ":audio_encoder_interface" ]
}
config("g722_config") {
@@ -196,6 +236,8 @@ source_set("g722") {
"../..:common_inherited_config",
":g722_config",
]
+
+ deps = [ ":audio_encoder_interface" ]
}
config("ilbc_config") {
@@ -357,7 +399,10 @@ source_set("ilbc") {
":ilbc_config",
]
- deps = [ "../../common_audio" ]
+ deps = [
+ "../../common_audio",
+ ":audio_encoder_interface",
+ ]
}
config("isac_config") {
@@ -443,6 +488,7 @@ source_set("isac") {
deps = [
":audio_decoder_interface",
+ ":audio_encoder_interface",
"../../common_audio",
]
}
@@ -520,6 +566,7 @@ source_set("isacfix") {
]
deps = [
+ ":audio_encoder_interface",
"../../common_audio",
"../../system_wrappers",
]
@@ -631,6 +678,7 @@ source_set("pcm16b") {
]
deps = [
+ ":audio_encoder_interface",
":g711",
]
@@ -654,13 +702,16 @@ source_set("webrtc_opus") {
"codecs/opus/opus_inst.h",
"codecs/opus/opus_interface.c",
]
+
+ deps = [ ":audio_encoder_interface" ]
+
if (build_with_mozilla) {
include_dirs = [ getenv("DIST") + "/include/opus" ]
} else {
configs += [ "../..:common_config" ]
public_configs = [ "../..:common_inherited_config" ]
- deps = [ "//third_party/opus" ]
+ deps += [ "//third_party/opus" ]
}
}
diff --git a/webrtc/modules/audio_coding/codecs/audio_encoder.cc b/webrtc/modules/audio_coding/codecs/audio_encoder.cc
new file mode 100644
index 0000000000..916111f512
--- /dev/null
+++ b/webrtc/modules/audio_coding/codecs/audio_encoder.cc
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/modules/audio_coding/codecs/audio_encoder.h"
+
+namespace webrtc {
+
+AudioEncoder::EncodedInfo::EncodedInfo() : EncodedInfoLeaf() {
+}
+
+AudioEncoder::EncodedInfo::~EncodedInfo() {
+}
+
+} // namespace webrtc
diff --git a/webrtc/modules/audio_coding/codecs/audio_encoder.h b/webrtc/modules/audio_coding/codecs/audio_encoder.h
index 27b9f2ffdd..5b81509b26 100644
--- a/webrtc/modules/audio_coding/codecs/audio_encoder.h
+++ b/webrtc/modules/audio_coding/codecs/audio_encoder.h
@@ -12,6 +12,7 @@
#define WEBRTC_MODULES_AUDIO_CODING_CODECS_AUDIO_ENCODER_H_
#include <algorithm>
+#include <vector>
#include "webrtc/base/checks.h"
#include "webrtc/typedefs.h"
@@ -19,17 +20,35 @@
namespace webrtc {
// This is the interface class for encoders in AudioCoding module. Each codec
-// codec type must have an implementation of this class.
+// type must have an implementation of this class.
class AudioEncoder {
public:
- struct EncodedInfo {
- EncodedInfo() : encoded_bytes(0), encoded_timestamp(0), payload_type(0) {}
+ struct EncodedInfoLeaf {
+ EncodedInfoLeaf()
+ : encoded_bytes(0), encoded_timestamp(0), payload_type(0) {}
size_t encoded_bytes;
uint32_t encoded_timestamp;
int payload_type;
};
+ // This is the main struct for auxiliary encoding information. Each encoded
+ // packet should be accompanied by one EncodedInfo struct, containing the
+ // total number of |encoded_bytes|, the |encoded_timestamp| and the
+ // |payload_type|. If the packet contains redundant encodings, the |redundant|
+ // vector will be populated with EncodedInfoLeaf structs. Each struct in the
+ // vector represents one encoding; the order of structs in the vector is the
+ // same as the order in which the actual payloads are written to the byte
+ // stream. When EncoderInfoLeaf structs are present in the vector, the main
+ // struct's |encoded_bytes| will be the sum of all the |encoded_bytes| in the
+ // vector.
+ struct EncodedInfo : public EncodedInfoLeaf {
+ EncodedInfo();
+ ~EncodedInfo();
+
+ std::vector<EncodedInfoLeaf> redundant;
+ };
+
virtual ~AudioEncoder() {}
// Accepts one 10 ms block of input audio (i.e., sample_rate_hz() / 100 *
diff --git a/webrtc/modules/audio_coding/codecs/cng/cng.gypi b/webrtc/modules/audio_coding/codecs/cng/cng.gypi
index ccc4f9506c..af9fbd3af0 100644
--- a/webrtc/modules/audio_coding/codecs/cng/cng.gypi
+++ b/webrtc/modules/audio_coding/codecs/cng/cng.gypi
@@ -13,6 +13,7 @@
'type': 'static_library',
'dependencies': [
'<(webrtc_root)/common_audio/common_audio.gyp:common_audio',
+ 'audio_encoder_interface',
],
'include_dirs': [
'include',
diff --git a/webrtc/modules/audio_coding/codecs/g711/g711.gypi b/webrtc/modules/audio_coding/codecs/g711/g711.gypi
index 2b637cf9fa..779f05339a 100644
--- a/webrtc/modules/audio_coding/codecs/g711/g711.gypi
+++ b/webrtc/modules/audio_coding/codecs/g711/g711.gypi
@@ -11,6 +11,9 @@
{
'target_name': 'G711',
'type': 'static_library',
+ 'dependencies': [
+ 'audio_encoder_interface',
+ ],
'include_dirs': [
'include',
'<(webrtc_root)',
diff --git a/webrtc/modules/audio_coding/codecs/g722/g722.gypi b/webrtc/modules/audio_coding/codecs/g722/g722.gypi
index 50c53e702d..38dac31231 100644
--- a/webrtc/modules/audio_coding/codecs/g722/g722.gypi
+++ b/webrtc/modules/audio_coding/codecs/g722/g722.gypi
@@ -10,6 +10,9 @@
{
'target_name': 'G722',
'type': 'static_library',
+ 'dependencies': [
+ 'audio_encoder_interface',
+ ],
'include_dirs': [
'include',
'<(webrtc_root)',
diff --git a/webrtc/modules/audio_coding/codecs/ilbc/ilbc.gypi b/webrtc/modules/audio_coding/codecs/ilbc/ilbc.gypi
index dcee4be905..5f6fed1a24 100644
--- a/webrtc/modules/audio_coding/codecs/ilbc/ilbc.gypi
+++ b/webrtc/modules/audio_coding/codecs/ilbc/ilbc.gypi
@@ -13,6 +13,7 @@
'type': 'static_library',
'dependencies': [
'<(webrtc_root)/common_audio/common_audio.gyp:common_audio',
+ 'audio_encoder_interface',
],
'include_dirs': [
'interface',
diff --git a/webrtc/modules/audio_coding/codecs/interfaces.gypi b/webrtc/modules/audio_coding/codecs/interfaces.gypi
index 931d7f72ed..d4f6a4a41e 100644
--- a/webrtc/modules/audio_coding/codecs/interfaces.gypi
+++ b/webrtc/modules/audio_coding/codecs/interfaces.gypi
@@ -16,5 +16,14 @@
'audio_decoder.h',
],
},
+
+ {
+ 'target_name': 'audio_encoder_interface',
+ 'type': 'static_library',
+ 'sources': [
+ 'audio_encoder.cc',
+ 'audio_encoder.h',
+ ],
+ },
],
}
diff --git a/webrtc/modules/audio_coding/codecs/isac/main/source/isac.gypi b/webrtc/modules/audio_coding/codecs/isac/main/source/isac.gypi
index 0b0c070638..86f930bb91 100644
--- a/webrtc/modules/audio_coding/codecs/isac/main/source/isac.gypi
+++ b/webrtc/modules/audio_coding/codecs/isac/main/source/isac.gypi
@@ -14,6 +14,7 @@
'dependencies': [
'<(webrtc_root)/common_audio/common_audio.gyp:common_audio',
'audio_decoder_interface',
+ 'audio_encoder_interface',
],
'include_dirs': [
'../interface',
diff --git a/webrtc/modules/audio_coding/codecs/opus/opus.gypi b/webrtc/modules/audio_coding/codecs/opus/opus.gypi
index b537285ae2..c9f65f93d9 100644
--- a/webrtc/modules/audio_coding/codecs/opus/opus.gypi
+++ b/webrtc/modules/audio_coding/codecs/opus/opus.gypi
@@ -23,6 +23,9 @@
],
}],
],
+ 'dependencies': [
+ 'audio_encoder_interface',
+ ],
'include_dirs': [
'<(webrtc_root)',
],
diff --git a/webrtc/modules/audio_coding/codecs/pcm16b/pcm16b.gypi b/webrtc/modules/audio_coding/codecs/pcm16b/pcm16b.gypi
index 1c15d3b440..44b4335407 100644
--- a/webrtc/modules/audio_coding/codecs/pcm16b/pcm16b.gypi
+++ b/webrtc/modules/audio_coding/codecs/pcm16b/pcm16b.gypi
@@ -12,6 +12,7 @@
'target_name': 'PCM16B',
'type': 'static_library',
'dependencies': [
+ 'audio_encoder_interface',
'G711',
],
'include_dirs': [
diff --git a/webrtc/modules/audio_coding/codecs/red/audio_encoder_copy_red.cc b/webrtc/modules/audio_coding/codecs/red/audio_encoder_copy_red.cc
new file mode 100644
index 0000000000..73e373cd9c
--- /dev/null
+++ b/webrtc/modules/audio_coding/codecs/red/audio_encoder_copy_red.cc
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/modules/audio_coding/codecs/red/audio_encoder_copy_red.h"
+
+#include <string.h>
+
+namespace webrtc {
+
+AudioEncoderCopyRed::AudioEncoderCopyRed(const Config& config)
+ : speech_encoder_(config.speech_encoder),
+ red_payload_type_(config.payload_type),
+ secondary_allocated_(0) {
+ CHECK(speech_encoder_) << "Speech encoder not provided.";
+}
+
+AudioEncoderCopyRed::~AudioEncoderCopyRed() {
+}
+
+int AudioEncoderCopyRed::sample_rate_hz() const {
+ return speech_encoder_->sample_rate_hz();
+}
+
+int AudioEncoderCopyRed::num_channels() const {
+ return speech_encoder_->num_channels();
+}
+
+int AudioEncoderCopyRed::Num10MsFramesInNextPacket() const {
+ return speech_encoder_->Num10MsFramesInNextPacket();
+}
+
+int AudioEncoderCopyRed::Max10MsFramesInAPacket() const {
+ return speech_encoder_->Max10MsFramesInAPacket();
+}
+
+bool AudioEncoderCopyRed::EncodeInternal(uint32_t timestamp,
+ const int16_t* audio,
+ size_t max_encoded_bytes,
+ uint8_t* encoded,
+ EncodedInfo* info) {
+ if (!speech_encoder_->Encode(timestamp, audio,
+ static_cast<size_t>(sample_rate_hz() / 100),
+ max_encoded_bytes, encoded, info))
+ return false;
+ if (max_encoded_bytes < info->encoded_bytes + secondary_info_.encoded_bytes)
+ return false;
+ CHECK(info->redundant.empty()) << "Cannot use nested redundant encoders.";
+
+ 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);
+ DCHECK_EQ(info->redundant.size(), 1u);
+ if (secondary_info_.encoded_bytes > 0) {
+ memcpy(&encoded[info->encoded_bytes], secondary_encoded_.get(),
+ secondary_info_.encoded_bytes);
+ info->redundant.push_back(secondary_info_);
+ DCHECK_EQ(info->redundant.size(), 2u);
+ }
+ // Save primary to secondary.
+ if (secondary_allocated_ < info->encoded_bytes) {
+ secondary_encoded_.reset(new uint8_t[info->encoded_bytes]);
+ secondary_allocated_ = info->encoded_bytes;
+ }
+ CHECK(secondary_encoded_);
+ memcpy(secondary_encoded_.get(), encoded, info->encoded_bytes);
+ secondary_info_ = *info;
+ }
+ // Update main EncodedInfo.
+ info->payload_type = red_payload_type_;
+ info->encoded_bytes = 0;
+ for (std::vector<EncodedInfoLeaf>::const_iterator it =
+ info->redundant.begin();
+ it != info->redundant.end(); ++it) {
+ info->encoded_bytes += it->encoded_bytes;
+ }
+ return true;
+}
+
+} // namespace webrtc
diff --git a/webrtc/modules/audio_coding/codecs/red/audio_encoder_copy_red.h b/webrtc/modules/audio_coding/codecs/red/audio_encoder_copy_red.h
new file mode 100644
index 0000000000..8d0e427c58
--- /dev/null
+++ b/webrtc/modules/audio_coding/codecs/red/audio_encoder_copy_red.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef WEBRTC_MODULES_AUDIO_CODING_CODECS_RED_AUDIO_ENCODER_COPY_RED_H_
+#define WEBRTC_MODULES_AUDIO_CODING_CODECS_RED_AUDIO_ENCODER_COPY_RED_H_
+
+#include <vector>
+
+#include "webrtc/modules/audio_coding/codecs/audio_encoder.h"
+#include "webrtc/system_wrappers/interface/scoped_ptr.h"
+
+namespace webrtc {
+
+// This class implements redundant audio coding. The class object will have an
+// underlying AudioEncoder object that performs the actual encodings. The
+// current class will gather the two latest encodings from the underlying codec
+// into one packet.
+class AudioEncoderCopyRed : public AudioEncoder {
+ public:
+ struct Config {
+ public:
+ int payload_type;
+ AudioEncoder* speech_encoder;
+ };
+
+ // Caller keeps ownership of the AudioEncoder object.
+ explicit AudioEncoderCopyRed(const Config& config);
+
+ virtual ~AudioEncoderCopyRed();
+
+ virtual int sample_rate_hz() const OVERRIDE;
+ virtual int num_channels() const OVERRIDE;
+ virtual int Num10MsFramesInNextPacket() const OVERRIDE;
+ virtual int Max10MsFramesInAPacket() const OVERRIDE;
+
+ protected:
+ virtual bool EncodeInternal(uint32_t timestamp,
+ const int16_t* audio,
+ size_t max_encoded_bytes,
+ uint8_t* encoded,
+ EncodedInfo* info) OVERRIDE;
+
+ private:
+ AudioEncoder* speech_encoder_;
+ int red_payload_type_;
+ scoped_ptr<uint8_t[]> secondary_encoded_;
+ size_t secondary_allocated_;
+ EncodedInfoLeaf secondary_info_;
+};
+
+} // namespace webrtc
+#endif // WEBRTC_MODULES_AUDIO_CODING_CODECS_RED_AUDIO_ENCODER_COPY_RED_H_
diff --git a/webrtc/modules/audio_coding/codecs/red/audio_encoder_copy_red_unittest.cc b/webrtc/modules/audio_coding/codecs/red/audio_encoder_copy_red_unittest.cc
new file mode 100644
index 0000000000..b470343c87
--- /dev/null
+++ b/webrtc/modules/audio_coding/codecs/red/audio_encoder_copy_red_unittest.cc
@@ -0,0 +1,321 @@
+/*
+ * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webrtc/modules/audio_coding/codecs/red/audio_encoder_copy_red.h"
+#include "webrtc/modules/audio_coding/codecs/mock/mock_audio_encoder.h"
+#include "webrtc/system_wrappers/interface/scoped_ptr.h"
+
+using ::testing::Return;
+using ::testing::_;
+using ::testing::SetArgPointee;
+using ::testing::InSequence;
+using ::testing::Invoke;
+using ::testing::MockFunction;
+
+namespace webrtc {
+
+namespace {
+static const size_t kMaxEncodedBytes = 1000;
+static const size_t kMaxNumSamples = 48 * 10 * 2; // 10 ms @ 48 kHz stereo.
+}
+
+class AudioEncoderCopyRedTest : public ::testing::Test {
+ protected:
+ AudioEncoderCopyRedTest()
+ : timestamp_(4711),
+ sample_rate_hz_(16000),
+ num_audio_samples_10ms(sample_rate_hz_ / 100),
+ red_payload_type_(200) {
+ AudioEncoderCopyRed::Config config;
+ config.payload_type = red_payload_type_;
+ config.speech_encoder = &mock_encoder_;
+ red_.reset(new AudioEncoderCopyRed(config));
+ memset(encoded_, 0, sizeof(encoded_));
+ memset(audio_, 0, sizeof(audio_));
+ EXPECT_CALL(mock_encoder_, num_channels()).WillRepeatedly(Return(1));
+ EXPECT_CALL(mock_encoder_, sample_rate_hz())
+ .WillRepeatedly(Return(sample_rate_hz_));
+ }
+
+ virtual void TearDown() OVERRIDE {
+ red_.reset();
+ // Don't expect the red_ object to delete the AudioEncoder object. But it
+ // will be deleted with the test fixture. This is why we explicitly delete
+ // the red_ object above, and set expectations on mock_encoder_ afterwards.
+ EXPECT_CALL(mock_encoder_, Die()).Times(1);
+ }
+
+ void Encode() {
+ ASSERT_TRUE(red_.get() != NULL);
+ encoded_info_ = AudioEncoder::EncodedInfo();
+ ASSERT_TRUE(red_->Encode(timestamp_, audio_, num_audio_samples_10ms,
+ kMaxEncodedBytes, encoded_, &encoded_info_));
+ timestamp_ += num_audio_samples_10ms;
+ }
+
+ MockAudioEncoder mock_encoder_;
+ scoped_ptr<AudioEncoderCopyRed> red_;
+ uint32_t timestamp_;
+ int16_t audio_[kMaxNumSamples];
+ const int sample_rate_hz_;
+ size_t num_audio_samples_10ms;
+ uint8_t encoded_[kMaxEncodedBytes];
+ AudioEncoder::EncodedInfo encoded_info_;
+ const int red_payload_type_;
+};
+
+class MockEncodeHelper {
+ public:
+ MockEncodeHelper() : write_payload_(false), payload_(NULL) {
+ memset(&info_, 0, sizeof(info_));
+ }
+
+ bool Encode(uint32_t timestamp,
+ const int16_t* audio,
+ size_t max_encoded_bytes,
+ uint8_t* encoded,
+ AudioEncoder::EncodedInfo* info) {
+ if (write_payload_) {
+ CHECK(encoded);
+ CHECK_LE(info_.encoded_bytes, max_encoded_bytes);
+ memcpy(encoded, payload_, info_.encoded_bytes);
+ }
+ CHECK(info);
+ *info = info_;
+ return true;
+ }
+
+ AudioEncoder::EncodedInfo info_;
+ bool write_payload_;
+ uint8_t* payload_;
+};
+
+TEST_F(AudioEncoderCopyRedTest, CreateAndDestroy) {
+}
+
+TEST_F(AudioEncoderCopyRedTest, CheckSampleRatePropagation) {
+ EXPECT_CALL(mock_encoder_, sample_rate_hz()).WillOnce(Return(17));
+ EXPECT_EQ(17, red_->sample_rate_hz());
+}
+
+TEST_F(AudioEncoderCopyRedTest, CheckNumChannelsPropagation) {
+ EXPECT_CALL(mock_encoder_, num_channels()).WillOnce(Return(17));
+ EXPECT_EQ(17, red_->num_channels());
+}
+
+TEST_F(AudioEncoderCopyRedTest, CheckFrameSizePropagation) {
+ EXPECT_CALL(mock_encoder_, Num10MsFramesInNextPacket()).WillOnce(Return(17));
+ EXPECT_EQ(17, red_->Num10MsFramesInNextPacket());
+}
+
+TEST_F(AudioEncoderCopyRedTest, CheckMaxFrameSizePropagation) {
+ EXPECT_CALL(mock_encoder_, Max10MsFramesInAPacket()).WillOnce(Return(17));
+ EXPECT_EQ(17, red_->Max10MsFramesInAPacket());
+}
+
+// Checks that the an Encode() call is immediately propagated to the speech
+// encoder.
+TEST_F(AudioEncoderCopyRedTest, CheckImmediateEncode) {
+ // Interleaving the EXPECT_CALL sequence with expectations on the MockFunction
+ // check ensures that exactly one call to EncodeInternal happens in each
+ // Encode call.
+ InSequence s;
+ MockFunction<void(int check_point_id)> check;
+ for (int i = 1; i <= 6; ++i) {
+ EXPECT_CALL(mock_encoder_, EncodeInternal(_, _, _, _, _))
+ .WillOnce(Return(true));
+ EXPECT_CALL(check, Call(i));
+ Encode();
+ check.Call(i);
+ }
+}
+
+// Checks that no output is produced if the underlying codec doesn't emit any
+// new data, even if the RED codec is loaded with a secondary encoding.
+TEST_F(AudioEncoderCopyRedTest, CheckNoOuput) {
+ // Start with one Encode() call that will produce output.
+ static const size_t kEncodedSize = 17;
+ AudioEncoder::EncodedInfo info;
+ info.encoded_bytes = kEncodedSize;
+ EXPECT_CALL(mock_encoder_, EncodeInternal(_, _, _, _, _))
+ .WillOnce(DoAll(SetArgPointee<4>(info), Return(true)));
+ Encode();
+ // First call is a special case, since it does not include a secondary
+ // payload.
+ EXPECT_EQ(1u, encoded_info_.redundant.size());
+ EXPECT_EQ(kEncodedSize, encoded_info_.encoded_bytes);
+
+ // Next call to the speech encoder will not produce any output.
+ info.encoded_bytes = 0;
+ EXPECT_CALL(mock_encoder_, EncodeInternal(_, _, _, _, _))
+ .WillOnce(DoAll(SetArgPointee<4>(info), Return(true)));
+ Encode();
+ EXPECT_EQ(0u, encoded_info_.encoded_bytes);
+
+ // Final call to the speech encoder will produce output.
+ info.encoded_bytes = kEncodedSize;
+ EXPECT_CALL(mock_encoder_, EncodeInternal(_, _, _, _, _))
+ .WillOnce(DoAll(SetArgPointee<4>(info), Return(true)));
+ Encode();
+ EXPECT_EQ(2 * kEncodedSize, encoded_info_.encoded_bytes);
+ ASSERT_EQ(2u, encoded_info_.redundant.size());
+}
+
+// Checks that the correct payload sizes are populated into the redundancy
+// information.
+TEST_F(AudioEncoderCopyRedTest, CheckPayloadSizes) {
+ // 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) {
+ AudioEncoder::EncodedInfo info;
+ info.encoded_bytes = encode_size;
+ EXPECT_CALL(mock_encoder_, EncodeInternal(_, _, _, _, _))
+ .WillOnce(DoAll(SetArgPointee<4>(info), Return(true)));
+ }
+
+ // First call is a special case, since it does not include a secondary
+ // payload.
+ Encode();
+ EXPECT_EQ(1u, encoded_info_.redundant.size());
+ EXPECT_EQ(1u, encoded_info_.encoded_bytes);
+
+ for (size_t i = 2; i <= kNumPackets; ++i) {
+ Encode();
+ 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);
+ }
+}
+
+// Checks that the correct timestamps are returned.
+TEST_F(AudioEncoderCopyRedTest, CheckTimestamps) {
+ MockEncodeHelper helper;
+
+ helper.info_.encoded_bytes = 17;
+ helper.info_.encoded_timestamp = timestamp_;
+ uint32_t primary_timestamp = timestamp_;
+ EXPECT_CALL(mock_encoder_, EncodeInternal(_, _, _, _, _))
+ .WillRepeatedly(Invoke(&helper, &MockEncodeHelper::Encode));
+
+ // First call is a special case, since it does not include a secondary
+ // payload.
+ Encode();
+ EXPECT_EQ(primary_timestamp, encoded_info_.encoded_timestamp);
+
+ uint32_t secondary_timestamp = primary_timestamp;
+ primary_timestamp = timestamp_;
+ helper.info_.encoded_timestamp = timestamp_;
+ Encode();
+ ASSERT_EQ(2u, encoded_info_.redundant.size());
+ EXPECT_EQ(primary_timestamp, encoded_info_.redundant[0].encoded_timestamp);
+ EXPECT_EQ(secondary_timestamp, encoded_info_.redundant[1].encoded_timestamp);
+ EXPECT_EQ(primary_timestamp, encoded_info_.encoded_timestamp);
+}
+
+// Checks that the primary and secondary payloads are written correctly.
+TEST_F(AudioEncoderCopyRedTest, CheckPayloads) {
+ // Let the mock encoder write payloads with increasing values. The first
+ // payload will have values 0, 1, 2, ..., kPayloadLenBytes - 1.
+ MockEncodeHelper helper;
+ static const size_t kPayloadLenBytes = 5;
+ helper.info_.encoded_bytes = kPayloadLenBytes;
+ helper.write_payload_ = true;
+ uint8_t payload[kPayloadLenBytes];
+ for (uint8_t i = 0; i < kPayloadLenBytes; ++i) {
+ payload[i] = i;
+ }
+ helper.payload_ = payload;
+ EXPECT_CALL(mock_encoder_, EncodeInternal(_, _, _, _, _))
+ .WillRepeatedly(Invoke(&helper, &MockEncodeHelper::Encode));
+
+ // First call is a special case, since it does not include a secondary
+ // payload.
+ Encode();
+ EXPECT_EQ(kPayloadLenBytes, encoded_info_.encoded_bytes);
+ for (size_t i = 0; i < kPayloadLenBytes; ++i) {
+ EXPECT_EQ(i, encoded_[i]);
+ }
+
+ for (int j = 0; j < 5; ++j) {
+ // Increment all values of the payload by 10.
+ for (size_t i = 0; i < kPayloadLenBytes; ++i)
+ helper.payload_[i] += 10;
+
+ Encode();
+ ASSERT_EQ(2u, encoded_info_.redundant.size());
+ 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_[i]);
+ // Check secondary payload.
+ EXPECT_EQ(j * 10 + i, encoded_[i + kPayloadLenBytes]);
+ }
+ }
+}
+
+// Checks correct propagation of payload type.
+// Checks that the correct timestamps are returned.
+TEST_F(AudioEncoderCopyRedTest, CheckPayloadType) {
+ MockEncodeHelper helper;
+
+ helper.info_.encoded_bytes = 17;
+ const int primary_payload_type = red_payload_type_ + 1;
+ helper.info_.payload_type = primary_payload_type;
+ EXPECT_CALL(mock_encoder_, EncodeInternal(_, _, _, _, _))
+ .WillRepeatedly(Invoke(&helper, &MockEncodeHelper::Encode));
+
+ // First call is a special case, since it does not include a secondary
+ // payload.
+ 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);
+
+ const int secondary_payload_type = red_payload_type_ + 2;
+ helper.info_.payload_type = secondary_payload_type;
+ Encode();
+ ASSERT_EQ(2u, encoded_info_.redundant.size());
+ EXPECT_EQ(secondary_payload_type, encoded_info_.redundant[0].payload_type);
+ EXPECT_EQ(primary_payload_type, encoded_info_.redundant[1].payload_type);
+ EXPECT_EQ(red_payload_type_, encoded_info_.payload_type);
+}
+
+#if GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID)
+
+// This test fixture tests various error conditions that makes the
+// AudioEncoderCng die via CHECKs.
+class AudioEncoderCopyRedDeathTest : public AudioEncoderCopyRedTest {
+ protected:
+ AudioEncoderCopyRedDeathTest() : AudioEncoderCopyRedTest() {}
+};
+
+TEST_F(AudioEncoderCopyRedDeathTest, WrongFrameSize) {
+ num_audio_samples_10ms *= 2; // 20 ms frame.
+ EXPECT_DEATH(Encode(), "");
+ num_audio_samples_10ms = 0; // Zero samples.
+ EXPECT_DEATH(Encode(), "");
+}
+
+TEST_F(AudioEncoderCopyRedDeathTest, NullSpeechEncoder) {
+ AudioEncoderCopyRed* red;
+ AudioEncoderCopyRed::Config config;
+ config.speech_encoder = NULL;
+ EXPECT_DEATH(red = new AudioEncoderCopyRed(config),
+ "Speech encoder not provided.");
+}
+
+#endif // GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID)
+
+} // namespace webrtc
diff --git a/webrtc/modules/audio_coding/codecs/red/red.gypi b/webrtc/modules/audio_coding/codecs/red/red.gypi
new file mode 100644
index 0000000000..667ac866c9
--- /dev/null
+++ b/webrtc/modules/audio_coding/codecs/red/red.gypi
@@ -0,0 +1,33 @@
+# Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
+#
+# Use of this source code is governed by a BSD-style license
+# that can be found in the LICENSE file in the root of the source
+# tree. An additional intellectual property rights grant can be found
+# in the file PATENTS. All contributing project authors may
+# be found in the AUTHORS file in the root of the source tree.
+
+{
+ 'targets': [
+ {
+ 'target_name': 'red',
+ 'type': 'static_library',
+ 'dependencies': [
+ 'audio_encoder_interface',
+ ],
+ 'include_dirs': [
+ 'include',
+ '<(webrtc_root)',
+ ],
+ 'direct_dependent_settings': {
+ 'include_dirs': [
+ 'include',
+ '<(webrtc_root)',
+ ],
+ },
+ 'sources': [
+ 'audio_encoder_copy_red.h',
+ 'audio_encoder_copy_red.cc',
+ ],
+ },
+ ], # targets
+}
diff --git a/webrtc/modules/audio_coding/main/acm2/audio_coding_module.gypi b/webrtc/modules/audio_coding/main/acm2/audio_coding_module.gypi
index efd1de78de..1ddad4533e 100644
--- a/webrtc/modules/audio_coding/main/acm2/audio_coding_module.gypi
+++ b/webrtc/modules/audio_coding/main/acm2/audio_coding_module.gypi
@@ -16,6 +16,7 @@
'iSAC',
'iSACFix',
'PCM16B',
+ 'red',
'<(webrtc_root)/common_audio/common_audio.gyp:common_audio',
'<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:system_wrappers',
],