Remove support for iSAC RCU

The current way that iSAC RCU is packetized and sent as a RED packet,
with the same payload type for primary and redundant payloads, does
not follow the specification for RED. As it is now, it is impossible
for a receiver to know if an incoming RED packet with iSAC payloads
inside consists of two "primary" (but time-shifted) payloads, or one
primary and one RCU payload. The RED standard stipulates that the
former option is the correct interpretation, while our implementation
currently applies the latter.

This CL removes support for iSAC RCU from Audio Coding Module, but
leaves it in the iSAC codec itself (i.e., in the C implementation).

BUG=4402
COAUTHOR=kwiberg@webrtc.org
R=tina.legrand@webrtc.org

Review URL: https://webrtc-codereview.appspot.com/45569004

Cr-Commit-Position: refs/heads/master@{#8713}
git-svn-id: http://webrtc.googlecode.com/svn/trunk@8713 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
henrik.lundin@webrtc.org 2015-03-13 08:27:57 +00:00
parent 9f41810c82
commit 0c5b137e7e
7 changed files with 5 additions and 208 deletions

View File

@ -35,13 +35,11 @@ class AudioEncoderDecoderIsacT : public AudioEncoder, public AudioDecoder {
Config();
bool IsOk() const;
int payload_type;
int red_payload_type;
int sample_rate_hz;
int frame_size_ms;
int bit_rate; // Limit on the short-term average bit rate, in bits/second.
int max_bit_rate;
int max_payload_size_bytes;
bool use_red;
};
// For constructing an encoder in channel-adaptive mode. Allowed combinations
@ -54,14 +52,12 @@ class AudioEncoderDecoderIsacT : public AudioEncoder, public AudioDecoder {
ConfigAdaptive();
bool IsOk() const;
int payload_type;
int red_payload_type;
int sample_rate_hz;
int initial_frame_size_ms;
int initial_bit_rate;
int max_bit_rate;
bool enforce_frame_size; // Prevent adaptive changes to the frame size?
int max_payload_size_bytes;
bool use_red;
};
explicit AudioEncoderDecoderIsacT(const Config& config);
@ -81,11 +77,6 @@ class AudioEncoderDecoderIsacT : public AudioEncoder, public AudioDecoder {
int sample_rate_hz,
int16_t* decoded,
SpeechType* speech_type) override;
int DecodeRedundant(const uint8_t* encoded,
size_t encoded_len,
int sample_rate_hz,
int16_t* decoded,
SpeechType* speech_type) override;
bool HasDecodePlc() const override;
int DecodePlc(int num_frames, int16_t* decoded) override;
int Init() override;
@ -110,8 +101,6 @@ class AudioEncoderDecoderIsacT : public AudioEncoder, public AudioDecoder {
static const size_t kSufficientEncodeBufferSizeBytes = 400;
const int payload_type_;
const int red_payload_type_;
const bool use_red_;
// iSAC encoder/decoder state, guarded by a mutex to ensure that encode calls
// from one thread won't clash with decode calls from another thread.
@ -134,10 +123,6 @@ class AudioEncoderDecoderIsacT : public AudioEncoder, public AudioDecoder {
// Timestamp of the previously encoded packet.
uint32_t last_encoded_timestamp_ GUARDED_BY(lock_);
// Redundant encoding from last time.
const rtc::scoped_ptr<uint8_t[]> redundant_payload_ GUARDED_BY(lock_);
size_t redundant_length_bytes_ GUARDED_BY(lock_);
DISALLOW_COPY_AND_ASSIGN(AudioEncoderDecoderIsacT);
};

View File

@ -22,19 +22,16 @@
namespace webrtc {
const int kIsacPayloadType = 103;
const int kInvalidPayloadType = -1;
const int kDefaultBitRate = 32000;
template <typename T>
AudioEncoderDecoderIsacT<T>::Config::Config()
: payload_type(kIsacPayloadType),
red_payload_type(kInvalidPayloadType),
sample_rate_hz(16000),
frame_size_ms(30),
bit_rate(kDefaultBitRate),
max_bit_rate(-1),
max_payload_size_bytes(-1),
use_red(false) {
max_payload_size_bytes(-1) {
}
template <typename T>
@ -43,8 +40,6 @@ bool AudioEncoderDecoderIsacT<T>::Config::IsOk() const {
return false;
if (max_payload_size_bytes < 120 && max_payload_size_bytes != -1)
return false;
if (use_red && red_payload_type == kInvalidPayloadType)
return false;
switch (sample_rate_hz) {
case 16000:
if (max_bit_rate > 53400)
@ -70,14 +65,12 @@ bool AudioEncoderDecoderIsacT<T>::Config::IsOk() const {
template <typename T>
AudioEncoderDecoderIsacT<T>::ConfigAdaptive::ConfigAdaptive()
: payload_type(kIsacPayloadType),
red_payload_type(kInvalidPayloadType),
sample_rate_hz(16000),
initial_frame_size_ms(30),
initial_bit_rate(kDefaultBitRate),
max_bit_rate(-1),
enforce_frame_size(false),
max_payload_size_bytes(-1),
use_red(false) {
max_payload_size_bytes(-1) {
}
template <typename T>
@ -86,8 +79,6 @@ bool AudioEncoderDecoderIsacT<T>::ConfigAdaptive::IsOk() const {
return false;
if (max_payload_size_bytes < 120 && max_payload_size_bytes != -1)
return false;
if (use_red && red_payload_type == kInvalidPayloadType)
return false;
switch (sample_rate_hz) {
case 16000:
if (max_bit_rate > 53400)
@ -113,15 +104,10 @@ bool AudioEncoderDecoderIsacT<T>::ConfigAdaptive::IsOk() const {
template <typename T>
AudioEncoderDecoderIsacT<T>::AudioEncoderDecoderIsacT(const Config& config)
: payload_type_(config.payload_type),
red_payload_type_(config.red_payload_type),
use_red_(config.use_red),
state_lock_(CriticalSectionWrapper::CreateCriticalSection()),
decoder_sample_rate_hz_(0),
lock_(CriticalSectionWrapper::CreateCriticalSection()),
packet_in_progress_(false),
redundant_payload_(
use_red_ ? new uint8_t[kSufficientEncodeBufferSizeBytes] : nullptr),
redundant_length_bytes_(0) {
packet_in_progress_(false) {
CHECK(config.IsOk());
CHECK_EQ(0, T::Create(&isac_state_));
CHECK_EQ(0, T::EncoderInit(isac_state_, 1));
@ -144,15 +130,10 @@ template <typename T>
AudioEncoderDecoderIsacT<T>::AudioEncoderDecoderIsacT(
const ConfigAdaptive& config)
: payload_type_(config.payload_type),
red_payload_type_(config.red_payload_type),
use_red_(config.use_red),
state_lock_(CriticalSectionWrapper::CreateCriticalSection()),
decoder_sample_rate_hz_(0),
lock_(CriticalSectionWrapper::CreateCriticalSection()),
packet_in_progress_(false),
redundant_payload_(
use_red_ ? new uint8_t[kSufficientEncodeBufferSizeBytes] : nullptr),
redundant_length_bytes_(0) {
packet_in_progress_(false) {
CHECK(config.IsOk());
CHECK_EQ(0, T::Create(&isac_state_));
CHECK_EQ(0, T::EncoderInit(isac_state_, 0));
@ -234,45 +215,6 @@ void AudioEncoderDecoderIsacT<T>::EncodeInternal(uint32_t rtp_timestamp,
packet_in_progress_ = false;
info->encoded_timestamp = packet_timestamp_;
info->payload_type = payload_type_;
if (!use_red_)
return;
if (redundant_length_bytes_ == 0) {
// Do not emit the first output frame when using redundant encoding.
info->encoded_bytes = 0;
} else {
// When a redundant payload from the last Encode call is available, the
// resulting payload consists of the primary encoding followed by the
// redundant encoding from last time.
const size_t primary_length = info->encoded_bytes;
memcpy(&encoded[primary_length], redundant_payload_.get(),
redundant_length_bytes_);
// The EncodedInfo struct |info| will have one root node and two leaves.
// |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 = redundant_length_bytes_;
secondary_info.encoded_timestamp = last_encoded_timestamp_;
info->redundant.push_back(secondary_info);
info->encoded_bytes +=
redundant_length_bytes_; // Sum of primary and secondary.
DCHECK_NE(red_payload_type_, kInvalidPayloadType)
<< "Config.red_payload_type must be set for "
"AudioEncoderDecoderIsacRed.";
info->payload_type = red_payload_type_;
}
{
CriticalSectionScoped cs(state_lock_.get());
// Call the encoder's method to get redundant encoding.
redundant_length_bytes_ =
T::GetRedPayload(isac_state_, redundant_payload_.get());
}
DCHECK_GE(redundant_length_bytes_, 0u);
last_encoded_timestamp_ = packet_timestamp_;
}
template <typename T>
@ -296,21 +238,6 @@ int AudioEncoderDecoderIsacT<T>::Decode(const uint8_t* encoded,
return ret;
}
template <typename T>
int AudioEncoderDecoderIsacT<T>::DecodeRedundant(const uint8_t* encoded,
size_t encoded_len,
int /*sample_rate_hz*/,
int16_t* decoded,
SpeechType* speech_type) {
CriticalSectionScoped cs(state_lock_.get());
int16_t temp_type = 1; // Default is speech.
int16_t ret =
T::DecodeRcu(isac_state_, encoded, static_cast<int16_t>(encoded_len),
decoded, &temp_type);
*speech_type = ConvertSpeechType(temp_type);
return ret;
}
template <typename T>
bool AudioEncoderDecoderIsacT<T>::HasDecodePlc() const {
return true;

View File

@ -48,14 +48,6 @@ struct IsacFix {
int16_t num_lost_frames) {
return WebRtcIsacfix_DecodePlc(inst, decoded, num_lost_frames);
}
static inline int16_t DecodeRcu(instance_type* inst,
const uint8_t* encoded,
int16_t len,
int16_t* decoded,
int16_t* speech_type) {
// iSACfix has no DecodeRcu; just call the normal Decode.
return WebRtcIsacfix_Decode(inst, encoded, len, decoded, speech_type);
}
static inline int16_t DecoderInit(instance_type* inst) {
return WebRtcIsacfix_DecoderInit(inst);
}
@ -101,10 +93,6 @@ 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;
}
static inline int16_t SetMaxPayloadSize(instance_type* inst,
int16_t max_payload_size_bytes) {
return WebRtcIsacfix_SetMaxPayloadSize(inst, max_payload_size_bytes);

View File

@ -48,13 +48,6 @@ struct IsacFloat {
return WebRtcIsac_DecodePlc(inst, decoded, num_lost_frames);
}
static inline int16_t DecodeRcu(instance_type* inst,
const uint8_t* encoded,
int16_t len,
int16_t* decoded,
int16_t* speech_type) {
return WebRtcIsac_DecodeRcu(inst, encoded, len, decoded, speech_type);
}
static inline int16_t DecoderInit(instance_type* inst) {
return WebRtcIsac_DecoderInit(inst);
}
@ -98,9 +91,6 @@ 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) {
return WebRtcIsac_GetRedPayload(inst, encoded);
}
static inline int16_t SetMaxPayloadSize(instance_type* inst,
int16_t max_payload_size_bytes) {
return WebRtcIsac_SetMaxPayloadSize(inst, max_payload_size_bytes);

View File

@ -1,82 +0,0 @@
/*
* 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 <stdlib.h>
#include "testing/gtest/include/gtest/gtest.h"
#include "webrtc/base/scoped_ptr.h"
#include "webrtc/modules/audio_coding/codecs/isac/main/interface/audio_encoder_isac.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;
static const int kRedPayloadType = 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;
}
AudioEncoderDecoderIsac::Config config;
config.sample_rate_hz = kSampleRateHz;
AudioEncoderDecoderIsac isac_encoder(config);
size_t max_encoded_bytes = isac_encoder.MaxEncodedBytes();
rtc::scoped_ptr<uint8_t[]> encoded(new uint8_t[max_encoded_bytes]);
AudioEncoderDecoderIsac::Config red_config;
red_config.sample_rate_hz = kSampleRateHz;
red_config.red_payload_type = kRedPayloadType;
red_config.use_red = true;
ASSERT_NE(red_config.red_payload_type, red_config.payload_type)
<< "iSAC and RED payload types must be different.";
AudioEncoderDecoderIsac isac_red_encoder(red_config);
size_t max_red_encoded_bytes = isac_red_encoder.MaxEncodedBytes();
rtc::scoped_ptr<uint8_t[]> red_encoded(new uint8_t[max_red_encoded_bytes]);
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<uint32_t>(i);
isac_encoder.Encode(timestamp, input, k10MsSamples, max_encoded_bytes,
encoded.get(), &info);
isac_red_encoder.Encode(timestamp, input, k10MsSamples,
max_red_encoded_bytes, red_encoded.get(),
&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.get(), red_encoded.get(), 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

View File

@ -261,7 +261,6 @@ int16_t ACMGenericCodec::InitEncoder(WebRtcACMCodecParams* codec_params,
void ACMGenericCodec::ResetAudioEncoder() {
const CodecInst& codec_inst = acm_codec_params_.codec_inst;
bool using_codec_internal_red = false;
if (!STR_CASE_CMP(codec_inst.plname, "PCMU")) {
AudioEncoderPcmU::Config config;
config.num_channels = codec_inst.channels;
@ -344,7 +343,6 @@ void ACMGenericCodec::ResetAudioEncoder() {
#ifdef WEBRTC_CODEC_ISAC
} else if (!STR_CASE_CMP(codec_inst.plname, "ISAC")) {
is_isac_ = true;
using_codec_internal_red = copy_red_enabled_;
AudioEncoderDecoderIsac* enc_dec;
if (codec_inst.rate == -1) {
// Adaptive mode.
@ -355,10 +353,6 @@ void ACMGenericCodec::ResetAudioEncoder() {
config.max_payload_size_bytes = max_payload_size_bytes_;
config.max_bit_rate = max_rate_bps_;
config.payload_type = codec_inst.pltype;
if (copy_red_enabled_) {
config.red_payload_type = red_payload_type_;
config.use_red = true;
}
enc_dec = new AudioEncoderDecoderIsac(config);
} else {
// Channel independent mode.
@ -370,10 +364,6 @@ void ACMGenericCodec::ResetAudioEncoder() {
config.max_payload_size_bytes = max_payload_size_bytes_;
config.max_bit_rate = max_rate_bps_;
config.payload_type = codec_inst.pltype;
if (copy_red_enabled_) {
config.red_payload_type = red_payload_type_;
config.use_red = true;
}
enc_dec = new AudioEncoderDecoderIsac(config);
}
audio_encoder_.reset(enc_dec);
@ -388,7 +378,7 @@ void ACMGenericCodec::ResetAudioEncoder() {
encoder_ = audio_encoder_.get();
// Attach RED if needed.
if (copy_red_enabled_ && !using_codec_internal_red) {
if (copy_red_enabled_) {
CHECK_NE(red_payload_type_, kInvalidPayloadType);
AudioEncoderCopyRed::Config config;
config.payload_type = red_payload_type_;

View File

@ -108,7 +108,6 @@
'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/audio_encoder_opus_unittest.cc',
'audio_coding/codecs/opus/opus_unittest.cc',
'audio_coding/codecs/red/audio_encoder_copy_red_unittest.cc',