NetEq: Add new method last_output_sample_rate_hz

This change moves the logics for keeping track of the last ouput
sample rate from AcmReceiver to NetEq, where it fits better. The
getter function AcmReceiver::current_sample_rate_hz() is renamed to
last_output_sample_rate_hz().

BUG=webrtc:3520

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

Cr-Commit-Position: refs/heads/master@{#10754}
This commit is contained in:
henrik.lundin
2015-11-23 06:49:25 -08:00
committed by Commit bot
parent dfafd12418
commit d89814bfd7
10 changed files with 50 additions and 36 deletions

View File

@ -123,7 +123,6 @@ AcmReceiver::AcmReceiver(const AudioCodingModule::Config& config)
id_(config.id), id_(config.id),
last_audio_decoder_(nullptr), last_audio_decoder_(nullptr),
previous_audio_activity_(AudioFrame::kVadPassive), previous_audio_activity_(AudioFrame::kVadPassive),
current_sample_rate_hz_(config.neteq_config.sample_rate_hz),
audio_buffer_(new int16_t[AudioFrame::kMaxDataSizeSamples]), audio_buffer_(new int16_t[AudioFrame::kMaxDataSizeSamples]),
last_audio_buffer_(new int16_t[AudioFrame::kMaxDataSizeSamples]), last_audio_buffer_(new int16_t[AudioFrame::kMaxDataSizeSamples]),
neteq_(NetEq::Create(config.neteq_config)), neteq_(NetEq::Create(config.neteq_config)),
@ -157,9 +156,8 @@ int AcmReceiver::LeastRequiredDelayMs() const {
return neteq_->LeastRequiredDelayMs(); return neteq_->LeastRequiredDelayMs();
} }
int AcmReceiver::current_sample_rate_hz() const { int AcmReceiver::last_output_sample_rate_hz() const {
CriticalSectionScoped lock(crit_sect_.get()); return neteq_->last_output_sample_rate_hz();
return current_sample_rate_hz_;
} }
int AcmReceiver::InsertPacket(const WebRtcRTPHeader& rtp_header, int AcmReceiver::InsertPacket(const WebRtcRTPHeader& rtp_header,
@ -224,23 +222,18 @@ int AcmReceiver::GetAudio(int desired_freq_hz, AudioFrame* audio_frame) {
return -1; return -1;
} }
// NetEq always returns 10 ms of audio. const int current_sample_rate_hz = neteq_->last_output_sample_rate_hz();
current_sample_rate_hz_ = static_cast<int>(samples_per_channel * 100);
// Update if resampling is required. // Update if resampling is required.
bool need_resampling = (desired_freq_hz != -1) && const bool need_resampling =
(current_sample_rate_hz_ != desired_freq_hz); (desired_freq_hz != -1) && (current_sample_rate_hz != desired_freq_hz);
if (need_resampling && !resampled_last_output_frame_) { if (need_resampling && !resampled_last_output_frame_) {
// Prime the resampler with the last frame. // Prime the resampler with the last frame.
int16_t temp_output[AudioFrame::kMaxDataSizeSamples]; int16_t temp_output[AudioFrame::kMaxDataSizeSamples];
int samples_per_channel_int = int samples_per_channel_int = resampler_.Resample10Msec(
resampler_.Resample10Msec(last_audio_buffer_.get(), last_audio_buffer_.get(), current_sample_rate_hz, desired_freq_hz,
current_sample_rate_hz_, num_channels, AudioFrame::kMaxDataSizeSamples, temp_output);
desired_freq_hz,
num_channels,
AudioFrame::kMaxDataSizeSamples,
temp_output);
if (samples_per_channel_int < 0) { if (samples_per_channel_int < 0) {
LOG(LERROR) << "AcmReceiver::GetAudio - " LOG(LERROR) << "AcmReceiver::GetAudio - "
"Resampling last_audio_buffer_ failed."; "Resampling last_audio_buffer_ failed.";
@ -254,13 +247,9 @@ int AcmReceiver::GetAudio(int desired_freq_hz, AudioFrame* audio_frame) {
// TODO(henrik.lundin) Glitches in the output may appear if the output rate // TODO(henrik.lundin) Glitches in the output may appear if the output rate
// from NetEq changes. See WebRTC issue 3923. // from NetEq changes. See WebRTC issue 3923.
if (need_resampling) { if (need_resampling) {
int samples_per_channel_int = int samples_per_channel_int = resampler_.Resample10Msec(
resampler_.Resample10Msec(audio_buffer_.get(), audio_buffer_.get(), current_sample_rate_hz, desired_freq_hz,
current_sample_rate_hz_, num_channels, AudioFrame::kMaxDataSizeSamples, audio_frame->data_);
desired_freq_hz,
num_channels,
AudioFrame::kMaxDataSizeSamples,
audio_frame->data_);
if (samples_per_channel_int < 0) { if (samples_per_channel_int < 0) {
LOG(LERROR) << "AcmReceiver::GetAudio - Resampling audio_buffer_ failed."; LOG(LERROR) << "AcmReceiver::GetAudio - Resampling audio_buffer_ failed.";
return -1; return -1;

View File

@ -154,12 +154,8 @@ class AcmReceiver {
// //
void ResetInitialDelay(); void ResetInitialDelay();
// // Returns last_output_sample_rate_hz from the NetEq instance.
// Get the current sampling frequency in Hz. int last_output_sample_rate_hz() const;
//
// Return value : Sampling frequency in Hz.
//
int current_sample_rate_hz() const;
// //
// Get the current network statistics from NetEq. // Get the current network statistics from NetEq.
@ -287,7 +283,6 @@ class AcmReceiver {
int id_; // TODO(henrik.lundin) Make const. int id_; // TODO(henrik.lundin) Make const.
const Decoder* last_audio_decoder_ GUARDED_BY(crit_sect_); const Decoder* last_audio_decoder_ GUARDED_BY(crit_sect_);
AudioFrame::VADActivity previous_audio_activity_ GUARDED_BY(crit_sect_); AudioFrame::VADActivity previous_audio_activity_ GUARDED_BY(crit_sect_);
int current_sample_rate_hz_ GUARDED_BY(crit_sect_);
ACMResampler resampler_ GUARDED_BY(crit_sect_); ACMResampler resampler_ GUARDED_BY(crit_sect_);
// Used in GetAudio, declared as member to avoid allocating every 10ms. // Used in GetAudio, declared as member to avoid allocating every 10ms.
// TODO(henrik.lundin) Stack-allocate in GetAudio instead? // TODO(henrik.lundin) Stack-allocate in GetAudio instead?

View File

@ -261,8 +261,7 @@ TEST_F(AcmReceiverTestOldApi, DISABLED_ON_ANDROID(SampleRate)) {
for (int k = 0; k < num_10ms_frames; ++k) { for (int k = 0; k < num_10ms_frames; ++k) {
EXPECT_EQ(0, receiver_->GetAudio(kOutSampleRateHz, &frame)); EXPECT_EQ(0, receiver_->GetAudio(kOutSampleRateHz, &frame));
} }
EXPECT_EQ(std::min(32000, codec.inst.plfreq), EXPECT_EQ(codec.inst.plfreq, receiver_->last_output_sample_rate_hz());
receiver_->current_sample_rate_hz());
} }
} }

View File

@ -532,17 +532,14 @@ int AudioCodingModuleImpl::ReceiveFrequency() const {
auto codec_id = RentACodec::CodecIdFromIndex(receiver_.last_audio_codec_id()); auto codec_id = RentACodec::CodecIdFromIndex(receiver_.last_audio_codec_id());
return codec_id ? RentACodec::CodecInstById(*codec_id)->plfreq return codec_id ? RentACodec::CodecInstById(*codec_id)->plfreq
: receiver_.current_sample_rate_hz(); : receiver_.last_output_sample_rate_hz();
} }
// Get current playout frequency. // Get current playout frequency.
int AudioCodingModuleImpl::PlayoutFrequency() const { int AudioCodingModuleImpl::PlayoutFrequency() const {
WEBRTC_TRACE(webrtc::kTraceStream, webrtc::kTraceAudioCoding, id_, WEBRTC_TRACE(webrtc::kTraceStream, webrtc::kTraceAudioCoding, id_,
"PlayoutFrequency()"); "PlayoutFrequency()");
return receiver_.last_output_sample_rate_hz();
CriticalSectionScoped lock(acm_crit_sect_.get());
return receiver_.current_sample_rate_hz();
} }
// Register possible receive codecs, can be called multiple times, // Register possible receive codecs, can be called multiple times,

View File

@ -251,6 +251,11 @@ class NetEq {
// Returns true if the RTP timestamp is valid, otherwise false. // Returns true if the RTP timestamp is valid, otherwise false.
virtual bool GetPlayoutTimestamp(uint32_t* timestamp) = 0; virtual bool GetPlayoutTimestamp(uint32_t* timestamp) = 0;
// Returns the sample rate in Hz of the audio produced in the last GetAudio
// call. If GetAudio has not been called yet, the configured sample rate
// (Config::sample_rate_hz) is returned.
virtual int last_output_sample_rate_hz() const = 0;
// Not implemented. // Not implemented.
virtual int SetTargetNumberOfChannels() = 0; virtual int SetTargetNumberOfChannels() = 0;

View File

@ -106,6 +106,7 @@ NetEqImpl::NetEqImpl(const NetEq::Config& config,
} }
fs_hz_ = fs; fs_hz_ = fs;
fs_mult_ = fs / 8000; fs_mult_ = fs / 8000;
last_output_sample_rate_hz_ = fs;
output_size_samples_ = static_cast<size_t>(kOutputSizeMs * 8 * fs_mult_); output_size_samples_ = static_cast<size_t>(kOutputSizeMs * 8 * fs_mult_);
decoder_frame_length_ = 3 * output_size_samples_; decoder_frame_length_ = 3 * output_size_samples_;
WebRtcSpl_Init(); WebRtcSpl_Init();
@ -160,6 +161,13 @@ int NetEqImpl::GetAudio(size_t max_length, int16_t* output_audio,
if (type) { if (type) {
*type = LastOutputType(); *type = LastOutputType();
} }
last_output_sample_rate_hz_ =
rtc::checked_cast<int>(*samples_per_channel * 100);
RTC_DCHECK(last_output_sample_rate_hz_ == 8000 ||
last_output_sample_rate_hz_ == 16000 ||
last_output_sample_rate_hz_ == 32000 ||
last_output_sample_rate_hz_ == 48000)
<< "Unexpected sample rate " << last_output_sample_rate_hz_;
return kOK; return kOK;
} }
@ -359,6 +367,11 @@ bool NetEqImpl::GetPlayoutTimestamp(uint32_t* timestamp) {
return true; return true;
} }
int NetEqImpl::last_output_sample_rate_hz() const {
CriticalSectionScoped lock(crit_sect_.get());
return last_output_sample_rate_hz_;
}
int NetEqImpl::SetTargetNumberOfChannels() { int NetEqImpl::SetTargetNumberOfChannels() {
return kNotImplemented; return kNotImplemented;
} }

View File

@ -168,6 +168,8 @@ class NetEqImpl : public webrtc::NetEq {
bool GetPlayoutTimestamp(uint32_t* timestamp) override; bool GetPlayoutTimestamp(uint32_t* timestamp) override;
int last_output_sample_rate_hz() const override;
int SetTargetNumberOfChannels() override; int SetTargetNumberOfChannels() override;
int SetTargetSampleRate() override; int SetTargetSampleRate() override;
@ -375,6 +377,7 @@ class NetEqImpl : public webrtc::NetEq {
StatisticsCalculator stats_ GUARDED_BY(crit_sect_); StatisticsCalculator stats_ GUARDED_BY(crit_sect_);
int fs_hz_ GUARDED_BY(crit_sect_); int fs_hz_ GUARDED_BY(crit_sect_);
int fs_mult_ GUARDED_BY(crit_sect_); int fs_mult_ GUARDED_BY(crit_sect_);
int last_output_sample_rate_hz_ GUARDED_BY(crit_sect_);
size_t output_size_samples_ GUARDED_BY(crit_sect_); size_t output_size_samples_ GUARDED_BY(crit_sect_);
size_t decoder_frame_length_ GUARDED_BY(crit_sect_); size_t decoder_frame_length_ GUARDED_BY(crit_sect_);
Modes last_mode_ GUARDED_BY(crit_sect_); Modes last_mode_ GUARDED_BY(crit_sect_);

View File

@ -1230,4 +1230,13 @@ TEST_F(NetEqImplTest, DecodingErrorDuringInternalCng) {
EXPECT_CALL(mock_decoder, Die()); EXPECT_CALL(mock_decoder, Die());
} }
// Tests that the return value from last_output_sample_rate_hz() is equal to the
// configured inital sample rate.
TEST_F(NetEqImplTest, InitialLastOutputSampleRate) {
UseNoMocks();
config_.sample_rate_hz = 48000;
CreateInstance();
EXPECT_EQ(48000, neteq_->last_output_sample_rate_hz());
}
}// namespace webrtc }// namespace webrtc

View File

@ -362,6 +362,7 @@ void NetEqDecodingTest::Process(size_t* out_len) {
(*out_len == kBlockSize16kHz) || (*out_len == kBlockSize16kHz) ||
(*out_len == kBlockSize32kHz)); (*out_len == kBlockSize32kHz));
output_sample_rate_ = static_cast<int>(*out_len / 10 * 1000); output_sample_rate_ = static_cast<int>(*out_len / 10 * 1000);
EXPECT_EQ(output_sample_rate_, neteq_->last_output_sample_rate_hz());
// Increase time. // Increase time.
sim_clock_ += kTimeStepMs; sim_clock_ += kTimeStepMs;
@ -895,6 +896,8 @@ TEST_F(NetEqDecodingTest, GetAudioBeforeInsertPacket) {
SCOPED_TRACE(ss.str()); // Print out the parameter values on failure. SCOPED_TRACE(ss.str()); // Print out the parameter values on failure.
EXPECT_EQ(0, out_data_[i]); EXPECT_EQ(0, out_data_[i]);
} }
// Verify that the sample rate did not change from the initial configuration.
EXPECT_EQ(config_.sample_rate_hz, neteq_->last_output_sample_rate_hz());
} }
class NetEqBgnTest : public NetEqDecodingTest { class NetEqBgnTest : public NetEqDecodingTest {

View File

@ -56,6 +56,7 @@ size_t NetEqExternalDecoderTest::GetOutputAudio(size_t max_length,
EXPECT_EQ(channels_, num_channels); EXPECT_EQ(channels_, num_channels);
EXPECT_EQ(static_cast<size_t>(kOutputLengthMs * sample_rate_hz_ / 1000), EXPECT_EQ(static_cast<size_t>(kOutputLengthMs * sample_rate_hz_ / 1000),
samples_per_channel); samples_per_channel);
EXPECT_EQ(sample_rate_hz_, neteq_->last_output_sample_rate_hz());
return samples_per_channel; return samples_per_channel;
} }