diff options
Diffstat (limited to 'modules/audio_coding/neteq')
26 files changed, 554 insertions, 374 deletions
diff --git a/modules/audio_coding/neteq/audio_decoder.cc b/modules/audio_coding/neteq/audio_decoder.cc index 0fdaa44b..04a74eef 100644 --- a/modules/audio_coding/neteq/audio_decoder.cc +++ b/modules/audio_coding/neteq/audio_decoder.cc @@ -51,8 +51,6 @@ bool AudioDecoder::PacketHasFec(const uint8_t* encoded, return false; } -NetEqDecoder AudioDecoder::codec_type() const { return codec_type_; } - bool AudioDecoder::CodecSupported(NetEqDecoder codec_type) { switch (codec_type) { case kDecoderPCMu: @@ -197,26 +195,24 @@ AudioDecoder* AudioDecoder::CreateAudioDecoder(NetEqDecoder codec_type) { return new AudioDecoderIsacFix; #elif defined(WEBRTC_CODEC_ISAC) case kDecoderISAC: - return new AudioDecoderIsac; -#endif -#ifdef WEBRTC_CODEC_ISAC + return new AudioDecoderIsac(16000); case kDecoderISACswb: - return new AudioDecoderIsacSwb; case kDecoderISACfb: - return new AudioDecoderIsacFb; + return new AudioDecoderIsac(32000); #endif #ifdef WEBRTC_CODEC_PCM16 case kDecoderPCM16B: case kDecoderPCM16Bwb: case kDecoderPCM16Bswb32kHz: case kDecoderPCM16Bswb48kHz: - return new AudioDecoderPcm16B(codec_type); + return new AudioDecoderPcm16B; case kDecoderPCM16B_2ch: case kDecoderPCM16Bwb_2ch: case kDecoderPCM16Bswb32kHz_2ch: case kDecoderPCM16Bswb48kHz_2ch: + return new AudioDecoderPcm16BMultiCh(2); case kDecoderPCM16B_5ch: - return new AudioDecoderPcm16BMultiCh(codec_type); + return new AudioDecoderPcm16BMultiCh(5); #endif #ifdef WEBRTC_CODEC_G722 case kDecoderG722: @@ -226,19 +222,21 @@ AudioDecoder* AudioDecoder::CreateAudioDecoder(NetEqDecoder codec_type) { #endif #ifdef WEBRTC_CODEC_CELT case kDecoderCELT_32: + return new AudioDecoderCelt(1); case kDecoderCELT_32_2ch: - return new AudioDecoderCelt(codec_type); + return new AudioDecoderCelt(2); #endif #ifdef WEBRTC_CODEC_OPUS case kDecoderOpus: + return new AudioDecoderOpus(1); case kDecoderOpus_2ch: - return new AudioDecoderOpus(codec_type); + return new AudioDecoderOpus(2); #endif case kDecoderCNGnb: case kDecoderCNGwb: case kDecoderCNGswb32kHz: case kDecoderCNGswb48kHz: - return new AudioDecoderCng(codec_type); + return new AudioDecoderCng; case kDecoderRED: case kDecoderAVT: case kDecoderArbitrary: diff --git a/modules/audio_coding/neteq/audio_decoder_impl.cc b/modules/audio_coding/neteq/audio_decoder_impl.cc index 661f2b11..07b1b4be 100644 --- a/modules/audio_coding/neteq/audio_decoder_impl.cc +++ b/modules/audio_coding/neteq/audio_decoder_impl.cc @@ -13,6 +13,7 @@ #include <assert.h> #include <string.h> // memmove +#include "webrtc/base/checks.h" #ifdef WEBRTC_CODEC_CELT #include "webrtc/modules/audio_coding/codecs/celt/include/celt_interface.h" #endif @@ -44,7 +45,7 @@ int AudioDecoderPcmU::Decode(const uint8_t* encoded, size_t encoded_len, int16_t* decoded, SpeechType* speech_type) { int16_t temp_type = 1; // Default is speech. int16_t ret = WebRtcG711_DecodeU( - state_, reinterpret_cast<int16_t*>(const_cast<uint8_t*>(encoded)), + reinterpret_cast<int16_t*>(const_cast<uint8_t*>(encoded)), static_cast<int16_t>(encoded_len), decoded, &temp_type); *speech_type = ConvertSpeechType(temp_type); return ret; @@ -61,7 +62,7 @@ int AudioDecoderPcmA::Decode(const uint8_t* encoded, size_t encoded_len, int16_t* decoded, SpeechType* speech_type) { int16_t temp_type = 1; // Default is speech. int16_t ret = WebRtcG711_DecodeA( - state_, reinterpret_cast<int16_t*>(const_cast<uint8_t*>(encoded)), + reinterpret_cast<int16_t*>(const_cast<uint8_t*>(encoded)), static_cast<int16_t>(encoded_len), decoded, &temp_type); *speech_type = ConvertSpeechType(temp_type); return ret; @@ -75,19 +76,13 @@ int AudioDecoderPcmA::PacketDuration(const uint8_t* encoded, // PCM16B #ifdef WEBRTC_CODEC_PCM16 -AudioDecoderPcm16B::AudioDecoderPcm16B(enum NetEqDecoder type) - : AudioDecoder(type) { - assert(type == kDecoderPCM16B || - type == kDecoderPCM16Bwb || - type == kDecoderPCM16Bswb32kHz || - type == kDecoderPCM16Bswb48kHz); -} +AudioDecoderPcm16B::AudioDecoderPcm16B() {} int AudioDecoderPcm16B::Decode(const uint8_t* encoded, size_t encoded_len, int16_t* decoded, SpeechType* speech_type) { int16_t temp_type = 1; // Default is speech. int16_t ret = WebRtcPcm16b_DecodeW16( - state_, reinterpret_cast<int16_t*>(const_cast<uint8_t*>(encoded)), + reinterpret_cast<int16_t*>(const_cast<uint8_t*>(encoded)), static_cast<int16_t>(encoded_len), decoded, &temp_type); *speech_type = ConvertSpeechType(temp_type); return ret; @@ -99,29 +94,15 @@ int AudioDecoderPcm16B::PacketDuration(const uint8_t* encoded, return static_cast<int>(encoded_len / (2 * channels_)); } -AudioDecoderPcm16BMultiCh::AudioDecoderPcm16BMultiCh( - enum NetEqDecoder type) - : AudioDecoderPcm16B(kDecoderPCM16B) { // This will be changed below. - codec_type_ = type; // Changing to actual type here. - switch (codec_type_) { - case kDecoderPCM16B_2ch: - case kDecoderPCM16Bwb_2ch: - case kDecoderPCM16Bswb32kHz_2ch: - case kDecoderPCM16Bswb48kHz_2ch: - channels_ = 2; - break; - case kDecoderPCM16B_5ch: - channels_ = 5; - break; - default: - assert(false); - } +AudioDecoderPcm16BMultiCh::AudioDecoderPcm16BMultiCh(int num_channels) { + DCHECK(num_channels > 0); + channels_ = num_channels; } #endif // iLBC #ifdef WEBRTC_CODEC_ILBC -AudioDecoderIlbc::AudioDecoderIlbc() : AudioDecoder(kDecoderILBC) { +AudioDecoderIlbc::AudioDecoderIlbc() { WebRtcIlbcfix_DecoderCreate(reinterpret_cast<iLBC_decinst_t**>(&state_)); } @@ -152,9 +133,11 @@ int AudioDecoderIlbc::Init() { // iSAC float #ifdef WEBRTC_CODEC_ISAC -AudioDecoderIsac::AudioDecoderIsac() : AudioDecoder(kDecoderISAC) { +AudioDecoderIsac::AudioDecoderIsac(int decode_sample_rate_hz) { + DCHECK(decode_sample_rate_hz == 16000 || decode_sample_rate_hz == 32000); WebRtcIsac_Create(reinterpret_cast<ISACStruct**>(&state_)); - WebRtcIsac_SetDecSampRate(static_cast<ISACStruct*>(state_), 16000); + WebRtcIsac_SetDecSampRate(static_cast<ISACStruct*>(state_), + decode_sample_rate_hz); } AudioDecoderIsac::~AudioDecoderIsac() { @@ -209,22 +192,11 @@ int AudioDecoderIsac::IncomingPacket(const uint8_t* payload, int AudioDecoderIsac::ErrorCode() { return WebRtcIsac_GetErrorCode(static_cast<ISACStruct*>(state_)); } - -// iSAC SWB -AudioDecoderIsacSwb::AudioDecoderIsacSwb() : AudioDecoderIsac() { - codec_type_ = kDecoderISACswb; - WebRtcIsac_SetDecSampRate(static_cast<ISACStruct*>(state_), 32000); -} - -// iSAC FB -AudioDecoderIsacFb::AudioDecoderIsacFb() : AudioDecoderIsacSwb() { - codec_type_ = kDecoderISACfb; -} #endif // iSAC fix #ifdef WEBRTC_CODEC_ISACFX -AudioDecoderIsacFix::AudioDecoderIsacFix() : AudioDecoder(kDecoderISAC) { +AudioDecoderIsacFix::AudioDecoderIsacFix() { WebRtcIsacfix_Create(reinterpret_cast<ISACFIX_MainStruct**>(&state_)); } @@ -266,7 +238,7 @@ int AudioDecoderIsacFix::ErrorCode() { // G.722 #ifdef WEBRTC_CODEC_G722 -AudioDecoderG722::AudioDecoderG722() : AudioDecoder(kDecoderG722) { +AudioDecoderG722::AudioDecoderG722() { WebRtcG722_CreateDecoder(reinterpret_cast<G722DecInst**>(&state_)); } @@ -382,14 +354,9 @@ void AudioDecoderG722Stereo::SplitStereoPacket(const uint8_t* encoded, // CELT #ifdef WEBRTC_CODEC_CELT -AudioDecoderCelt::AudioDecoderCelt(enum NetEqDecoder type) - : AudioDecoder(type) { - assert(type == kDecoderCELT_32 || type == kDecoderCELT_32_2ch); - if (type == kDecoderCELT_32) { - channels_ = 1; - } else { - channels_ = 2; - } +AudioDecoderCelt::AudioDecoderCelt(int num_channels) { + DCHECK(num_channels == 1 || num_channels == 2); + channels_ = num_channels; WebRtcCelt_CreateDec(reinterpret_cast<CELT_decinst_t**>(&state_), static_cast<int>(channels_)); } @@ -431,13 +398,9 @@ int AudioDecoderCelt::DecodePlc(int num_frames, int16_t* decoded) { // Opus #ifdef WEBRTC_CODEC_OPUS -AudioDecoderOpus::AudioDecoderOpus(enum NetEqDecoder type) - : AudioDecoder(type) { - if (type == kDecoderOpus_2ch) { - channels_ = 2; - } else { - channels_ = 1; - } +AudioDecoderOpus::AudioDecoderOpus(int num_channels) { + DCHECK(num_channels == 1 || num_channels == 2); + channels_ = num_channels; WebRtcOpus_DecoderCreate(reinterpret_cast<OpusDecInst**>(&state_), static_cast<int>(channels_)); } @@ -494,10 +457,7 @@ bool AudioDecoderOpus::PacketHasFec(const uint8_t* encoded, } #endif -AudioDecoderCng::AudioDecoderCng(enum NetEqDecoder type) - : AudioDecoder(type) { - assert(type == kDecoderCNGnb || type == kDecoderCNGwb || - kDecoderCNGswb32kHz || type == kDecoderCNGswb48kHz); +AudioDecoderCng::AudioDecoderCng() { WebRtcCng_CreateDec(reinterpret_cast<CNG_dec_inst**>(&state_)); assert(state_); } diff --git a/modules/audio_coding/neteq/audio_decoder_impl.h b/modules/audio_coding/neteq/audio_decoder_impl.h index 265d660b..214392e7 100644 --- a/modules/audio_coding/neteq/audio_decoder_impl.h +++ b/modules/audio_coding/neteq/audio_decoder_impl.h @@ -26,7 +26,7 @@ namespace webrtc { class AudioDecoderPcmU : public AudioDecoder { public: - AudioDecoderPcmU() : AudioDecoder(kDecoderPCMu) {} + AudioDecoderPcmU() {} virtual int Decode(const uint8_t* encoded, size_t encoded_len, int16_t* decoded, SpeechType* speech_type); virtual int Init() { return 0; } @@ -38,7 +38,7 @@ class AudioDecoderPcmU : public AudioDecoder { class AudioDecoderPcmA : public AudioDecoder { public: - AudioDecoderPcmA() : AudioDecoder(kDecoderPCMa) {} + AudioDecoderPcmA() {} virtual int Decode(const uint8_t* encoded, size_t encoded_len, int16_t* decoded, SpeechType* speech_type); virtual int Init() { return 0; } @@ -75,7 +75,7 @@ class AudioDecoderPcmAMultiCh : public AudioDecoderPcmA { // The type is specified in the constructor parameter |type|. class AudioDecoderPcm16B : public AudioDecoder { public: - explicit AudioDecoderPcm16B(enum NetEqDecoder type); + AudioDecoderPcm16B(); virtual int Decode(const uint8_t* encoded, size_t encoded_len, int16_t* decoded, SpeechType* speech_type); virtual int Init() { return 0; } @@ -90,7 +90,7 @@ class AudioDecoderPcm16B : public AudioDecoder { // of channels is derived from the type. class AudioDecoderPcm16BMultiCh : public AudioDecoderPcm16B { public: - explicit AudioDecoderPcm16BMultiCh(enum NetEqDecoder type); + explicit AudioDecoderPcm16BMultiCh(int num_channels); private: DISALLOW_COPY_AND_ASSIGN(AudioDecoderPcm16BMultiCh); @@ -116,7 +116,7 @@ class AudioDecoderIlbc : public AudioDecoder { #ifdef WEBRTC_CODEC_ISAC class AudioDecoderIsac : public AudioDecoder { public: - AudioDecoderIsac(); + explicit AudioDecoderIsac(int decode_sample_rate_hz); virtual ~AudioDecoderIsac(); virtual int Decode(const uint8_t* encoded, size_t encoded_len, int16_t* decoded, SpeechType* speech_type); @@ -135,22 +135,6 @@ class AudioDecoderIsac : public AudioDecoder { private: DISALLOW_COPY_AND_ASSIGN(AudioDecoderIsac); }; - -class AudioDecoderIsacSwb : public AudioDecoderIsac { - public: - AudioDecoderIsacSwb(); - - private: - DISALLOW_COPY_AND_ASSIGN(AudioDecoderIsacSwb); -}; - -class AudioDecoderIsacFb : public AudioDecoderIsacSwb { - public: - AudioDecoderIsacFb(); - - private: - DISALLOW_COPY_AND_ASSIGN(AudioDecoderIsacFb); -}; #endif #ifdef WEBRTC_CODEC_ISACFX @@ -215,7 +199,7 @@ class AudioDecoderG722Stereo : public AudioDecoderG722 { #ifdef WEBRTC_CODEC_CELT class AudioDecoderCelt : public AudioDecoder { public: - explicit AudioDecoderCelt(enum NetEqDecoder type); + explicit AudioDecoderCelt(int num_channels); virtual ~AudioDecoderCelt(); virtual int Decode(const uint8_t* encoded, size_t encoded_len, @@ -232,7 +216,7 @@ class AudioDecoderCelt : public AudioDecoder { #ifdef WEBRTC_CODEC_OPUS class AudioDecoderOpus : public AudioDecoder { public: - explicit AudioDecoderOpus(enum NetEqDecoder type); + explicit AudioDecoderOpus(int num_channels); virtual ~AudioDecoderOpus(); virtual int Decode(const uint8_t* encoded, size_t encoded_len, int16_t* decoded, SpeechType* speech_type); @@ -257,7 +241,7 @@ class AudioDecoderOpus : public AudioDecoder { // specific CngDecoder class could both inherit from that class. class AudioDecoderCng : public AudioDecoder { public: - explicit AudioDecoderCng(enum NetEqDecoder type); + explicit AudioDecoderCng(); virtual ~AudioDecoderCng(); virtual int Decode(const uint8_t* encoded, size_t encoded_len, int16_t* decoded, SpeechType* speech_type) { return -1; } diff --git a/modules/audio_coding/neteq/audio_decoder_unittest.cc b/modules/audio_coding/neteq/audio_decoder_unittest.cc index 624e6a4d..c95214b2 100644 --- a/modules/audio_coding/neteq/audio_decoder_unittest.cc +++ b/modules/audio_coding/neteq/audio_decoder_unittest.cc @@ -14,56 +14,106 @@ #include <stdlib.h> #include <string> +#include <vector> #include "testing/gtest/include/gtest/gtest.h" -#include "webrtc/common_audio/resampler/include/resampler.h" #ifdef WEBRTC_CODEC_CELT #include "webrtc/modules/audio_coding/codecs/celt/include/celt_interface.h" #endif #include "webrtc/modules/audio_coding/codecs/g711/include/g711_interface.h" +#include "webrtc/modules/audio_coding/codecs/g711/include/audio_encoder_pcm.h" #include "webrtc/modules/audio_coding/codecs/g722/include/g722_interface.h" #include "webrtc/modules/audio_coding/codecs/ilbc/interface/ilbc.h" #include "webrtc/modules/audio_coding/codecs/isac/fix/interface/isacfix.h" #include "webrtc/modules/audio_coding/codecs/isac/main/interface/isac.h" -#include "webrtc/modules/audio_coding/codecs/opus/interface/opus_interface.h" +#include "webrtc/modules/audio_coding/codecs/opus/interface/audio_encoder_opus.h" #include "webrtc/modules/audio_coding/codecs/pcm16b/include/pcm16b.h" +#include "webrtc/modules/audio_coding/neteq/tools/resample_input_audio_file.h" #include "webrtc/system_wrappers/interface/data_log.h" +#include "webrtc/system_wrappers/interface/scoped_ptr.h" #include "webrtc/test/testsupport/fileutils.h" namespace webrtc { +namespace { +// The absolute difference between the input and output (the first channel) is +// compared vs |tolerance|. The parameter |delay| is used to correct for codec +// delays. +void CompareInputOutput(const std::vector<int16_t>& input, + const std::vector<int16_t>& output, + size_t num_samples, + size_t channels, + int tolerance, + int delay) { + ASSERT_LE(num_samples, input.size()); + ASSERT_LE(num_samples * channels, output.size()); + for (unsigned int n = 0; n < num_samples - delay; ++n) { + ASSERT_NEAR(input[n], output[channels * n + delay], tolerance) + << "Exit test on first diff; n = " << n; + DataLog::InsertCell("CodecTest", "input", input[n]); + DataLog::InsertCell("CodecTest", "output", output[channels * n]); + DataLog::NextRow("CodecTest"); + } +} + +// The absolute difference between the first two channels in |output| is +// compared vs |tolerance|. +void CompareTwoChannels(const std::vector<int16_t>& output, + size_t samples_per_channel, + size_t channels, + int tolerance) { + ASSERT_GE(channels, 2u); + ASSERT_LE(samples_per_channel * channels, output.size()); + for (unsigned int n = 0; n < samples_per_channel; ++n) + ASSERT_NEAR(output[channels * n], output[channels * n + 1], tolerance) + << "Stereo samples differ."; +} + +// Calculates mean-squared error between input and output (the first channel). +// The parameter |delay| is used to correct for codec delays. +double MseInputOutput(const std::vector<int16_t>& input, + const std::vector<int16_t>& output, + size_t num_samples, + size_t channels, + int delay) { + assert(delay < static_cast<int>(num_samples)); + assert(num_samples <= input.size()); + assert(num_samples * channels <= output.size()); + if (num_samples == 0) + return 0.0; + double squared_sum = 0.0; + for (unsigned int n = 0; n < num_samples - delay; ++n) { + squared_sum += (input[n] - output[channels * n + delay]) * + (input[n] - output[channels * n + delay]); + } + return squared_sum / (num_samples - delay); +} +} // namespace + class AudioDecoderTest : public ::testing::Test { protected: AudioDecoderTest() - : input_fp_(NULL), - input_(NULL), - encoded_(NULL), - decoded_(NULL), - frame_size_(0), - data_length_(0), - encoded_bytes_(0), - channels_(1), - decoder_(NULL) { - input_file_ = webrtc::test::ProjectRootPath() + - "resources/audio_coding/testfile32kHz.pcm"; - } + : input_audio_(webrtc::test::ProjectRootPath() + + "resources/audio_coding/testfile32kHz.pcm", + 32000), + codec_input_rate_hz_(32000), // Legacy default value. + encoded_(NULL), + frame_size_(0), + data_length_(0), + encoded_bytes_(0), + channels_(1), + output_timestamp_(0), + decoder_(NULL) {} virtual ~AudioDecoderTest() {} virtual void SetUp() { + if (audio_encoder_) + codec_input_rate_hz_ = audio_encoder_->sample_rate_hz(); // Create arrays. ASSERT_GT(data_length_, 0u) << "The test must set data_length_ > 0"; - input_ = new int16_t[data_length_]; // Longest encoded data is produced by PCM16b with 2 bytes per sample. encoded_ = new uint8_t[data_length_ * 2]; - decoded_ = new int16_t[data_length_ * channels_]; - // Open input file. - input_fp_ = fopen(input_file_.c_str(), "rb"); - ASSERT_TRUE(input_fp_ != NULL) << "Failed to open file " << input_file_; - // Read data to |input_|. - ASSERT_EQ(data_length_, - fread(input_, sizeof(int16_t), data_length_, input_fp_)) << - "Could not read enough data from file"; // Logging to view input and output in Matlab. // Use 'gyp -Denable_data_logging=1' to enable logging. DataLog::CreateLog(); @@ -75,24 +125,37 @@ class AudioDecoderTest : public ::testing::Test { virtual void TearDown() { delete decoder_; decoder_ = NULL; - // Close input file. - fclose(input_fp_); // Delete arrays. - delete [] input_; - input_ = NULL; delete [] encoded_; encoded_ = NULL; - delete [] decoded_; - decoded_ = NULL; // Close log. DataLog::ReturnLog(); } virtual void InitEncoder() { } - // This method must be implemented for all tests derived from this class. - virtual int EncodeFrame(const int16_t* input, size_t input_len, - uint8_t* output) = 0; + // TODO(henrik.lundin) Change return type to size_t once most/all overriding + // implementations are gone. + virtual int EncodeFrame(const int16_t* input, + size_t input_len_samples, + uint8_t* output) { + size_t enc_len_bytes = 0; + scoped_ptr<int16_t[]> interleaved_input( + new int16_t[channels_ * input_len_samples]); + for (int i = 0; i < audio_encoder_->Num10MsFramesInNextPacket(); ++i) { + EXPECT_EQ(0u, enc_len_bytes); + + // Duplicate the mono input signal to however many channels the test + // wants. + test::InputAudioFile::DuplicateInterleaved( + input, input_len_samples, channels_, interleaved_input.get()); + + EXPECT_TRUE(audio_encoder_->Encode( + 0, interleaved_input.get(), audio_encoder_->sample_rate_hz() / 100, + data_length_ * 2, output, &enc_len_bytes, &output_timestamp_)); + } + return static_cast<int>(enc_len_bytes); + } // Encodes and decodes audio. The absolute difference between the input and // output is compared vs |tolerance|, and the mean-squared error is compared @@ -108,13 +171,23 @@ class AudioDecoderTest : public ::testing::Test { encoded_bytes_ = 0u; InitEncoder(); EXPECT_EQ(0, decoder_->Init()); + std::vector<int16_t> input; + std::vector<int16_t> decoded; while (processed_samples + frame_size_ <= data_length_) { - size_t enc_len = EncodeFrame(&input_[processed_samples], frame_size_, - &encoded_[encoded_bytes_]); + // Extend input vector with |frame_size_|. + input.resize(input.size() + frame_size_, 0); + // Read from input file. + ASSERT_GE(input.size() - processed_samples, frame_size_); + ASSERT_TRUE(input_audio_.Read( + frame_size_, codec_input_rate_hz_, &input[processed_samples])); + size_t enc_len = EncodeFrame( + &input[processed_samples], frame_size_, &encoded_[encoded_bytes_]); + // Make sure that frame_size_ * channels_ samples are allocated and free. + decoded.resize((processed_samples + frame_size_) * channels_, 0); AudioDecoder::SpeechType speech_type; - size_t dec_len = decoder_->Decode(&encoded_[encoded_bytes_], enc_len, - &decoded_[processed_samples * - channels_], + size_t dec_len = decoder_->Decode(&encoded_[encoded_bytes_], + enc_len, + &decoded[processed_samples * channels_], &speech_type); EXPECT_EQ(frame_size_ * channels_, dec_len); encoded_bytes_ += enc_len; @@ -126,65 +199,36 @@ class AudioDecoderTest : public ::testing::Test { if (expected_bytes) { EXPECT_EQ(expected_bytes, encoded_bytes_); } - CompareInputOutput(processed_samples, tolerance, delay); + CompareInputOutput( + input, decoded, processed_samples, channels_, tolerance, delay); if (channels_ == 2) - CompareTwoChannels(processed_samples, channel_diff_tolerance); - EXPECT_LE(MseInputOutput(processed_samples, delay), mse); - } - - // The absolute difference between the input and output (the first channel) is - // compared vs |tolerance|. The parameter |delay| is used to correct for codec - // delays. - virtual void CompareInputOutput(size_t num_samples, int tolerance, - int delay) const { - assert(num_samples <= data_length_); - for (unsigned int n = 0; n < num_samples - delay; ++n) { - ASSERT_NEAR(input_[n], decoded_[channels_ * n + delay], tolerance) << - "Exit test on first diff; n = " << n; - DataLog::InsertCell("CodecTest", "input", input_[n]); - DataLog::InsertCell("CodecTest", "output", decoded_[channels_ * n]); - DataLog::NextRow("CodecTest"); - } - } - - // The absolute difference between the two channels in a stereo is compared vs - // |tolerance|. - virtual void CompareTwoChannels(size_t samples_per_channel, - int tolerance) const { - assert(samples_per_channel <= data_length_); - for (unsigned int n = 0; n < samples_per_channel; ++n) - ASSERT_NEAR(decoded_[channels_ * n], decoded_[channels_ * n + 1], - tolerance) << "Stereo samples differ."; - } - - // Calculates mean-squared error between input and output (the first channel). - // The parameter |delay| is used to correct for codec delays. - virtual double MseInputOutput(size_t num_samples, int delay) const { - assert(num_samples <= data_length_); - if (num_samples == 0) return 0.0; - double squared_sum = 0.0; - for (unsigned int n = 0; n < num_samples - delay; ++n) { - squared_sum += (input_[n] - decoded_[channels_ * n + delay]) * - (input_[n] - decoded_[channels_ * n + delay]); - } - return squared_sum / (num_samples - delay); + CompareTwoChannels( + decoded, processed_samples, channels_, channel_diff_tolerance); + EXPECT_LE( + MseInputOutput(input, decoded, processed_samples, channels_, delay), + mse); } // Encodes a payload and decodes it twice with decoder re-init before each // decode. Verifies that the decoded result is the same. void ReInitTest() { - int16_t* output1 = decoded_; - int16_t* output2 = decoded_ + frame_size_; InitEncoder(); - size_t enc_len = EncodeFrame(input_, frame_size_, encoded_); + scoped_ptr<int16_t[]> input(new int16_t[frame_size_]); + ASSERT_TRUE( + input_audio_.Read(frame_size_, codec_input_rate_hz_, input.get())); + size_t enc_len = EncodeFrame(input.get(), frame_size_, encoded_); size_t dec_len; AudioDecoder::SpeechType speech_type1, speech_type2; EXPECT_EQ(0, decoder_->Init()); - dec_len = decoder_->Decode(encoded_, enc_len, output1, &speech_type1); + scoped_ptr<int16_t[]> output1(new int16_t[frame_size_ * channels_]); + dec_len = decoder_->Decode(encoded_, enc_len, output1.get(), &speech_type1); + ASSERT_LE(dec_len, frame_size_ * channels_); EXPECT_EQ(frame_size_ * channels_, dec_len); // Re-init decoder and decode again. EXPECT_EQ(0, decoder_->Init()); - dec_len = decoder_->Decode(encoded_, enc_len, output2, &speech_type2); + scoped_ptr<int16_t[]> output2(new int16_t[frame_size_ * channels_]); + dec_len = decoder_->Decode(encoded_, enc_len, output2.get(), &speech_type2); + ASSERT_LE(dec_len, frame_size_ * channels_); EXPECT_EQ(frame_size_ * channels_, dec_len); for (unsigned int n = 0; n < frame_size_; ++n) { ASSERT_EQ(output1[n], output2[n]) << "Exit test on first diff; n = " << n; @@ -195,29 +239,33 @@ class AudioDecoderTest : public ::testing::Test { // Call DecodePlc and verify that the correct number of samples is produced. void DecodePlcTest() { InitEncoder(); - size_t enc_len = EncodeFrame(input_, frame_size_, encoded_); + scoped_ptr<int16_t[]> input(new int16_t[frame_size_]); + ASSERT_TRUE( + input_audio_.Read(frame_size_, codec_input_rate_hz_, input.get())); + size_t enc_len = EncodeFrame(input.get(), frame_size_, encoded_); AudioDecoder::SpeechType speech_type; EXPECT_EQ(0, decoder_->Init()); + scoped_ptr<int16_t[]> output(new int16_t[frame_size_ * channels_]); size_t dec_len = - decoder_->Decode(encoded_, enc_len, decoded_, &speech_type); + decoder_->Decode(encoded_, enc_len, output.get(), &speech_type); EXPECT_EQ(frame_size_ * channels_, dec_len); // Call DecodePlc and verify that we get one frame of data. // (Overwrite the output from the above Decode call, but that does not // matter.) - dec_len = decoder_->DecodePlc(1, decoded_); + dec_len = decoder_->DecodePlc(1, output.get()); EXPECT_EQ(frame_size_ * channels_, dec_len); } - std::string input_file_; - FILE* input_fp_; - int16_t* input_; + test::ResampleInputAudioFile input_audio_; + int codec_input_rate_hz_; uint8_t* encoded_; - int16_t* decoded_; size_t frame_size_; size_t data_length_; size_t encoded_bytes_; size_t channels_; + uint32_t output_timestamp_; AudioDecoder* decoder_; + scoped_ptr<AudioEncoder> audio_encoder_; }; class AudioDecoderPcmUTest : public AudioDecoderTest { @@ -226,17 +274,9 @@ class AudioDecoderPcmUTest : public AudioDecoderTest { frame_size_ = 160; data_length_ = 10 * frame_size_; decoder_ = new AudioDecoderPcmU; - assert(decoder_); - } - - virtual int EncodeFrame(const int16_t* input, size_t input_len_samples, - uint8_t* output) { - int enc_len_bytes = - WebRtcG711_EncodeU(NULL, const_cast<int16_t*>(input), - static_cast<int>(input_len_samples), - reinterpret_cast<int16_t*>(output)); - EXPECT_EQ(input_len_samples, static_cast<size_t>(enc_len_bytes)); - return enc_len_bytes; + AudioEncoderPcmU::Config config; + config.frame_size_ms = static_cast<int>(frame_size_ / 8); + audio_encoder_.reset(new AudioEncoderPcmU(config)); } }; @@ -246,26 +286,19 @@ class AudioDecoderPcmATest : public AudioDecoderTest { frame_size_ = 160; data_length_ = 10 * frame_size_; decoder_ = new AudioDecoderPcmA; - assert(decoder_); - } - - virtual int EncodeFrame(const int16_t* input, size_t input_len_samples, - uint8_t* output) { - int enc_len_bytes = - WebRtcG711_EncodeA(NULL, const_cast<int16_t*>(input), - static_cast<int>(input_len_samples), - reinterpret_cast<int16_t*>(output)); - EXPECT_EQ(input_len_samples, static_cast<size_t>(enc_len_bytes)); - return enc_len_bytes; + AudioEncoderPcmA::Config config; + config.frame_size_ms = static_cast<int>(frame_size_ / 8); + audio_encoder_.reset(new AudioEncoderPcmA(config)); } }; class AudioDecoderPcm16BTest : public AudioDecoderTest { protected: AudioDecoderPcm16BTest() : AudioDecoderTest() { + codec_input_rate_hz_ = 8000; frame_size_ = 160; data_length_ = 10 * frame_size_; - decoder_ = new AudioDecoderPcm16B(kDecoderPCM16B); + decoder_ = new AudioDecoderPcm16B; assert(decoder_); } @@ -282,6 +315,7 @@ class AudioDecoderPcm16BTest : public AudioDecoderTest { class AudioDecoderIlbcTest : public AudioDecoderTest { protected: AudioDecoderIlbcTest() : AudioDecoderTest() { + codec_input_rate_hz_ = 8000; frame_size_ = 240; data_length_ = 10 * frame_size_; decoder_ = new AudioDecoderIlbc; @@ -311,14 +345,18 @@ class AudioDecoderIlbcTest : public AudioDecoderTest { // not return any data. It simply resets a few states and returns 0. void DecodePlcTest() { InitEncoder(); - size_t enc_len = EncodeFrame(input_, frame_size_, encoded_); + scoped_ptr<int16_t[]> input(new int16_t[frame_size_]); + ASSERT_TRUE( + input_audio_.Read(frame_size_, codec_input_rate_hz_, input.get())); + size_t enc_len = EncodeFrame(input.get(), frame_size_, encoded_); AudioDecoder::SpeechType speech_type; EXPECT_EQ(0, decoder_->Init()); + scoped_ptr<int16_t[]> output(new int16_t[frame_size_ * channels_]); size_t dec_len = - decoder_->Decode(encoded_, enc_len, decoded_, &speech_type); + decoder_->Decode(encoded_, enc_len, output.get(), &speech_type); EXPECT_EQ(frame_size_, dec_len); // Simply call DecodePlc and verify that we get 0 as return value. - EXPECT_EQ(0, decoder_->DecodePlc(1, decoded_)); + EXPECT_EQ(0, decoder_->DecodePlc(1, output.get())); } iLBC_encinst_t* encoder_; @@ -327,10 +365,11 @@ class AudioDecoderIlbcTest : public AudioDecoderTest { class AudioDecoderIsacFloatTest : public AudioDecoderTest { protected: AudioDecoderIsacFloatTest() : AudioDecoderTest() { + codec_input_rate_hz_ = 16000; input_size_ = 160; frame_size_ = 480; data_length_ = 10 * frame_size_; - decoder_ = new AudioDecoderIsac; + decoder_ = new AudioDecoderIsac(16000); assert(decoder_); WebRtcIsac_Create(&encoder_); WebRtcIsac_SetEncSampRate(encoder_, 16000); @@ -364,10 +403,11 @@ class AudioDecoderIsacFloatTest : public AudioDecoderTest { class AudioDecoderIsacSwbTest : public AudioDecoderTest { protected: AudioDecoderIsacSwbTest() : AudioDecoderTest() { + codec_input_rate_hz_ = 32000; input_size_ = 320; frame_size_ = 960; data_length_ = 10 * frame_size_; - decoder_ = new AudioDecoderIsacSwb; + decoder_ = new AudioDecoderIsac(32000); assert(decoder_); WebRtcIsac_Create(&encoder_); WebRtcIsac_SetEncSampRate(encoder_, 32000); @@ -398,22 +438,10 @@ class AudioDecoderIsacSwbTest : public AudioDecoderTest { int input_size_; }; -// This test is identical to AudioDecoderIsacSwbTest, except that it creates -// an AudioDecoderIsacFb decoder object. -class AudioDecoderIsacFbTest : public AudioDecoderIsacSwbTest { - protected: - AudioDecoderIsacFbTest() : AudioDecoderIsacSwbTest() { - // Delete the |decoder_| that was created by AudioDecoderIsacSwbTest and - // create an AudioDecoderIsacFb object instead. - delete decoder_; - decoder_ = new AudioDecoderIsacFb; - assert(decoder_); - } -}; - class AudioDecoderIsacFixTest : public AudioDecoderTest { protected: AudioDecoderIsacFixTest() : AudioDecoderTest() { + codec_input_rate_hz_ = 16000; input_size_ = 160; frame_size_ = 480; data_length_ = 10 * frame_size_; @@ -451,6 +479,7 @@ class AudioDecoderIsacFixTest : public AudioDecoderTest { class AudioDecoderG722Test : public AudioDecoderTest { protected: AudioDecoderG722Test() : AudioDecoderTest() { + codec_input_rate_hz_ = 16000; frame_size_ = 160; data_length_ = 10 * frame_size_; decoder_ = new AudioDecoderG722; @@ -593,79 +622,26 @@ class AudioDecoderCeltStereoTest : public AudioDecoderTest { class AudioDecoderOpusTest : public AudioDecoderTest { protected: AudioDecoderOpusTest() : AudioDecoderTest() { + codec_input_rate_hz_ = 48000; frame_size_ = 480; data_length_ = 10 * frame_size_; - decoder_ = new AudioDecoderOpus(kDecoderOpus); - assert(decoder_); - WebRtcOpus_EncoderCreate(&encoder_, 1); - } - - ~AudioDecoderOpusTest() { - WebRtcOpus_EncoderFree(encoder_); - } - - virtual void SetUp() OVERRIDE { - AudioDecoderTest::SetUp(); - // Upsample from 32 to 48 kHz. - // Because Opus is 48 kHz codec but the input file is 32 kHz, so the data - // read in |AudioDecoderTest::SetUp| has to be upsampled. - // |AudioDecoderTest::SetUp| has read |data_length_| samples, which is more - // than necessary after upsampling, so the end of audio that has been read - // is unused and the end of the buffer is overwritten by the resampled data. - Resampler rs; - rs.Reset(32000, 48000, kResamplerSynchronous); - const int before_resamp_len_samples = static_cast<int>(data_length_) * 2 - / 3; - int16_t* before_resamp_input = new int16_t[before_resamp_len_samples]; - memcpy(before_resamp_input, input_, - sizeof(int16_t) * before_resamp_len_samples); - int resamp_len_samples; - EXPECT_EQ(0, rs.Push(before_resamp_input, before_resamp_len_samples, - input_, static_cast<int>(data_length_), - resamp_len_samples)); - EXPECT_EQ(static_cast<int>(data_length_), resamp_len_samples); - delete[] before_resamp_input; - } - - virtual void InitEncoder() {} - - virtual int EncodeFrame(const int16_t* input, size_t input_len_samples, - uint8_t* output) OVERRIDE { - int enc_len_bytes = WebRtcOpus_Encode(encoder_, const_cast<int16_t*>(input), - static_cast<int16_t>(input_len_samples), - static_cast<int16_t>(data_length_), output); - EXPECT_GT(enc_len_bytes, 0); - return enc_len_bytes; + decoder_ = new AudioDecoderOpus(1); + AudioEncoderOpus::Config config; + config.frame_size_ms = static_cast<int>(frame_size_) / 48; + audio_encoder_.reset(new AudioEncoderOpus(config)); } - - OpusEncInst* encoder_; }; class AudioDecoderOpusStereoTest : public AudioDecoderOpusTest { protected: AudioDecoderOpusStereoTest() : AudioDecoderOpusTest() { channels_ = 2; - WebRtcOpus_EncoderFree(encoder_); delete decoder_; - decoder_ = new AudioDecoderOpus(kDecoderOpus_2ch); - assert(decoder_); - WebRtcOpus_EncoderCreate(&encoder_, 2); - } - - virtual int EncodeFrame(const int16_t* input, size_t input_len_samples, - uint8_t* output) OVERRIDE { - // Create stereo by duplicating each sample in |input|. - const int input_stereo_samples = static_cast<int>(input_len_samples) * 2; - int16_t* input_stereo = new int16_t[input_stereo_samples]; - for (size_t i = 0; i < input_len_samples; i++) - input_stereo[i * 2] = input_stereo[i * 2 + 1] = input[i]; - - int enc_len_bytes = WebRtcOpus_Encode( - encoder_, input_stereo, static_cast<int16_t>(input_len_samples), - static_cast<int16_t>(data_length_), output); - EXPECT_GT(enc_len_bytes, 0); - delete[] input_stereo; - return enc_len_bytes; + decoder_ = new AudioDecoderOpus(2); + AudioEncoderOpus::Config config; + config.frame_size_ms = static_cast<int>(frame_size_) / 48; + config.num_channels = 2; + audio_encoder_.reset(new AudioEncoderOpus(config)); } }; @@ -732,17 +708,6 @@ TEST_F(AudioDecoderIsacSwbTest, EncodeDecode) { DecodePlcTest(); } -TEST_F(AudioDecoderIsacFbTest, EncodeDecode) { - int tolerance = 19757; - double mse = 8.18e6; - int delay = 160; // Delay from input to output. - EXPECT_TRUE(AudioDecoder::CodecSupported(kDecoderISACswb)); - EncodeDecodeTest(0, tolerance, mse, delay); - ReInitTest(); - EXPECT_TRUE(decoder_->HasDecodePlc()); - DecodePlcTest(); -} - TEST_F(AudioDecoderIsacFixTest, DISABLED_EncodeDecode) { int tolerance = 11034; double mse = 3.46e6; diff --git a/modules/audio_coding/neteq/audio_decoder_unittests.isolate b/modules/audio_coding/neteq/audio_decoder_unittests.isolate index e4a18ca8..f10b327b 100644 --- a/modules/audio_coding/neteq/audio_decoder_unittests.isolate +++ b/modules/audio_coding/neteq/audio_decoder_unittests.isolate @@ -9,7 +9,7 @@ 'conditions': [ ['OS=="android"', { 'variables': { - 'isolate_dependency_untracked': [ + 'files': [ '<(DEPTH)/resources/', '<(DEPTH)/data/', ], @@ -21,17 +21,12 @@ '<(DEPTH)/testing/test_env.py', '<(PRODUCT_DIR)/audio_decoder_unittests<(EXECUTABLE_SUFFIX)', ], - 'isolate_dependency_touched': [ + 'files': [ '<(DEPTH)/DEPS', - ], - 'isolate_dependency_tracked': [ '<(DEPTH)/resources/audio_coding/testfile32kHz.pcm', '<(DEPTH)/testing/test_env.py', '<(PRODUCT_DIR)/audio_decoder_unittests<(EXECUTABLE_SUFFIX)', ], - 'isolate_dependency_untracked': [ - '<(DEPTH)/tools/swarming_client/', - ], }, }], ], diff --git a/modules/audio_coding/neteq/decision_logic_normal.cc b/modules/audio_coding/neteq/decision_logic_normal.cc index 9e422041..f2382845 100644 --- a/modules/audio_coding/neteq/decision_logic_normal.cc +++ b/modules/audio_coding/neteq/decision_logic_normal.cc @@ -67,19 +67,19 @@ Operations DecisionLogicNormal::GetDecisionSpecialized( return kNormal; } + const uint32_t five_seconds_samples = 5 * 8000 * fs_mult_; // Check if the required packet is available. if (target_timestamp == available_timestamp) { return ExpectedPacketAvailable(prev_mode, play_dtmf); - } else if (IsNewerTimestamp(available_timestamp, target_timestamp)) { + } else if (!PacketBuffer::IsObsoleteTimestamp( + available_timestamp, target_timestamp, five_seconds_samples)) { return FuturePacketAvailable(sync_buffer, expand, decoder_frame_length, prev_mode, target_timestamp, available_timestamp, play_dtmf); } else { // This implies that available_timestamp < target_timestamp, which can - // happen when a new stream or codec is received. Do Expand instead, and - // wait for a newer packet to arrive, or for the buffer to flush (resulting - // in a master reset). - return kExpand; + // happen when a new stream or codec is received. Signal for a reset. + return kUndefined; } } diff --git a/modules/audio_coding/neteq/decoder_database_unittest.cc b/modules/audio_coding/neteq/decoder_database_unittest.cc index 6f497199..1e4e58af 100644 --- a/modules/audio_coding/neteq/decoder_database_unittest.cc +++ b/modules/audio_coding/neteq/decoder_database_unittest.cc @@ -189,21 +189,18 @@ TEST(DecoderDatabase, ActiveDecoders) { EXPECT_TRUE(changed); AudioDecoder* decoder = db.GetActiveDecoder(); ASSERT_FALSE(decoder == NULL); // Should get a decoder here. - EXPECT_EQ(kDecoderPCMu, decoder->codec_type()); // Set the same again. Expect no change. EXPECT_EQ(DecoderDatabase::kOK, db.SetActiveDecoder(0, &changed)); EXPECT_FALSE(changed); decoder = db.GetActiveDecoder(); ASSERT_FALSE(decoder == NULL); // Should get a decoder here. - EXPECT_EQ(kDecoderPCMu, decoder->codec_type()); // Change active decoder. EXPECT_EQ(DecoderDatabase::kOK, db.SetActiveDecoder(103, &changed)); EXPECT_TRUE(changed); decoder = db.GetActiveDecoder(); ASSERT_FALSE(decoder == NULL); // Should get a decoder here. - EXPECT_EQ(kDecoderISAC, decoder->codec_type()); // Remove the active decoder, and verify that the active becomes NULL. EXPECT_EQ(DecoderDatabase::kOK, db.Remove(103)); @@ -213,7 +210,6 @@ TEST(DecoderDatabase, ActiveDecoders) { EXPECT_EQ(DecoderDatabase::kOK, db.SetActiveCngDecoder(13)); decoder = db.GetActiveCngDecoder(); ASSERT_FALSE(decoder == NULL); // Should get a decoder here. - EXPECT_EQ(kDecoderCNGnb, decoder->codec_type()); // Remove the active CNG decoder, and verify that the active becomes NULL. EXPECT_EQ(DecoderDatabase::kOK, db.Remove(13)); diff --git a/modules/audio_coding/neteq/expand.cc b/modules/audio_coding/neteq/expand.cc index 1f46e66c..49ea45f4 100644 --- a/modules/audio_coding/neteq/expand.cc +++ b/modules/audio_coding/neteq/expand.cc @@ -488,7 +488,7 @@ void Expand::AnalyzeSignal(int16_t* random_vector) { // Calculate scaled_energy1 / scaled_energy2 in Q13. int32_t energy_ratio = WebRtcSpl_DivW32W16( WEBRTC_SPL_SHIFT_W32(energy1, -scaled_energy1), - WEBRTC_SPL_RSHIFT_W32(energy2, scaled_energy2)); + energy2 >> scaled_energy2); // Calculate sqrt ratio in Q13 (sqrt of en1/en2 in Q26). amplitude_ratio = WebRtcSpl_SqrtFloor(energy_ratio << 13); // Copy the two vectors and give them the same energy. diff --git a/modules/audio_coding/neteq/interface/audio_decoder.h b/modules/audio_coding/neteq/interface/audio_decoder.h index 9a2fb8b4..16d78c9e 100644 --- a/modules/audio_coding/neteq/interface/audio_decoder.h +++ b/modules/audio_coding/neteq/interface/audio_decoder.h @@ -63,12 +63,7 @@ class AudioDecoder { // Used by PacketDuration below. Save the value -1 for errors. enum { kNotImplemented = -2 }; - explicit AudioDecoder(enum NetEqDecoder type) - : codec_type_(type), - channels_(1), - state_(NULL) { - } - + AudioDecoder() : channels_(1), state_(NULL) {} virtual ~AudioDecoder() {} // Decodes |encode_len| bytes from |encoded| and writes the result in @@ -119,8 +114,6 @@ class AudioDecoder { // Returns true if the packet has FEC and false otherwise. virtual bool PacketHasFec(const uint8_t* encoded, size_t encoded_len) const; - virtual NetEqDecoder codec_type() const; - // Returns the underlying decoder state. void* state() { return state_; } @@ -140,7 +133,6 @@ class AudioDecoder { protected: static SpeechType ConvertSpeechType(int16_t type); - enum NetEqDecoder codec_type_; size_t channels_; void* state_; diff --git a/modules/audio_coding/neteq/interface/neteq.h b/modules/audio_coding/neteq/interface/neteq.h index 925cb231..560e77ba 100644 --- a/modules/audio_coding/neteq/interface/neteq.h +++ b/modules/audio_coding/neteq/interface/neteq.h @@ -248,7 +248,7 @@ class NetEq { // Returns the error code for the last occurred error. If no error has // occurred, 0 is returned. - virtual int LastError() = 0; + virtual int LastError() const = 0; // Returns the error code last returned by a decoder (audio or comfort noise). // When LastError() returns kDecoderErrorCode or kComfortNoiseErrorCode, check diff --git a/modules/audio_coding/neteq/mock/mock_audio_decoder.h b/modules/audio_coding/neteq/mock/mock_audio_decoder.h index f3cecc24..95b564d1 100644 --- a/modules/audio_coding/neteq/mock/mock_audio_decoder.h +++ b/modules/audio_coding/neteq/mock/mock_audio_decoder.h @@ -19,7 +19,7 @@ namespace webrtc { class MockAudioDecoder : public AudioDecoder { public: - MockAudioDecoder() : AudioDecoder(kDecoderArbitrary) {} + MockAudioDecoder() {} virtual ~MockAudioDecoder() { Die(); } MOCK_METHOD0(Die, void()); MOCK_METHOD4(Decode, int(const uint8_t*, size_t, int16_t*, diff --git a/modules/audio_coding/neteq/mock/mock_external_decoder_pcm16b.h b/modules/audio_coding/neteq/mock/mock_external_decoder_pcm16b.h index ef5e03e5..400c0b03 100644 --- a/modules/audio_coding/neteq/mock/mock_external_decoder_pcm16b.h +++ b/modules/audio_coding/neteq/mock/mock_external_decoder_pcm16b.h @@ -27,15 +27,13 @@ using ::testing::Invoke; // audio_decoder_impl.{cc, h}. class ExternalPcm16B : public AudioDecoder { public: - explicit ExternalPcm16B(enum NetEqDecoder type) - : AudioDecoder(type) { - } + ExternalPcm16B() {} virtual int Decode(const uint8_t* encoded, size_t encoded_len, int16_t* decoded, SpeechType* speech_type) { int16_t temp_type; int16_t ret = WebRtcPcm16b_DecodeW16( - state_, reinterpret_cast<int16_t*>(const_cast<uint8_t*>(encoded)), + reinterpret_cast<int16_t*>(const_cast<uint8_t*>(encoded)), static_cast<int16_t>(encoded_len), decoded, &temp_type); *speech_type = ConvertSpeechType(temp_type); return ret; @@ -51,9 +49,7 @@ class ExternalPcm16B : public AudioDecoder { // The reason is that we can then track that the correct calls are being made. class MockExternalPcm16B : public ExternalPcm16B { public: - explicit MockExternalPcm16B(enum NetEqDecoder type) - : ExternalPcm16B(type), - real_(type) { + MockExternalPcm16B() { // By default, all calls are delegated to the real object. ON_CALL(*this, Decode(_, _, _, _)) .WillByDefault(Invoke(&real_, &ExternalPcm16B::Decode)); @@ -67,8 +63,6 @@ class MockExternalPcm16B : public ExternalPcm16B { .WillByDefault(Invoke(&real_, &ExternalPcm16B::IncomingPacket)); ON_CALL(*this, ErrorCode()) .WillByDefault(Invoke(&real_, &ExternalPcm16B::ErrorCode)); - ON_CALL(*this, codec_type()) - .WillByDefault(Invoke(&real_, &ExternalPcm16B::codec_type)); } virtual ~MockExternalPcm16B() { Die(); } diff --git a/modules/audio_coding/neteq/mock/mock_packet_buffer.h b/modules/audio_coding/neteq/mock/mock_packet_buffer.h index 74eea6f0..0eb7edc9 100644 --- a/modules/audio_coding/neteq/mock/mock_packet_buffer.h +++ b/modules/audio_coding/neteq/mock/mock_packet_buffer.h @@ -44,7 +44,9 @@ class MockPacketBuffer : public PacketBuffer { Packet*(int* discard_count)); MOCK_METHOD0(DiscardNextPacket, int()); - MOCK_METHOD1(DiscardOldPackets, + MOCK_METHOD2(DiscardOldPackets, + int(uint32_t timestamp_limit, uint32_t horizon_samples)); + MOCK_METHOD1(DiscardAllOldPackets, int(uint32_t timestamp_limit)); MOCK_CONST_METHOD0(NumPacketsInBuffer, int()); diff --git a/modules/audio_coding/neteq/neteq.gypi b/modules/audio_coding/neteq/neteq.gypi index 0901615a..c68651d6 100644 --- a/modules/audio_coding/neteq/neteq.gypi +++ b/modules/audio_coding/neteq/neteq.gypi @@ -136,6 +136,7 @@ 'type': '<(gtest_target_type)', 'dependencies': [ '<@(codecs)', + 'neteq_unittest_tools', '<(DEPTH)/testing/gtest.gyp:gtest', '<(webrtc_root)/common_audio/common_audio.gyp:common_audio', '<(webrtc_root)/test/test.gyp:test_support_main', @@ -170,7 +171,7 @@ 'type': 'static_library', 'dependencies': [ 'rtp_rtcp', - '<(webrtc_root)/test/webrtc_test_common.gyp:webrtc_test_common', + '<(webrtc_root)/test/test.gyp:rtp_test_utils', ], 'direct_dependent_settings': { 'include_dirs': [ @@ -193,6 +194,8 @@ 'tools/packet.cc', 'tools/packet.h', 'tools/packet_source.h', + 'tools/resample_input_audio_file.cc', + 'tools/resample_input_audio_file.h', 'tools/rtp_file_source.cc', 'tools/rtp_file_source.h', 'tools/rtp_generator.cc', @@ -222,7 +225,6 @@ ], 'includes': [ '../../../build/isolate.gypi', - 'audio_decoder_unittests.isolate', ], 'sources': [ 'audio_decoder_unittests.isolate', diff --git a/modules/audio_coding/neteq/neteq_external_decoder_unittest.cc b/modules/audio_coding/neteq/neteq_external_decoder_unittest.cc index 6a8eafa1..d41bc543 100644 --- a/modules/audio_coding/neteq/neteq_external_decoder_unittest.cc +++ b/modules/audio_coding/neteq/neteq_external_decoder_unittest.cc @@ -47,7 +47,7 @@ class NetEqExternalDecoderTest : public ::testing::Test { frame_size_ms_(10), frame_size_samples_(frame_size_ms_ * samples_per_ms_), output_size_samples_(frame_size_ms_ * samples_per_ms_), - external_decoder_(new MockExternalPcm16B(kDecoderPCM16Bswb32kHz)), + external_decoder_(new MockExternalPcm16B), rtp_generator_(new test::RtpGenerator(samples_per_ms_)), payload_size_bytes_(0), last_send_time_(0), @@ -241,7 +241,7 @@ class LargeTimestampJumpTest : public NetEqExternalDecoderTest { frame_size_samples_ = frame_size_ms_ * samples_per_ms_; output_size_samples_ = frame_size_ms_ * samples_per_ms_; EXPECT_CALL(*external_decoder_, Die()).Times(1); - external_decoder_.reset(new MockExternalPcm16B(kDecoderPCM16B)); + external_decoder_.reset(new MockExternalPcm16B); } void SetUp() OVERRIDE { @@ -308,6 +308,8 @@ class LargeTimestampJumpTest : public NetEqExternalDecoderTest { case kExpandPhase: { if (output_type == kOutputPLCtoCNG) { test_state_ = kFadedExpandPhase; + } else if (output_type == kOutputNormal) { + test_state_ = kRecovered; } break; } @@ -337,9 +339,14 @@ class LargeTimestampJumpTest : public NetEqExternalDecoderTest { } int NumExpectedDecodeCalls(int num_loops) const OVERRIDE { - // Some packets won't be decoded because of the buffer being flushed after - // the timestamp jump. - return num_loops - (config_.max_packets_in_buffer + 1); + // Some packets at the end of the stream won't be decoded. When the jump in + // timestamp happens, NetEq will do Expand during one GetAudio call. In the + // next call it will decode the packet after the jump, but the net result is + // that the delay increased by 1 packet. In another call, a Pre-emptive + // Expand operation is performed, leading to delay increase by 1 packet. In + // total, the test will end with a 2-packet delay, which results in the 2 + // last packets not being decoded. + return num_loops - 2; } TestStates test_state_; diff --git a/modules/audio_coding/neteq/neteq_impl.cc b/modules/audio_coding/neteq/neteq_impl.cc index edf618ef..f3d1a4f6 100644 --- a/modules/audio_coding/neteq/neteq_impl.cc +++ b/modules/audio_coding/neteq/neteq_impl.cc @@ -352,7 +352,7 @@ bool NetEqImpl::GetPlayoutTimestamp(uint32_t* timestamp) { return true; } -int NetEqImpl::LastError() { +int NetEqImpl::LastError() const { CriticalSectionScoped lock(crit_sect_.get()); return error_code_; } @@ -868,6 +868,10 @@ int NetEqImpl::GetDecision(Operations* operation, assert(sync_buffer_.get()); uint32_t end_timestamp = sync_buffer_->end_timestamp(); + if (!new_codec_) { + const uint32_t five_seconds_samples = 5 * fs_hz_; + packet_buffer_->DiscardOldPackets(end_timestamp, five_seconds_samples); + } const RTPHeader* header = packet_buffer_->NextRtpHeader(); if (decision_logic_->CngRfc3389On() || last_mode_ == kModeRfc3389Cng) { @@ -884,7 +888,7 @@ int NetEqImpl::GetDecision(Operations* operation, } // Check buffer again. if (!new_codec_) { - packet_buffer_->DiscardOldPackets(end_timestamp); + packet_buffer_->DiscardOldPackets(end_timestamp, 5 * fs_hz_); } header = packet_buffer_->NextRtpHeader(); } @@ -1823,7 +1827,7 @@ int NetEqImpl::ExtractPackets(int required_samples, PacketList* packet_list) { // we could end up in the situation where we never decode anything, since // all incoming packets are considered too old but the buffer will also // never be flooded and flushed. - packet_buffer_->DiscardOldPackets(timestamp_); + packet_buffer_->DiscardAllOldPackets(timestamp_); } return extracted_samples; diff --git a/modules/audio_coding/neteq/neteq_impl.h b/modules/audio_coding/neteq/neteq_impl.h index fc2284d9..348f483c 100644 --- a/modules/audio_coding/neteq/neteq_impl.h +++ b/modules/audio_coding/neteq/neteq_impl.h @@ -178,7 +178,7 @@ class NetEqImpl : public webrtc::NetEq { // Returns the error code for the last occurred error. If no error has // occurred, 0 is returned. - virtual int LastError() OVERRIDE; + virtual int LastError() const OVERRIDE; // Returns the error code last returned by a decoder (audio or comfort noise). // When LastError() returns kDecoderErrorCode or kComfortNoiseErrorCode, check diff --git a/modules/audio_coding/neteq/neteq_impl_unittest.cc b/modules/audio_coding/neteq/neteq_impl_unittest.cc index d5676d7b..8047fe20 100644 --- a/modules/audio_coding/neteq/neteq_impl_unittest.cc +++ b/modules/audio_coding/neteq/neteq_impl_unittest.cc @@ -32,9 +32,11 @@ using ::testing::Return; using ::testing::ReturnNull; using ::testing::_; using ::testing::SetArgPointee; +using ::testing::SetArrayArgument; using ::testing::InSequence; using ::testing::Invoke; using ::testing::WithArg; +using ::testing::Pointee; namespace webrtc { @@ -422,8 +424,7 @@ TEST_F(NetEqImplTest, VerifyTimestampPropagation) { // sample, and then increasing by 1 for each sample. class CountingSamplesDecoder : public AudioDecoder { public: - explicit CountingSamplesDecoder(enum NetEqDecoder type) - : AudioDecoder(type), next_value_(1) {} + CountingSamplesDecoder() : next_value_(1) {} // Produce as many samples as input bytes (|encoded_len|). virtual int Decode(const uint8_t* encoded, @@ -446,7 +447,7 @@ TEST_F(NetEqImplTest, VerifyTimestampPropagation) { private: int16_t next_value_; - } decoder_(kDecoderPCM16B); + } decoder_; EXPECT_EQ(NetEq::kOK, neteq_->RegisterExternalDecoder( @@ -495,4 +496,95 @@ TEST_F(NetEqImplTest, VerifyTimestampPropagation) { static_cast<int>(sync_buffer->FutureLength())); } +TEST_F(NetEqImplTest, ReorderedPacket) { + UseNoMocks(); + CreateInstance(); + + const uint8_t kPayloadType = 17; // Just an arbitrary number. + const uint32_t kReceiveTime = 17; // Value doesn't matter for this test. + const int kSampleRateHz = 8000; + const int kPayloadLengthSamples = 10 * kSampleRateHz / 1000; // 10 ms. + const size_t kPayloadLengthBytes = kPayloadLengthSamples; + uint8_t payload[kPayloadLengthBytes] = {0}; + WebRtcRTPHeader rtp_header; + rtp_header.header.payloadType = kPayloadType; + rtp_header.header.sequenceNumber = 0x1234; + rtp_header.header.timestamp = 0x12345678; + rtp_header.header.ssrc = 0x87654321; + + // Create a mock decoder object. + MockAudioDecoder mock_decoder; + EXPECT_CALL(mock_decoder, Init()).WillRepeatedly(Return(0)); + EXPECT_CALL(mock_decoder, IncomingPacket(_, kPayloadLengthBytes, _, _, _)) + .WillRepeatedly(Return(0)); + int16_t dummy_output[kPayloadLengthSamples] = {0}; + // The below expectation will make the mock decoder write + // |kPayloadLengthSamples| zeros to the output array, and mark it as speech. + EXPECT_CALL(mock_decoder, Decode(Pointee(0), kPayloadLengthBytes, _, _)) + .WillOnce(DoAll(SetArrayArgument<2>(dummy_output, + dummy_output + kPayloadLengthSamples), + SetArgPointee<3>(AudioDecoder::kSpeech), + Return(kPayloadLengthSamples))); + EXPECT_EQ(NetEq::kOK, + neteq_->RegisterExternalDecoder( + &mock_decoder, kDecoderPCM16B, kPayloadType)); + + // Insert one packet. + EXPECT_EQ(NetEq::kOK, + neteq_->InsertPacket( + rtp_header, payload, kPayloadLengthBytes, kReceiveTime)); + + // Pull audio once. + const int kMaxOutputSize = 10 * kSampleRateHz / 1000; + int16_t output[kMaxOutputSize]; + int samples_per_channel; + int num_channels; + NetEqOutputType type; + EXPECT_EQ( + NetEq::kOK, + neteq_->GetAudio( + kMaxOutputSize, output, &samples_per_channel, &num_channels, &type)); + ASSERT_EQ(kMaxOutputSize, samples_per_channel); + EXPECT_EQ(1, num_channels); + EXPECT_EQ(kOutputNormal, type); + + // Insert two more packets. The first one is out of order, and is already too + // old, the second one is the expected next packet. + rtp_header.header.sequenceNumber -= 1; + rtp_header.header.timestamp -= kPayloadLengthSamples; + payload[0] = 1; + EXPECT_EQ(NetEq::kOK, + neteq_->InsertPacket( + rtp_header, payload, kPayloadLengthBytes, kReceiveTime)); + rtp_header.header.sequenceNumber += 2; + rtp_header.header.timestamp += 2 * kPayloadLengthSamples; + payload[0] = 2; + EXPECT_EQ(NetEq::kOK, + neteq_->InsertPacket( + rtp_header, payload, kPayloadLengthBytes, kReceiveTime)); + + // Expect only the second packet to be decoded (the one with "2" as the first + // payload byte). + EXPECT_CALL(mock_decoder, Decode(Pointee(2), kPayloadLengthBytes, _, _)) + .WillOnce(DoAll(SetArrayArgument<2>(dummy_output, + dummy_output + kPayloadLengthSamples), + SetArgPointee<3>(AudioDecoder::kSpeech), + Return(kPayloadLengthSamples))); + + // Pull audio once. + EXPECT_EQ( + NetEq::kOK, + neteq_->GetAudio( + kMaxOutputSize, output, &samples_per_channel, &num_channels, &type)); + ASSERT_EQ(kMaxOutputSize, samples_per_channel); + EXPECT_EQ(1, num_channels); + EXPECT_EQ(kOutputNormal, type); + + // Now check the packet buffer, and make sure it is empty, since the + // out-of-order packet should have been discarded. + EXPECT_TRUE(packet_buffer_->Empty()); + + EXPECT_CALL(mock_decoder, Die()); +} + } // namespace webrtc diff --git a/modules/audio_coding/neteq/neteq_tests.gypi b/modules/audio_coding/neteq/neteq_tests.gypi index d134dcd4..48cd9ebe 100644 --- a/modules/audio_coding/neteq/neteq_tests.gypi +++ b/modules/audio_coding/neteq/neteq_tests.gypi @@ -87,7 +87,7 @@ 'neteq_unittest_tools', '<(DEPTH)/testing/gtest.gyp:gtest', '<(DEPTH)/third_party/gflags/gflags.gyp:gflags', - '<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:field_trial_default', + '<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:system_wrappers_default', ], 'sources': [ 'tools/rtp_analyze.cc', diff --git a/modules/audio_coding/neteq/packet_buffer.cc b/modules/audio_coding/neteq/packet_buffer.cc index 4c484185..816713d8 100644 --- a/modules/audio_coding/neteq/packet_buffer.cc +++ b/modules/audio_coding/neteq/packet_buffer.cc @@ -216,12 +216,12 @@ int PacketBuffer::DiscardNextPacket() { return kOK; } -int PacketBuffer::DiscardOldPackets(uint32_t timestamp_limit) { - while (!Empty() && - timestamp_limit != buffer_.front()->header.timestamp && - static_cast<uint32_t>(timestamp_limit - - buffer_.front()->header.timestamp) < - 0xFFFFFFFF / 2) { +int PacketBuffer::DiscardOldPackets(uint32_t timestamp_limit, + uint32_t horizon_samples) { + while (!Empty() && timestamp_limit != buffer_.front()->header.timestamp && + IsObsoleteTimestamp(buffer_.front()->header.timestamp, + timestamp_limit, + horizon_samples)) { if (DiscardNextPacket() != kOK) { assert(false); // Must be ok by design. } diff --git a/modules/audio_coding/neteq/packet_buffer.h b/modules/audio_coding/neteq/packet_buffer.h index 76c4ddd1..b9a16189 100644 --- a/modules/audio_coding/neteq/packet_buffer.h +++ b/modules/audio_coding/neteq/packet_buffer.h @@ -95,9 +95,19 @@ class PacketBuffer { // PacketBuffer::kOK otherwise. virtual int DiscardNextPacket(); - // Discards all packets that are (strictly) older than |timestamp_limit|. + // Discards all packets that are (strictly) older than timestamp_limit, + // but newer than timestamp_limit - horizon_samples. Setting horizon_samples + // to zero implies that the horizon is set to half the timestamp range. That + // is, if a packet is more than 2^31 timestamps into the future compared with + // timestamp_limit (including wrap-around), it is considered old. // Returns number of packets discarded. - virtual int DiscardOldPackets(uint32_t timestamp_limit); + virtual int DiscardOldPackets(uint32_t timestamp_limit, + uint32_t horizon_samples); + + // Discards all packets that are (strictly) older than timestamp_limit. + virtual int DiscardAllOldPackets(uint32_t timestamp_limit) { + return DiscardOldPackets(timestamp_limit, 0); + } // Returns the number of packets in the buffer, including duplicates and // redundant packets. @@ -125,6 +135,20 @@ class PacketBuffer { // in |packet_list|. static void DeleteAllPackets(PacketList* packet_list); + // Static method returning true if |timestamp| is older than |timestamp_limit| + // but less than |horizon_samples| behind |timestamp_limit|. For instance, + // with timestamp_limit = 100 and horizon_samples = 10, a timestamp in the + // range (90, 100) is considered obsolete, and will yield true. + // Setting |horizon_samples| to 0 is the same as setting it to 2^31, i.e., + // half the 32-bit timestamp range. + static bool IsObsoleteTimestamp(uint32_t timestamp, + uint32_t timestamp_limit, + uint32_t horizon_samples) { + return IsNewerTimestamp(timestamp_limit, timestamp) && + (horizon_samples == 0 || + IsNewerTimestamp(timestamp, timestamp_limit - horizon_samples)); + } + private: size_t max_number_of_packets_; PacketList buffer_; diff --git a/modules/audio_coding/neteq/packet_buffer_unittest.cc b/modules/audio_coding/neteq/packet_buffer_unittest.cc index 478328cb..dc8b68c3 100644 --- a/modules/audio_coding/neteq/packet_buffer_unittest.cc +++ b/modules/audio_coding/neteq/packet_buffer_unittest.cc @@ -391,7 +391,7 @@ TEST(PacketBuffer, Failures) { EXPECT_EQ(NULL, buffer->NextRtpHeader()); EXPECT_EQ(NULL, buffer->GetNextPacket(NULL)); EXPECT_EQ(PacketBuffer::kBufferEmpty, buffer->DiscardNextPacket()); - EXPECT_EQ(0, buffer->DiscardOldPackets(0)); // 0 packets discarded. + EXPECT_EQ(0, buffer->DiscardAllOldPackets(0)); // 0 packets discarded. // Insert one packet to make the buffer non-empty. packet = gen.NextPacket(payload_len); @@ -513,4 +513,61 @@ TEST(PacketBuffer, DeleteAllPackets) { EXPECT_FALSE(PacketBuffer::DeleteFirstPacket(&list)); } +namespace { +void TestIsObsoleteTimestamp(uint32_t limit_timestamp) { + // Check with zero horizon, which implies that the horizon is at 2^31, i.e., + // half the timestamp range. + static const uint32_t kZeroHorizon = 0; + static const uint32_t k2Pow31Minus1 = 0x7FFFFFFF; + // Timestamp on the limit is not old. + EXPECT_FALSE(PacketBuffer::IsObsoleteTimestamp( + limit_timestamp, limit_timestamp, kZeroHorizon)); + // 1 sample behind is old. + EXPECT_TRUE(PacketBuffer::IsObsoleteTimestamp( + limit_timestamp - 1, limit_timestamp, kZeroHorizon)); + // 2^31 - 1 samples behind is old. + EXPECT_TRUE(PacketBuffer::IsObsoleteTimestamp( + limit_timestamp - k2Pow31Minus1, limit_timestamp, kZeroHorizon)); + // 1 sample ahead is not old. + EXPECT_FALSE(PacketBuffer::IsObsoleteTimestamp( + limit_timestamp + 1, limit_timestamp, kZeroHorizon)); + // 2^31 samples ahead is not old. + EXPECT_FALSE(PacketBuffer::IsObsoleteTimestamp( + limit_timestamp + (1 << 31), limit_timestamp, kZeroHorizon)); + + // Fixed horizon at 10 samples. + static const uint32_t kHorizon = 10; + // Timestamp on the limit is not old. + EXPECT_FALSE(PacketBuffer::IsObsoleteTimestamp( + limit_timestamp, limit_timestamp, kHorizon)); + // 1 sample behind is old. + EXPECT_TRUE(PacketBuffer::IsObsoleteTimestamp( + limit_timestamp - 1, limit_timestamp, kHorizon)); + // 9 samples behind is old. + EXPECT_TRUE(PacketBuffer::IsObsoleteTimestamp( + limit_timestamp - 9, limit_timestamp, kHorizon)); + // 10 samples behind is not old. + EXPECT_FALSE(PacketBuffer::IsObsoleteTimestamp( + limit_timestamp - 10, limit_timestamp, kHorizon)); + // 2^31 - 1 samples behind is not old. + EXPECT_FALSE(PacketBuffer::IsObsoleteTimestamp( + limit_timestamp - k2Pow31Minus1, limit_timestamp, kHorizon)); + // 1 sample ahead is not old. + EXPECT_FALSE(PacketBuffer::IsObsoleteTimestamp( + limit_timestamp + 1, limit_timestamp, kHorizon)); + // 2^31 samples ahead is not old. + EXPECT_FALSE(PacketBuffer::IsObsoleteTimestamp( + limit_timestamp + (1 << 31), limit_timestamp, kHorizon)); +} +} // namespace + +// Test the IsObsoleteTimestamp method with different limit timestamps. +TEST(PacketBuffer, IsObsoleteTimestamp) { + TestIsObsoleteTimestamp(0); + TestIsObsoleteTimestamp(1); + TestIsObsoleteTimestamp(0xFFFFFFFF); // -1 in uint32_t. + TestIsObsoleteTimestamp(0x80000000); // 2^31. + TestIsObsoleteTimestamp(0x80000001); // 2^31 + 1. + TestIsObsoleteTimestamp(0x7FFFFFFF); // 2^31 - 1. +} } // namespace webrtc diff --git a/modules/audio_coding/neteq/test/RTPencode.cc b/modules/audio_coding/neteq/test/RTPencode.cc index ab338a73..b73e70e5 100644 --- a/modules/audio_coding/neteq/test/RTPencode.cc +++ b/modules/audio_coding/neteq/test/RTPencode.cc @@ -235,9 +235,6 @@ WebRtcVadInst *VAD_inst[2]; #ifdef CODEC_CELT_32 CELT_encinst_t *CELT32enc_inst[2]; #endif -#ifdef CODEC_G711 - void *G711state[2]={NULL, NULL}; -#endif int main(int argc, char* argv[]) @@ -1602,12 +1599,12 @@ int NetEQTest_encode(int coder, int16_t *indata, int frameLen, unsigned char * e /* Encode with the selected coder type */ if (coder==webrtc::kDecoderPCMu) { /*g711 u-law */ #ifdef CODEC_G711 - cdlen = WebRtcG711_EncodeU(G711state[k], indata, frameLen, (int16_t*) encoded); + cdlen = WebRtcG711_EncodeU(indata, frameLen, (int16_t*) encoded); #endif } else if (coder==webrtc::kDecoderPCMa) { /*g711 A-law */ #ifdef CODEC_G711 - cdlen = WebRtcG711_EncodeA(G711state[k], indata, frameLen, (int16_t*) encoded); + cdlen = WebRtcG711_EncodeA(indata, frameLen, (int16_t*) encoded); } #endif #ifdef CODEC_PCM16B diff --git a/modules/audio_coding/neteq/tools/neteq_rtpplay.cc b/modules/audio_coding/neteq/tools/neteq_rtpplay.cc index 46ef3d08..ef2c0b6b 100644 --- a/modules/audio_coding/neteq/tools/neteq_rtpplay.cc +++ b/modules/audio_coding/neteq/tools/neteq_rtpplay.cc @@ -92,6 +92,9 @@ static const bool isac_dummy = DEFINE_int32(isac_swb, 104, "RTP payload type for iSAC-swb (32 kHz)"); static const bool isac_swb_dummy = google::RegisterFlagValidator(&FLAGS_isac_swb, &ValidatePayloadType); +DEFINE_int32(opus, 111, "RTP payload type for Opus"); +static const bool opus_dummy = + google::RegisterFlagValidator(&FLAGS_opus, &ValidatePayloadType); DEFINE_int32(pcm16b, 93, "RTP payload type for PCM16b-nb (8 kHz)"); static const bool pcm16b_dummy = google::RegisterFlagValidator(&FLAGS_pcm16b, &ValidatePayloadType); @@ -286,8 +289,24 @@ int main(int argc, char* argv[]) { static_cast<int>(payload_len), packet->time_ms() * sample_rate_hz / 1000); if (error != NetEq::kOK) { - std::cerr << "InsertPacket returned error code " << neteq->LastError() - << std::endl; + if (neteq->LastError() == NetEq::kUnknownRtpPayloadType) { + std::cerr << "RTP Payload type " + << static_cast<int>(rtp_header.header.payloadType) + << " is unknown." << std::endl; + std::cerr << "Use --codec_map to view default mapping." << std::endl; + std::cerr << "Use --helpshort for information on how to make custom " + "mappings." << std::endl; + } else { + std::cerr << "InsertPacket returned error code " << neteq->LastError() + << std::endl; + std::cerr << "Header data:" << std::endl; + std::cerr << " PT = " + << static_cast<int>(rtp_header.header.payloadType) + << std::endl; + std::cerr << " SN = " << rtp_header.header.sequenceNumber + << std::endl; + std::cerr << " TS = " << rtp_header.header.timestamp << std::endl; + } } // Get next packet from file. @@ -366,6 +385,8 @@ std::string CodecName(webrtc::NetEqDecoder codec) { return "iSAC"; case webrtc::kDecoderISACswb: return "iSAC-swb (32 kHz)"; + case webrtc::kDecoderOpus: + return "Opus"; case webrtc::kDecoderPCM16B: return "PCM16b-nb (8 kHz)"; case webrtc::kDecoderPCM16Bwb: @@ -428,6 +449,12 @@ void RegisterPayloadTypes(NetEq* neteq) { " as " << CodecName(webrtc::kDecoderISACswb).c_str() << std::endl; exit(1); } + error = neteq->RegisterPayloadType(webrtc::kDecoderOpus, FLAGS_opus); + if (error) { + std::cerr << "Cannot register payload type " << FLAGS_opus << " as " + << CodecName(webrtc::kDecoderOpus).c_str() << std::endl; + exit(1); + } error = neteq->RegisterPayloadType(webrtc::kDecoderPCM16B, FLAGS_pcm16b); if (error) { std::cerr << "Cannot register payload type " << FLAGS_pcm16b << @@ -514,6 +541,8 @@ void PrintCodecMapping() { std::endl; std::cout << CodecName(webrtc::kDecoderISACswb).c_str() << ": " << FLAGS_isac_swb << std::endl; + std::cout << CodecName(webrtc::kDecoderOpus).c_str() << ": " << FLAGS_opus + << std::endl; std::cout << CodecName(webrtc::kDecoderPCM16B).c_str() << ": " << FLAGS_pcm16b << std::endl; std::cout << CodecName(webrtc::kDecoderPCM16Bwb).c_str() << ": " << @@ -637,8 +666,8 @@ int CodecSampleRate(uint8_t payload_type) { payload_type == FLAGS_pcm16b_swb32 || payload_type == FLAGS_cn_swb32) { return 32000; - } else if (payload_type == FLAGS_pcm16b_swb48 || - payload_type == FLAGS_cn_swb48) { + } else if (payload_type == FLAGS_opus || payload_type == FLAGS_pcm16b_swb48 || + payload_type == FLAGS_cn_swb48) { return 48000; } else if (payload_type == FLAGS_avt || payload_type == FLAGS_red) { diff --git a/modules/audio_coding/neteq/tools/resample_input_audio_file.cc b/modules/audio_coding/neteq/tools/resample_input_audio_file.cc new file mode 100644 index 00000000..f391466c --- /dev/null +++ b/modules/audio_coding/neteq/tools/resample_input_audio_file.cc @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/audio_coding/neteq/tools/resample_input_audio_file.h" + +#include "webrtc/base/checks.h" +#include "webrtc/system_wrappers/interface/scoped_ptr.h" + +namespace webrtc { +namespace test { + +bool ResampleInputAudioFile::Read(size_t samples, + int output_rate_hz, + int16_t* destination) { + const size_t samples_to_read = samples * file_rate_hz_ / output_rate_hz; + CHECK_EQ(samples_to_read * output_rate_hz, samples * file_rate_hz_) + << "Frame size and sample rates don't add up to an integer."; + scoped_ptr<int16_t[]> temp_destination(new int16_t[samples_to_read]); + if (!InputAudioFile::Read(samples_to_read, temp_destination.get())) + return false; + resampler_.ResetIfNeeded( + file_rate_hz_, output_rate_hz, kResamplerSynchronous); + int output_length = 0; + CHECK_EQ(resampler_.Push(temp_destination.get(), + static_cast<int>(samples_to_read), + destination, + static_cast<int>(samples), + output_length), + 0); + CHECK_EQ(static_cast<int>(samples), output_length); + return true; +} + +} // namespace test +} // namespace webrtc diff --git a/modules/audio_coding/neteq/tools/resample_input_audio_file.h b/modules/audio_coding/neteq/tools/resample_input_audio_file.h new file mode 100644 index 00000000..8c028005 --- /dev/null +++ b/modules/audio_coding/neteq/tools/resample_input_audio_file.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_AUDIO_CODING_NETEQ_TOOLS_RESAMPLE_INPUT_AUDIO_FILE_H_ +#define WEBRTC_MODULES_AUDIO_CODING_NETEQ_TOOLS_RESAMPLE_INPUT_AUDIO_FILE_H_ + +#include <string> + +#include "webrtc/base/constructormagic.h" +#include "webrtc/common_audio/resampler/include/resampler.h" +#include "webrtc/modules/audio_coding/neteq/tools/input_audio_file.h" +#include "webrtc/typedefs.h" + +namespace webrtc { +namespace test { + +// Class for handling a looping input audio file with resampling. +class ResampleInputAudioFile : public InputAudioFile { + public: + ResampleInputAudioFile(const std::string file_name, int file_rate_hz) + : InputAudioFile(file_name), file_rate_hz_(file_rate_hz) {} + + bool Read(size_t samples, int output_rate_hz, int16_t* destination); + + private: + const int file_rate_hz_; + Resampler resampler_; + DISALLOW_COPY_AND_ASSIGN(ResampleInputAudioFile); +}; + +} // namespace test +} // namespace webrtc +#endif // WEBRTC_MODULES_AUDIO_CODING_NETEQ_TOOLS_RESAMPLE_INPUT_AUDIO_FILE_H_ |