Added SetBitRate function to VoE API to allow changing the audio bitrate.

If the requested bitrate is not valid for the codec, the codec will decide on
an appropriate value.
Updated VoE command line tool to use new SetBitRate function.
Includes unittests for SetBitRate function.

BUG=
R=henrik.lundin@webrtc.org, henrika@webrtc.org, kwiberg@webrtc.org, pbos@webrtc.org

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

Cr-Commit-Position: refs/heads/master@{#9115}
This commit is contained in:
Ivo Creusen
2015-04-29 16:03:33 +02:00
parent 23fba1ffa0
commit adf89b7e33
14 changed files with 241 additions and 17 deletions

View File

@@ -540,6 +540,7 @@ class FakeWebRtcVoiceEngine
codec = channels_[channel]->send_codec;
return 0;
}
WEBRTC_STUB(SetBitRate, (int channel, int bitrate_bps));
WEBRTC_FUNC(GetRecCodec, (int channel, webrtc::CodecInst& codec)) {
WEBRTC_CHECK_CHANNEL(channel);
const Channel* c = channels_[channel];

View File

@@ -386,10 +386,9 @@ OpusApplicationMode ACMGenericCodec::GetOpusApplication(
return num_channels == 1 || enable_dtx ? kVoip : kAudio;
}
int16_t ACMGenericCodec::SetBitRate(const int32_t bitrate_bps) {
void ACMGenericCodec::SetBitRate(const int bitrate_bps) {
encoder_->SetTargetBitrate(bitrate_bps);
bitrate_bps_ = bitrate_bps;
return 0;
}
int16_t ACMGenericCodec::SetVAD(bool* enable_dtx,

View File

@@ -162,18 +162,14 @@ class ACMGenericCodec {
void ResetNoMissedSamples();
///////////////////////////////////////////////////////////////////////////
// int16_t SetBitRate()
// The function is called to set the encoding rate.
// void SetBitRate()
// The function is called to set the encoding rate. If the value is not
// supported by the codec, another appropriate value is used.
//
// Input:
// -bitrate_bps : encoding rate in bits per second
//
// Return value:
// -1 if failed to set the rate, due to invalid input or given
// codec is not rate-adjustable.
// 0 if the rate is adjusted successfully
//
int16_t SetBitRate(const int32_t bitrate_bps);
void SetBitRate(const int bitrate_bps);
///////////////////////////////////////////////////////////////////////////
// uint32_t EarliestTimestamp()

View File

@@ -287,6 +287,14 @@ int AudioCodingModuleImpl::SendBitrate() const {
return encoder_param.codec_inst.rate;
}
void AudioCodingModuleImpl::SetBitRate(int bitrate_bps) {
CriticalSectionScoped lock(acm_crit_sect_);
if (codec_manager_.current_encoder()) {
codec_manager_.current_encoder()->SetBitRate(bitrate_bps);
}
}
// Set available bandwidth, inform the encoder about the estimated bandwidth
// received from the remote party.
// TODO(henrik.lundin): Remove; not used.

View File

@@ -60,6 +60,11 @@ class AudioCodingModuleImpl : public AudioCodingModule {
// codecs return there long-term average or their fixed rate.
int SendBitrate() const override;
// Sets the bitrate to the specified value in bits/sec. In case the codec does
// not support the requested value it will choose an appropriate value
// instead.
void SetBitRate(int bitrate_bps) override;
// Set available bandwidth, inform the encoder about the
// estimated bandwidth received from the remote party.
int SetReceivedEstimatedBandwidth(int bw) override;

View File

@@ -1169,6 +1169,192 @@ TEST_F(AcmSenderBitExactnessOldApi, MAYBE_Opus_stereo_20ms_voip) {
test::AcmReceiveTestOldApi::kStereoOutput);
}
// This test is for verifying the SetBitRate function. The bitrate is changed at
// the beginning, and the number of generated bytes are checked.
class AcmSetBitRateOldApi : public ::testing::Test {
protected:
static const int kTestDurationMs = 1000;
// Sets up the test::AcmSendTest object. Returns true on success, otherwise
// false.
bool SetUpSender() {
const std::string input_file_name =
webrtc::test::ResourcePath("audio_coding/testfile32kHz", "pcm");
// Note that |audio_source_| will loop forever. The test duration is set
// explicitly by |kTestDurationMs|.
audio_source_.reset(new test::InputAudioFile(input_file_name));
static const int kSourceRateHz = 32000;
send_test_.reset(new test::AcmSendTestOldApi(
audio_source_.get(), kSourceRateHz, kTestDurationMs));
return send_test_.get();
}
// Registers a send codec in the test::AcmSendTest object. Returns true on
// success, false on failure.
virtual bool RegisterSendCodec(const char* payload_name,
int sampling_freq_hz,
int channels,
int payload_type,
int frame_size_samples,
int frame_size_rtp_timestamps) {
return send_test_->RegisterCodec(payload_name, sampling_freq_hz, channels,
payload_type, frame_size_samples);
}
// Runs the test. SetUpSender() and RegisterSendCodec() must have been called
// before calling this method.
void Run(int target_bitrate_bps, int expected_total_bits) {
ASSERT_TRUE(send_test_->acm());
send_test_->acm()->SetBitRate(target_bitrate_bps);
int nr_bytes = 0;
while (test::Packet* next_packet = send_test_->NextPacket()) {
nr_bytes += next_packet->payload_length_bytes();
delete next_packet;
}
EXPECT_EQ(expected_total_bits, nr_bytes * 8);
}
void SetUpTest(const char* codec_name,
int codec_sample_rate_hz,
int channels,
int payload_type,
int codec_frame_size_samples,
int codec_frame_size_rtp_timestamps) {
ASSERT_TRUE(SetUpSender());
ASSERT_TRUE(RegisterSendCodec(codec_name, codec_sample_rate_hz, channels,
payload_type, codec_frame_size_samples,
codec_frame_size_rtp_timestamps));
}
rtc::scoped_ptr<test::AcmSendTestOldApi> send_test_;
rtc::scoped_ptr<test::InputAudioFile> audio_source_;
};
TEST_F(AcmSetBitRateOldApi, Opus_48khz_20ms_10kbps) {
ASSERT_NO_FATAL_FAILURE(SetUpTest("opus", 48000, 1, 107, 960, 960));
#if defined(WEBRTC_ANDROID)
Run(10000, 9328);
#else
Run(10000, 9072);
#endif // WEBRTC_ANDROID
}
TEST_F(AcmSetBitRateOldApi, Opus_48khz_20ms_50kbps) {
ASSERT_NO_FATAL_FAILURE(SetUpTest("opus", 48000, 1, 107, 960, 960));
#if defined(WEBRTC_ANDROID)
Run(50000, 47952);
#else
Run(50000, 49600);
#endif // WEBRTC_ANDROID
}
// The result on the Android platforms is inconsistent for this test case.
// On android_rel the result is different from android and android arm64 rel.
TEST_F(AcmSetBitRateOldApi, DISABLED_ON_ANDROID(Opus_48khz_20ms_100kbps)) {
ASSERT_NO_FATAL_FAILURE(SetUpTest("opus", 48000, 1, 107, 960, 960));
Run(100000, 100888);
}
// These next 2 tests ensure that the SetBitRate function has no effect on PCM
TEST_F(AcmSetBitRateOldApi, Pcm16_8khz_10ms_8kbps) {
ASSERT_NO_FATAL_FAILURE(SetUpTest("L16", 8000, 1, 107, 80, 80));
Run(8000, 128000);
}
TEST_F(AcmSetBitRateOldApi, Pcm16_8khz_10ms_32kbps) {
ASSERT_NO_FATAL_FAILURE(SetUpTest("L16", 8000, 1, 107, 80, 80));
Run(32000, 128000);
}
// This test is for verifying the SetBitRate function. The bitrate is changed
// in the middle, and the number of generated bytes are before and after the
// change are checked.
class AcmChangeBitRateOldApi : public AcmSetBitRateOldApi {
protected:
AcmChangeBitRateOldApi() : sampling_freq_hz_(0), frame_size_samples_(0) {}
// Registers a send codec in the test::AcmSendTest object. Returns true on
// success, false on failure.
bool RegisterSendCodec(const char* payload_name,
int sampling_freq_hz,
int channels,
int payload_type,
int frame_size_samples,
int frame_size_rtp_timestamps) override {
frame_size_samples_ = frame_size_samples;
sampling_freq_hz_ = sampling_freq_hz;
return AcmSetBitRateOldApi::RegisterSendCodec(
payload_name, sampling_freq_hz, channels, payload_type,
frame_size_samples, frame_size_rtp_timestamps);
}
// Runs the test. SetUpSender() and RegisterSendCodec() must have been called
// before calling this method.
void Run(int target_bitrate_bps,
int expected_before_switch_bits,
int expected_after_switch_bits) {
ASSERT_TRUE(send_test_->acm());
int nr_packets =
sampling_freq_hz_ * kTestDurationMs / (frame_size_samples_ * 1000);
int nr_bytes_before = 0, nr_bytes_after = 0;
int packet_counter = 0;
while (test::Packet* next_packet = send_test_->NextPacket()) {
if (packet_counter == nr_packets / 2)
send_test_->acm()->SetBitRate(target_bitrate_bps);
if (packet_counter < nr_packets / 2)
nr_bytes_before += next_packet->payload_length_bytes();
else
nr_bytes_after += next_packet->payload_length_bytes();
packet_counter++;
delete next_packet;
}
EXPECT_EQ(expected_before_switch_bits, nr_bytes_before * 8);
EXPECT_EQ(expected_after_switch_bits, nr_bytes_after * 8);
}
uint32_t sampling_freq_hz_;
uint32_t frame_size_samples_;
};
TEST_F(AcmChangeBitRateOldApi, Opus_48khz_20ms_10kbps) {
ASSERT_NO_FATAL_FAILURE(SetUpTest("opus", 48000, 1, 107, 960, 960));
#if defined(WEBRTC_ANDROID)
Run(10000, 32200, 5496);
#else
Run(10000, 32200, 5432);
#endif // WEBRTC_ANDROID
}
TEST_F(AcmChangeBitRateOldApi, Opus_48khz_20ms_50kbps) {
ASSERT_NO_FATAL_FAILURE(SetUpTest("opus", 48000, 1, 107, 960, 960));
#if defined(WEBRTC_ANDROID)
Run(50000, 32200, 24912);
#else
Run(50000, 32200, 24792);
#endif // WEBRTC_ANDROID
}
TEST_F(AcmChangeBitRateOldApi, Opus_48khz_20ms_100kbps) {
ASSERT_NO_FATAL_FAILURE(SetUpTest("opus", 48000, 1, 107, 960, 960));
#if defined(WEBRTC_ANDROID)
Run(100000, 32200, 51480);
#else
Run(100000, 32200, 50584);
#endif // WEBRTC_ANDROID
}
// These next 2 tests ensure that the SetBitRate function has no effect on PCM
TEST_F(AcmChangeBitRateOldApi, Pcm16_8khz_10ms_8kbps) {
ASSERT_NO_FATAL_FAILURE(SetUpTest("L16", 8000, 1, 107, 80, 80));
Run(8000, 64000, 64000);
}
TEST_F(AcmChangeBitRateOldApi, Pcm16_8khz_10ms_32kbps) {
ASSERT_NO_FATAL_FAILURE(SetUpTest("L16", 8000, 1, 107, 80, 80));
Run(32000, 64000, 64000);
}
// This test fixture is implemented to run ACM and change the desired output
// frequency during the call. The input packets are simply PCM16b-wb encoded
// payloads with a constant value of |kSampleValue|. The test fixture itself

View File

@@ -365,11 +365,7 @@ int CodecManager::RegisterSendCodec(const CodecInst& send_codec) {
// Check if a change in Rate is required.
if (send_codec.rate != send_codec_inst_.rate) {
if (current_encoder_->SetBitRate(send_codec.rate) < 0) {
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, dummy_id,
"Could not change the codec rate.");
return -1;
}
current_encoder_->SetBitRate(send_codec.rate);
send_codec_inst_.rate = send_codec.rate;
}

View File

@@ -263,6 +263,11 @@ class AudioCodingModule {
//
virtual int32_t SendBitrate() const = 0;
///////////////////////////////////////////////////////////////////////////
// Sets the bitrate to the specified value in bits/sec. If the value is not
// supported by the codec, it will choose another appropriate value.
virtual void SetBitRate(int bitrate_bps) = 0;
///////////////////////////////////////////////////////////////////////////
// int32_t SetReceivedEstimatedBandwidth()
// Set available bandwidth [bits/sec] of the up-link channel.

View File

@@ -1342,6 +1342,12 @@ Channel::SetSendCodec(const CodecInst& codec)
return 0;
}
void Channel::SetBitRate(int bitrate_bps) {
WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, _channelId),
"Channel::SetBitRate(bitrate_bps=%d)", bitrate_bps);
audio_coding_->SetBitRate(bitrate_bps);
}
void Channel::OnIncomingFractionLoss(int fraction_lost) {
network_predictor_->UpdatePacketLossRate(fraction_lost);
uint8_t average_fraction_loss = network_predictor_->GetLossRate();

View File

@@ -202,6 +202,7 @@ public:
int32_t GetSendCodec(CodecInst& codec);
int32_t GetRecCodec(CodecInst& codec);
int32_t SetSendCodec(const CodecInst& codec);
void SetBitRate(int bitrate_bps);
int32_t SetVADStatus(bool enableVAD, ACMVADMode mode, bool disableDTX);
int32_t GetVADStatus(bool& enabledVAD, ACMVADMode& mode, bool& disabledDTX);
int32_t SetRecPayloadType(const CodecInst& codec);

View File

@@ -64,6 +64,12 @@ public:
// |channel|.
virtual int GetSendCodec(int channel, CodecInst& codec) = 0;
// Sets the bitrate on a specified |channel| to the specified value
// (in bits/sec). If the value is not supported by the codec, the codec will
// choose an appropriate value.
// Returns -1 on failure and 0 on success.
virtual int SetBitRate(int channel, int bitrate_bps) = 0;
// Gets the currently received |codec| for a specific |channel|.
virtual int GetRecCodec(int channel, CodecInst& codec) = 0;

View File

@@ -784,8 +784,9 @@ void RunTest(std::string out_path) {
res = codec->GetSendCodec(chan, cinst);
VALIDATE;
printf("Current bit rate is %i bps, set to: ", cinst.rate);
ASSERT_EQ(1, scanf("%i", &cinst.rate));
res = codec->SetSendCodec(chan, cinst);
int new_bitrate_bps;
ASSERT_EQ(1, scanf("%i", &new_bitrate_bps));
res = codec->SetBitRate(chan, new_bitrate_bps);
VALIDATE;
} else if (option_selection == option_index++) {
const char* kDebugFileName = "audio.aecdump";

View File

@@ -180,6 +180,18 @@ int VoECodecImpl::GetSendCodec(int channel, CodecInst& codec)
return 0;
}
int VoECodecImpl::SetBitRate(int channel, int bitrate_bps) {
WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
"SetBitRate(bitrate_bps=%d)", bitrate_bps);
if (!_shared->statistics().Initialized()) {
_shared->SetLastError(VE_NOT_INITED, kTraceError);
return -1;
}
_shared->channel_manager().GetChannel(channel).channel()->SetBitRate(
bitrate_bps);
return 0;
}
int VoECodecImpl::GetRecCodec(int channel, CodecInst& codec)
{
WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),

View File

@@ -29,6 +29,8 @@ public:
virtual int GetSendCodec(int channel, CodecInst& codec);
int SetBitRate(int channel, int bitrate_bps) override;
virtual int GetRecCodec(int channel, CodecInst& codec);
virtual int SetSendCNPayloadType(