Add AudioEncoder::GetTargetBitrate

The GetTargetBitrate implementation will return the
target bitrate of the codec. This may differ from the
desired target bitrate, as set by SetTargetBitrate, depending on implementation.

Tests are updated to exercise the new functionality.

R=kwiberg@webrtc.org

Review URL: https://codereview.webrtc.org/1184313002.

Cr-Commit-Position: refs/heads/master@{#9461}
This commit is contained in:
Henrik Lundin
2015-06-18 14:58:34 +02:00
parent e9bdfd859c
commit 3e89dbf458
21 changed files with 185 additions and 10 deletions

View File

@@ -96,6 +96,11 @@ class AudioEncoder {
// Num10MsFramesInNextPacket().
virtual int Max10MsFramesInAPacket() const = 0;
// Returns the current target bitrate in bits/s. The value -1 means that the
// codec adapts the target automatically, and a current target cannot be
// provided.
virtual int GetTargetBitrate() const = 0;
// Changes the target bitrate. The implementation is free to alter this value,
// e.g., if the desired value is outside the valid range.
virtual void SetTargetBitrate(int bits_per_second) {}

View File

@@ -82,6 +82,10 @@ class AudioEncoderMutableImpl : public P {
CriticalSectionScoped cs(encoder_lock_.get());
return encoder_->Max10MsFramesInAPacket();
}
int GetTargetBitrate() const override {
CriticalSectionScoped cs(encoder_lock_.get());
return encoder_->GetTargetBitrate();
}
void SetTargetBitrate(int bits_per_second) override {
CriticalSectionScoped cs(encoder_lock_.get());
encoder_->SetTargetBitrate(bits_per_second);

View File

@@ -97,6 +97,10 @@ int AudioEncoderCng::Max10MsFramesInAPacket() const {
return speech_encoder_->Max10MsFramesInAPacket();
}
int AudioEncoderCng::GetTargetBitrate() const {
return speech_encoder_->GetTargetBitrate();
}
void AudioEncoderCng::SetTargetBitrate(int bits_per_second) {
speech_encoder_->SetTargetBitrate(bits_per_second);
}

View File

@@ -52,6 +52,7 @@ class AudioEncoderCng final : public AudioEncoder {
int RtpTimestampRateHz() const override;
int Num10MsFramesInNextPacket() const override;
int Max10MsFramesInAPacket() const override;
int GetTargetBitrate() const override;
void SetTargetBitrate(int bits_per_second) override;
void SetProjectedPacketLossRate(double fraction) override;
EncodedInfo EncodeInternal(uint32_t rtp_timestamp,

View File

@@ -60,7 +60,7 @@ int AudioEncoderPcm::NumChannels() const {
}
size_t AudioEncoderPcm::MaxEncodedBytes() const {
return full_frame_samples_;
return full_frame_samples_ * BytesPerSample();
}
int AudioEncoderPcm::Num10MsFramesInNextPacket() const {
@@ -71,6 +71,10 @@ int AudioEncoderPcm::Max10MsFramesInAPacket() const {
return num_10ms_frames_per_packet_;
}
int AudioEncoderPcm::GetTargetBitrate() const {
return 8 * BytesPerSample() * SampleRateHz() * NumChannels();
}
AudioEncoder::EncodedInfo AudioEncoderPcm::EncodeInternal(
uint32_t rtp_timestamp,
const int16_t* audio,
@@ -104,12 +108,20 @@ int16_t AudioEncoderPcmA::EncodeCall(const int16_t* audio,
return WebRtcG711_EncodeA(audio, static_cast<int16_t>(input_len), encoded);
}
int AudioEncoderPcmA::BytesPerSample() const {
return 1;
}
int16_t AudioEncoderPcmU::EncodeCall(const int16_t* audio,
size_t input_len,
uint8_t* encoded) {
return WebRtcG711_EncodeU(audio, static_cast<int16_t>(input_len), encoded);
}
int AudioEncoderPcmU::BytesPerSample() const {
return 1;
}
namespace {
template <typename T>
typename T::Config CreateConfig(const CodecInst& codec_inst) {

View File

@@ -41,6 +41,7 @@ class AudioEncoderPcm : public AudioEncoder {
size_t MaxEncodedBytes() const override;
int Num10MsFramesInNextPacket() const override;
int Max10MsFramesInAPacket() const override;
int GetTargetBitrate() const override;
EncodedInfo EncodeInternal(uint32_t rtp_timestamp,
const int16_t* audio,
size_t max_encoded_bytes,
@@ -53,6 +54,8 @@ class AudioEncoderPcm : public AudioEncoder {
size_t input_len,
uint8_t* encoded) = 0;
virtual int BytesPerSample() const = 0;
private:
const int sample_rate_hz_;
const int num_channels_;
@@ -63,7 +66,7 @@ class AudioEncoderPcm : public AudioEncoder {
uint32_t first_timestamp_in_buffer_;
};
class AudioEncoderPcmA : public AudioEncoderPcm {
class AudioEncoderPcmA final : public AudioEncoderPcm {
public:
struct Config : public AudioEncoderPcm::Config {
Config() : AudioEncoderPcm::Config(8) {}
@@ -77,11 +80,13 @@ class AudioEncoderPcmA : public AudioEncoderPcm {
size_t input_len,
uint8_t* encoded) override;
int BytesPerSample() const override;
private:
static const int kSampleRateHz = 8000;
};
class AudioEncoderPcmU : public AudioEncoderPcm {
class AudioEncoderPcmU final : public AudioEncoderPcm {
public:
struct Config : public AudioEncoderPcm::Config {
Config() : AudioEncoderPcm::Config(0) {}
@@ -95,6 +100,8 @@ class AudioEncoderPcmU : public AudioEncoderPcm {
size_t input_len,
uint8_t* encoded) override;
int BytesPerSample() const override;
private:
static const int kSampleRateHz = 8000;
};

View File

@@ -82,6 +82,11 @@ int AudioEncoderG722::Max10MsFramesInAPacket() const {
return num_10ms_frames_per_packet_;
}
int AudioEncoderG722::GetTargetBitrate() const {
// 4 bits/sample, 16000 samples/s/channel.
return 64000 * NumChannels();
}
AudioEncoder::EncodedInfo AudioEncoderG722::EncodeInternal(
uint32_t rtp_timestamp,
const int16_t* audio,

View File

@@ -19,7 +19,7 @@
namespace webrtc {
class AudioEncoderG722 : public AudioEncoder {
class AudioEncoderG722 final : public AudioEncoder {
public:
struct Config {
Config() : payload_type(9), frame_size_ms(20), num_channels(1) {}
@@ -39,6 +39,7 @@ class AudioEncoderG722 : public AudioEncoder {
int RtpTimestampRateHz() const override;
int Num10MsFramesInNextPacket() const override;
int Max10MsFramesInAPacket() const override;
int GetTargetBitrate() const override;
EncodedInfo EncodeInternal(uint32_t rtp_timestamp,
const int16_t* audio,
size_t max_encoded_bytes,

View File

@@ -66,6 +66,19 @@ int AudioEncoderIlbc::Max10MsFramesInAPacket() const {
return num_10ms_frames_per_packet_;
}
int AudioEncoderIlbc::GetTargetBitrate() const {
switch (num_10ms_frames_per_packet_) {
case 2: case 4:
// 38 bytes per frame of 20 ms => 15200 bits/s.
return 15200;
case 3: case 6:
// 50 bytes per frame of 30 ms => (approx) 13333 bits/s.
return 13333;
default:
FATAL();
}
}
AudioEncoder::EncodedInfo AudioEncoderIlbc::EncodeInternal(
uint32_t rtp_timestamp,
const int16_t* audio,

View File

@@ -18,7 +18,7 @@
namespace webrtc {
class AudioEncoderIlbc : public AudioEncoder {
class AudioEncoderIlbc final : public AudioEncoder {
public:
struct Config {
Config() : payload_type(102), frame_size_ms(30) {}
@@ -38,6 +38,7 @@ class AudioEncoderIlbc : public AudioEncoder {
size_t MaxEncodedBytes() const override;
int Num10MsFramesInNextPacket() const override;
int Max10MsFramesInAPacket() const override;
int GetTargetBitrate() const override;
EncodedInfo EncodeInternal(uint32_t rtp_timestamp,
const int16_t* audio,
size_t max_encoded_bytes,

View File

@@ -59,6 +59,7 @@ class AudioEncoderDecoderIsacT : public AudioEncoder, public AudioDecoder {
size_t MaxEncodedBytes() const override;
int Num10MsFramesInNextPacket() const override;
int Max10MsFramesInAPacket() const override;
int GetTargetBitrate() const override;
// AudioDecoder methods.
bool HasDecodePlc() const override;
@@ -113,6 +114,8 @@ class AudioEncoderDecoderIsacT : public AudioEncoder, public AudioDecoder {
// Timestamp of the previously encoded packet.
uint32_t last_encoded_timestamp_ GUARDED_BY(lock_);
const int target_bitrate_bps_;
DISALLOW_COPY_AND_ASSIGN(AudioEncoderDecoderIsacT);
};

View File

@@ -70,16 +70,18 @@ AudioEncoderDecoderIsacT<T>::AudioEncoderDecoderIsacT(const Config& config)
state_lock_(CriticalSectionWrapper::CreateCriticalSection()),
decoder_sample_rate_hz_(0),
lock_(CriticalSectionWrapper::CreateCriticalSection()),
packet_in_progress_(false) {
packet_in_progress_(false),
target_bitrate_bps_(config.adaptive_mode ? -1 : (config.bit_rate == 0
? kDefaultBitRate
: config.bit_rate)) {
CHECK(config.IsOk());
CHECK_EQ(0, T::Create(&isac_state_));
CHECK_EQ(0, T::EncoderInit(isac_state_, config.adaptive_mode ? 0 : 1));
CHECK_EQ(0, T::SetEncSampRate(isac_state_, config.sample_rate_hz));
const int bit_rate = config.bit_rate == 0 ? kDefaultBitRate : config.bit_rate;
if (config.adaptive_mode) {
CHECK_EQ(0, T::ControlBwe(isac_state_, bit_rate,
config.frame_size_ms, config.enforce_frame_size));
CHECK_EQ(0, T::ControlBwe(isac_state_, bit_rate, config.frame_size_ms,
config.enforce_frame_size));
} else {
CHECK_EQ(0, T::Control(isac_state_, bit_rate, config.frame_size_ms));
}
@@ -129,6 +131,11 @@ int AudioEncoderDecoderIsacT<T>::Max10MsFramesInAPacket() const {
return 6; // iSAC puts at most 60 ms in a packet.
}
template <typename T>
int AudioEncoderDecoderIsacT<T>::GetTargetBitrate() const {
return target_bitrate_bps_;
}
template <typename T>
AudioEncoder::EncodedInfo AudioEncoderDecoderIsacT<T>::EncodeInternal(
uint32_t rtp_timestamp,

View File

@@ -26,6 +26,7 @@ class MockAudioEncoder : public AudioEncoder {
MOCK_CONST_METHOD0(MaxEncodedBytes, size_t());
MOCK_CONST_METHOD0(Num10MsFramesInNextPacket, int());
MOCK_CONST_METHOD0(Max10MsFramesInAPacket, int());
MOCK_CONST_METHOD0(GetTargetBitrate, int());
MOCK_METHOD1(SetTargetBitrate, void(int));
MOCK_METHOD1(SetProjectedPacketLossRate, void(double));
// Note, we explicitly chose not to create a mock for the Encode method.
@@ -43,6 +44,7 @@ class MockAudioEncoderMutable : public AudioEncoderMutable {
MOCK_CONST_METHOD0(MaxEncodedBytes, size_t());
MOCK_CONST_METHOD0(Num10MsFramesInNextPacket, int());
MOCK_CONST_METHOD0(Max10MsFramesInAPacket, int());
MOCK_CONST_METHOD0(GetTargetBitrate, int());
MOCK_METHOD1(SetTargetBitrate, void(int));
MOCK_METHOD1(SetProjectedPacketLossRate, void(double));
// Note, we explicitly chose not to create a mock for the Encode method.

View File

@@ -79,6 +79,30 @@ TEST_F(AudioEncoderMutableOpusTest, ToggleDtx) {
// Turn off DTX.
EXPECT_TRUE(encoder_->SetDtx(false));
}
TEST_F(AudioEncoderMutableOpusTest, SetBitrate) {
CreateCodec(1);
// Constants are replicated from audio_encoder_opus.cc.
const int kMinBitrateBps = 500;
const int kMaxBitrateBps = 512000;
// Set a too low bitrate.
encoder_->SetTargetBitrate(kMinBitrateBps - 1);
EXPECT_EQ(kMinBitrateBps, encoder_->GetTargetBitrate());
// Set a too high bitrate.
encoder_->SetTargetBitrate(kMaxBitrateBps + 1);
EXPECT_EQ(kMaxBitrateBps, encoder_->GetTargetBitrate());
// Set the minimum rate.
encoder_->SetTargetBitrate(kMinBitrateBps);
EXPECT_EQ(kMinBitrateBps, encoder_->GetTargetBitrate());
// Set the maximum rate.
encoder_->SetTargetBitrate(kMaxBitrateBps);
EXPECT_EQ(kMaxBitrateBps, encoder_->GetTargetBitrate());
// Set rates from 1000 up to 32000 bps.
for (int rate = 1000; rate <= 32000; rate += 1000) {
encoder_->SetTargetBitrate(rate);
EXPECT_EQ(rate, encoder_->GetTargetBitrate());
}
}
#endif // WEBRTC_CODEC_OPUS
} // namespace acm2

View File

@@ -129,6 +129,10 @@ int AudioEncoderOpus::Max10MsFramesInAPacket() const {
return num_10ms_frames_per_packet_;
}
int AudioEncoderOpus::GetTargetBitrate() const {
return bitrate_bps_;
}
void AudioEncoderOpus::SetTargetBitrate(int bits_per_second) {
bitrate_bps_ = std::max(std::min(bits_per_second, kMaxBitrateBps),
kMinBitrateBps);

View File

@@ -52,12 +52,14 @@ class AudioEncoderOpus final : public AudioEncoder {
size_t MaxEncodedBytes() const override;
int Num10MsFramesInNextPacket() const override;
int Max10MsFramesInAPacket() const override;
int GetTargetBitrate() const override;
void SetTargetBitrate(int bits_per_second) override;
void SetProjectedPacketLossRate(double fraction) override;
double packet_loss_rate() const { return packet_loss_rate_; }
ApplicationMode application() const { return application_; }
bool dtx_enabled() const { return dtx_enabled_; }
EncodedInfo EncodeInternal(uint32_t rtp_timestamp,
const int16_t* audio,
size_t max_encoded_bytes,

View File

@@ -29,6 +29,10 @@ int16_t AudioEncoderPcm16B::EncodeCall(const int16_t* audio,
return WebRtcPcm16b_Encode(audio, static_cast<int16_t>(input_len), encoded);
}
int AudioEncoderPcm16B::BytesPerSample() const {
return 2;
}
namespace {
AudioEncoderPcm16B::Config CreateConfig(const CodecInst& codec_inst) {
AudioEncoderPcm16B::Config config;

View File

@@ -17,7 +17,7 @@
namespace webrtc {
class AudioEncoderPcm16B : public AudioEncoderPcm {
class AudioEncoderPcm16B final : public AudioEncoderPcm {
public:
struct Config : public AudioEncoderPcm::Config {
public:
@@ -34,6 +34,8 @@ class AudioEncoderPcm16B : public AudioEncoderPcm {
int16_t EncodeCall(const int16_t* audio,
size_t input_len,
uint8_t* encoded) override;
int BytesPerSample() const override;
};
struct CodecInst;

View File

@@ -49,6 +49,10 @@ int AudioEncoderCopyRed::Max10MsFramesInAPacket() const {
return speech_encoder_->Max10MsFramesInAPacket();
}
int AudioEncoderCopyRed::GetTargetBitrate() const {
return speech_encoder_->GetTargetBitrate();
}
void AudioEncoderCopyRed::SetTargetBitrate(int bits_per_second) {
speech_encoder_->SetTargetBitrate(bits_per_second);
}

View File

@@ -42,6 +42,7 @@ class AudioEncoderCopyRed : public AudioEncoder {
int RtpTimestampRateHz() const override;
int Num10MsFramesInNextPacket() const override;
int Max10MsFramesInAPacket() const override;
int GetTargetBitrate() const override;
void SetTargetBitrate(int bits_per_second) override;
void SetProjectedPacketLossRate(double fraction) override;
EncodedInfo EncodeInternal(uint32_t rtp_timestamp,

View File

@@ -485,6 +485,24 @@ TEST_F(AudioDecoderPcmUTest, EncodeDecode) {
EXPECT_FALSE(decoder_->HasDecodePlc());
}
namespace {
int SetAndGetTargetBitrate(AudioEncoder* audio_encoder, int rate) {
audio_encoder->SetTargetBitrate(rate);
return audio_encoder->GetTargetBitrate();
}
void TestSetAndGetTargetBitratesWithFixedCodec(AudioEncoder* audio_encoder,
int fixed_rate) {
EXPECT_EQ(fixed_rate, SetAndGetTargetBitrate(audio_encoder, 32000));
EXPECT_EQ(fixed_rate, SetAndGetTargetBitrate(audio_encoder, fixed_rate - 1));
EXPECT_EQ(fixed_rate, SetAndGetTargetBitrate(audio_encoder, fixed_rate));
EXPECT_EQ(fixed_rate, SetAndGetTargetBitrate(audio_encoder, fixed_rate + 1));
}
} // namespace
TEST_F(AudioDecoderPcmUTest, SetTargetBitrate) {
TestSetAndGetTargetBitratesWithFixedCodec(audio_encoder_.get(), 64000);
}
TEST_F(AudioDecoderPcmATest, EncodeDecode) {
int tolerance = 308;
double mse = 1931.0;
@@ -494,6 +512,10 @@ TEST_F(AudioDecoderPcmATest, EncodeDecode) {
EXPECT_FALSE(decoder_->HasDecodePlc());
}
TEST_F(AudioDecoderPcmATest, SetTargetBitrate) {
TestSetAndGetTargetBitratesWithFixedCodec(audio_encoder_.get(), 64000);
}
TEST_F(AudioDecoderPcm16BTest, EncodeDecode) {
int tolerance = 0;
double mse = 0.0;
@@ -506,6 +528,11 @@ TEST_F(AudioDecoderPcm16BTest, EncodeDecode) {
EXPECT_FALSE(decoder_->HasDecodePlc());
}
TEST_F(AudioDecoderPcm16BTest, SetTargetBitrate) {
TestSetAndGetTargetBitratesWithFixedCodec(audio_encoder_.get(),
codec_input_rate_hz_ * 16);
}
TEST_F(AudioDecoderIlbcTest, EncodeDecode) {
int tolerance = 6808;
double mse = 2.13e6;
@@ -517,6 +544,10 @@ TEST_F(AudioDecoderIlbcTest, EncodeDecode) {
DecodePlcTest();
}
TEST_F(AudioDecoderIlbcTest, SetTargetBitrate) {
TestSetAndGetTargetBitratesWithFixedCodec(audio_encoder_.get(), 13333);
}
TEST_F(AudioDecoderIsacFloatTest, EncodeDecode) {
int tolerance = 3399;
double mse = 434951.0;
@@ -527,6 +558,10 @@ TEST_F(AudioDecoderIsacFloatTest, EncodeDecode) {
EXPECT_FALSE(decoder_->HasDecodePlc());
}
TEST_F(AudioDecoderIsacFloatTest, SetTargetBitrate) {
TestSetAndGetTargetBitratesWithFixedCodec(audio_encoder_.get(), 32000);
}
TEST_F(AudioDecoderIsacSwbTest, EncodeDecode) {
int tolerance = 19757;
double mse = 8.18e6;
@@ -537,6 +572,10 @@ TEST_F(AudioDecoderIsacSwbTest, EncodeDecode) {
EXPECT_FALSE(decoder_->HasDecodePlc());
}
TEST_F(AudioDecoderIsacSwbTest, SetTargetBitrate) {
TestSetAndGetTargetBitratesWithFixedCodec(audio_encoder_.get(), 32000);
}
// Fails Android ARM64. https://code.google.com/p/webrtc/issues/detail?id=4198
#if defined(WEBRTC_ANDROID) && defined(WEBRTC_ARCH_ARM64)
#define MAYBE_EncodeDecode DISABLED_EncodeDecode
@@ -558,6 +597,10 @@ TEST_F(AudioDecoderIsacFixTest, MAYBE_EncodeDecode) {
EXPECT_FALSE(decoder_->HasDecodePlc());
}
TEST_F(AudioDecoderIsacFixTest, SetTargetBitrate) {
TestSetAndGetTargetBitratesWithFixedCodec(audio_encoder_.get(), 32000);
}
TEST_F(AudioDecoderG722Test, EncodeDecode) {
int tolerance = 6176;
double mse = 238630.0;
@@ -568,6 +611,10 @@ TEST_F(AudioDecoderG722Test, EncodeDecode) {
EXPECT_FALSE(decoder_->HasDecodePlc());
}
TEST_F(AudioDecoderG722Test, SetTargetBitrate) {
TestSetAndGetTargetBitratesWithFixedCodec(audio_encoder_.get(), 64000);
}
TEST_F(AudioDecoderG722StereoTest, CreateAndDestroy) {
EXPECT_TRUE(CodecSupported(kDecoderG722_2ch));
}
@@ -583,6 +630,10 @@ TEST_F(AudioDecoderG722StereoTest, EncodeDecode) {
EXPECT_FALSE(decoder_->HasDecodePlc());
}
TEST_F(AudioDecoderG722StereoTest, SetTargetBitrate) {
TestSetAndGetTargetBitratesWithFixedCodec(audio_encoder_.get(), 128000);
}
TEST_F(AudioDecoderOpusTest, EncodeDecode) {
int tolerance = 6176;
double mse = 238630.0;
@@ -593,6 +644,20 @@ TEST_F(AudioDecoderOpusTest, EncodeDecode) {
EXPECT_FALSE(decoder_->HasDecodePlc());
}
namespace {
void TestOpusSetTargetBitrates(AudioEncoder* audio_encoder) {
EXPECT_EQ(500, SetAndGetTargetBitrate(audio_encoder, 499));
EXPECT_EQ(500, SetAndGetTargetBitrate(audio_encoder, 500));
EXPECT_EQ(32000, SetAndGetTargetBitrate(audio_encoder, 32000));
EXPECT_EQ(512000, SetAndGetTargetBitrate(audio_encoder, 512000));
EXPECT_EQ(512000, SetAndGetTargetBitrate(audio_encoder, 513000));
}
} // namespace
TEST_F(AudioDecoderOpusTest, SetTargetBitrate) {
TestOpusSetTargetBitrates(audio_encoder_.get());
}
TEST_F(AudioDecoderOpusStereoTest, EncodeDecode) {
int tolerance = 6176;
int channel_diff_tolerance = 0;
@@ -604,6 +669,10 @@ TEST_F(AudioDecoderOpusStereoTest, EncodeDecode) {
EXPECT_FALSE(decoder_->HasDecodePlc());
}
TEST_F(AudioDecoderOpusStereoTest, SetTargetBitrate) {
TestOpusSetTargetBitrates(audio_encoder_.get());
}
TEST(AudioDecoder, CodecSampleRateHz) {
EXPECT_EQ(8000, CodecSampleRateHz(kDecoderPCMu));
EXPECT_EQ(8000, CodecSampleRateHz(kDecoderPCMa));