diff --git a/webrtc/modules/audio_coding/codecs/audio_encoder.h b/webrtc/modules/audio_coding/codecs/audio_encoder.h index c0ec2109c..20ac8b922 100644 --- a/webrtc/modules/audio_coding/codecs/audio_encoder.h +++ b/webrtc/modules/audio_coding/codecs/audio_encoder.h @@ -74,6 +74,14 @@ class AudioEncoder { virtual int SampleRateHz() const = 0; virtual int NumChannels() const = 0; + // Return the maximum number of bytes that can be produced by the encoder + // at each Encode() call. The caller can use the return value to determine + // the size of the buffer that needs to be allocated. This value is allowed + // to depend on encoder parameters like bitrate, frame size etc., so if + // any of these change, the caller of Encode() is responsible for checking + // that the buffer is large enough by calling MaxEncodedBytes() again. + virtual size_t MaxEncodedBytes() const = 0; + // Returns the rate with which the RTP timestamps are updated. By default, // this is the same as sample_rate_hz(). virtual int RtpTimestampRateHz() const; diff --git a/webrtc/modules/audio_coding/codecs/cng/audio_encoder_cng.cc b/webrtc/modules/audio_coding/codecs/cng/audio_encoder_cng.cc index 14b210c3e..d7c1ea013 100644 --- a/webrtc/modules/audio_coding/codecs/cng/audio_encoder_cng.cc +++ b/webrtc/modules/audio_coding/codecs/cng/audio_encoder_cng.cc @@ -10,10 +10,17 @@ #include "webrtc/modules/audio_coding/codecs/cng/include/audio_encoder_cng.h" +#include #include namespace webrtc { +namespace { + +const int kMaxFrameSizeMs = 60; + +} // namespace + AudioEncoderCng::Config::Config() : num_channels(1), payload_type(13), @@ -77,6 +84,13 @@ int AudioEncoderCng::NumChannels() const { return 1; } +size_t AudioEncoderCng::MaxEncodedBytes() const { + const size_t max_encoded_bytes_active = speech_encoder_->MaxEncodedBytes(); + const size_t max_encoded_bytes_passive = + rtc::CheckedDivExact(kMaxFrameSizeMs, 10) * SamplesPer10msFrame(); + return std::max(max_encoded_bytes_active, max_encoded_bytes_passive); +} + int AudioEncoderCng::Num10MsFramesInNextPacket() const { return speech_encoder_->Num10MsFramesInNextPacket(); } @@ -114,8 +128,9 @@ void AudioEncoderCng::EncodeInternal(uint32_t rtp_timestamp, if (frames_in_buffer_ < speech_encoder_->Num10MsFramesInNextPacket()) { return; } - CHECK_LE(frames_in_buffer_, 6) - << "Frame size cannot be larger than 60 ms when using VAD/CNG."; + CHECK_LE(frames_in_buffer_ * 10, kMaxFrameSizeMs) + << "Frame size cannot be larger than " << kMaxFrameSizeMs + << " ms when using VAD/CNG."; const size_t samples_per_10ms_frame = 10 * SampleRateHz() / 1000; CHECK_EQ(speech_buffer_.size(), static_cast(frames_in_buffer_) * samples_per_10ms_frame); @@ -146,11 +161,7 @@ void AudioEncoderCng::EncodeInternal(uint32_t rtp_timestamp, switch (activity) { case Vad::kPassive: { - EncodePassive(encoded, &info->encoded_bytes); - info->encoded_timestamp = first_timestamp_in_buffer_; - info->payload_type = cng_payload_type_; - info->send_even_if_empty = true; - info->speech = false; + EncodePassive(max_encoded_bytes, encoded, info); last_frame_active_ = false; break; } @@ -169,10 +180,13 @@ void AudioEncoderCng::EncodeInternal(uint32_t rtp_timestamp, frames_in_buffer_ = 0; } -void AudioEncoderCng::EncodePassive(uint8_t* encoded, size_t* encoded_bytes) { +void AudioEncoderCng::EncodePassive(size_t max_encoded_bytes, + uint8_t* encoded, + EncodedInfo* info) { bool force_sid = last_frame_active_; bool output_produced = false; - const size_t samples_per_10ms_frame = 10 * SampleRateHz() / 1000; + const size_t samples_per_10ms_frame = SamplesPer10msFrame(); + CHECK_GE(max_encoded_bytes, frames_in_buffer_ * samples_per_10ms_frame); for (int i = 0; i < frames_in_buffer_; ++i) { int16_t encoded_bytes_tmp = 0; CHECK_GE(WebRtcCng_Encode(cng_inst_.get(), @@ -181,17 +195,21 @@ void AudioEncoderCng::EncodePassive(uint8_t* encoded, size_t* encoded_bytes) { encoded, &encoded_bytes_tmp, force_sid), 0); if (encoded_bytes_tmp > 0) { CHECK(!output_produced); - *encoded_bytes = static_cast(encoded_bytes_tmp); + info->encoded_bytes = static_cast(encoded_bytes_tmp); output_produced = true; force_sid = false; } } + info->encoded_timestamp = first_timestamp_in_buffer_; + info->payload_type = cng_payload_type_; + info->send_even_if_empty = true; + info->speech = false; } void AudioEncoderCng::EncodeActive(size_t max_encoded_bytes, uint8_t* encoded, EncodedInfo* info) { - const size_t samples_per_10ms_frame = 10 * SampleRateHz() / 1000; + const size_t samples_per_10ms_frame = SamplesPer10msFrame(); for (int i = 0; i < frames_in_buffer_; ++i) { speech_encoder_->Encode(first_timestamp_in_buffer_, &speech_buffer_[i * samples_per_10ms_frame], @@ -203,4 +221,8 @@ void AudioEncoderCng::EncodeActive(size_t max_encoded_bytes, } } +size_t AudioEncoderCng::SamplesPer10msFrame() const { + return rtc::CheckedDivExact(10 * SampleRateHz(), 1000); +} + } // namespace webrtc diff --git a/webrtc/modules/audio_coding/codecs/cng/audio_encoder_cng_unittest.cc b/webrtc/modules/audio_coding/codecs/cng/audio_encoder_cng_unittest.cc index c5d436eb8..5dfa4d545 100644 --- a/webrtc/modules/audio_coding/codecs/cng/audio_encoder_cng_unittest.cc +++ b/webrtc/modules/audio_coding/codecs/cng/audio_encoder_cng_unittest.cc @@ -8,6 +8,8 @@ * be found in the AUTHORS file in the root of the source tree. */ +#include + #include "testing/gtest/include/gtest/gtest.h" #include "webrtc/base/scoped_ptr.h" #include "webrtc/common_audio/vad/mock/mock_vad.h" @@ -23,7 +25,7 @@ using ::testing::Invoke; namespace webrtc { namespace { -static const size_t kMaxEncodedBytes = 1000; +static const size_t kMockMaxEncodedBytes = 1000; static const size_t kMaxNumSamples = 48 * 10 * 2; // 10 ms @ 48 kHz stereo. static const size_t kMockReturnEncodedBytes = 17; static const int kCngPayloadType = 18; @@ -36,7 +38,6 @@ class AudioEncoderCngTest : public ::testing::Test { timestamp_(4711), num_audio_samples_10ms_(0), sample_rate_hz_(8000) { - memset(encoded_, 0, kMaxEncodedBytes); memset(audio_, 0, kMaxNumSamples * 2); config_.speech_encoder = &mock_encoder_; EXPECT_CALL(mock_encoder_, NumChannels()).WillRepeatedly(Return(1)); @@ -66,14 +67,17 @@ class AudioEncoderCngTest : public ::testing::Test { // is not too small. The return value does not matter that much, as long as // it is smaller than 10. EXPECT_CALL(mock_encoder_, Max10MsFramesInAPacket()).WillOnce(Return(1)); + EXPECT_CALL(mock_encoder_, MaxEncodedBytes()) + .WillRepeatedly(Return(kMockMaxEncodedBytes)); cng_.reset(new AudioEncoderCng(config_)); + encoded_.resize(cng_->MaxEncodedBytes(), 0); } void Encode() { ASSERT_TRUE(cng_) << "Must call CreateCng() first."; encoded_info_ = AudioEncoder::EncodedInfo(); cng_->Encode(timestamp_, audio_, num_audio_samples_10ms_, - kMaxEncodedBytes, encoded_, &encoded_info_); + encoded_.size(), &encoded_[0], &encoded_info_); timestamp_ += num_audio_samples_10ms_; } @@ -182,7 +186,7 @@ class AudioEncoderCngTest : public ::testing::Test { uint32_t timestamp_; int16_t audio_[kMaxNumSamples]; size_t num_audio_samples_10ms_; - uint8_t encoded_[kMaxEncodedBytes]; + std::vector encoded_; AudioEncoder::EncodedInfo encoded_info_; int sample_rate_hz_; }; diff --git a/webrtc/modules/audio_coding/codecs/cng/include/audio_encoder_cng.h b/webrtc/modules/audio_coding/codecs/cng/include/audio_encoder_cng.h index 1d3c2f3fa..cc0165054 100644 --- a/webrtc/modules/audio_coding/codecs/cng/include/audio_encoder_cng.h +++ b/webrtc/modules/audio_coding/codecs/cng/include/audio_encoder_cng.h @@ -48,6 +48,7 @@ class AudioEncoderCng final : public AudioEncoder { int SampleRateHz() const override; int NumChannels() const override; + size_t MaxEncodedBytes() const override; int RtpTimestampRateHz() const override; int Num10MsFramesInNextPacket() const override; int Max10MsFramesInAPacket() const override; @@ -68,11 +69,13 @@ class AudioEncoderCng final : public AudioEncoder { inline void operator()(CNG_enc_inst* ptr) const { WebRtcCng_FreeEnc(ptr); } }; - void EncodePassive(uint8_t* encoded, size_t* encoded_bytes); - + void EncodePassive(size_t max_encoded_bytes, + uint8_t* encoded, + EncodedInfo* info); void EncodeActive(size_t max_encoded_bytes, uint8_t* encoded, EncodedInfo* info); + size_t SamplesPer10msFrame() const; AudioEncoder* speech_encoder_; const int cng_payload_type_; diff --git a/webrtc/modules/audio_coding/codecs/g711/audio_encoder_pcm.cc b/webrtc/modules/audio_coding/codecs/g711/audio_encoder_pcm.cc index 284a086db..5c43a85cb 100644 --- a/webrtc/modules/audio_coding/codecs/g711/audio_encoder_pcm.cc +++ b/webrtc/modules/audio_coding/codecs/g711/audio_encoder_pcm.cc @@ -49,9 +49,15 @@ AudioEncoderPcm::~AudioEncoderPcm() { int AudioEncoderPcm::SampleRateHz() const { return sample_rate_hz_; } + int AudioEncoderPcm::NumChannels() const { return num_channels_; } + +size_t AudioEncoderPcm::MaxEncodedBytes() const { + return full_frame_samples_; +} + int AudioEncoderPcm::Num10MsFramesInNextPacket() const { return num_10ms_frames_per_packet_; } @@ -72,11 +78,12 @@ void AudioEncoderPcm::EncodeInternal(uint32_t rtp_timestamp, for (int i = 0; i < num_samples; ++i) { speech_buffer_.push_back(audio[i]); } - if (speech_buffer_.size() < static_cast(full_frame_samples_)) { + if (speech_buffer_.size() < full_frame_samples_) { info->encoded_bytes = 0; return; } - CHECK_EQ(speech_buffer_.size(), static_cast(full_frame_samples_)); + CHECK_EQ(speech_buffer_.size(), full_frame_samples_); + CHECK_GE(max_encoded_bytes, full_frame_samples_); int16_t ret = EncodeCall(&speech_buffer_[0], full_frame_samples_, encoded); CHECK_GE(ret, 0); speech_buffer_.clear(); diff --git a/webrtc/modules/audio_coding/codecs/g711/include/audio_encoder_pcm.h b/webrtc/modules/audio_coding/codecs/g711/include/audio_encoder_pcm.h index 3967c5e00..e64bcea27 100644 --- a/webrtc/modules/audio_coding/codecs/g711/include/audio_encoder_pcm.h +++ b/webrtc/modules/audio_coding/codecs/g711/include/audio_encoder_pcm.h @@ -34,6 +34,7 @@ class AudioEncoderPcm : public AudioEncoder { int SampleRateHz() const override; int NumChannels() const override; + size_t MaxEncodedBytes() const override; int Num10MsFramesInNextPacket() const override; int Max10MsFramesInAPacket() const override; @@ -55,7 +56,7 @@ class AudioEncoderPcm : public AudioEncoder { const int num_channels_; const int payload_type_; const int num_10ms_frames_per_packet_; - const int16_t full_frame_samples_; + const size_t full_frame_samples_; std::vector speech_buffer_; uint32_t first_timestamp_in_buffer_; }; diff --git a/webrtc/modules/audio_coding/codecs/g722/audio_encoder_g722.cc b/webrtc/modules/audio_coding/codecs/g722/audio_encoder_g722.cc index f02bda03a..bbdcb1da7 100644 --- a/webrtc/modules/audio_coding/codecs/g722/audio_encoder_g722.cc +++ b/webrtc/modules/audio_coding/codecs/g722/audio_encoder_g722.cc @@ -54,17 +54,25 @@ AudioEncoderG722::~AudioEncoderG722() {} int AudioEncoderG722::SampleRateHz() const { return kSampleRateHz; } + int AudioEncoderG722::RtpTimestampRateHz() const { // The RTP timestamp rate for G.722 is 8000 Hz, even though it is a 16 kHz // codec. return kSampleRateHz / 2; } + int AudioEncoderG722::NumChannels() const { return num_channels_; } + +size_t AudioEncoderG722::MaxEncodedBytes() const { + return static_cast(SamplesPerChannel() / 2 * num_channels_); +} + int AudioEncoderG722::Num10MsFramesInNextPacket() const { return num_10ms_frames_per_packet_; } + int AudioEncoderG722::Max10MsFramesInAPacket() const { return num_10ms_frames_per_packet_; } @@ -74,10 +82,7 @@ void AudioEncoderG722::EncodeInternal(uint32_t rtp_timestamp, size_t max_encoded_bytes, uint8_t* encoded, EncodedInfo* info) { - const int samples_per_channel = - kSampleRateHz / 100 * num_10ms_frames_per_packet_; - CHECK_GE(max_encoded_bytes, - static_cast(samples_per_channel) / 2 * num_channels_); + CHECK_GE(max_encoded_bytes, MaxEncodedBytes()); if (num_10ms_frames_buffered_ == 0) first_timestamp_in_buffer_ = rtp_timestamp; @@ -97,6 +102,7 @@ void AudioEncoderG722::EncodeInternal(uint32_t rtp_timestamp, // Encode each channel separately. CHECK_EQ(num_10ms_frames_buffered_, num_10ms_frames_per_packet_); num_10ms_frames_buffered_ = 0; + const int samples_per_channel = SamplesPerChannel(); for (int i = 0; i < num_channels_; ++i) { const int encoded = WebRtcG722_Encode( encoders_[i].encoder, encoders_[i].speech_buffer.get(), @@ -123,4 +129,8 @@ void AudioEncoderG722::EncodeInternal(uint32_t rtp_timestamp, info->payload_type = payload_type_; } +int AudioEncoderG722::SamplesPerChannel() const { + return kSampleRateHz / 100 * num_10ms_frames_per_packet_; +} + } // namespace webrtc diff --git a/webrtc/modules/audio_coding/codecs/g722/include/audio_encoder_g722.h b/webrtc/modules/audio_coding/codecs/g722/include/audio_encoder_g722.h index 229c06e46..81b44d625 100644 --- a/webrtc/modules/audio_coding/codecs/g722/include/audio_encoder_g722.h +++ b/webrtc/modules/audio_coding/codecs/g722/include/audio_encoder_g722.h @@ -31,8 +31,9 @@ class AudioEncoderG722 : public AudioEncoder { ~AudioEncoderG722() override; int SampleRateHz() const override; - int RtpTimestampRateHz() const override; int NumChannels() const override; + size_t MaxEncodedBytes() const override; + int RtpTimestampRateHz() const override; int Num10MsFramesInNextPacket() const override; int Max10MsFramesInAPacket() const override; @@ -53,6 +54,8 @@ class AudioEncoderG722 : public AudioEncoder { ~EncoderState(); }; + int SamplesPerChannel() const; + const int num_channels_; const int payload_type_; const int num_10ms_frames_per_packet_; diff --git a/webrtc/modules/audio_coding/codecs/ilbc/audio_encoder_ilbc.cc b/webrtc/modules/audio_coding/codecs/ilbc/audio_encoder_ilbc.cc index b93934121..1e85a077f 100644 --- a/webrtc/modules/audio_coding/codecs/ilbc/audio_encoder_ilbc.cc +++ b/webrtc/modules/audio_coding/codecs/ilbc/audio_encoder_ilbc.cc @@ -46,12 +46,19 @@ AudioEncoderIlbc::~AudioEncoderIlbc() { int AudioEncoderIlbc::SampleRateHz() const { return kSampleRateHz; } + int AudioEncoderIlbc::NumChannels() const { return 1; } + +size_t AudioEncoderIlbc::MaxEncodedBytes() const { + return RequiredOutputSizeBytes(); +} + int AudioEncoderIlbc::Num10MsFramesInNextPacket() const { return num_10ms_frames_per_packet_; } + int AudioEncoderIlbc::Max10MsFramesInAPacket() const { return num_10ms_frames_per_packet_; } @@ -61,24 +68,7 @@ void AudioEncoderIlbc::EncodeInternal(uint32_t rtp_timestamp, size_t max_encoded_bytes, uint8_t* encoded, EncodedInfo* info) { - size_t expected_output_len; - switch (num_10ms_frames_per_packet_) { - case 2: - expected_output_len = 38; - break; - case 3: - expected_output_len = 50; - break; - case 4: - expected_output_len = 2 * 38; - break; - case 6: - expected_output_len = 2 * 50; - break; - default: - FATAL(); - } - DCHECK_GE(max_encoded_bytes, expected_output_len); + DCHECK_GE(max_encoded_bytes, RequiredOutputSizeBytes()); // Save timestamp if starting a new packet. if (num_10ms_frames_buffered_ == 0) @@ -105,10 +95,20 @@ void AudioEncoderIlbc::EncodeInternal(uint32_t rtp_timestamp, kSampleRateHz / 100 * num_10ms_frames_per_packet_, encoded); CHECK_GE(output_len, 0); - DCHECK_EQ(output_len, static_cast(expected_output_len)); info->encoded_bytes = output_len; + DCHECK_EQ(info->encoded_bytes, RequiredOutputSizeBytes()); info->encoded_timestamp = first_timestamp_in_buffer_; info->payload_type = payload_type_; } +size_t AudioEncoderIlbc::RequiredOutputSizeBytes() const { + switch (num_10ms_frames_per_packet_) { + case 2: return 38; + case 3: return 50; + case 4: return 2 * 38; + case 6: return 2 * 50; + default: FATAL(); + } +} + } // namespace webrtc diff --git a/webrtc/modules/audio_coding/codecs/ilbc/interface/audio_encoder_ilbc.h b/webrtc/modules/audio_coding/codecs/ilbc/interface/audio_encoder_ilbc.h index fc3aa0d4b..a5378d1ed 100644 --- a/webrtc/modules/audio_coding/codecs/ilbc/interface/audio_encoder_ilbc.h +++ b/webrtc/modules/audio_coding/codecs/ilbc/interface/audio_encoder_ilbc.h @@ -33,6 +33,7 @@ class AudioEncoderIlbc : public AudioEncoder { int SampleRateHz() const override; int NumChannels() const override; + size_t MaxEncodedBytes() const override; int Num10MsFramesInNextPacket() const override; int Max10MsFramesInAPacket() const override; @@ -44,6 +45,8 @@ class AudioEncoderIlbc : public AudioEncoder { EncodedInfo* info) override; private: + size_t RequiredOutputSizeBytes() const; + static const int kMaxSamplesPerPacket = 480; const int payload_type_; const int num_10ms_frames_per_packet_; diff --git a/webrtc/modules/audio_coding/codecs/isac/audio_encoder_isac_t.h b/webrtc/modules/audio_coding/codecs/isac/audio_encoder_isac_t.h index c918eafc4..88ead9340 100644 --- a/webrtc/modules/audio_coding/codecs/isac/audio_encoder_isac_t.h +++ b/webrtc/modules/audio_coding/codecs/isac/audio_encoder_isac_t.h @@ -71,6 +71,7 @@ class AudioEncoderDecoderIsacT : public AudioEncoder, public AudioDecoder { // AudioEncoder public methods. int SampleRateHz() const override; int NumChannels() const override; + size_t MaxEncodedBytes() const override; int Num10MsFramesInNextPacket() const override; int Max10MsFramesInAPacket() const override; diff --git a/webrtc/modules/audio_coding/codecs/isac/audio_encoder_isac_t_impl.h b/webrtc/modules/audio_coding/codecs/isac/audio_encoder_isac_t_impl.h index 024efa141..b9c3e98e8 100644 --- a/webrtc/modules/audio_coding/codecs/isac/audio_encoder_isac_t_impl.h +++ b/webrtc/modules/audio_coding/codecs/isac/audio_encoder_isac_t_impl.h @@ -184,6 +184,11 @@ int AudioEncoderDecoderIsacT::NumChannels() const { return 1; } +template +size_t AudioEncoderDecoderIsacT::MaxEncodedBytes() const { + return kSufficientEncodeBufferSizeBytes; +} + template int AudioEncoderDecoderIsacT::Num10MsFramesInNextPacket() const { CriticalSectionScoped cs(state_lock_.get()); diff --git a/webrtc/modules/audio_coding/codecs/isac/main/source/audio_encoder_isac_red_unittest.cc b/webrtc/modules/audio_coding/codecs/isac/main/source/audio_encoder_isac_red_unittest.cc index 4bc616605..22949a927 100644 --- a/webrtc/modules/audio_coding/codecs/isac/main/source/audio_encoder_isac_red_unittest.cc +++ b/webrtc/modules/audio_coding/codecs/isac/main/source/audio_encoder_isac_red_unittest.cc @@ -30,12 +30,11 @@ TEST(AudioEncoderIsacRedTest, CompareRedAndNoRed) { double r = rand(); // NOLINT(runtime/threadsafe_fn) input[i] = (r / RAND_MAX) * 2000 - 1000; } - static const size_t kMaxEncodedSizeBytes = 1000; - uint8_t encoded[kMaxEncodedSizeBytes]; - uint8_t red_encoded[kMaxEncodedSizeBytes]; AudioEncoderDecoderIsac::Config config; config.sample_rate_hz = kSampleRateHz; AudioEncoderDecoderIsac isac_encoder(config); + size_t max_encoded_bytes = isac_encoder.MaxEncodedBytes(); + rtc::scoped_ptr encoded(new uint8_t[max_encoded_bytes]); AudioEncoderDecoderIsac::Config red_config; red_config.sample_rate_hz = kSampleRateHz; red_config.red_payload_type = kRedPayloadType; @@ -43,6 +42,8 @@ TEST(AudioEncoderIsacRedTest, CompareRedAndNoRed) { ASSERT_NE(red_config.red_payload_type, red_config.payload_type) << "iSAC and RED payload types must be different."; AudioEncoderDecoderIsac isac_red_encoder(red_config); + size_t max_red_encoded_bytes = isac_red_encoder.MaxEncodedBytes(); + rtc::scoped_ptr red_encoded(new uint8_t[max_red_encoded_bytes]); AudioEncoder::EncodedInfo info, red_info; // Note that we are not expecting any output from the redundant encoder until @@ -52,10 +53,11 @@ TEST(AudioEncoderIsacRedTest, CompareRedAndNoRed) { EXPECT_EQ(0u, red_info.encoded_bytes); EXPECT_EQ(0u, red_info.redundant.size()); const uint32_t timestamp = static_cast(i); - isac_encoder.Encode(timestamp, input, k10MsSamples, kMaxEncodedSizeBytes, - encoded, &info); + isac_encoder.Encode(timestamp, input, k10MsSamples, max_encoded_bytes, + encoded.get(), &info); isac_red_encoder.Encode(timestamp, input, k10MsSamples, - kMaxEncodedSizeBytes, red_encoded, &red_info); + max_red_encoded_bytes, red_encoded.get(), + &red_info); } EXPECT_GT(info.encoded_bytes, 0u) << "Regular codec did not produce any output"; @@ -65,7 +67,7 @@ TEST(AudioEncoderIsacRedTest, CompareRedAndNoRed) { ASSERT_EQ(info.encoded_bytes, red_info.redundant[0].encoded_bytes) << "Primary payload should be same length as non-redundant payload"; // Check that |encoded| and the primary part of |red_encoded| are identical. - EXPECT_EQ(0, memcmp(encoded, red_encoded, info.encoded_bytes)); + EXPECT_EQ(0, memcmp(encoded.get(), red_encoded.get(), info.encoded_bytes)); EXPECT_GT(red_info.redundant[0].encoded_bytes, red_info.redundant[1].encoded_bytes) << "Redundant payload should be smaller than primary"; diff --git a/webrtc/modules/audio_coding/codecs/mock/mock_audio_encoder.h b/webrtc/modules/audio_coding/codecs/mock/mock_audio_encoder.h index 70ee49745..7425e9a18 100644 --- a/webrtc/modules/audio_coding/codecs/mock/mock_audio_encoder.h +++ b/webrtc/modules/audio_coding/codecs/mock/mock_audio_encoder.h @@ -23,6 +23,7 @@ class MockAudioEncoder : public AudioEncoder { MOCK_METHOD0(Die, void()); MOCK_CONST_METHOD0(SampleRateHz, int()); MOCK_CONST_METHOD0(NumChannels, int()); + MOCK_CONST_METHOD0(MaxEncodedBytes, size_t()); MOCK_CONST_METHOD0(Num10MsFramesInNextPacket, int()); MOCK_CONST_METHOD0(Max10MsFramesInAPacket, int()); MOCK_METHOD1(SetTargetBitrate, void(int)); diff --git a/webrtc/modules/audio_coding/codecs/opus/audio_encoder_opus.cc b/webrtc/modules/audio_coding/codecs/opus/audio_encoder_opus.cc index e4779aa0b..b98033e06 100644 --- a/webrtc/modules/audio_coding/codecs/opus/audio_encoder_opus.cc +++ b/webrtc/modules/audio_coding/codecs/opus/audio_encoder_opus.cc @@ -111,6 +111,16 @@ int AudioEncoderOpus::NumChannels() const { return num_channels_; } +size_t AudioEncoderOpus::MaxEncodedBytes() const { + // Calculate the number of bytes we expect the encoder to produce, + // then multiply by two to give a wide margin for error. + int frame_size_ms = num_10ms_frames_per_packet_ * 10; + int bytes_per_millisecond = bitrate_bps_ / (1000 * 8) + 1; + size_t approx_encoded_bytes = + static_cast(frame_size_ms * bytes_per_millisecond); + return 2 * approx_encoded_bytes; +} + int AudioEncoderOpus::Num10MsFramesInNextPacket() const { return num_10ms_frames_per_packet_; } @@ -120,10 +130,9 @@ int AudioEncoderOpus::Max10MsFramesInAPacket() const { } void AudioEncoderOpus::SetTargetBitrate(int bits_per_second) { - CHECK_EQ(WebRtcOpus_SetBitRate( - inst_, std::max(std::min(bits_per_second, kMaxBitrateBps), - kMinBitrateBps)), - 0); + bitrate_bps_ = std::max(std::min(bits_per_second, kMaxBitrateBps), + kMinBitrateBps); + CHECK_EQ(WebRtcOpus_SetBitRate(inst_, bitrate_bps_), 0); } void AudioEncoderOpus::SetProjectedPacketLossRate(double fraction) { diff --git a/webrtc/modules/audio_coding/codecs/opus/interface/audio_encoder_opus.h b/webrtc/modules/audio_coding/codecs/opus/interface/audio_encoder_opus.h index 417faf806..bfd4ba86a 100644 --- a/webrtc/modules/audio_coding/codecs/opus/interface/audio_encoder_opus.h +++ b/webrtc/modules/audio_coding/codecs/opus/interface/audio_encoder_opus.h @@ -47,10 +47,12 @@ class AudioEncoderOpus final : public AudioEncoder { int SampleRateHz() const override; int NumChannels() const override; + size_t MaxEncodedBytes() const override; int Num10MsFramesInNextPacket() const override; int Max10MsFramesInAPacket() const override; void SetTargetBitrate(int bits_per_second) override; void SetProjectedPacketLossRate(double fraction) override; + double packet_loss_rate() const { return packet_loss_rate_; } ApplicationMode application() const { return application_; } @@ -66,6 +68,7 @@ class AudioEncoderOpus final : public AudioEncoder { const int num_channels_; const int payload_type_; const ApplicationMode application_; + int bitrate_bps_; const int samples_per_10ms_frame_; std::vector input_buffer_; OpusEncInst* inst_; diff --git a/webrtc/modules/audio_coding/codecs/red/audio_encoder_copy_red.cc b/webrtc/modules/audio_coding/codecs/red/audio_encoder_copy_red.cc index 92e1c0b37..28c72fb3b 100644 --- a/webrtc/modules/audio_coding/codecs/red/audio_encoder_copy_red.cc +++ b/webrtc/modules/audio_coding/codecs/red/audio_encoder_copy_red.cc @@ -38,6 +38,10 @@ int AudioEncoderCopyRed::NumChannels() const { return speech_encoder_->NumChannels(); } +size_t AudioEncoderCopyRed::MaxEncodedBytes() const { + return 2 * speech_encoder_->MaxEncodedBytes(); +} + int AudioEncoderCopyRed::Num10MsFramesInNextPacket() const { return speech_encoder_->Num10MsFramesInNextPacket(); } diff --git a/webrtc/modules/audio_coding/codecs/red/audio_encoder_copy_red.h b/webrtc/modules/audio_coding/codecs/red/audio_encoder_copy_red.h index 39f1615d8..7ce9ca0f0 100644 --- a/webrtc/modules/audio_coding/codecs/red/audio_encoder_copy_red.h +++ b/webrtc/modules/audio_coding/codecs/red/audio_encoder_copy_red.h @@ -36,8 +36,9 @@ class AudioEncoderCopyRed : public AudioEncoder { ~AudioEncoderCopyRed() override; int SampleRateHz() const override; - int RtpTimestampRateHz() const override; int NumChannels() const override; + size_t MaxEncodedBytes() const override; + int RtpTimestampRateHz() const override; int Num10MsFramesInNextPacket() const override; int Max10MsFramesInAPacket() const override; void SetTargetBitrate(int bits_per_second) override; diff --git a/webrtc/modules/audio_coding/codecs/red/audio_encoder_copy_red_unittest.cc b/webrtc/modules/audio_coding/codecs/red/audio_encoder_copy_red_unittest.cc index ea3ad436a..2ae2fa21b 100644 --- a/webrtc/modules/audio_coding/codecs/red/audio_encoder_copy_red_unittest.cc +++ b/webrtc/modules/audio_coding/codecs/red/audio_encoder_copy_red_unittest.cc @@ -8,6 +8,8 @@ * be found in the AUTHORS file in the root of the source tree. */ +#include + #include "testing/gtest/include/gtest/gtest.h" #include "webrtc/base/checks.h" #include "webrtc/base/scoped_ptr.h" @@ -24,7 +26,7 @@ using ::testing::MockFunction; namespace webrtc { namespace { -static const size_t kMaxEncodedBytes = 1000; +static const size_t kMockMaxEncodedBytes = 1000; static const size_t kMaxNumSamples = 48 * 10 * 2; // 10 ms @ 48 kHz stereo. } @@ -39,11 +41,13 @@ class AudioEncoderCopyRedTest : public ::testing::Test { config.payload_type = red_payload_type_; config.speech_encoder = &mock_encoder_; red_.reset(new AudioEncoderCopyRed(config)); - memset(encoded_, 0, sizeof(encoded_)); memset(audio_, 0, sizeof(audio_)); EXPECT_CALL(mock_encoder_, NumChannels()).WillRepeatedly(Return(1)); EXPECT_CALL(mock_encoder_, SampleRateHz()) .WillRepeatedly(Return(sample_rate_hz_)); + EXPECT_CALL(mock_encoder_, MaxEncodedBytes()) + .WillRepeatedly(Return(kMockMaxEncodedBytes)); + encoded_.resize(red_->MaxEncodedBytes(), 0); } void TearDown() override { @@ -58,7 +62,7 @@ class AudioEncoderCopyRedTest : public ::testing::Test { ASSERT_TRUE(red_.get() != NULL); encoded_info_ = AudioEncoder::EncodedInfo(); red_->Encode(timestamp_, audio_, num_audio_samples_10ms, - kMaxEncodedBytes, encoded_, &encoded_info_); + encoded_.size(), &encoded_[0], &encoded_info_); timestamp_ += num_audio_samples_10ms; } @@ -68,7 +72,7 @@ class AudioEncoderCopyRedTest : public ::testing::Test { int16_t audio_[kMaxNumSamples]; const int sample_rate_hz_; size_t num_audio_samples_10ms; - uint8_t encoded_[kMaxEncodedBytes]; + std::vector encoded_; AudioEncoder::EncodedInfo encoded_info_; const int red_payload_type_; };