AudioEncoder: add method MaxEncodedBytes

Added method AudioEncoder::MaxEncodedBytes() and provided implementations in derived encoders. This method returns the number of bytes that can be produced by the encoder at each Encode() call.
Unit tests were updated to use the new method.
Buffer allocation was not changed in AudioCodingModuleImpl::Encode(). It will be done after additional investigation.
Other refactoring work that was done, that may not be obvious why:
1. Moved some code into AudioEncoderCng::EncodePassive() to make it more consistent with EncodeActive().
2. Changed the order of NumChannels() and  RtpTimestampRateHz() declarations in AudioEncoderG722 and AudioEncoderCopyRed classes. It just bothered me that the order was not the same as in AudioEncoder class and its other derived classes.

R=kwiberg@webrtc.org

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

Cr-Commit-Position: refs/heads/master@{#8671}
git-svn-id: http://webrtc.googlecode.com/svn/trunk@8671 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
jmarusic@webrtc.org 2015-03-10 15:41:26 +00:00
parent d7452a0168
commit 51ccf37638
19 changed files with 151 additions and 60 deletions

View File

@ -74,6 +74,14 @@ class AudioEncoder {
virtual int SampleRateHz() const = 0; virtual int SampleRateHz() const = 0;
virtual int NumChannels() const = 0; virtual int NumChannels() const = 0;
// Return the maximum number of bytes that can be produced by the encoder
// at each Encode() call. The caller can use the return value to determine
// the size of the buffer that needs to be allocated. This value is allowed
// to depend on encoder parameters like bitrate, frame size etc., so if
// any of these change, the caller of Encode() is responsible for checking
// that the buffer is large enough by calling MaxEncodedBytes() again.
virtual size_t MaxEncodedBytes() const = 0;
// Returns the rate with which the RTP timestamps are updated. By default, // Returns the rate with which the RTP timestamps are updated. By default,
// this is the same as sample_rate_hz(). // this is the same as sample_rate_hz().
virtual int RtpTimestampRateHz() const; virtual int RtpTimestampRateHz() const;

View File

@ -10,10 +10,17 @@
#include "webrtc/modules/audio_coding/codecs/cng/include/audio_encoder_cng.h" #include "webrtc/modules/audio_coding/codecs/cng/include/audio_encoder_cng.h"
#include <algorithm>
#include <limits> #include <limits>
namespace webrtc { namespace webrtc {
namespace {
const int kMaxFrameSizeMs = 60;
} // namespace
AudioEncoderCng::Config::Config() AudioEncoderCng::Config::Config()
: num_channels(1), : num_channels(1),
payload_type(13), payload_type(13),
@ -77,6 +84,13 @@ int AudioEncoderCng::NumChannels() const {
return 1; return 1;
} }
size_t AudioEncoderCng::MaxEncodedBytes() const {
const size_t max_encoded_bytes_active = speech_encoder_->MaxEncodedBytes();
const size_t max_encoded_bytes_passive =
rtc::CheckedDivExact(kMaxFrameSizeMs, 10) * SamplesPer10msFrame();
return std::max(max_encoded_bytes_active, max_encoded_bytes_passive);
}
int AudioEncoderCng::Num10MsFramesInNextPacket() const { int AudioEncoderCng::Num10MsFramesInNextPacket() const {
return speech_encoder_->Num10MsFramesInNextPacket(); return speech_encoder_->Num10MsFramesInNextPacket();
} }
@ -114,8 +128,9 @@ void AudioEncoderCng::EncodeInternal(uint32_t rtp_timestamp,
if (frames_in_buffer_ < speech_encoder_->Num10MsFramesInNextPacket()) { if (frames_in_buffer_ < speech_encoder_->Num10MsFramesInNextPacket()) {
return; return;
} }
CHECK_LE(frames_in_buffer_, 6) CHECK_LE(frames_in_buffer_ * 10, kMaxFrameSizeMs)
<< "Frame size cannot be larger than 60 ms when using VAD/CNG."; << "Frame size cannot be larger than " << kMaxFrameSizeMs
<< " ms when using VAD/CNG.";
const size_t samples_per_10ms_frame = 10 * SampleRateHz() / 1000; const size_t samples_per_10ms_frame = 10 * SampleRateHz() / 1000;
CHECK_EQ(speech_buffer_.size(), CHECK_EQ(speech_buffer_.size(),
static_cast<size_t>(frames_in_buffer_) * samples_per_10ms_frame); static_cast<size_t>(frames_in_buffer_) * samples_per_10ms_frame);
@ -146,11 +161,7 @@ void AudioEncoderCng::EncodeInternal(uint32_t rtp_timestamp,
switch (activity) { switch (activity) {
case Vad::kPassive: { case Vad::kPassive: {
EncodePassive(encoded, &info->encoded_bytes); EncodePassive(max_encoded_bytes, encoded, info);
info->encoded_timestamp = first_timestamp_in_buffer_;
info->payload_type = cng_payload_type_;
info->send_even_if_empty = true;
info->speech = false;
last_frame_active_ = false; last_frame_active_ = false;
break; break;
} }
@ -169,10 +180,13 @@ void AudioEncoderCng::EncodeInternal(uint32_t rtp_timestamp,
frames_in_buffer_ = 0; frames_in_buffer_ = 0;
} }
void AudioEncoderCng::EncodePassive(uint8_t* encoded, size_t* encoded_bytes) { void AudioEncoderCng::EncodePassive(size_t max_encoded_bytes,
uint8_t* encoded,
EncodedInfo* info) {
bool force_sid = last_frame_active_; bool force_sid = last_frame_active_;
bool output_produced = false; bool output_produced = false;
const size_t samples_per_10ms_frame = 10 * SampleRateHz() / 1000; const size_t samples_per_10ms_frame = SamplesPer10msFrame();
CHECK_GE(max_encoded_bytes, frames_in_buffer_ * samples_per_10ms_frame);
for (int i = 0; i < frames_in_buffer_; ++i) { for (int i = 0; i < frames_in_buffer_; ++i) {
int16_t encoded_bytes_tmp = 0; int16_t encoded_bytes_tmp = 0;
CHECK_GE(WebRtcCng_Encode(cng_inst_.get(), CHECK_GE(WebRtcCng_Encode(cng_inst_.get(),
@ -181,17 +195,21 @@ void AudioEncoderCng::EncodePassive(uint8_t* encoded, size_t* encoded_bytes) {
encoded, &encoded_bytes_tmp, force_sid), 0); encoded, &encoded_bytes_tmp, force_sid), 0);
if (encoded_bytes_tmp > 0) { if (encoded_bytes_tmp > 0) {
CHECK(!output_produced); CHECK(!output_produced);
*encoded_bytes = static_cast<size_t>(encoded_bytes_tmp); info->encoded_bytes = static_cast<size_t>(encoded_bytes_tmp);
output_produced = true; output_produced = true;
force_sid = false; force_sid = false;
} }
} }
info->encoded_timestamp = first_timestamp_in_buffer_;
info->payload_type = cng_payload_type_;
info->send_even_if_empty = true;
info->speech = false;
} }
void AudioEncoderCng::EncodeActive(size_t max_encoded_bytes, void AudioEncoderCng::EncodeActive(size_t max_encoded_bytes,
uint8_t* encoded, uint8_t* encoded,
EncodedInfo* info) { EncodedInfo* info) {
const size_t samples_per_10ms_frame = 10 * SampleRateHz() / 1000; const size_t samples_per_10ms_frame = SamplesPer10msFrame();
for (int i = 0; i < frames_in_buffer_; ++i) { for (int i = 0; i < frames_in_buffer_; ++i) {
speech_encoder_->Encode(first_timestamp_in_buffer_, speech_encoder_->Encode(first_timestamp_in_buffer_,
&speech_buffer_[i * samples_per_10ms_frame], &speech_buffer_[i * samples_per_10ms_frame],
@ -203,4 +221,8 @@ void AudioEncoderCng::EncodeActive(size_t max_encoded_bytes,
} }
} }
size_t AudioEncoderCng::SamplesPer10msFrame() const {
return rtc::CheckedDivExact(10 * SampleRateHz(), 1000);
}
} // namespace webrtc } // namespace webrtc

View File

@ -8,6 +8,8 @@
* be found in the AUTHORS file in the root of the source tree. * be found in the AUTHORS file in the root of the source tree.
*/ */
#include <vector>
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
#include "webrtc/base/scoped_ptr.h" #include "webrtc/base/scoped_ptr.h"
#include "webrtc/common_audio/vad/mock/mock_vad.h" #include "webrtc/common_audio/vad/mock/mock_vad.h"
@ -23,7 +25,7 @@ using ::testing::Invoke;
namespace webrtc { namespace webrtc {
namespace { namespace {
static const size_t kMaxEncodedBytes = 1000; static const size_t kMockMaxEncodedBytes = 1000;
static const size_t kMaxNumSamples = 48 * 10 * 2; // 10 ms @ 48 kHz stereo. static const size_t kMaxNumSamples = 48 * 10 * 2; // 10 ms @ 48 kHz stereo.
static const size_t kMockReturnEncodedBytes = 17; static const size_t kMockReturnEncodedBytes = 17;
static const int kCngPayloadType = 18; static const int kCngPayloadType = 18;
@ -36,7 +38,6 @@ class AudioEncoderCngTest : public ::testing::Test {
timestamp_(4711), timestamp_(4711),
num_audio_samples_10ms_(0), num_audio_samples_10ms_(0),
sample_rate_hz_(8000) { sample_rate_hz_(8000) {
memset(encoded_, 0, kMaxEncodedBytes);
memset(audio_, 0, kMaxNumSamples * 2); memset(audio_, 0, kMaxNumSamples * 2);
config_.speech_encoder = &mock_encoder_; config_.speech_encoder = &mock_encoder_;
EXPECT_CALL(mock_encoder_, NumChannels()).WillRepeatedly(Return(1)); EXPECT_CALL(mock_encoder_, NumChannels()).WillRepeatedly(Return(1));
@ -66,14 +67,17 @@ class AudioEncoderCngTest : public ::testing::Test {
// is not too small. The return value does not matter that much, as long as // is not too small. The return value does not matter that much, as long as
// it is smaller than 10. // it is smaller than 10.
EXPECT_CALL(mock_encoder_, Max10MsFramesInAPacket()).WillOnce(Return(1)); EXPECT_CALL(mock_encoder_, Max10MsFramesInAPacket()).WillOnce(Return(1));
EXPECT_CALL(mock_encoder_, MaxEncodedBytes())
.WillRepeatedly(Return(kMockMaxEncodedBytes));
cng_.reset(new AudioEncoderCng(config_)); cng_.reset(new AudioEncoderCng(config_));
encoded_.resize(cng_->MaxEncodedBytes(), 0);
} }
void Encode() { void Encode() {
ASSERT_TRUE(cng_) << "Must call CreateCng() first."; ASSERT_TRUE(cng_) << "Must call CreateCng() first.";
encoded_info_ = AudioEncoder::EncodedInfo(); encoded_info_ = AudioEncoder::EncodedInfo();
cng_->Encode(timestamp_, audio_, num_audio_samples_10ms_, cng_->Encode(timestamp_, audio_, num_audio_samples_10ms_,
kMaxEncodedBytes, encoded_, &encoded_info_); encoded_.size(), &encoded_[0], &encoded_info_);
timestamp_ += num_audio_samples_10ms_; timestamp_ += num_audio_samples_10ms_;
} }
@ -182,7 +186,7 @@ class AudioEncoderCngTest : public ::testing::Test {
uint32_t timestamp_; uint32_t timestamp_;
int16_t audio_[kMaxNumSamples]; int16_t audio_[kMaxNumSamples];
size_t num_audio_samples_10ms_; size_t num_audio_samples_10ms_;
uint8_t encoded_[kMaxEncodedBytes]; std::vector<uint8_t> encoded_;
AudioEncoder::EncodedInfo encoded_info_; AudioEncoder::EncodedInfo encoded_info_;
int sample_rate_hz_; int sample_rate_hz_;
}; };

View File

@ -48,6 +48,7 @@ class AudioEncoderCng final : public AudioEncoder {
int SampleRateHz() const override; int SampleRateHz() const override;
int NumChannels() const override; int NumChannels() const override;
size_t MaxEncodedBytes() const override;
int RtpTimestampRateHz() const override; int RtpTimestampRateHz() const override;
int Num10MsFramesInNextPacket() const override; int Num10MsFramesInNextPacket() const override;
int Max10MsFramesInAPacket() const override; int Max10MsFramesInAPacket() const override;
@ -68,11 +69,13 @@ class AudioEncoderCng final : public AudioEncoder {
inline void operator()(CNG_enc_inst* ptr) const { WebRtcCng_FreeEnc(ptr); } inline void operator()(CNG_enc_inst* ptr) const { WebRtcCng_FreeEnc(ptr); }
}; };
void EncodePassive(uint8_t* encoded, size_t* encoded_bytes); void EncodePassive(size_t max_encoded_bytes,
uint8_t* encoded,
EncodedInfo* info);
void EncodeActive(size_t max_encoded_bytes, void EncodeActive(size_t max_encoded_bytes,
uint8_t* encoded, uint8_t* encoded,
EncodedInfo* info); EncodedInfo* info);
size_t SamplesPer10msFrame() const;
AudioEncoder* speech_encoder_; AudioEncoder* speech_encoder_;
const int cng_payload_type_; const int cng_payload_type_;

View File

@ -49,9 +49,15 @@ AudioEncoderPcm::~AudioEncoderPcm() {
int AudioEncoderPcm::SampleRateHz() const { int AudioEncoderPcm::SampleRateHz() const {
return sample_rate_hz_; return sample_rate_hz_;
} }
int AudioEncoderPcm::NumChannels() const { int AudioEncoderPcm::NumChannels() const {
return num_channels_; return num_channels_;
} }
size_t AudioEncoderPcm::MaxEncodedBytes() const {
return full_frame_samples_;
}
int AudioEncoderPcm::Num10MsFramesInNextPacket() const { int AudioEncoderPcm::Num10MsFramesInNextPacket() const {
return num_10ms_frames_per_packet_; return num_10ms_frames_per_packet_;
} }
@ -72,11 +78,12 @@ void AudioEncoderPcm::EncodeInternal(uint32_t rtp_timestamp,
for (int i = 0; i < num_samples; ++i) { for (int i = 0; i < num_samples; ++i) {
speech_buffer_.push_back(audio[i]); speech_buffer_.push_back(audio[i]);
} }
if (speech_buffer_.size() < static_cast<size_t>(full_frame_samples_)) { if (speech_buffer_.size() < full_frame_samples_) {
info->encoded_bytes = 0; info->encoded_bytes = 0;
return; return;
} }
CHECK_EQ(speech_buffer_.size(), static_cast<size_t>(full_frame_samples_)); CHECK_EQ(speech_buffer_.size(), full_frame_samples_);
CHECK_GE(max_encoded_bytes, full_frame_samples_);
int16_t ret = EncodeCall(&speech_buffer_[0], full_frame_samples_, encoded); int16_t ret = EncodeCall(&speech_buffer_[0], full_frame_samples_, encoded);
CHECK_GE(ret, 0); CHECK_GE(ret, 0);
speech_buffer_.clear(); speech_buffer_.clear();

View File

@ -34,6 +34,7 @@ class AudioEncoderPcm : public AudioEncoder {
int SampleRateHz() const override; int SampleRateHz() const override;
int NumChannels() const override; int NumChannels() const override;
size_t MaxEncodedBytes() const override;
int Num10MsFramesInNextPacket() const override; int Num10MsFramesInNextPacket() const override;
int Max10MsFramesInAPacket() const override; int Max10MsFramesInAPacket() const override;
@ -55,7 +56,7 @@ class AudioEncoderPcm : public AudioEncoder {
const int num_channels_; const int num_channels_;
const int payload_type_; const int payload_type_;
const int num_10ms_frames_per_packet_; const int num_10ms_frames_per_packet_;
const int16_t full_frame_samples_; const size_t full_frame_samples_;
std::vector<int16_t> speech_buffer_; std::vector<int16_t> speech_buffer_;
uint32_t first_timestamp_in_buffer_; uint32_t first_timestamp_in_buffer_;
}; };

View File

@ -54,17 +54,25 @@ AudioEncoderG722::~AudioEncoderG722() {}
int AudioEncoderG722::SampleRateHz() const { int AudioEncoderG722::SampleRateHz() const {
return kSampleRateHz; return kSampleRateHz;
} }
int AudioEncoderG722::RtpTimestampRateHz() const { int AudioEncoderG722::RtpTimestampRateHz() const {
// The RTP timestamp rate for G.722 is 8000 Hz, even though it is a 16 kHz // The RTP timestamp rate for G.722 is 8000 Hz, even though it is a 16 kHz
// codec. // codec.
return kSampleRateHz / 2; return kSampleRateHz / 2;
} }
int AudioEncoderG722::NumChannels() const { int AudioEncoderG722::NumChannels() const {
return num_channels_; return num_channels_;
} }
size_t AudioEncoderG722::MaxEncodedBytes() const {
return static_cast<size_t>(SamplesPerChannel() / 2 * num_channels_);
}
int AudioEncoderG722::Num10MsFramesInNextPacket() const { int AudioEncoderG722::Num10MsFramesInNextPacket() const {
return num_10ms_frames_per_packet_; return num_10ms_frames_per_packet_;
} }
int AudioEncoderG722::Max10MsFramesInAPacket() const { int AudioEncoderG722::Max10MsFramesInAPacket() const {
return num_10ms_frames_per_packet_; return num_10ms_frames_per_packet_;
} }
@ -74,10 +82,7 @@ void AudioEncoderG722::EncodeInternal(uint32_t rtp_timestamp,
size_t max_encoded_bytes, size_t max_encoded_bytes,
uint8_t* encoded, uint8_t* encoded,
EncodedInfo* info) { EncodedInfo* info) {
const int samples_per_channel = CHECK_GE(max_encoded_bytes, MaxEncodedBytes());
kSampleRateHz / 100 * num_10ms_frames_per_packet_;
CHECK_GE(max_encoded_bytes,
static_cast<size_t>(samples_per_channel) / 2 * num_channels_);
if (num_10ms_frames_buffered_ == 0) if (num_10ms_frames_buffered_ == 0)
first_timestamp_in_buffer_ = rtp_timestamp; first_timestamp_in_buffer_ = rtp_timestamp;
@ -97,6 +102,7 @@ void AudioEncoderG722::EncodeInternal(uint32_t rtp_timestamp,
// Encode each channel separately. // Encode each channel separately.
CHECK_EQ(num_10ms_frames_buffered_, num_10ms_frames_per_packet_); CHECK_EQ(num_10ms_frames_buffered_, num_10ms_frames_per_packet_);
num_10ms_frames_buffered_ = 0; num_10ms_frames_buffered_ = 0;
const int samples_per_channel = SamplesPerChannel();
for (int i = 0; i < num_channels_; ++i) { for (int i = 0; i < num_channels_; ++i) {
const int encoded = WebRtcG722_Encode( const int encoded = WebRtcG722_Encode(
encoders_[i].encoder, encoders_[i].speech_buffer.get(), encoders_[i].encoder, encoders_[i].speech_buffer.get(),
@ -123,4 +129,8 @@ void AudioEncoderG722::EncodeInternal(uint32_t rtp_timestamp,
info->payload_type = payload_type_; info->payload_type = payload_type_;
} }
int AudioEncoderG722::SamplesPerChannel() const {
return kSampleRateHz / 100 * num_10ms_frames_per_packet_;
}
} // namespace webrtc } // namespace webrtc

View File

@ -31,8 +31,9 @@ class AudioEncoderG722 : public AudioEncoder {
~AudioEncoderG722() override; ~AudioEncoderG722() override;
int SampleRateHz() const override; int SampleRateHz() const override;
int RtpTimestampRateHz() const override;
int NumChannels() const override; int NumChannels() const override;
size_t MaxEncodedBytes() const override;
int RtpTimestampRateHz() const override;
int Num10MsFramesInNextPacket() const override; int Num10MsFramesInNextPacket() const override;
int Max10MsFramesInAPacket() const override; int Max10MsFramesInAPacket() const override;
@ -53,6 +54,8 @@ class AudioEncoderG722 : public AudioEncoder {
~EncoderState(); ~EncoderState();
}; };
int SamplesPerChannel() const;
const int num_channels_; const int num_channels_;
const int payload_type_; const int payload_type_;
const int num_10ms_frames_per_packet_; const int num_10ms_frames_per_packet_;

View File

@ -46,12 +46,19 @@ AudioEncoderIlbc::~AudioEncoderIlbc() {
int AudioEncoderIlbc::SampleRateHz() const { int AudioEncoderIlbc::SampleRateHz() const {
return kSampleRateHz; return kSampleRateHz;
} }
int AudioEncoderIlbc::NumChannels() const { int AudioEncoderIlbc::NumChannels() const {
return 1; return 1;
} }
size_t AudioEncoderIlbc::MaxEncodedBytes() const {
return RequiredOutputSizeBytes();
}
int AudioEncoderIlbc::Num10MsFramesInNextPacket() const { int AudioEncoderIlbc::Num10MsFramesInNextPacket() const {
return num_10ms_frames_per_packet_; return num_10ms_frames_per_packet_;
} }
int AudioEncoderIlbc::Max10MsFramesInAPacket() const { int AudioEncoderIlbc::Max10MsFramesInAPacket() const {
return num_10ms_frames_per_packet_; return num_10ms_frames_per_packet_;
} }
@ -61,24 +68,7 @@ void AudioEncoderIlbc::EncodeInternal(uint32_t rtp_timestamp,
size_t max_encoded_bytes, size_t max_encoded_bytes,
uint8_t* encoded, uint8_t* encoded,
EncodedInfo* info) { EncodedInfo* info) {
size_t expected_output_len; DCHECK_GE(max_encoded_bytes, RequiredOutputSizeBytes());
switch (num_10ms_frames_per_packet_) {
case 2:
expected_output_len = 38;
break;
case 3:
expected_output_len = 50;
break;
case 4:
expected_output_len = 2 * 38;
break;
case 6:
expected_output_len = 2 * 50;
break;
default:
FATAL();
}
DCHECK_GE(max_encoded_bytes, expected_output_len);
// Save timestamp if starting a new packet. // Save timestamp if starting a new packet.
if (num_10ms_frames_buffered_ == 0) if (num_10ms_frames_buffered_ == 0)
@ -105,10 +95,20 @@ void AudioEncoderIlbc::EncodeInternal(uint32_t rtp_timestamp,
kSampleRateHz / 100 * num_10ms_frames_per_packet_, kSampleRateHz / 100 * num_10ms_frames_per_packet_,
encoded); encoded);
CHECK_GE(output_len, 0); CHECK_GE(output_len, 0);
DCHECK_EQ(output_len, static_cast<int>(expected_output_len));
info->encoded_bytes = output_len; info->encoded_bytes = output_len;
DCHECK_EQ(info->encoded_bytes, RequiredOutputSizeBytes());
info->encoded_timestamp = first_timestamp_in_buffer_; info->encoded_timestamp = first_timestamp_in_buffer_;
info->payload_type = payload_type_; info->payload_type = payload_type_;
} }
size_t AudioEncoderIlbc::RequiredOutputSizeBytes() const {
switch (num_10ms_frames_per_packet_) {
case 2: return 38;
case 3: return 50;
case 4: return 2 * 38;
case 6: return 2 * 50;
default: FATAL();
}
}
} // namespace webrtc } // namespace webrtc

View File

@ -33,6 +33,7 @@ class AudioEncoderIlbc : public AudioEncoder {
int SampleRateHz() const override; int SampleRateHz() const override;
int NumChannels() const override; int NumChannels() const override;
size_t MaxEncodedBytes() const override;
int Num10MsFramesInNextPacket() const override; int Num10MsFramesInNextPacket() const override;
int Max10MsFramesInAPacket() const override; int Max10MsFramesInAPacket() const override;
@ -44,6 +45,8 @@ class AudioEncoderIlbc : public AudioEncoder {
EncodedInfo* info) override; EncodedInfo* info) override;
private: private:
size_t RequiredOutputSizeBytes() const;
static const int kMaxSamplesPerPacket = 480; static const int kMaxSamplesPerPacket = 480;
const int payload_type_; const int payload_type_;
const int num_10ms_frames_per_packet_; const int num_10ms_frames_per_packet_;

View File

@ -71,6 +71,7 @@ class AudioEncoderDecoderIsacT : public AudioEncoder, public AudioDecoder {
// AudioEncoder public methods. // AudioEncoder public methods.
int SampleRateHz() const override; int SampleRateHz() const override;
int NumChannels() const override; int NumChannels() const override;
size_t MaxEncodedBytes() const override;
int Num10MsFramesInNextPacket() const override; int Num10MsFramesInNextPacket() const override;
int Max10MsFramesInAPacket() const override; int Max10MsFramesInAPacket() const override;

View File

@ -184,6 +184,11 @@ int AudioEncoderDecoderIsacT<T>::NumChannels() const {
return 1; return 1;
} }
template <typename T>
size_t AudioEncoderDecoderIsacT<T>::MaxEncodedBytes() const {
return kSufficientEncodeBufferSizeBytes;
}
template <typename T> template <typename T>
int AudioEncoderDecoderIsacT<T>::Num10MsFramesInNextPacket() const { int AudioEncoderDecoderIsacT<T>::Num10MsFramesInNextPacket() const {
CriticalSectionScoped cs(state_lock_.get()); CriticalSectionScoped cs(state_lock_.get());

View File

@ -30,12 +30,11 @@ TEST(AudioEncoderIsacRedTest, CompareRedAndNoRed) {
double r = rand(); // NOLINT(runtime/threadsafe_fn) double r = rand(); // NOLINT(runtime/threadsafe_fn)
input[i] = (r / RAND_MAX) * 2000 - 1000; 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; AudioEncoderDecoderIsac::Config config;
config.sample_rate_hz = kSampleRateHz; config.sample_rate_hz = kSampleRateHz;
AudioEncoderDecoderIsac isac_encoder(config); 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; AudioEncoderDecoderIsac::Config red_config;
red_config.sample_rate_hz = kSampleRateHz; red_config.sample_rate_hz = kSampleRateHz;
red_config.red_payload_type = kRedPayloadType; red_config.red_payload_type = kRedPayloadType;
@ -43,6 +42,8 @@ TEST(AudioEncoderIsacRedTest, CompareRedAndNoRed) {
ASSERT_NE(red_config.red_payload_type, red_config.payload_type) ASSERT_NE(red_config.red_payload_type, red_config.payload_type)
<< "iSAC and RED payload types must be different."; << "iSAC and RED payload types must be different.";
AudioEncoderDecoderIsac isac_red_encoder(red_config); 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; AudioEncoder::EncodedInfo info, red_info;
// Note that we are not expecting any output from the redundant encoder until // Note that we are not expecting any output from the redundant encoder until
@ -52,10 +53,11 @@ TEST(AudioEncoderIsacRedTest, CompareRedAndNoRed) {
EXPECT_EQ(0u, red_info.encoded_bytes); EXPECT_EQ(0u, red_info.encoded_bytes);
EXPECT_EQ(0u, red_info.redundant.size()); EXPECT_EQ(0u, red_info.redundant.size());
const uint32_t timestamp = static_cast<uint32_t>(i); const uint32_t timestamp = static_cast<uint32_t>(i);
isac_encoder.Encode(timestamp, input, k10MsSamples, kMaxEncodedSizeBytes, isac_encoder.Encode(timestamp, input, k10MsSamples, max_encoded_bytes,
encoded, &info); encoded.get(), &info);
isac_red_encoder.Encode(timestamp, input, k10MsSamples, isac_red_encoder.Encode(timestamp, input, k10MsSamples,
kMaxEncodedSizeBytes, red_encoded, &red_info); max_red_encoded_bytes, red_encoded.get(),
&red_info);
} }
EXPECT_GT(info.encoded_bytes, 0u) EXPECT_GT(info.encoded_bytes, 0u)
<< "Regular codec did not produce any output"; << "Regular codec did not produce any output";
@ -65,7 +67,7 @@ TEST(AudioEncoderIsacRedTest, CompareRedAndNoRed) {
ASSERT_EQ(info.encoded_bytes, red_info.redundant[0].encoded_bytes) ASSERT_EQ(info.encoded_bytes, red_info.redundant[0].encoded_bytes)
<< "Primary payload should be same length as non-redundant payload"; << "Primary payload should be same length as non-redundant payload";
// Check that |encoded| and the primary part of |red_encoded| are identical. // Check that |encoded| and the primary part of |red_encoded| are identical.
EXPECT_EQ(0, memcmp(encoded, red_encoded, info.encoded_bytes)); EXPECT_EQ(0, memcmp(encoded.get(), red_encoded.get(), info.encoded_bytes));
EXPECT_GT(red_info.redundant[0].encoded_bytes, EXPECT_GT(red_info.redundant[0].encoded_bytes,
red_info.redundant[1].encoded_bytes) red_info.redundant[1].encoded_bytes)
<< "Redundant payload should be smaller than primary"; << "Redundant payload should be smaller than primary";

View File

@ -23,6 +23,7 @@ class MockAudioEncoder : public AudioEncoder {
MOCK_METHOD0(Die, void()); MOCK_METHOD0(Die, void());
MOCK_CONST_METHOD0(SampleRateHz, int()); MOCK_CONST_METHOD0(SampleRateHz, int());
MOCK_CONST_METHOD0(NumChannels, int()); MOCK_CONST_METHOD0(NumChannels, int());
MOCK_CONST_METHOD0(MaxEncodedBytes, size_t());
MOCK_CONST_METHOD0(Num10MsFramesInNextPacket, int()); MOCK_CONST_METHOD0(Num10MsFramesInNextPacket, int());
MOCK_CONST_METHOD0(Max10MsFramesInAPacket, int()); MOCK_CONST_METHOD0(Max10MsFramesInAPacket, int());
MOCK_METHOD1(SetTargetBitrate, void(int)); MOCK_METHOD1(SetTargetBitrate, void(int));

View File

@ -111,6 +111,16 @@ int AudioEncoderOpus::NumChannels() const {
return num_channels_; return num_channels_;
} }
size_t AudioEncoderOpus::MaxEncodedBytes() const {
// Calculate the number of bytes we expect the encoder to produce,
// then multiply by two to give a wide margin for error.
int frame_size_ms = num_10ms_frames_per_packet_ * 10;
int bytes_per_millisecond = bitrate_bps_ / (1000 * 8) + 1;
size_t approx_encoded_bytes =
static_cast<size_t>(frame_size_ms * bytes_per_millisecond);
return 2 * approx_encoded_bytes;
}
int AudioEncoderOpus::Num10MsFramesInNextPacket() const { int AudioEncoderOpus::Num10MsFramesInNextPacket() const {
return num_10ms_frames_per_packet_; return num_10ms_frames_per_packet_;
} }
@ -120,10 +130,9 @@ int AudioEncoderOpus::Max10MsFramesInAPacket() const {
} }
void AudioEncoderOpus::SetTargetBitrate(int bits_per_second) { void AudioEncoderOpus::SetTargetBitrate(int bits_per_second) {
CHECK_EQ(WebRtcOpus_SetBitRate( bitrate_bps_ = std::max(std::min(bits_per_second, kMaxBitrateBps),
inst_, std::max(std::min(bits_per_second, kMaxBitrateBps), kMinBitrateBps);
kMinBitrateBps)), CHECK_EQ(WebRtcOpus_SetBitRate(inst_, bitrate_bps_), 0);
0);
} }
void AudioEncoderOpus::SetProjectedPacketLossRate(double fraction) { void AudioEncoderOpus::SetProjectedPacketLossRate(double fraction) {

View File

@ -47,10 +47,12 @@ class AudioEncoderOpus final : public AudioEncoder {
int SampleRateHz() const override; int SampleRateHz() const override;
int NumChannels() const override; int NumChannels() const override;
size_t MaxEncodedBytes() const override;
int Num10MsFramesInNextPacket() const override; int Num10MsFramesInNextPacket() const override;
int Max10MsFramesInAPacket() const override; int Max10MsFramesInAPacket() const override;
void SetTargetBitrate(int bits_per_second) override; void SetTargetBitrate(int bits_per_second) override;
void SetProjectedPacketLossRate(double fraction) override; void SetProjectedPacketLossRate(double fraction) override;
double packet_loss_rate() const { return packet_loss_rate_; } double packet_loss_rate() const { return packet_loss_rate_; }
ApplicationMode application() const { return application_; } ApplicationMode application() const { return application_; }
@ -66,6 +68,7 @@ class AudioEncoderOpus final : public AudioEncoder {
const int num_channels_; const int num_channels_;
const int payload_type_; const int payload_type_;
const ApplicationMode application_; const ApplicationMode application_;
int bitrate_bps_;
const int samples_per_10ms_frame_; const int samples_per_10ms_frame_;
std::vector<int16_t> input_buffer_; std::vector<int16_t> input_buffer_;
OpusEncInst* inst_; OpusEncInst* inst_;

View File

@ -38,6 +38,10 @@ int AudioEncoderCopyRed::NumChannels() const {
return speech_encoder_->NumChannels(); return speech_encoder_->NumChannels();
} }
size_t AudioEncoderCopyRed::MaxEncodedBytes() const {
return 2 * speech_encoder_->MaxEncodedBytes();
}
int AudioEncoderCopyRed::Num10MsFramesInNextPacket() const { int AudioEncoderCopyRed::Num10MsFramesInNextPacket() const {
return speech_encoder_->Num10MsFramesInNextPacket(); return speech_encoder_->Num10MsFramesInNextPacket();
} }

View File

@ -36,8 +36,9 @@ class AudioEncoderCopyRed : public AudioEncoder {
~AudioEncoderCopyRed() override; ~AudioEncoderCopyRed() override;
int SampleRateHz() const override; int SampleRateHz() const override;
int RtpTimestampRateHz() const override;
int NumChannels() const override; int NumChannels() const override;
size_t MaxEncodedBytes() const override;
int RtpTimestampRateHz() const override;
int Num10MsFramesInNextPacket() const override; int Num10MsFramesInNextPacket() const override;
int Max10MsFramesInAPacket() const override; int Max10MsFramesInAPacket() const override;
void SetTargetBitrate(int bits_per_second) override; void SetTargetBitrate(int bits_per_second) override;

View File

@ -8,6 +8,8 @@
* be found in the AUTHORS file in the root of the source tree. * be found in the AUTHORS file in the root of the source tree.
*/ */
#include <vector>
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
#include "webrtc/base/checks.h" #include "webrtc/base/checks.h"
#include "webrtc/base/scoped_ptr.h" #include "webrtc/base/scoped_ptr.h"
@ -24,7 +26,7 @@ using ::testing::MockFunction;
namespace webrtc { namespace webrtc {
namespace { namespace {
static const size_t kMaxEncodedBytes = 1000; static const size_t kMockMaxEncodedBytes = 1000;
static const size_t kMaxNumSamples = 48 * 10 * 2; // 10 ms @ 48 kHz stereo. static const size_t kMaxNumSamples = 48 * 10 * 2; // 10 ms @ 48 kHz stereo.
} }
@ -39,11 +41,13 @@ class AudioEncoderCopyRedTest : public ::testing::Test {
config.payload_type = red_payload_type_; config.payload_type = red_payload_type_;
config.speech_encoder = &mock_encoder_; config.speech_encoder = &mock_encoder_;
red_.reset(new AudioEncoderCopyRed(config)); red_.reset(new AudioEncoderCopyRed(config));
memset(encoded_, 0, sizeof(encoded_));
memset(audio_, 0, sizeof(audio_)); memset(audio_, 0, sizeof(audio_));
EXPECT_CALL(mock_encoder_, NumChannels()).WillRepeatedly(Return(1)); EXPECT_CALL(mock_encoder_, NumChannels()).WillRepeatedly(Return(1));
EXPECT_CALL(mock_encoder_, SampleRateHz()) EXPECT_CALL(mock_encoder_, SampleRateHz())
.WillRepeatedly(Return(sample_rate_hz_)); .WillRepeatedly(Return(sample_rate_hz_));
EXPECT_CALL(mock_encoder_, MaxEncodedBytes())
.WillRepeatedly(Return(kMockMaxEncodedBytes));
encoded_.resize(red_->MaxEncodedBytes(), 0);
} }
void TearDown() override { void TearDown() override {
@ -58,7 +62,7 @@ class AudioEncoderCopyRedTest : public ::testing::Test {
ASSERT_TRUE(red_.get() != NULL); ASSERT_TRUE(red_.get() != NULL);
encoded_info_ = AudioEncoder::EncodedInfo(); encoded_info_ = AudioEncoder::EncodedInfo();
red_->Encode(timestamp_, audio_, num_audio_samples_10ms, red_->Encode(timestamp_, audio_, num_audio_samples_10ms,
kMaxEncodedBytes, encoded_, &encoded_info_); encoded_.size(), &encoded_[0], &encoded_info_);
timestamp_ += num_audio_samples_10ms; timestamp_ += num_audio_samples_10ms;
} }
@ -68,7 +72,7 @@ class AudioEncoderCopyRedTest : public ::testing::Test {
int16_t audio_[kMaxNumSamples]; int16_t audio_[kMaxNumSamples];
const int sample_rate_hz_; const int sample_rate_hz_;
size_t num_audio_samples_10ms; size_t num_audio_samples_10ms;
uint8_t encoded_[kMaxEncodedBytes]; std::vector<uint8_t> encoded_;
AudioEncoder::EncodedInfo encoded_info_; AudioEncoder::EncodedInfo encoded_info_;
const int red_payload_type_; const int red_payload_type_;
}; };