diff --git a/webrtc/modules/audio_coding/codecs/isac/audio_encoder_isac_t.h b/webrtc/modules/audio_coding/codecs/isac/audio_encoder_isac_t.h index a20da9da9f..81871591a2 100644 --- a/webrtc/modules/audio_coding/codecs/isac/audio_encoder_isac_t.h +++ b/webrtc/modules/audio_coding/codecs/isac/audio_encoder_isac_t.h @@ -101,9 +101,15 @@ class AudioEncoderDecoderIsacT : public AudioEncoder, public AudioDecoder { // Have we accepted input but not yet emitted it in a packet? bool packet_in_progress_; + // Working on the very first output frame. + bool first_output_frame_; + // Timestamp of the first input of the currently in-progress packet. uint32_t packet_timestamp_; + // Timestamp of the previously encoded packet. + uint32_t last_encoded_timestamp_; + DISALLOW_COPY_AND_ASSIGN(AudioEncoderDecoderIsacT); }; diff --git a/webrtc/modules/audio_coding/codecs/isac/audio_encoder_isac_t_impl.h b/webrtc/modules/audio_coding/codecs/isac/audio_encoder_isac_t_impl.h index 7c13e87288..9d36663320 100644 --- a/webrtc/modules/audio_coding/codecs/isac/audio_encoder_isac_t_impl.h +++ b/webrtc/modules/audio_coding/codecs/isac/audio_encoder_isac_t_impl.h @@ -68,7 +68,8 @@ template AudioEncoderDecoderIsacT::AudioEncoderDecoderIsacT(const Config& config) : payload_type_(config.payload_type), lock_(CriticalSectionWrapper::CreateCriticalSection()), - packet_in_progress_(false) { + packet_in_progress_(false), + first_output_frame_(true) { CHECK(config.IsOk()); CHECK_EQ(0, T::Create(&isac_state_)); CHECK_EQ(0, T::EncoderInit(isac_state_, 1)); @@ -82,7 +83,8 @@ AudioEncoderDecoderIsacT::AudioEncoderDecoderIsacT( const ConfigAdaptive& config) : payload_type_(config.payload_type), lock_(CriticalSectionWrapper::CreateCriticalSection()), - packet_in_progress_(false) { + packet_in_progress_(false), + first_output_frame_(true) { CHECK(config.IsOk()); CHECK_EQ(0, T::Create(&isac_state_)); CHECK_EQ(0, T::EncoderInit(isac_state_, 0)); @@ -148,13 +150,43 @@ bool AudioEncoderDecoderIsacT::EncodeInternal(uint32_t timestamp, CHECK(static_cast(r) <= max_encoded_bytes); info->encoded_bytes = r; - if (r > 0) { - // Got enough input to produce a packet. Return the saved timestamp from - // the first chunk of input that went into the packet. - packet_in_progress_ = false; - info->encoded_timestamp = packet_timestamp_; - info->payload_type = payload_type_; + if (r == 0) + return true; + + // Got enough input to produce a packet. Return the saved timestamp from + // the first chunk of input that went into the packet. + packet_in_progress_ = false; + info->encoded_timestamp = packet_timestamp_; + info->payload_type = payload_type_; + + if (!T::has_redundant_encoder) + return true; + + if (first_output_frame_) { + // Do not emit the first output frame when using redundant encoding. + info->encoded_bytes = 0; + first_output_frame_ = false; + } else { + // Call the encoder's method to get redundant encoding. + const size_t primary_length = info->encoded_bytes; + int16_t secondary_len; + { + CriticalSectionScoped cs(lock_.get()); + secondary_len = T::GetRedPayload(isac_state_, &encoded[primary_length]); + } + DCHECK_GE(secondary_len, 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); + EncodedInfoLeaf secondary_info; + secondary_info.payload_type = info->payload_type; + secondary_info.encoded_bytes = secondary_len; + secondary_info.encoded_timestamp = last_encoded_timestamp_; + info->redundant.push_back(secondary_info); + info->encoded_bytes += secondary_len; // Sum of primary and secondary. } + last_encoded_timestamp_ = packet_timestamp_; return true; } diff --git a/webrtc/modules/audio_coding/codecs/isac/fix/interface/audio_encoder_isacfix.h b/webrtc/modules/audio_coding/codecs/isac/fix/interface/audio_encoder_isacfix.h index 06722b1058..944152be71 100644 --- a/webrtc/modules/audio_coding/codecs/isac/fix/interface/audio_encoder_isacfix.h +++ b/webrtc/modules/audio_coding/codecs/isac/fix/interface/audio_encoder_isacfix.h @@ -19,6 +19,7 @@ namespace webrtc { struct IsacFix { typedef ISACFIX_MainStruct instance_type; static const bool has_32kHz = false; + static const bool has_redundant_encoder = false; static const uint16_t kFixSampleRate = 16000; static inline int16_t Control(instance_type* inst, int32_t rate, @@ -100,6 +101,10 @@ struct IsacFix { return WebRtcIsacfix_UpdateBwEstimate(inst, encoded, packet_size, rtp_seq_number, send_ts, arr_ts); } + static inline int16_t GetRedPayload(instance_type* inst, uint8_t* encoded) { + FATAL() << "Should never be called."; + return -1; + } }; typedef AudioEncoderDecoderIsacT AudioEncoderDecoderIsacFix; diff --git a/webrtc/modules/audio_coding/codecs/isac/main/interface/audio_encoder_isac.h b/webrtc/modules/audio_coding/codecs/isac/main/interface/audio_encoder_isac.h index 091b775748..bcfe222c24 100644 --- a/webrtc/modules/audio_coding/codecs/isac/main/interface/audio_encoder_isac.h +++ b/webrtc/modules/audio_coding/codecs/isac/main/interface/audio_encoder_isac.h @@ -19,6 +19,7 @@ namespace webrtc { struct IsacFloat { typedef ISACStruct instance_type; static const bool has_32kHz = true; + static const bool has_redundant_encoder = false; static inline int16_t Control(instance_type* inst, int32_t rate, int16_t framesize) { @@ -97,9 +98,23 @@ struct IsacFloat { return WebRtcIsac_UpdateBwEstimate(inst, encoded, packet_size, rtp_seq_number, send_ts, arr_ts); } + static inline int16_t GetRedPayload(instance_type* inst, uint8_t* encoded) { + FATAL() << "Should never be called."; + return -1; + } }; typedef AudioEncoderDecoderIsacT AudioEncoderDecoderIsac; +struct IsacRed : public IsacFloat { + static const bool has_redundant_encoder = true; + + static inline int16_t GetRedPayload(instance_type* inst, uint8_t* encoded) { + return WebRtcIsac_GetRedPayload(inst, encoded); + } +}; + +typedef AudioEncoderDecoderIsacT AudioEncoderDecoderIsacRed; + } // namespace webrtc #endif // WEBRTC_MODULES_AUDIO_CODING_CODECS_ISAC_MAIN_INTERFACE_AUDIO_ENCODER_ISAC_H_ diff --git a/webrtc/modules/audio_coding/codecs/isac/main/source/audio_encoder_isac.cc b/webrtc/modules/audio_coding/codecs/isac/main/source/audio_encoder_isac.cc index ba08603637..5e554bac5d 100644 --- a/webrtc/modules/audio_coding/codecs/isac/main/source/audio_encoder_isac.cc +++ b/webrtc/modules/audio_coding/codecs/isac/main/source/audio_encoder_isac.cc @@ -18,4 +18,8 @@ namespace webrtc { // AudioEncoderDecoderIsac. template class AudioEncoderDecoderIsacT; +// Explicit instantiation of AudioEncoderDecoderIsacT, a.k.a. +// AudioEncoderDecoderIsacRed. +template class AudioEncoderDecoderIsacT; + } // namespace webrtc diff --git a/webrtc/modules/audio_coding/codecs/isac/main/source/audio_encoder_isac_red_unittest.cc b/webrtc/modules/audio_coding/codecs/isac/main/source/audio_encoder_isac_red_unittest.cc new file mode 100644 index 0000000000..fb6cadf476 --- /dev/null +++ b/webrtc/modules/audio_coding/codecs/isac/main/source/audio_encoder_isac_red_unittest.cc @@ -0,0 +1,76 @@ +/* + * 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 + +#include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/modules/audio_coding/codecs/isac/main/interface/audio_encoder_isac.h" +#include "webrtc/system_wrappers/interface/scoped_ptr.h" + +namespace webrtc { + +// Simply check that AudioEncoderDecoderIsacRed produces more encoded bytes +// than AudioEncoderDecoderIsac. Also check that the redundancy information is +// populated in the EncodedInfo. +TEST(AudioEncoderIsacRedTest, CompareRedAndNoRed) { + static const int kSampleRateHz = 16000; + static const int k10MsSamples = kSampleRateHz / 100; + // Fill the input array with pseudo-random noise in the range [-1000, 1000]. + int16_t input[k10MsSamples]; + srand(1418811752); + for (int i = 0; i < k10MsSamples; ++i) { + double r = rand(); // NOLINT(runtime/threadsafe_fn) + input[i] = (r / RAND_MAX) * 2000 - 1000; + } + static const size_t kMaxEncodedSizeBytes = 1000; + uint8_t encoded[kMaxEncodedSizeBytes]; + uint8_t red_encoded[kMaxEncodedSizeBytes]; + AudioEncoderDecoderIsac::Config config; + config.sample_rate_hz = kSampleRateHz; + AudioEncoderDecoderIsac isac_encoder(config); + AudioEncoderDecoderIsacRed::Config red_config; + red_config.sample_rate_hz = kSampleRateHz; + AudioEncoderDecoderIsacRed isac_red_encoder(red_config); + AudioEncoder::EncodedInfo info, red_info; + + // Note that we are not expecting any output from the redundant encoder until + // the 6th block of 10 ms has been processed. This is because in RED mode, + // iSAC will not output the first 30 ms frame. + for (int i = 0; i < 6; ++i) { + EXPECT_EQ(0u, red_info.encoded_bytes); + EXPECT_EQ(0u, red_info.redundant.size()); + const uint32_t timestamp = static_cast(i); + EXPECT_TRUE(isac_encoder.Encode(timestamp, input, k10MsSamples, + kMaxEncodedSizeBytes, encoded, &info)); + EXPECT_TRUE(isac_red_encoder.Encode(timestamp, input, k10MsSamples, + kMaxEncodedSizeBytes, red_encoded, + &red_info)); + } + EXPECT_GT(info.encoded_bytes, 0u) + << "Regular codec did not produce any output"; + EXPECT_GT(red_info.encoded_bytes, info.encoded_bytes) + << "Redundant payload seems to be missing"; + ASSERT_EQ(2u, red_info.redundant.size()) << "Redundancy vector not populated"; + ASSERT_EQ(info.encoded_bytes, red_info.redundant[0].encoded_bytes) + << "Primary payload should be same length as non-redundant payload"; + // Check that |encoded| and the primary part of |red_encoded| are identical. + EXPECT_EQ(0, memcmp(encoded, red_encoded, info.encoded_bytes)); + EXPECT_GT(red_info.redundant[0].encoded_bytes, + red_info.redundant[1].encoded_bytes) + << "Redundant payload should be smaller than primary"; + EXPECT_EQ(red_info.encoded_bytes, red_info.redundant[0].encoded_bytes + + red_info.redundant[1].encoded_bytes) + << "Encoded sizes don't add up"; + EXPECT_EQ(3u, red_info.redundant[0].encoded_timestamp) + << "Primary timestamp is wrong"; + EXPECT_EQ(0u, red_info.redundant[1].encoded_timestamp) + << "Secondary timestamp is wrong"; +} +} // namespace webrtc diff --git a/webrtc/modules/modules.gyp b/webrtc/modules/modules.gyp index d1af2654ae..6cd2343237 100644 --- a/webrtc/modules/modules.gyp +++ b/webrtc/modules/modules.gyp @@ -119,6 +119,7 @@ 'audio_coding/codecs/isac/fix/source/lpc_masking_model_unittest.cc', 'audio_coding/codecs/isac/fix/source/transform_unittest.cc', 'audio_coding/codecs/isac/main/source/isac_unittest.cc', + 'audio_coding/codecs/isac/main/source/audio_encoder_isac_red_unittest.cc', 'audio_coding/codecs/opus/opus_unittest.cc', 'audio_coding/codecs/red/audio_encoder_copy_red_unittest.cc', 'audio_coding/neteq/audio_classifier_unittest.cc',