AudioDecoderOpus: Add support for 16 kHz output sample rate
In addition to the 48 kHz that we've always used. Bug: webrtc:10631 Change-Id: If73bf7ff9c1c0d22e0d1caa245128612850f8e41 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/138268 Commit-Queue: Karl Wiberg <kwiberg@webrtc.org> Reviewed-by: Minyue Li <minyue@webrtc.org> Cr-Commit-Position: refs/heads/master@{#28104}
This commit is contained in:
@ -20,6 +20,18 @@
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
bool AudioDecoderOpus::Config::IsOk() const {
|
||||
if (sample_rate_hz != 16000 && sample_rate_hz != 48000) {
|
||||
// Unsupported sample rate. (libopus supports a few other rates as
|
||||
// well; we can add support for them when needed.)
|
||||
return false;
|
||||
}
|
||||
if (num_channels != 1 && num_channels != 2) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
absl::optional<AudioDecoderOpus::Config> AudioDecoderOpus::SdpToConfig(
|
||||
const SdpAudioFormat& format) {
|
||||
const auto num_channels = [&]() -> absl::optional<int> {
|
||||
@ -38,7 +50,10 @@ absl::optional<AudioDecoderOpus::Config> AudioDecoderOpus::SdpToConfig(
|
||||
if (absl::EqualsIgnoreCase(format.name, "opus") &&
|
||||
format.clockrate_hz == 48000 && format.num_channels == 2 &&
|
||||
num_channels) {
|
||||
return Config{*num_channels};
|
||||
Config config;
|
||||
config.num_channels = *num_channels;
|
||||
RTC_DCHECK(config.IsOk());
|
||||
return config;
|
||||
} else {
|
||||
return absl::nullopt;
|
||||
}
|
||||
@ -57,7 +72,9 @@ void AudioDecoderOpus::AppendSupportedDecoders(
|
||||
std::unique_ptr<AudioDecoder> AudioDecoderOpus::MakeAudioDecoder(
|
||||
Config config,
|
||||
absl::optional<AudioCodecPairId> /*codec_pair_id*/) {
|
||||
return absl::make_unique<AudioDecoderOpusImpl>(config.num_channels);
|
||||
RTC_DCHECK(config.IsOk());
|
||||
return absl::make_unique<AudioDecoderOpusImpl>(config.num_channels,
|
||||
config.sample_rate_hz);
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
@ -26,7 +26,9 @@ namespace webrtc {
|
||||
// CreateAudioDecoderFactory<...>().
|
||||
struct RTC_EXPORT AudioDecoderOpus {
|
||||
struct Config {
|
||||
int num_channels;
|
||||
bool IsOk() const; // Checks if the values are currently OK.
|
||||
int sample_rate_hz = 48000;
|
||||
int num_channels = 1;
|
||||
};
|
||||
static absl::optional<Config> SdpToConfig(const SdpAudioFormat& audio_format);
|
||||
static void AppendSupportedDecoders(std::vector<AudioCodecSpec>* specs);
|
||||
|
@ -20,10 +20,13 @@
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
AudioDecoderOpusImpl::AudioDecoderOpusImpl(size_t num_channels)
|
||||
: channels_(num_channels) {
|
||||
AudioDecoderOpusImpl::AudioDecoderOpusImpl(size_t num_channels,
|
||||
int sample_rate_hz)
|
||||
: channels_{num_channels}, sample_rate_hz_{sample_rate_hz} {
|
||||
RTC_DCHECK(num_channels == 1 || num_channels == 2);
|
||||
const int error = WebRtcOpus_DecoderCreate(&dec_state_, channels_, 48000);
|
||||
RTC_DCHECK(sample_rate_hz == 16000 || sample_rate_hz == 48000);
|
||||
const int error =
|
||||
WebRtcOpus_DecoderCreate(&dec_state_, channels_, sample_rate_hz_);
|
||||
RTC_DCHECK(error == 0);
|
||||
WebRtcOpus_DecoderInit(dec_state_);
|
||||
}
|
||||
@ -57,7 +60,7 @@ int AudioDecoderOpusImpl::DecodeInternal(const uint8_t* encoded,
|
||||
int sample_rate_hz,
|
||||
int16_t* decoded,
|
||||
SpeechType* speech_type) {
|
||||
RTC_DCHECK_EQ(sample_rate_hz, 48000);
|
||||
RTC_DCHECK_EQ(sample_rate_hz, sample_rate_hz_);
|
||||
int16_t temp_type = 1; // Default is speech.
|
||||
int ret =
|
||||
WebRtcOpus_Decode(dec_state_, encoded, encoded_len, decoded, &temp_type);
|
||||
@ -78,7 +81,7 @@ int AudioDecoderOpusImpl::DecodeRedundantInternal(const uint8_t* encoded,
|
||||
speech_type);
|
||||
}
|
||||
|
||||
RTC_DCHECK_EQ(sample_rate_hz, 48000);
|
||||
RTC_DCHECK_EQ(sample_rate_hz, sample_rate_hz_);
|
||||
int16_t temp_type = 1; // Default is speech.
|
||||
int ret = WebRtcOpus_DecodeFec(dec_state_, encoded, encoded_len, decoded,
|
||||
&temp_type);
|
||||
@ -104,7 +107,7 @@ int AudioDecoderOpusImpl::PacketDurationRedundant(const uint8_t* encoded,
|
||||
return PacketDuration(encoded, encoded_len);
|
||||
}
|
||||
|
||||
return WebRtcOpus_FecDurationEst(encoded, encoded_len, 48000);
|
||||
return WebRtcOpus_FecDurationEst(encoded, encoded_len, sample_rate_hz_);
|
||||
}
|
||||
|
||||
bool AudioDecoderOpusImpl::PacketHasFec(const uint8_t* encoded,
|
||||
@ -115,7 +118,7 @@ bool AudioDecoderOpusImpl::PacketHasFec(const uint8_t* encoded,
|
||||
}
|
||||
|
||||
int AudioDecoderOpusImpl::SampleRateHz() const {
|
||||
return 48000;
|
||||
return sample_rate_hz_;
|
||||
}
|
||||
|
||||
size_t AudioDecoderOpusImpl::Channels() const {
|
||||
|
@ -24,7 +24,8 @@ namespace webrtc {
|
||||
|
||||
class AudioDecoderOpusImpl final : public AudioDecoder {
|
||||
public:
|
||||
explicit AudioDecoderOpusImpl(size_t num_channels);
|
||||
explicit AudioDecoderOpusImpl(size_t num_channels,
|
||||
int sample_rate_hz = 48000);
|
||||
~AudioDecoderOpusImpl() override;
|
||||
|
||||
std::vector<ParseResult> ParsePayload(rtc::Buffer&& payload,
|
||||
@ -52,6 +53,7 @@ class AudioDecoderOpusImpl final : public AudioDecoder {
|
||||
private:
|
||||
OpusDecInst* dec_state_;
|
||||
const size_t channels_;
|
||||
const int sample_rate_hz_;
|
||||
RTC_DISALLOW_COPY_AND_ASSIGN(AudioDecoderOpusImpl);
|
||||
};
|
||||
|
||||
|
@ -426,33 +426,34 @@ class AudioDecoderG722StereoTest : public AudioDecoderTest {
|
||||
}
|
||||
};
|
||||
|
||||
class AudioDecoderOpusTest : public AudioDecoderTest {
|
||||
class AudioDecoderOpusTest
|
||||
: public AudioDecoderTest,
|
||||
public testing::WithParamInterface<std::tuple<int, int>> {
|
||||
protected:
|
||||
AudioDecoderOpusTest() : AudioDecoderTest() {
|
||||
codec_input_rate_hz_ = 48000;
|
||||
frame_size_ = 480;
|
||||
channels_ = opus_num_channels_;
|
||||
codec_input_rate_hz_ = opus_sample_rate_hz_;
|
||||
frame_size_ = rtc::CheckedDivExact(opus_sample_rate_hz_, 100);
|
||||
data_length_ = 10 * frame_size_;
|
||||
decoder_ = new AudioDecoderOpusImpl(1);
|
||||
decoder_ =
|
||||
new AudioDecoderOpusImpl(opus_num_channels_, opus_sample_rate_hz_);
|
||||
AudioEncoderOpusConfig config;
|
||||
config.frame_size_ms = static_cast<int>(frame_size_) / 48;
|
||||
config.application = AudioEncoderOpusConfig::ApplicationMode::kVoip;
|
||||
config.frame_size_ms = 10;
|
||||
config.sample_rate_hz = opus_sample_rate_hz_;
|
||||
config.num_channels = opus_num_channels_;
|
||||
config.application = opus_num_channels_ == 1
|
||||
? AudioEncoderOpusConfig::ApplicationMode::kVoip
|
||||
: AudioEncoderOpusConfig::ApplicationMode::kAudio;
|
||||
audio_encoder_ = AudioEncoderOpus::MakeAudioEncoder(config, payload_type_);
|
||||
}
|
||||
const int opus_sample_rate_hz_{std::get<0>(GetParam())};
|
||||
const int opus_num_channels_{std::get<1>(GetParam())};
|
||||
};
|
||||
|
||||
class AudioDecoderOpusStereoTest : public AudioDecoderOpusTest {
|
||||
protected:
|
||||
AudioDecoderOpusStereoTest() : AudioDecoderOpusTest() {
|
||||
channels_ = 2;
|
||||
delete decoder_;
|
||||
decoder_ = new AudioDecoderOpusImpl(2);
|
||||
AudioEncoderOpusConfig config;
|
||||
config.frame_size_ms = static_cast<int>(frame_size_) / 48;
|
||||
config.num_channels = 2;
|
||||
config.application = AudioEncoderOpusConfig::ApplicationMode::kAudio;
|
||||
audio_encoder_ = AudioEncoderOpus::MakeAudioEncoder(config, payload_type_);
|
||||
}
|
||||
};
|
||||
INSTANTIATE_TEST_SUITE_P(Param,
|
||||
AudioDecoderOpusTest,
|
||||
testing::Combine(testing::Values(16000, 48000),
|
||||
testing::Values(1, 2)));
|
||||
|
||||
TEST_F(AudioDecoderPcmUTest, EncodeDecode) {
|
||||
int tolerance = 251;
|
||||
@ -592,41 +593,22 @@ TEST_F(AudioDecoderG722StereoTest, SetTargetBitrate) {
|
||||
TestSetAndGetTargetBitratesWithFixedCodec(audio_encoder_.get(), 128000);
|
||||
}
|
||||
|
||||
TEST_F(AudioDecoderOpusTest, EncodeDecode) {
|
||||
int tolerance = 6176;
|
||||
double mse = 238630.0;
|
||||
int delay = 22; // Delay from input to output.
|
||||
EncodeDecodeTest(0, tolerance, mse, delay);
|
||||
ReInitTest();
|
||||
EXPECT_FALSE(decoder_->HasDecodePlc());
|
||||
}
|
||||
|
||||
namespace {
|
||||
void TestOpusSetTargetBitrates(AudioEncoder* audio_encoder) {
|
||||
EXPECT_EQ(6000, SetAndGetTargetBitrate(audio_encoder, 5999));
|
||||
EXPECT_EQ(6000, SetAndGetTargetBitrate(audio_encoder, 6000));
|
||||
EXPECT_EQ(32000, SetAndGetTargetBitrate(audio_encoder, 32000));
|
||||
EXPECT_EQ(510000, SetAndGetTargetBitrate(audio_encoder, 510000));
|
||||
EXPECT_EQ(510000, SetAndGetTargetBitrate(audio_encoder, 511000));
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TEST_F(AudioDecoderOpusTest, SetTargetBitrate) {
|
||||
TestOpusSetTargetBitrates(audio_encoder_.get());
|
||||
}
|
||||
|
||||
TEST_F(AudioDecoderOpusStereoTest, EncodeDecode) {
|
||||
int tolerance = 6176;
|
||||
int channel_diff_tolerance = 0;
|
||||
double mse = 238630.0;
|
||||
int delay = 22; // Delay from input to output.
|
||||
TEST_P(AudioDecoderOpusTest, EncodeDecode) {
|
||||
constexpr int tolerance = 6176;
|
||||
const int channel_diff_tolerance = opus_sample_rate_hz_ == 16000 ? 6 : 0;
|
||||
constexpr double mse = 238630.0;
|
||||
constexpr int delay = 22; // Delay from input to output.
|
||||
EncodeDecodeTest(0, tolerance, mse, delay, channel_diff_tolerance);
|
||||
ReInitTest();
|
||||
EXPECT_FALSE(decoder_->HasDecodePlc());
|
||||
}
|
||||
|
||||
TEST_F(AudioDecoderOpusStereoTest, SetTargetBitrate) {
|
||||
TestOpusSetTargetBitrates(audio_encoder_.get());
|
||||
TEST_P(AudioDecoderOpusTest, SetTargetBitrate) {
|
||||
EXPECT_EQ(6000, SetAndGetTargetBitrate(audio_encoder_.get(), 5999));
|
||||
EXPECT_EQ(6000, SetAndGetTargetBitrate(audio_encoder_.get(), 6000));
|
||||
EXPECT_EQ(32000, SetAndGetTargetBitrate(audio_encoder_.get(), 32000));
|
||||
EXPECT_EQ(510000, SetAndGetTargetBitrate(audio_encoder_.get(), 510000));
|
||||
EXPECT_EQ(510000, SetAndGetTargetBitrate(audio_encoder_.get(), 511000));
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
Reference in New Issue
Block a user