Fix a number of things in AudioEncoderDecoderIsac*
- Add max_bit_rate and max_payload_size_bytes to config structs.
- Fix support for 48 kHz sample rate.
- Fix iSAC-RED.
- Add method UpdateDecoderSampleRate().
- Update locking structure with a separate lock for local member
variables used by the encoder methods.
BUG=3926
COAUTHOR:kwiberg@webrtc.org
R=minyue@webrtc.org
Review URL: https://webrtc-codereview.appspot.com/41659004
Cr-Commit-Position: refs/heads/master@{#8204}
git-svn-id: http://webrtc.googlecode.com/svn/trunk@8204 4adac7df-926f-26a2-2b94-8c16560cd09d
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 712d3e6..65a1204 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
@@ -29,34 +29,45 @@
// are
// - 16000 Hz, 30 ms, 10000-32000 bps
// - 16000 Hz, 60 ms, 10000-32000 bps
- // - 32000 Hz, 30 ms, 10000-56000 bps (if T has 32 kHz support)
+ // - 32000 Hz, 30 ms, 10000-56000 bps (if T has super-wideband support)
+ // - 48000 Hz, 30 ms, 10000-56000 bps (if T has super-wideband support)
struct Config {
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;
};
- // For constructing an encoder in channel-adaptive mode. The sample rate must
- // be 16000 Hz; the initial frame size can be 30 or 60 ms; and the initial
- // bit rate can be 10000-56000 bps if T has 32 kHz support, 10000-32000 bps
- // otherwise.
+ // For constructing an encoder in channel-adaptive mode. Allowed combinations
+ // are
+ // - 16000 Hz, 30 ms, 10000-32000 bps
+ // - 16000 Hz, 60 ms, 10000-32000 bps
+ // - 32000 Hz, 30 ms, 10000-56000 bps (if T has super-wideband support)
+ // - 48000 Hz, 30 ms, 10000-56000 bps (if T has super-wideband support)
struct ConfigAdaptive {
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;
};
explicit AudioEncoderDecoderIsacT(const Config& config);
explicit AudioEncoderDecoderIsacT(const ConfigAdaptive& config);
virtual ~AudioEncoderDecoderIsacT() OVERRIDE;
+ void UpdateDecoderSampleRate(int sample_rate_hz);
+
// AudioEncoder public methods.
virtual int sample_rate_hz() const OVERRIDE;
virtual int num_channels() const OVERRIDE;
@@ -91,24 +102,39 @@
EncodedInfo* info) OVERRIDE;
private:
+ // This value is taken from STREAM_SIZE_MAX_60 for iSAC float (60 ms) and
+ // STREAM_MAXW16_60MS for iSAC fix (60 ms).
+ static const size_t kSufficientEncodeBufferSizeBytes = 400;
+
const int payload_type_;
+ const int red_payload_type_;
// 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.
+ // Note: PT_GUARDED_BY is disabled since it is not yet supported by clang.
+ const scoped_ptr<CriticalSectionWrapper> state_lock_;
+ typename T::instance_type* isac_state_
+ GUARDED_BY(state_lock_) /* PT_GUARDED_BY(lock_)*/;
+
+ // Must be acquired before state_lock_.
const scoped_ptr<CriticalSectionWrapper> lock_;
- typename T::instance_type* isac_state_ GUARDED_BY(lock_);
// 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_;
+ bool packet_in_progress_ GUARDED_BY(lock_);
// Timestamp of the first input of the currently in-progress packet.
- uint32_t packet_timestamp_;
+ uint32_t packet_timestamp_ GUARDED_BY(lock_);
// Timestamp of the previously encoded packet.
- uint32_t last_encoded_timestamp_;
+ uint32_t last_encoded_timestamp_ GUARDED_BY(lock_);
+
+ // Redundant encoding from last time.
+ // Note: If has_redundant_encoder is false, we set the array length to 1,
+ // since zero-length arrays are not supported by all compilers.
+ uint8_t redundant_payload_[T::has_redundant_encoder
+ ? kSufficientEncodeBufferSizeBytes
+ : 1] GUARDED_BY(lock_);
+ size_t redundant_length_bytes_ GUARDED_BY(lock_);
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 5b337c1..b45fb265 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
@@ -13,29 +13,49 @@
#include "webrtc/modules/audio_coding/codecs/isac/main/interface/audio_encoder_isac.h"
+#include <algorithm>
+
+#include "webrtc/base/checks.h"
#include "webrtc/modules/audio_coding/codecs/isac/main/interface/isac.h"
#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
namespace webrtc {
const int kIsacPayloadType = 103;
+const int kInvalidPayloadType = -1;
template <typename T>
AudioEncoderDecoderIsacT<T>::Config::Config()
: payload_type(kIsacPayloadType),
+ red_payload_type(kInvalidPayloadType),
sample_rate_hz(16000),
frame_size_ms(30),
- bit_rate(32000) {
+ bit_rate(32000),
+ max_bit_rate(-1),
+ max_payload_size_bytes(-1) {
}
template <typename T>
bool AudioEncoderDecoderIsacT<T>::Config::IsOk() const {
+ if (max_bit_rate < 32000 && max_bit_rate != -1)
+ return false;
+ if (max_payload_size_bytes < 120 && max_payload_size_bytes != -1)
+ return false;
switch (sample_rate_hz) {
case 16000:
+ if (max_bit_rate > 53400)
+ return false;
+ if (max_payload_size_bytes > 400)
+ return false;
return (frame_size_ms == 30 || frame_size_ms == 60) &&
bit_rate >= 10000 && bit_rate <= 32000;
case 32000:
- return T::has_32kHz &&
+ case 48000:
+ if (max_bit_rate > 160000)
+ return false;
+ if (max_payload_size_bytes > 600)
+ return false;
+ return T::has_swb &&
(frame_size_ms == 30 && bit_rate >= 10000 && bit_rate <= 56000);
default:
return false;
@@ -45,41 +65,76 @@
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(32000),
- enforce_frame_size(false) {
+ max_bit_rate(-1),
+ enforce_frame_size(false),
+ max_payload_size_bytes(-1) {
}
template <typename T>
bool AudioEncoderDecoderIsacT<T>::ConfigAdaptive::IsOk() const {
- static const int max_rate = T::has_32kHz ? 56000 : 32000;
- return sample_rate_hz == 16000 &&
- (initial_frame_size_ms == 30 || initial_frame_size_ms == 60) &&
- initial_bit_rate >= 10000 && initial_bit_rate <= max_rate;
+ if (max_bit_rate < 32000 && max_bit_rate != -1)
+ return false;
+ if (max_payload_size_bytes < 120 && max_payload_size_bytes != -1)
+ return false;
+ switch (sample_rate_hz) {
+ case 16000:
+ if (max_bit_rate > 53400)
+ return false;
+ if (max_payload_size_bytes > 400)
+ return false;
+ return (initial_frame_size_ms == 30 || initial_frame_size_ms == 60) &&
+ initial_bit_rate >= 10000 && initial_bit_rate <= 32000;
+ case 32000:
+ case 48000:
+ if (max_bit_rate > 160000)
+ return false;
+ if (max_payload_size_bytes > 600)
+ return false;
+ return T::has_swb &&
+ (initial_frame_size_ms == 30 && initial_bit_rate >= 10000 &&
+ initial_bit_rate <= 56000);
+ default:
+ return false;
+ }
}
template <typename T>
AudioEncoderDecoderIsacT<T>::AudioEncoderDecoderIsacT(const Config& config)
: payload_type_(config.payload_type),
+ red_payload_type_(config.red_payload_type),
+ state_lock_(CriticalSectionWrapper::CreateCriticalSection()),
lock_(CriticalSectionWrapper::CreateCriticalSection()),
packet_in_progress_(false),
- first_output_frame_(true) {
+ redundant_length_bytes_(0) {
CHECK(config.IsOk());
CHECK_EQ(0, T::Create(&isac_state_));
CHECK_EQ(0, T::EncoderInit(isac_state_, 1));
CHECK_EQ(0, T::SetEncSampRate(isac_state_, config.sample_rate_hz));
CHECK_EQ(0, T::Control(isac_state_, config.bit_rate, config.frame_size_ms));
- CHECK_EQ(0, T::SetDecSampRate(isac_state_, config.sample_rate_hz));
+ // When config.sample_rate_hz is set to 48000 Hz (iSAC-fb), the decoder is
+ // still set to 32000 Hz, since there is no full-band mode in the decoder.
+ CHECK_EQ(0, T::SetDecSampRate(isac_state_,
+ std::min(config.sample_rate_hz, 32000)));
+ if (config.max_payload_size_bytes != -1)
+ CHECK_EQ(0,
+ T::SetMaxPayloadSize(isac_state_, config.max_payload_size_bytes));
+ if (config.max_bit_rate != -1)
+ CHECK_EQ(0, T::SetMaxRate(isac_state_, config.max_bit_rate));
}
template <typename T>
AudioEncoderDecoderIsacT<T>::AudioEncoderDecoderIsacT(
const ConfigAdaptive& config)
: payload_type_(config.payload_type),
+ red_payload_type_(config.red_payload_type),
+ state_lock_(CriticalSectionWrapper::CreateCriticalSection()),
lock_(CriticalSectionWrapper::CreateCriticalSection()),
packet_in_progress_(false),
- first_output_frame_(true) {
+ redundant_length_bytes_(0) {
CHECK(config.IsOk());
CHECK_EQ(0, T::Create(&isac_state_));
CHECK_EQ(0, T::EncoderInit(isac_state_, 0));
@@ -88,6 +143,11 @@
config.initial_frame_size_ms,
config.enforce_frame_size));
CHECK_EQ(0, T::SetDecSampRate(isac_state_, config.sample_rate_hz));
+ if (config.max_payload_size_bytes != -1)
+ CHECK_EQ(0,
+ T::SetMaxPayloadSize(isac_state_, config.max_payload_size_bytes));
+ if (config.max_bit_rate != -1)
+ CHECK_EQ(0, T::SetMaxRate(isac_state_, config.max_bit_rate));
}
template <typename T>
@@ -96,8 +156,14 @@
}
template <typename T>
+void AudioEncoderDecoderIsacT<T>::UpdateDecoderSampleRate(int sample_rate_hz) {
+ CriticalSectionScoped cs(state_lock_.get());
+ CHECK_EQ(0, T::SetDecSampRate(isac_state_, sample_rate_hz));
+}
+
+template <typename T>
int AudioEncoderDecoderIsacT<T>::sample_rate_hz() const {
- CriticalSectionScoped cs(lock_.get());
+ CriticalSectionScoped cs(state_lock_.get());
return T::EncSampRate(isac_state_);
}
@@ -108,7 +174,7 @@
template <typename T>
int AudioEncoderDecoderIsacT<T>::Num10MsFramesInNextPacket() const {
- CriticalSectionScoped cs(lock_.get());
+ CriticalSectionScoped cs(state_lock_.get());
const int samples_in_next_packet = T::GetNewFrameLen(isac_state_);
return rtc::CheckedDivExact(samples_in_next_packet,
rtc::CheckedDivExact(sample_rate_hz(), 100));
@@ -125,6 +191,7 @@
size_t max_encoded_bytes,
uint8_t* encoded,
EncodedInfo* info) {
+ CriticalSectionScoped cs(lock_.get());
if (!packet_in_progress_) {
// Starting a new packet; remember the timestamp for later.
packet_in_progress_ = true;
@@ -132,7 +199,7 @@
}
int r;
{
- CriticalSectionScoped cs(lock_.get());
+ CriticalSectionScoped cs(state_lock_.get());
r = T::Encode(isac_state_, audio, encoded);
}
if (r < 0) {
@@ -158,30 +225,40 @@
if (!T::has_redundant_encoder)
return true;
- if (first_output_frame_) {
+ if (redundant_length_bytes_ == 0) {
// 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.
+ // 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;
- int16_t secondary_len;
- {
- CriticalSectionScoped cs(lock_.get());
- secondary_len = T::GetRedPayload(isac_state_, &encoded[primary_length]);
- }
- DCHECK_GE(secondary_len, 0);
+ memcpy(&encoded[primary_length], redundant_payload_,
+ 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 = secondary_len;
+ secondary_info.encoded_bytes = redundant_length_bytes_;
secondary_info.encoded_timestamp = last_encoded_timestamp_;
info->redundant.push_back(secondary_info);
- info->encoded_bytes += secondary_len; // Sum of primary and secondary.
+ 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_);
+ }
+ DCHECK_LE(redundant_length_bytes_, sizeof(redundant_payload_));
+ DCHECK_GE(redundant_length_bytes_, 0u);
last_encoded_timestamp_ = packet_timestamp_;
return true;
}
@@ -191,7 +268,7 @@
size_t encoded_len,
int16_t* decoded,
SpeechType* speech_type) {
- CriticalSectionScoped cs(lock_.get());
+ CriticalSectionScoped cs(state_lock_.get());
int16_t temp_type = 1; // Default is speech.
int16_t ret =
T::Decode(isac_state_, encoded, static_cast<int16_t>(encoded_len),
@@ -205,7 +282,7 @@
size_t encoded_len,
int16_t* decoded,
SpeechType* speech_type) {
- CriticalSectionScoped cs(lock_.get());
+ 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),
@@ -221,13 +298,13 @@
template <typename T>
int AudioEncoderDecoderIsacT<T>::DecodePlc(int num_frames, int16_t* decoded) {
- CriticalSectionScoped cs(lock_.get());
+ CriticalSectionScoped cs(state_lock_.get());
return T::DecodePlc(isac_state_, decoded, num_frames);
}
template <typename T>
int AudioEncoderDecoderIsacT<T>::Init() {
- CriticalSectionScoped cs(lock_.get());
+ CriticalSectionScoped cs(state_lock_.get());
return T::DecoderInit(isac_state_);
}
@@ -237,7 +314,7 @@
uint16_t rtp_sequence_number,
uint32_t rtp_timestamp,
uint32_t arrival_timestamp) {
- CriticalSectionScoped cs(lock_.get());
+ CriticalSectionScoped cs(state_lock_.get());
return T::UpdateBwEstimate(
isac_state_, payload, static_cast<int32_t>(payload_len),
rtp_sequence_number, rtp_timestamp, arrival_timestamp);
@@ -245,7 +322,7 @@
template <typename T>
int AudioEncoderDecoderIsacT<T>::ErrorCode() {
- CriticalSectionScoped cs(lock_.get());
+ CriticalSectionScoped cs(state_lock_.get());
return T::GetErrorCode(isac_state_);
}
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 944152b..4982e5a 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
@@ -18,7 +18,7 @@
struct IsacFix {
typedef ISACFIX_MainStruct instance_type;
- static const bool has_32kHz = false;
+ static const bool has_swb = false;
static const bool has_redundant_encoder = false;
static const uint16_t kFixSampleRate = 16000;
static inline int16_t Control(instance_type* inst,
@@ -105,6 +105,13 @@
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);
+ }
+ static inline int16_t SetMaxRate(instance_type* inst, int32_t max_bit_rate) {
+ return WebRtcIsacfix_SetMaxRate(inst, max_bit_rate);
+ }
};
typedef AudioEncoderDecoderIsacT<IsacFix> 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 bcfe222..41ed490 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
@@ -18,7 +18,7 @@
struct IsacFloat {
typedef ISACStruct instance_type;
- static const bool has_32kHz = true;
+ static const bool has_swb = true;
static const bool has_redundant_encoder = false;
static inline int16_t Control(instance_type* inst,
int32_t rate,
@@ -102,6 +102,13 @@
FATAL() << "Should never be called.";
return -1;
}
+ static inline int16_t SetMaxPayloadSize(instance_type* inst,
+ int16_t max_payload_size_bytes) {
+ return WebRtcIsac_SetMaxPayloadSize(inst, max_payload_size_bytes);
+ }
+ static inline int16_t SetMaxRate(instance_type* inst, int32_t max_bit_rate) {
+ return WebRtcIsac_SetMaxRate(inst, max_bit_rate);
+ }
};
typedef AudioEncoderDecoderIsacT<IsacFloat> AudioEncoderDecoderIsac;
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
index fb6cadf..7ff1b46 100644
--- 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
@@ -22,6 +22,7 @@
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);
@@ -37,6 +38,9 @@
AudioEncoderDecoderIsac isac_encoder(config);
AudioEncoderDecoderIsacRed::Config red_config;
red_config.sample_rate_hz = kSampleRateHz;
+ red_config.red_payload_type = kRedPayloadType;
+ ASSERT_NE(red_config.red_payload_type, red_config.payload_type)
+ << "iSAC and RED payload types must be different.";
AudioEncoderDecoderIsacRed isac_red_encoder(red_config);
AudioEncoder::EncodedInfo info, red_info;