Opus implementation of the AudioEncoderFactoryTemplate API
Now the templated AudioEncoderFactory can create Opus encoders! BUG=webrtc:7831 Review-Url: https://codereview.webrtc.org/2930243003 Cr-Commit-Position: refs/heads/master@{#18645}
This commit is contained in:
43
webrtc/api/audio_codecs/opus/BUILD.gn
Normal file
43
webrtc/api/audio_codecs/opus/BUILD.gn
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
# Copyright (c) 2017 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.
|
||||||
|
|
||||||
|
import("../../../webrtc.gni")
|
||||||
|
if (is_android) {
|
||||||
|
import("//build/config/android/config.gni")
|
||||||
|
import("//build/config/android/rules.gni")
|
||||||
|
}
|
||||||
|
|
||||||
|
rtc_static_library("audio_encoder_opus_config") {
|
||||||
|
sources = [
|
||||||
|
"audio_encoder_opus_config.cc",
|
||||||
|
"audio_encoder_opus_config.h",
|
||||||
|
]
|
||||||
|
deps = [
|
||||||
|
"../../../base:rtc_base_approved",
|
||||||
|
]
|
||||||
|
defines = []
|
||||||
|
if (rtc_opus_variable_complexity) {
|
||||||
|
defines += [ "WEBRTC_OPUS_VARIABLE_COMPLEXITY=1" ]
|
||||||
|
} else {
|
||||||
|
defines += [ "WEBRTC_OPUS_VARIABLE_COMPLEXITY=0" ]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rtc_static_library("audio_encoder_opus") {
|
||||||
|
sources = [
|
||||||
|
"audio_encoder_opus.cc",
|
||||||
|
"audio_encoder_opus.h",
|
||||||
|
]
|
||||||
|
deps = [
|
||||||
|
":audio_encoder_opus_config",
|
||||||
|
"..:audio_codecs_api",
|
||||||
|
"../../../base:protobuf_utils", # TODO(kwiberg): Why is this needed?
|
||||||
|
"../../../base:rtc_base_approved",
|
||||||
|
"../../../modules/audio_coding:webrtc_opus",
|
||||||
|
]
|
||||||
|
}
|
||||||
52
webrtc/api/audio_codecs/opus/audio_encoder_opus.cc
Normal file
52
webrtc/api/audio_codecs/opus/audio_encoder_opus.cc
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2017 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/api/audio_codecs/opus/audio_encoder_opus.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "webrtc/base/ptr_util.h"
|
||||||
|
#include "webrtc/modules/audio_coding/codecs/opus/audio_encoder_opus.h"
|
||||||
|
|
||||||
|
namespace webrtc {
|
||||||
|
|
||||||
|
rtc::Optional<AudioEncoderOpusConfig> AudioEncoderOpus::SdpToConfig(
|
||||||
|
const SdpAudioFormat& format) {
|
||||||
|
return AudioEncoderOpusImpl::SdpToConfig(format);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioEncoderOpus::AppendSupportedEncoders(
|
||||||
|
std::vector<AudioCodecSpec>* specs) {
|
||||||
|
const SdpAudioFormat fmt = {
|
||||||
|
"opus", 48000, 2, {{"minptime", "10"}, {"useinbandfec", "1"}}};
|
||||||
|
const AudioCodecInfo info = QueryAudioEncoder(*SdpToConfig(fmt));
|
||||||
|
specs->push_back({fmt, info});
|
||||||
|
}
|
||||||
|
|
||||||
|
AudioCodecInfo AudioEncoderOpus::QueryAudioEncoder(
|
||||||
|
const AudioEncoderOpusConfig& config) {
|
||||||
|
RTC_DCHECK(config.IsOk());
|
||||||
|
AudioCodecInfo info(48000, config.num_channels, config.bitrate_bps,
|
||||||
|
AudioEncoderOpusConfig::kMinBitrateBps,
|
||||||
|
AudioEncoderOpusConfig::kMaxBitrateBps);
|
||||||
|
info.allow_comfort_noise = false;
|
||||||
|
info.supports_network_adaption = true;
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<AudioEncoder> AudioEncoderOpus::MakeAudioEncoder(
|
||||||
|
const AudioEncoderOpusConfig& config,
|
||||||
|
int payload_type) {
|
||||||
|
RTC_DCHECK(config.IsOk());
|
||||||
|
return rtc::MakeUnique<AudioEncoderOpusImpl>(config, payload_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace webrtc
|
||||||
40
webrtc/api/audio_codecs/opus/audio_encoder_opus.h
Normal file
40
webrtc/api/audio_codecs/opus/audio_encoder_opus.h
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2017 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_API_AUDIO_CODECS_OPUS_AUDIO_ENCODER_OPUS_H_
|
||||||
|
#define WEBRTC_API_AUDIO_CODECS_OPUS_AUDIO_ENCODER_OPUS_H_
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "webrtc/api/audio_codecs/audio_encoder.h"
|
||||||
|
#include "webrtc/api/audio_codecs/audio_format.h"
|
||||||
|
#include "webrtc/api/audio_codecs/opus/audio_encoder_opus_config.h"
|
||||||
|
#include "webrtc/base/optional.h"
|
||||||
|
|
||||||
|
namespace webrtc {
|
||||||
|
|
||||||
|
// Opus encoder API for use as a template parameter to
|
||||||
|
// CreateAudioEncoderFactory<...>().
|
||||||
|
//
|
||||||
|
// NOTE: This struct is still under development and may change without notice.
|
||||||
|
struct AudioEncoderOpus {
|
||||||
|
static rtc::Optional<AudioEncoderOpusConfig> SdpToConfig(
|
||||||
|
const SdpAudioFormat& audio_format);
|
||||||
|
static void AppendSupportedEncoders(std::vector<AudioCodecSpec>* specs);
|
||||||
|
static AudioCodecInfo QueryAudioEncoder(const AudioEncoderOpusConfig& config);
|
||||||
|
static std::unique_ptr<AudioEncoder> MakeAudioEncoder(
|
||||||
|
const AudioEncoderOpusConfig&,
|
||||||
|
int payload_type);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace webrtc
|
||||||
|
|
||||||
|
#endif // WEBRTC_API_AUDIO_CODECS_OPUS_AUDIO_ENCODER_OPUS_H_
|
||||||
67
webrtc/api/audio_codecs/opus/audio_encoder_opus_config.cc
Normal file
67
webrtc/api/audio_codecs/opus/audio_encoder_opus_config.cc
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2017 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/api/audio_codecs/opus/audio_encoder_opus_config.h"
|
||||||
|
|
||||||
|
namespace webrtc {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
#if defined(WEBRTC_ANDROID) || defined(WEBRTC_IOS) || defined(WEBRTC_ARCH_ARM)
|
||||||
|
// If we are on Android, iOS and/or ARM, use a lower complexity setting by
|
||||||
|
// default, to save encoder complexity.
|
||||||
|
constexpr int kDefaultComplexity = 5;
|
||||||
|
#else
|
||||||
|
constexpr int kDefaultComplexity = 9;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
constexpr int kDefaultLowRateComplexity =
|
||||||
|
WEBRTC_OPUS_VARIABLE_COMPLEXITY ? 9 : kDefaultComplexity;
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
constexpr int AudioEncoderOpusConfig::kDefaultFrameSizeMs;
|
||||||
|
constexpr int AudioEncoderOpusConfig::kMinBitrateBps;
|
||||||
|
constexpr int AudioEncoderOpusConfig::kMaxBitrateBps;
|
||||||
|
|
||||||
|
AudioEncoderOpusConfig::AudioEncoderOpusConfig()
|
||||||
|
: frame_size_ms(kDefaultFrameSizeMs),
|
||||||
|
num_channels(1),
|
||||||
|
application(ApplicationMode::kVoip),
|
||||||
|
bitrate_bps(32000),
|
||||||
|
fec_enabled(false),
|
||||||
|
cbr_enabled(false),
|
||||||
|
max_playback_rate_hz(48000),
|
||||||
|
complexity(kDefaultComplexity),
|
||||||
|
low_rate_complexity(kDefaultLowRateComplexity),
|
||||||
|
complexity_threshold_bps(12500),
|
||||||
|
complexity_threshold_window_bps(1500),
|
||||||
|
dtx_enabled(false),
|
||||||
|
uplink_bandwidth_update_interval_ms(200) {}
|
||||||
|
AudioEncoderOpusConfig::AudioEncoderOpusConfig(const AudioEncoderOpusConfig&) =
|
||||||
|
default;
|
||||||
|
AudioEncoderOpusConfig::~AudioEncoderOpusConfig() = default;
|
||||||
|
AudioEncoderOpusConfig& AudioEncoderOpusConfig::operator=(
|
||||||
|
const AudioEncoderOpusConfig&) = default;
|
||||||
|
|
||||||
|
bool AudioEncoderOpusConfig::IsOk() const {
|
||||||
|
if (frame_size_ms <= 0 || frame_size_ms % 10 != 0)
|
||||||
|
return false;
|
||||||
|
if (num_channels != 1 && num_channels != 2)
|
||||||
|
return false;
|
||||||
|
if (bitrate_bps < kMinBitrateBps || bitrate_bps > kMaxBitrateBps)
|
||||||
|
return false;
|
||||||
|
if (complexity < 0 || complexity > 10)
|
||||||
|
return false;
|
||||||
|
if (low_rate_complexity < 0 || low_rate_complexity > 10)
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} // namespace webrtc
|
||||||
63
webrtc/api/audio_codecs/opus/audio_encoder_opus_config.h
Normal file
63
webrtc/api/audio_codecs/opus/audio_encoder_opus_config.h
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2017 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_API_AUDIO_CODECS_OPUS_AUDIO_ENCODER_OPUS_CONFIG_H_
|
||||||
|
#define WEBRTC_API_AUDIO_CODECS_OPUS_AUDIO_ENCODER_OPUS_CONFIG_H_
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace webrtc {
|
||||||
|
|
||||||
|
// NOTE: This struct is still under development and may change without notice.
|
||||||
|
struct AudioEncoderOpusConfig {
|
||||||
|
static constexpr int kDefaultFrameSizeMs = 20;
|
||||||
|
|
||||||
|
// Opus API allows a min bitrate of 500bps, but Opus documentation suggests
|
||||||
|
// bitrate should be in the range of 6000 to 510000, inclusive.
|
||||||
|
static constexpr int kMinBitrateBps = 6000;
|
||||||
|
static constexpr int kMaxBitrateBps = 510000;
|
||||||
|
|
||||||
|
AudioEncoderOpusConfig();
|
||||||
|
AudioEncoderOpusConfig(const AudioEncoderOpusConfig&);
|
||||||
|
~AudioEncoderOpusConfig();
|
||||||
|
AudioEncoderOpusConfig& operator=(const AudioEncoderOpusConfig&);
|
||||||
|
|
||||||
|
bool IsOk() const; // Checks if the values are currently OK.
|
||||||
|
|
||||||
|
int frame_size_ms;
|
||||||
|
size_t num_channels;
|
||||||
|
enum class ApplicationMode { kVoip, kAudio };
|
||||||
|
ApplicationMode application;
|
||||||
|
int bitrate_bps;
|
||||||
|
bool fec_enabled;
|
||||||
|
bool cbr_enabled;
|
||||||
|
int max_playback_rate_hz;
|
||||||
|
|
||||||
|
// |complexity| is used when the bitrate goes above
|
||||||
|
// |complexity_threshold_bps| + |complexity_threshold_window_bps|;
|
||||||
|
// |low_rate_complexity| is used when the bitrate falls below
|
||||||
|
// |complexity_threshold_bps| - |complexity_threshold_window_bps|. In the
|
||||||
|
// interval in the middle, we keep using the most recent of the two
|
||||||
|
// complexity settings.
|
||||||
|
int complexity;
|
||||||
|
int low_rate_complexity;
|
||||||
|
int complexity_threshold_bps;
|
||||||
|
int complexity_threshold_window_bps;
|
||||||
|
|
||||||
|
bool dtx_enabled;
|
||||||
|
std::vector<int> supported_frame_lengths_ms;
|
||||||
|
int uplink_bandwidth_update_interval_ms;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace webrtc
|
||||||
|
|
||||||
|
#endif // WEBRTC_API_AUDIO_CODECS_OPUS_AUDIO_ENCODER_OPUS_CONFIG_H_
|
||||||
@ -26,6 +26,7 @@ if (rtc_include_tests) {
|
|||||||
"../../../test:test_support",
|
"../../../test:test_support",
|
||||||
"../g722:audio_decoder_g722",
|
"../g722:audio_decoder_g722",
|
||||||
"../g722:audio_encoder_g722",
|
"../g722:audio_encoder_g722",
|
||||||
|
"../opus:audio_encoder_opus",
|
||||||
"//testing/gmock",
|
"//testing/gmock",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,6 +10,7 @@
|
|||||||
|
|
||||||
#include "webrtc/api/audio_codecs/audio_encoder_factory_template.h"
|
#include "webrtc/api/audio_codecs/audio_encoder_factory_template.h"
|
||||||
#include "webrtc/api/audio_codecs/g722/audio_encoder_g722.h"
|
#include "webrtc/api/audio_codecs/g722/audio_encoder_g722.h"
|
||||||
|
#include "webrtc/api/audio_codecs/opus/audio_encoder_opus.h"
|
||||||
#include "webrtc/base/ptr_util.h"
|
#include "webrtc/base/ptr_util.h"
|
||||||
#include "webrtc/test/gmock.h"
|
#include "webrtc/test/gmock.h"
|
||||||
#include "webrtc/test/gtest.h"
|
#include "webrtc/test/gtest.h"
|
||||||
@ -133,4 +134,26 @@ TEST(AudioEncoderFactoryTemplateTest, G722) {
|
|||||||
EXPECT_EQ(16000, enc->SampleRateHz());
|
EXPECT_EQ(16000, enc->SampleRateHz());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(AudioEncoderFactoryTemplateTest, Opus) {
|
||||||
|
auto factory = CreateAudioEncoderFactory<AudioEncoderOpus>();
|
||||||
|
AudioCodecInfo info = {48000, 1, 32000, 6000, 510000};
|
||||||
|
info.allow_comfort_noise = false;
|
||||||
|
info.supports_network_adaption = true;
|
||||||
|
EXPECT_THAT(
|
||||||
|
factory->GetSupportedEncoders(),
|
||||||
|
testing::ElementsAre(AudioCodecSpec{
|
||||||
|
{"opus", 48000, 2, {{"minptime", "10"}, {"useinbandfec", "1"}}},
|
||||||
|
info}));
|
||||||
|
EXPECT_EQ(rtc::Optional<AudioCodecInfo>(),
|
||||||
|
factory->QueryAudioEncoder({"foo", 8000, 1}));
|
||||||
|
EXPECT_EQ(
|
||||||
|
rtc::Optional<AudioCodecInfo>(info),
|
||||||
|
factory->QueryAudioEncoder(
|
||||||
|
{"opus", 48000, 2, {{"minptime", "10"}, {"useinbandfec", "1"}}}));
|
||||||
|
EXPECT_EQ(nullptr, factory->MakeAudioEncoder(17, {"bar", 16000, 1}));
|
||||||
|
auto enc = factory->MakeAudioEncoder(17, {"opus", 48000, 2});
|
||||||
|
ASSERT_NE(nullptr, enc);
|
||||||
|
EXPECT_EQ(48000, enc->SampleRateHz());
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
|||||||
@ -828,6 +828,7 @@ rtc_static_library("webrtc_opus") {
|
|||||||
":audio_network_adaptor",
|
":audio_network_adaptor",
|
||||||
"../..:webrtc_common",
|
"../..:webrtc_common",
|
||||||
"../../api/audio_codecs:audio_codecs_api",
|
"../../api/audio_codecs:audio_codecs_api",
|
||||||
|
"../../api/audio_codecs/opus:audio_encoder_opus_config",
|
||||||
"../../base:protobuf_utils",
|
"../../base:protobuf_utils",
|
||||||
"../../base:rtc_base_approved",
|
"../../base:rtc_base_approved",
|
||||||
"../../base:rtc_numerics",
|
"../../base:rtc_numerics",
|
||||||
@ -839,11 +840,6 @@ rtc_static_library("webrtc_opus") {
|
|||||||
]
|
]
|
||||||
|
|
||||||
defines = audio_codec_defines
|
defines = audio_codec_defines
|
||||||
if (rtc_opus_variable_complexity) {
|
|
||||||
defines += [ "WEBRTC_OPUS_VARIABLE_COMPLEXITY=1" ]
|
|
||||||
} else {
|
|
||||||
defines += [ "WEBRTC_OPUS_VARIABLE_COMPLEXITY=0" ]
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rtc_build_opus) {
|
if (rtc_build_opus) {
|
||||||
public_deps += [ rtc_opus_dir ]
|
public_deps += [ rtc_opus_dir ]
|
||||||
@ -1484,6 +1480,7 @@ if (rtc_include_tests) {
|
|||||||
":neteq",
|
":neteq",
|
||||||
":neteq_tools",
|
":neteq_tools",
|
||||||
"../../api/audio_codecs:audio_codecs_api",
|
"../../api/audio_codecs:audio_codecs_api",
|
||||||
|
"../../api/audio_codecs/opus:audio_encoder_opus",
|
||||||
"../../base:protobuf_utils",
|
"../../base:protobuf_utils",
|
||||||
"../../common_audio",
|
"../../common_audio",
|
||||||
"../../test:test_main",
|
"../../test:test_main",
|
||||||
|
|||||||
@ -1501,7 +1501,7 @@ TEST_F(AcmSenderBitExactnessOldApi, MAYBE_Opus_stereo_20ms) {
|
|||||||
|
|
||||||
TEST_F(AcmSenderBitExactnessNewApi, MAYBE_OpusFromFormat_stereo_20ms) {
|
TEST_F(AcmSenderBitExactnessNewApi, MAYBE_OpusFromFormat_stereo_20ms) {
|
||||||
const SdpAudioFormat kOpusFormat("opus", 48000, 2, {{"stereo", "1"}});
|
const SdpAudioFormat kOpusFormat("opus", 48000, 2, {{"stereo", "1"}});
|
||||||
AudioEncoderOpus encoder(120, kOpusFormat);
|
AudioEncoderOpusImpl encoder(120, kOpusFormat);
|
||||||
ASSERT_NO_FATAL_FAILURE(SetUpTestExternalEncoder(&encoder, 120));
|
ASSERT_NO_FATAL_FAILURE(SetUpTestExternalEncoder(&encoder, 120));
|
||||||
Run(AcmReceiverBitExactnessOldApi::PlatformChecksum(
|
Run(AcmReceiverBitExactnessOldApi::PlatformChecksum(
|
||||||
"855041f2490b887302bce9d544731849",
|
"855041f2490b887302bce9d544731849",
|
||||||
@ -1547,7 +1547,7 @@ TEST_F(AcmSenderBitExactnessOldApi, MAYBE_Opus_stereo_20ms_voip) {
|
|||||||
|
|
||||||
TEST_F(AcmSenderBitExactnessNewApi, MAYBE_OpusFromFormat_stereo_20ms_voip) {
|
TEST_F(AcmSenderBitExactnessNewApi, MAYBE_OpusFromFormat_stereo_20ms_voip) {
|
||||||
const SdpAudioFormat kOpusFormat("opus", 48000, 2, {{"stereo", "1"}});
|
const SdpAudioFormat kOpusFormat("opus", 48000, 2, {{"stereo", "1"}});
|
||||||
AudioEncoderOpus encoder(120, kOpusFormat);
|
AudioEncoderOpusImpl encoder(120, kOpusFormat);
|
||||||
ASSERT_NO_FATAL_FAILURE(SetUpTestExternalEncoder(&encoder, 120));
|
ASSERT_NO_FATAL_FAILURE(SetUpTestExternalEncoder(&encoder, 120));
|
||||||
// If not set, default will be kAudio in case of stereo.
|
// If not set, default will be kAudio in case of stereo.
|
||||||
EXPECT_EQ(0, send_test_->acm()->SetOpusApplication(kVoip));
|
EXPECT_EQ(0, send_test_->acm()->SetOpusApplication(kVoip));
|
||||||
@ -1664,7 +1664,7 @@ TEST_F(AcmSetBitRateOldApi, MAYBE_Opus_48khz_20ms_10kbps) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(AcmSetBitRateNewApi, MAYBE_OpusFromFormat_48khz_20ms_10kbps) {
|
TEST_F(AcmSetBitRateNewApi, MAYBE_OpusFromFormat_48khz_20ms_10kbps) {
|
||||||
AudioEncoderOpus encoder(
|
AudioEncoderOpusImpl encoder(
|
||||||
107, SdpAudioFormat("opus", 48000, 2, {{"maxaveragebitrate", "10000"}}));
|
107, SdpAudioFormat("opus", 48000, 2, {{"maxaveragebitrate", "10000"}}));
|
||||||
ASSERT_TRUE(SetUpSender());
|
ASSERT_TRUE(SetUpSender());
|
||||||
ASSERT_TRUE(RegisterExternalSendCodec(&encoder, 107));
|
ASSERT_TRUE(RegisterExternalSendCodec(&encoder, 107));
|
||||||
@ -1693,7 +1693,7 @@ TEST_F(AcmSetBitRateOldApi, MAYBE_Opus_48khz_20ms_50kbps) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(AcmSetBitRateNewApi, MAYBE_OpusFromFormat_48khz_20ms_50kbps) {
|
TEST_F(AcmSetBitRateNewApi, MAYBE_OpusFromFormat_48khz_20ms_50kbps) {
|
||||||
AudioEncoderOpus encoder(
|
AudioEncoderOpusImpl encoder(
|
||||||
107, SdpAudioFormat("opus", 48000, 2, {{"maxaveragebitrate", "50000"}}));
|
107, SdpAudioFormat("opus", 48000, 2, {{"maxaveragebitrate", "50000"}}));
|
||||||
ASSERT_TRUE(SetUpSender());
|
ASSERT_TRUE(SetUpSender());
|
||||||
ASSERT_TRUE(RegisterExternalSendCodec(&encoder, 107));
|
ASSERT_TRUE(RegisterExternalSendCodec(&encoder, 107));
|
||||||
@ -1721,7 +1721,7 @@ TEST_F(AcmSetBitRateOldApi, MAYBE_Opus_48khz_20ms_100kbps) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(AcmSetBitRateNewApi, MAYBE_OpusFromFormat_48khz_20ms_100kbps) {
|
TEST_F(AcmSetBitRateNewApi, MAYBE_OpusFromFormat_48khz_20ms_100kbps) {
|
||||||
AudioEncoderOpus encoder(
|
AudioEncoderOpusImpl encoder(
|
||||||
107, SdpAudioFormat("opus", 48000, 2, {{"maxaveragebitrate", "100000"}}));
|
107, SdpAudioFormat("opus", 48000, 2, {{"maxaveragebitrate", "100000"}}));
|
||||||
ASSERT_TRUE(SetUpSender());
|
ASSERT_TRUE(SetUpSender());
|
||||||
ASSERT_TRUE(RegisterExternalSendCodec(&encoder, 107));
|
ASSERT_TRUE(RegisterExternalSendCodec(&encoder, 107));
|
||||||
|
|||||||
@ -163,7 +163,7 @@ std::unique_ptr<AudioEncoder> CreateEncoder(
|
|||||||
#endif
|
#endif
|
||||||
#ifdef WEBRTC_CODEC_OPUS
|
#ifdef WEBRTC_CODEC_OPUS
|
||||||
if (STR_CASE_CMP(speech_inst.plname, "opus") == 0)
|
if (STR_CASE_CMP(speech_inst.plname, "opus") == 0)
|
||||||
return std::unique_ptr<AudioEncoder>(new AudioEncoderOpus(speech_inst));
|
return std::unique_ptr<AudioEncoder>(new AudioEncoderOpusImpl(speech_inst));
|
||||||
#endif
|
#endif
|
||||||
if (STR_CASE_CMP(speech_inst.plname, "pcmu") == 0)
|
if (STR_CASE_CMP(speech_inst.plname, "pcmu") == 0)
|
||||||
return std::unique_ptr<AudioEncoder>(new AudioEncoderPcmU(speech_inst));
|
return std::unique_ptr<AudioEncoder>(new AudioEncoderPcmU(speech_inst));
|
||||||
|
|||||||
@ -74,7 +74,7 @@ NamedEncoderFactory encoder_factories[] = {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef WEBRTC_CODEC_OPUS
|
#ifdef WEBRTC_CODEC_OPUS
|
||||||
NamedEncoderFactory::ForEncoder<AudioEncoderOpus>(),
|
NamedEncoderFactory::ForEncoder<AudioEncoderOpusImpl>(),
|
||||||
#endif
|
#endif
|
||||||
NamedEncoderFactory::ForEncoder<AudioEncoderPcm16B>(),
|
NamedEncoderFactory::ForEncoder<AudioEncoderPcm16B>(),
|
||||||
NamedEncoderFactory::ForEncoder<AudioEncoderPcmA>(),
|
NamedEncoderFactory::ForEncoder<AudioEncoderPcmA>(),
|
||||||
|
|||||||
@ -48,11 +48,6 @@ constexpr int kOpusBitrateNbBps = 12000;
|
|||||||
constexpr int kOpusBitrateWbBps = 20000;
|
constexpr int kOpusBitrateWbBps = 20000;
|
||||||
constexpr int kOpusBitrateFbBps = 32000;
|
constexpr int kOpusBitrateFbBps = 32000;
|
||||||
|
|
||||||
// Opus API allows a min bitrate of 500bps, but Opus documentation suggests
|
|
||||||
// bitrate should be in the range of 6000 to 510000, inclusive.
|
|
||||||
constexpr int kOpusMinBitrateBps = 6000;
|
|
||||||
constexpr int kOpusMaxBitrateBps = 510000;
|
|
||||||
|
|
||||||
constexpr int kSampleRateHz = 48000;
|
constexpr int kSampleRateHz = 48000;
|
||||||
constexpr int kDefaultMaxPlaybackRate = 48000;
|
constexpr int kDefaultMaxPlaybackRate = 48000;
|
||||||
|
|
||||||
@ -133,8 +128,8 @@ int CalculateDefaultBitrate(int max_playback_rate, size_t num_channels) {
|
|||||||
return kOpusBitrateFbBps * rtc::dchecked_cast<int>(num_channels);
|
return kOpusBitrateFbBps * rtc::dchecked_cast<int>(num_channels);
|
||||||
}
|
}
|
||||||
}();
|
}();
|
||||||
RTC_DCHECK_GE(bitrate, kOpusMinBitrateBps);
|
RTC_DCHECK_GE(bitrate, AudioEncoderOpusConfig::kMinBitrateBps);
|
||||||
RTC_DCHECK_LE(bitrate, kOpusMaxBitrateBps);
|
RTC_DCHECK_LE(bitrate, AudioEncoderOpusConfig::kMaxBitrateBps);
|
||||||
return bitrate;
|
return bitrate;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -150,7 +145,8 @@ int CalculateBitrate(int max_playback_rate_hz,
|
|||||||
const auto bitrate = rtc::StringToNumber<int>(*bitrate_param);
|
const auto bitrate = rtc::StringToNumber<int>(*bitrate_param);
|
||||||
if (bitrate) {
|
if (bitrate) {
|
||||||
const int chosen_bitrate =
|
const int chosen_bitrate =
|
||||||
std::max(kOpusMinBitrateBps, std::min(*bitrate, kOpusMaxBitrateBps));
|
std::max(AudioEncoderOpusConfig::kMinBitrateBps,
|
||||||
|
std::min(*bitrate, AudioEncoderOpusConfig::kMaxBitrateBps));
|
||||||
if (bitrate != chosen_bitrate) {
|
if (bitrate != chosen_bitrate) {
|
||||||
LOG(LS_WARNING) << "Invalid maxaveragebitrate " << *bitrate
|
LOG(LS_WARNING) << "Invalid maxaveragebitrate " << *bitrate
|
||||||
<< " clamped to " << chosen_bitrate;
|
<< " clamped to " << chosen_bitrate;
|
||||||
@ -195,7 +191,7 @@ int GetFrameSizeMs(const SdpAudioFormat& format) {
|
|||||||
return *(std::end(kOpusSupportedFrameLengths) - 1);
|
return *(std::end(kOpusSupportedFrameLengths) - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
return AudioEncoderOpus::Config::kDefaultFrameSizeMs;
|
return AudioEncoderOpusConfig::kDefaultFrameSizeMs;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FindSupportedFrameLengths(int min_frame_length_ms,
|
void FindSupportedFrameLengths(int min_frame_length_ms,
|
||||||
@ -211,9 +207,14 @@ void FindSupportedFrameLengths(int min_frame_length_ms,
|
|||||||
RTC_DCHECK(std::is_sorted(out->begin(), out->end()));
|
RTC_DCHECK(std::is_sorted(out->begin(), out->end()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int GetBitrateBps(const AudioEncoderOpusConfig& config) {
|
||||||
|
RTC_DCHECK(config.IsOk());
|
||||||
|
return config.bitrate_bps;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
rtc::Optional<AudioCodecInfo> AudioEncoderOpus::QueryAudioEncoder(
|
rtc::Optional<AudioCodecInfo> AudioEncoderOpusImpl::QueryAudioEncoder(
|
||||||
const SdpAudioFormat& format) {
|
const SdpAudioFormat& format) {
|
||||||
if (STR_CASE_CMP(format.name.c_str(), GetPayloadName()) == 0 &&
|
if (STR_CASE_CMP(format.name.c_str(), GetPayloadName()) == 0 &&
|
||||||
format.clockrate_hz == 48000 && format.num_channels == 2) {
|
format.clockrate_hz == 48000 && format.num_channels == 2) {
|
||||||
@ -221,8 +222,9 @@ rtc::Optional<AudioCodecInfo> AudioEncoderOpus::QueryAudioEncoder(
|
|||||||
const int bitrate =
|
const int bitrate =
|
||||||
CalculateBitrate(GetMaxPlaybackRate(format), num_channels,
|
CalculateBitrate(GetMaxPlaybackRate(format), num_channels,
|
||||||
GetFormatParameter(format, "maxaveragebitrate"));
|
GetFormatParameter(format, "maxaveragebitrate"));
|
||||||
AudioCodecInfo info(48000, num_channels, bitrate, kOpusMinBitrateBps,
|
AudioCodecInfo info(48000, num_channels, bitrate,
|
||||||
kOpusMaxBitrateBps);
|
AudioEncoderOpusConfig::kMinBitrateBps,
|
||||||
|
AudioEncoderOpusConfig::kMaxBitrateBps);
|
||||||
info.allow_comfort_noise = false;
|
info.allow_comfort_noise = false;
|
||||||
info.supports_network_adaption = true;
|
info.supports_network_adaption = true;
|
||||||
|
|
||||||
@ -231,46 +233,44 @@ rtc::Optional<AudioCodecInfo> AudioEncoderOpus::QueryAudioEncoder(
|
|||||||
return rtc::Optional<AudioCodecInfo>();
|
return rtc::Optional<AudioCodecInfo>();
|
||||||
}
|
}
|
||||||
|
|
||||||
AudioEncoderOpus::Config AudioEncoderOpus::CreateConfig(
|
AudioEncoderOpusConfig AudioEncoderOpusImpl::CreateConfig(
|
||||||
const CodecInst& codec_inst) {
|
const CodecInst& codec_inst) {
|
||||||
AudioEncoderOpus::Config config;
|
AudioEncoderOpusConfig config;
|
||||||
config.frame_size_ms = rtc::CheckedDivExact(codec_inst.pacsize, 48);
|
config.frame_size_ms = rtc::CheckedDivExact(codec_inst.pacsize, 48);
|
||||||
config.num_channels = codec_inst.channels;
|
config.num_channels = codec_inst.channels;
|
||||||
config.bitrate_bps = rtc::Optional<int>(codec_inst.rate);
|
config.bitrate_bps = codec_inst.rate;
|
||||||
config.payload_type = codec_inst.pltype;
|
config.application = config.num_channels == 1
|
||||||
config.application = config.num_channels == 1 ? AudioEncoderOpus::kVoip
|
? AudioEncoderOpusConfig::ApplicationMode::kVoip
|
||||||
: AudioEncoderOpus::kAudio;
|
: AudioEncoderOpusConfig::ApplicationMode::kAudio;
|
||||||
config.supported_frame_lengths_ms.push_back(config.frame_size_ms);
|
config.supported_frame_lengths_ms.push_back(config.frame_size_ms);
|
||||||
#if WEBRTC_OPUS_VARIABLE_COMPLEXITY
|
|
||||||
config.low_rate_complexity = 9;
|
|
||||||
#endif
|
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
AudioEncoderOpus::Config AudioEncoderOpus::CreateConfig(
|
rtc::Optional<AudioEncoderOpusConfig> AudioEncoderOpusImpl::SdpToConfig(
|
||||||
int payload_type,
|
|
||||||
const SdpAudioFormat& format) {
|
const SdpAudioFormat& format) {
|
||||||
AudioEncoderOpus::Config config;
|
if (STR_CASE_CMP(format.name.c_str(), "opus") != 0 ||
|
||||||
|
format.clockrate_hz != 48000 || format.num_channels != 2) {
|
||||||
|
return rtc::Optional<AudioEncoderOpusConfig>();
|
||||||
|
}
|
||||||
|
|
||||||
|
AudioEncoderOpusConfig config;
|
||||||
config.num_channels = GetChannelCount(format);
|
config.num_channels = GetChannelCount(format);
|
||||||
config.frame_size_ms = GetFrameSizeMs(format);
|
config.frame_size_ms = GetFrameSizeMs(format);
|
||||||
config.max_playback_rate_hz = GetMaxPlaybackRate(format);
|
config.max_playback_rate_hz = GetMaxPlaybackRate(format);
|
||||||
config.fec_enabled = (GetFormatParameter(format, "useinbandfec") == "1");
|
config.fec_enabled = (GetFormatParameter(format, "useinbandfec") == "1");
|
||||||
config.dtx_enabled = (GetFormatParameter(format, "usedtx") == "1");
|
config.dtx_enabled = (GetFormatParameter(format, "usedtx") == "1");
|
||||||
config.cbr_enabled = (GetFormatParameter(format, "cbr") == "1");
|
config.cbr_enabled = (GetFormatParameter(format, "cbr") == "1");
|
||||||
config.bitrate_bps = rtc::Optional<int>(
|
config.bitrate_bps =
|
||||||
CalculateBitrate(config.max_playback_rate_hz, config.num_channels,
|
CalculateBitrate(config.max_playback_rate_hz, config.num_channels,
|
||||||
GetFormatParameter(format, "maxaveragebitrate")));
|
GetFormatParameter(format, "maxaveragebitrate"));
|
||||||
config.payload_type = payload_type;
|
config.application = config.num_channels == 1
|
||||||
config.application = config.num_channels == 1 ? AudioEncoderOpus::kVoip
|
? AudioEncoderOpusConfig::ApplicationMode::kVoip
|
||||||
: AudioEncoderOpus::kAudio;
|
: AudioEncoderOpusConfig::ApplicationMode::kAudio;
|
||||||
#if WEBRTC_OPUS_VARIABLE_COMPLEXITY
|
|
||||||
config.low_rate_complexity = 9;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
constexpr int kMinANAFrameLength = kANASupportedFrameLengths[0];
|
constexpr int kMinANAFrameLength = kANASupportedFrameLengths[0];
|
||||||
constexpr int kMaxANAFrameLength =
|
constexpr int kMaxANAFrameLength =
|
||||||
kANASupportedFrameLengths[arraysize(kANASupportedFrameLengths) - 1];
|
kANASupportedFrameLengths[arraysize(kANASupportedFrameLengths) - 1];
|
||||||
|
|
||||||
// For now, minptime and maxptime are only used with ANA. If ptime is outside
|
// For now, minptime and maxptime are only used with ANA. If ptime is outside
|
||||||
// of this range, it will get adjusted once ANA takes hold. Ideally, we'd know
|
// of this range, it will get adjusted once ANA takes hold. Ideally, we'd know
|
||||||
// if ANA was to be used when setting up the config, and adjust accordingly.
|
// if ANA was to be used when setting up the config, and adjust accordingly.
|
||||||
@ -281,10 +281,28 @@ AudioEncoderOpus::Config AudioEncoderOpus::CreateConfig(
|
|||||||
|
|
||||||
FindSupportedFrameLengths(min_frame_length_ms, max_frame_length_ms,
|
FindSupportedFrameLengths(min_frame_length_ms, max_frame_length_ms,
|
||||||
&config.supported_frame_lengths_ms);
|
&config.supported_frame_lengths_ms);
|
||||||
return config;
|
RTC_DCHECK(config.IsOk());
|
||||||
|
return rtc::Optional<AudioEncoderOpusConfig>(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
class AudioEncoderOpus::PacketLossFractionSmoother {
|
rtc::Optional<int> AudioEncoderOpusImpl::GetNewComplexity(
|
||||||
|
const AudioEncoderOpusConfig& config) {
|
||||||
|
RTC_DCHECK(config.IsOk());
|
||||||
|
const int bitrate_bps = GetBitrateBps(config);
|
||||||
|
if (bitrate_bps >= config.complexity_threshold_bps -
|
||||||
|
config.complexity_threshold_window_bps &&
|
||||||
|
bitrate_bps <= config.complexity_threshold_bps +
|
||||||
|
config.complexity_threshold_window_bps) {
|
||||||
|
// Within the hysteresis window; make no change.
|
||||||
|
return rtc::Optional<int>();
|
||||||
|
} else {
|
||||||
|
return rtc::Optional<int>(bitrate_bps <= config.complexity_threshold_bps
|
||||||
|
? config.low_rate_complexity
|
||||||
|
: config.complexity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class AudioEncoderOpusImpl::PacketLossFractionSmoother {
|
||||||
public:
|
public:
|
||||||
explicit PacketLossFractionSmoother()
|
explicit PacketLossFractionSmoother()
|
||||||
: last_sample_time_ms_(rtc::TimeMillis()),
|
: last_sample_time_ms_(rtc::TimeMillis()),
|
||||||
@ -311,58 +329,13 @@ class AudioEncoderOpus::PacketLossFractionSmoother {
|
|||||||
rtc::ExpFilter smoother_;
|
rtc::ExpFilter smoother_;
|
||||||
};
|
};
|
||||||
|
|
||||||
AudioEncoderOpus::Config::Config() {
|
AudioEncoderOpusImpl::AudioEncoderOpusImpl(
|
||||||
#if WEBRTC_OPUS_VARIABLE_COMPLEXITY
|
const AudioEncoderOpusConfig& config,
|
||||||
low_rate_complexity = 9;
|
int payload_type,
|
||||||
#endif
|
|
||||||
}
|
|
||||||
AudioEncoderOpus::Config::Config(const Config&) = default;
|
|
||||||
AudioEncoderOpus::Config::~Config() = default;
|
|
||||||
auto AudioEncoderOpus::Config::operator=(const Config&) -> Config& = default;
|
|
||||||
|
|
||||||
bool AudioEncoderOpus::Config::IsOk() const {
|
|
||||||
if (frame_size_ms <= 0 || frame_size_ms % 10 != 0)
|
|
||||||
return false;
|
|
||||||
if (num_channels != 1 && num_channels != 2)
|
|
||||||
return false;
|
|
||||||
if (bitrate_bps &&
|
|
||||||
(*bitrate_bps < kOpusMinBitrateBps || *bitrate_bps > kOpusMaxBitrateBps))
|
|
||||||
return false;
|
|
||||||
if (complexity < 0 || complexity > 10)
|
|
||||||
return false;
|
|
||||||
if (low_rate_complexity < 0 || low_rate_complexity > 10)
|
|
||||||
return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
int AudioEncoderOpus::Config::GetBitrateBps() const {
|
|
||||||
RTC_DCHECK(IsOk());
|
|
||||||
if (bitrate_bps)
|
|
||||||
return *bitrate_bps; // Explicitly set value.
|
|
||||||
else
|
|
||||||
return num_channels == 1 ? 32000 : 64000; // Default value.
|
|
||||||
}
|
|
||||||
|
|
||||||
rtc::Optional<int> AudioEncoderOpus::Config::GetNewComplexity() const {
|
|
||||||
RTC_DCHECK(IsOk());
|
|
||||||
const int bitrate_bps = GetBitrateBps();
|
|
||||||
if (bitrate_bps >=
|
|
||||||
complexity_threshold_bps - complexity_threshold_window_bps &&
|
|
||||||
bitrate_bps <=
|
|
||||||
complexity_threshold_bps + complexity_threshold_window_bps) {
|
|
||||||
// Within the hysteresis window; make no change.
|
|
||||||
return rtc::Optional<int>();
|
|
||||||
}
|
|
||||||
return bitrate_bps <= complexity_threshold_bps
|
|
||||||
? rtc::Optional<int>(low_rate_complexity)
|
|
||||||
: rtc::Optional<int>(complexity);
|
|
||||||
}
|
|
||||||
|
|
||||||
AudioEncoderOpus::AudioEncoderOpus(
|
|
||||||
const Config& config,
|
|
||||||
AudioNetworkAdaptorCreator&& audio_network_adaptor_creator,
|
AudioNetworkAdaptorCreator&& audio_network_adaptor_creator,
|
||||||
std::unique_ptr<SmoothingFilter> bitrate_smoother)
|
std::unique_ptr<SmoothingFilter> bitrate_smoother)
|
||||||
: send_side_bwe_with_overhead_(webrtc::field_trial::IsEnabled(
|
: payload_type_(payload_type),
|
||||||
|
send_side_bwe_with_overhead_(webrtc::field_trial::IsEnabled(
|
||||||
"WebRTC-SendSideBwe-WithOverhead")),
|
"WebRTC-SendSideBwe-WithOverhead")),
|
||||||
packet_loss_rate_(0.0),
|
packet_loss_rate_(0.0),
|
||||||
inst_(nullptr),
|
inst_(nullptr),
|
||||||
@ -382,42 +355,42 @@ AudioEncoderOpus::AudioEncoderOpus(
|
|||||||
RTC_CHECK(RecreateEncoderInstance(config));
|
RTC_CHECK(RecreateEncoderInstance(config));
|
||||||
}
|
}
|
||||||
|
|
||||||
AudioEncoderOpus::AudioEncoderOpus(const CodecInst& codec_inst)
|
AudioEncoderOpusImpl::AudioEncoderOpusImpl(const CodecInst& codec_inst)
|
||||||
: AudioEncoderOpus(CreateConfig(codec_inst), nullptr) {}
|
: AudioEncoderOpusImpl(CreateConfig(codec_inst), codec_inst.pltype) {}
|
||||||
|
|
||||||
AudioEncoderOpus::AudioEncoderOpus(int payload_type,
|
AudioEncoderOpusImpl::AudioEncoderOpusImpl(int payload_type,
|
||||||
const SdpAudioFormat& format)
|
const SdpAudioFormat& format)
|
||||||
: AudioEncoderOpus(CreateConfig(payload_type, format), nullptr) {}
|
: AudioEncoderOpusImpl(*SdpToConfig(format), payload_type) {}
|
||||||
|
|
||||||
AudioEncoderOpus::~AudioEncoderOpus() {
|
AudioEncoderOpusImpl::~AudioEncoderOpusImpl() {
|
||||||
RTC_CHECK_EQ(0, WebRtcOpus_EncoderFree(inst_));
|
RTC_CHECK_EQ(0, WebRtcOpus_EncoderFree(inst_));
|
||||||
}
|
}
|
||||||
|
|
||||||
int AudioEncoderOpus::SampleRateHz() const {
|
int AudioEncoderOpusImpl::SampleRateHz() const {
|
||||||
return kSampleRateHz;
|
return kSampleRateHz;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t AudioEncoderOpus::NumChannels() const {
|
size_t AudioEncoderOpusImpl::NumChannels() const {
|
||||||
return config_.num_channels;
|
return config_.num_channels;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t AudioEncoderOpus::Num10MsFramesInNextPacket() const {
|
size_t AudioEncoderOpusImpl::Num10MsFramesInNextPacket() const {
|
||||||
return Num10msFramesPerPacket();
|
return Num10msFramesPerPacket();
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t AudioEncoderOpus::Max10MsFramesInAPacket() const {
|
size_t AudioEncoderOpusImpl::Max10MsFramesInAPacket() const {
|
||||||
return Num10msFramesPerPacket();
|
return Num10msFramesPerPacket();
|
||||||
}
|
}
|
||||||
|
|
||||||
int AudioEncoderOpus::GetTargetBitrate() const {
|
int AudioEncoderOpusImpl::GetTargetBitrate() const {
|
||||||
return config_.GetBitrateBps();
|
return GetBitrateBps(config_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioEncoderOpus::Reset() {
|
void AudioEncoderOpusImpl::Reset() {
|
||||||
RTC_CHECK(RecreateEncoderInstance(config_));
|
RTC_CHECK(RecreateEncoderInstance(config_));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AudioEncoderOpus::SetFec(bool enable) {
|
bool AudioEncoderOpusImpl::SetFec(bool enable) {
|
||||||
if (enable) {
|
if (enable) {
|
||||||
RTC_CHECK_EQ(0, WebRtcOpus_EnableFec(inst_));
|
RTC_CHECK_EQ(0, WebRtcOpus_EnableFec(inst_));
|
||||||
} else {
|
} else {
|
||||||
@ -427,7 +400,7 @@ bool AudioEncoderOpus::SetFec(bool enable) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AudioEncoderOpus::SetDtx(bool enable) {
|
bool AudioEncoderOpusImpl::SetDtx(bool enable) {
|
||||||
if (enable) {
|
if (enable) {
|
||||||
RTC_CHECK_EQ(0, WebRtcOpus_EnableDtx(inst_));
|
RTC_CHECK_EQ(0, WebRtcOpus_EnableDtx(inst_));
|
||||||
} else {
|
} else {
|
||||||
@ -437,30 +410,30 @@ bool AudioEncoderOpus::SetDtx(bool enable) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AudioEncoderOpus::GetDtx() const {
|
bool AudioEncoderOpusImpl::GetDtx() const {
|
||||||
return config_.dtx_enabled;
|
return config_.dtx_enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AudioEncoderOpus::SetApplication(Application application) {
|
bool AudioEncoderOpusImpl::SetApplication(Application application) {
|
||||||
auto conf = config_;
|
auto conf = config_;
|
||||||
switch (application) {
|
switch (application) {
|
||||||
case Application::kSpeech:
|
case Application::kSpeech:
|
||||||
conf.application = AudioEncoderOpus::kVoip;
|
conf.application = AudioEncoderOpusConfig::ApplicationMode::kVoip;
|
||||||
break;
|
break;
|
||||||
case Application::kAudio:
|
case Application::kAudio:
|
||||||
conf.application = AudioEncoderOpus::kAudio;
|
conf.application = AudioEncoderOpusConfig::ApplicationMode::kAudio;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return RecreateEncoderInstance(conf);
|
return RecreateEncoderInstance(conf);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioEncoderOpus::SetMaxPlaybackRate(int frequency_hz) {
|
void AudioEncoderOpusImpl::SetMaxPlaybackRate(int frequency_hz) {
|
||||||
auto conf = config_;
|
auto conf = config_;
|
||||||
conf.max_playback_rate_hz = frequency_hz;
|
conf.max_playback_rate_hz = frequency_hz;
|
||||||
RTC_CHECK(RecreateEncoderInstance(conf));
|
RTC_CHECK(RecreateEncoderInstance(conf));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AudioEncoderOpus::EnableAudioNetworkAdaptor(
|
bool AudioEncoderOpusImpl::EnableAudioNetworkAdaptor(
|
||||||
const std::string& config_string,
|
const std::string& config_string,
|
||||||
RtcEventLog* event_log) {
|
RtcEventLog* event_log) {
|
||||||
audio_network_adaptor_ =
|
audio_network_adaptor_ =
|
||||||
@ -468,11 +441,11 @@ bool AudioEncoderOpus::EnableAudioNetworkAdaptor(
|
|||||||
return audio_network_adaptor_.get() != nullptr;
|
return audio_network_adaptor_.get() != nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioEncoderOpus::DisableAudioNetworkAdaptor() {
|
void AudioEncoderOpusImpl::DisableAudioNetworkAdaptor() {
|
||||||
audio_network_adaptor_.reset(nullptr);
|
audio_network_adaptor_.reset(nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioEncoderOpus::OnReceivedUplinkPacketLossFraction(
|
void AudioEncoderOpusImpl::OnReceivedUplinkPacketLossFraction(
|
||||||
float uplink_packet_loss_fraction) {
|
float uplink_packet_loss_fraction) {
|
||||||
if (!audio_network_adaptor_) {
|
if (!audio_network_adaptor_) {
|
||||||
packet_loss_fraction_smoother_->AddSample(uplink_packet_loss_fraction);
|
packet_loss_fraction_smoother_->AddSample(uplink_packet_loss_fraction);
|
||||||
@ -484,7 +457,7 @@ void AudioEncoderOpus::OnReceivedUplinkPacketLossFraction(
|
|||||||
ApplyAudioNetworkAdaptor();
|
ApplyAudioNetworkAdaptor();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioEncoderOpus::OnReceivedUplinkRecoverablePacketLossFraction(
|
void AudioEncoderOpusImpl::OnReceivedUplinkRecoverablePacketLossFraction(
|
||||||
float uplink_recoverable_packet_loss_fraction) {
|
float uplink_recoverable_packet_loss_fraction) {
|
||||||
if (!audio_network_adaptor_)
|
if (!audio_network_adaptor_)
|
||||||
return;
|
return;
|
||||||
@ -493,7 +466,7 @@ void AudioEncoderOpus::OnReceivedUplinkRecoverablePacketLossFraction(
|
|||||||
ApplyAudioNetworkAdaptor();
|
ApplyAudioNetworkAdaptor();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioEncoderOpus::OnReceivedUplinkBandwidth(
|
void AudioEncoderOpusImpl::OnReceivedUplinkBandwidth(
|
||||||
int target_audio_bitrate_bps,
|
int target_audio_bitrate_bps,
|
||||||
rtc::Optional<int64_t> probing_interval_ms) {
|
rtc::Optional<int64_t> probing_interval_ms) {
|
||||||
if (audio_network_adaptor_) {
|
if (audio_network_adaptor_) {
|
||||||
@ -517,28 +490,30 @@ void AudioEncoderOpus::OnReceivedUplinkBandwidth(
|
|||||||
} else if (send_side_bwe_with_overhead_) {
|
} else if (send_side_bwe_with_overhead_) {
|
||||||
if (!overhead_bytes_per_packet_) {
|
if (!overhead_bytes_per_packet_) {
|
||||||
LOG(LS_INFO)
|
LOG(LS_INFO)
|
||||||
<< "AudioEncoderOpus: Overhead unknown, target audio bitrate "
|
<< "AudioEncoderOpusImpl: Overhead unknown, target audio bitrate "
|
||||||
<< target_audio_bitrate_bps << " bps is ignored.";
|
<< target_audio_bitrate_bps << " bps is ignored.";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const int overhead_bps = static_cast<int>(
|
const int overhead_bps = static_cast<int>(
|
||||||
*overhead_bytes_per_packet_ * 8 * 100 / Num10MsFramesInNextPacket());
|
*overhead_bytes_per_packet_ * 8 * 100 / Num10MsFramesInNextPacket());
|
||||||
SetTargetBitrate(std::min(
|
SetTargetBitrate(
|
||||||
kOpusMaxBitrateBps,
|
std::min(AudioEncoderOpusConfig::kMaxBitrateBps,
|
||||||
std::max(kOpusMinBitrateBps, target_audio_bitrate_bps - overhead_bps)));
|
std::max(AudioEncoderOpusConfig::kMinBitrateBps,
|
||||||
|
target_audio_bitrate_bps - overhead_bps)));
|
||||||
} else {
|
} else {
|
||||||
SetTargetBitrate(target_audio_bitrate_bps);
|
SetTargetBitrate(target_audio_bitrate_bps);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioEncoderOpus::OnReceivedRtt(int rtt_ms) {
|
void AudioEncoderOpusImpl::OnReceivedRtt(int rtt_ms) {
|
||||||
if (!audio_network_adaptor_)
|
if (!audio_network_adaptor_)
|
||||||
return;
|
return;
|
||||||
audio_network_adaptor_->SetRtt(rtt_ms);
|
audio_network_adaptor_->SetRtt(rtt_ms);
|
||||||
ApplyAudioNetworkAdaptor();
|
ApplyAudioNetworkAdaptor();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioEncoderOpus::OnReceivedOverhead(size_t overhead_bytes_per_packet) {
|
void AudioEncoderOpusImpl::OnReceivedOverhead(
|
||||||
|
size_t overhead_bytes_per_packet) {
|
||||||
if (audio_network_adaptor_) {
|
if (audio_network_adaptor_) {
|
||||||
audio_network_adaptor_->SetOverhead(overhead_bytes_per_packet);
|
audio_network_adaptor_->SetOverhead(overhead_bytes_per_packet);
|
||||||
ApplyAudioNetworkAdaptor();
|
ApplyAudioNetworkAdaptor();
|
||||||
@ -548,8 +523,9 @@ void AudioEncoderOpus::OnReceivedOverhead(size_t overhead_bytes_per_packet) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioEncoderOpus::SetReceiverFrameLengthRange(int min_frame_length_ms,
|
void AudioEncoderOpusImpl::SetReceiverFrameLengthRange(
|
||||||
int max_frame_length_ms) {
|
int min_frame_length_ms,
|
||||||
|
int max_frame_length_ms) {
|
||||||
// Ensure that |SetReceiverFrameLengthRange| is called before
|
// Ensure that |SetReceiverFrameLengthRange| is called before
|
||||||
// |EnableAudioNetworkAdaptor|, otherwise we need to recreate
|
// |EnableAudioNetworkAdaptor|, otherwise we need to recreate
|
||||||
// |audio_network_adaptor_|, which is not a needed use case.
|
// |audio_network_adaptor_|, which is not a needed use case.
|
||||||
@ -558,7 +534,7 @@ void AudioEncoderOpus::SetReceiverFrameLengthRange(int min_frame_length_ms,
|
|||||||
&config_.supported_frame_lengths_ms);
|
&config_.supported_frame_lengths_ms);
|
||||||
}
|
}
|
||||||
|
|
||||||
AudioEncoder::EncodedInfo AudioEncoderOpus::EncodeImpl(
|
AudioEncoder::EncodedInfo AudioEncoderOpusImpl::EncodeImpl(
|
||||||
uint32_t rtp_timestamp,
|
uint32_t rtp_timestamp,
|
||||||
rtc::ArrayView<const int16_t> audio,
|
rtc::ArrayView<const int16_t> audio,
|
||||||
rtc::Buffer* encoded) {
|
rtc::Buffer* encoded) {
|
||||||
@ -597,26 +573,26 @@ AudioEncoder::EncodedInfo AudioEncoderOpus::EncodeImpl(
|
|||||||
config_.frame_size_ms = next_frame_length_ms_;
|
config_.frame_size_ms = next_frame_length_ms_;
|
||||||
|
|
||||||
info.encoded_timestamp = first_timestamp_in_buffer_;
|
info.encoded_timestamp = first_timestamp_in_buffer_;
|
||||||
info.payload_type = config_.payload_type;
|
info.payload_type = payload_type_;
|
||||||
info.send_even_if_empty = true; // Allows Opus to send empty packets.
|
info.send_even_if_empty = true; // Allows Opus to send empty packets.
|
||||||
info.speech = (info.encoded_bytes > 0);
|
info.speech = (info.encoded_bytes > 0);
|
||||||
info.encoder_type = CodecType::kOpus;
|
info.encoder_type = CodecType::kOpus;
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t AudioEncoderOpus::Num10msFramesPerPacket() const {
|
size_t AudioEncoderOpusImpl::Num10msFramesPerPacket() const {
|
||||||
return static_cast<size_t>(rtc::CheckedDivExact(config_.frame_size_ms, 10));
|
return static_cast<size_t>(rtc::CheckedDivExact(config_.frame_size_ms, 10));
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t AudioEncoderOpus::SamplesPer10msFrame() const {
|
size_t AudioEncoderOpusImpl::SamplesPer10msFrame() const {
|
||||||
return rtc::CheckedDivExact(kSampleRateHz, 100) * config_.num_channels;
|
return rtc::CheckedDivExact(kSampleRateHz, 100) * config_.num_channels;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t AudioEncoderOpus::SufficientOutputBufferSize() const {
|
size_t AudioEncoderOpusImpl::SufficientOutputBufferSize() const {
|
||||||
// Calculate the number of bytes we expect the encoder to produce,
|
// Calculate the number of bytes we expect the encoder to produce,
|
||||||
// then multiply by two to give a wide margin for error.
|
// then multiply by two to give a wide margin for error.
|
||||||
const size_t bytes_per_millisecond =
|
const size_t bytes_per_millisecond =
|
||||||
static_cast<size_t>(config_.GetBitrateBps() / (1000 * 8) + 1);
|
static_cast<size_t>(GetBitrateBps(config_) / (1000 * 8) + 1);
|
||||||
const size_t approx_encoded_bytes =
|
const size_t approx_encoded_bytes =
|
||||||
Num10msFramesPerPacket() * 10 * bytes_per_millisecond;
|
Num10msFramesPerPacket() * 10 * bytes_per_millisecond;
|
||||||
return 2 * approx_encoded_bytes;
|
return 2 * approx_encoded_bytes;
|
||||||
@ -625,7 +601,8 @@ size_t AudioEncoderOpus::SufficientOutputBufferSize() const {
|
|||||||
// If the given config is OK, recreate the Opus encoder instance with those
|
// If the given config is OK, recreate the Opus encoder instance with those
|
||||||
// settings, save the config, and return true. Otherwise, do nothing and return
|
// settings, save the config, and return true. Otherwise, do nothing and return
|
||||||
// false.
|
// false.
|
||||||
bool AudioEncoderOpus::RecreateEncoderInstance(const Config& config) {
|
bool AudioEncoderOpusImpl::RecreateEncoderInstance(
|
||||||
|
const AudioEncoderOpusConfig& config) {
|
||||||
if (!config.IsOk())
|
if (!config.IsOk())
|
||||||
return false;
|
return false;
|
||||||
config_ = config;
|
config_ = config;
|
||||||
@ -633,9 +610,13 @@ bool AudioEncoderOpus::RecreateEncoderInstance(const Config& config) {
|
|||||||
RTC_CHECK_EQ(0, WebRtcOpus_EncoderFree(inst_));
|
RTC_CHECK_EQ(0, WebRtcOpus_EncoderFree(inst_));
|
||||||
input_buffer_.clear();
|
input_buffer_.clear();
|
||||||
input_buffer_.reserve(Num10msFramesPerPacket() * SamplesPer10msFrame());
|
input_buffer_.reserve(Num10msFramesPerPacket() * SamplesPer10msFrame());
|
||||||
RTC_CHECK_EQ(0, WebRtcOpus_EncoderCreate(&inst_, config.num_channels,
|
RTC_CHECK_EQ(0, WebRtcOpus_EncoderCreate(
|
||||||
config.application));
|
&inst_, config.num_channels,
|
||||||
RTC_CHECK_EQ(0, WebRtcOpus_SetBitRate(inst_, config.GetBitrateBps()));
|
config.application ==
|
||||||
|
AudioEncoderOpusConfig::ApplicationMode::kVoip
|
||||||
|
? 0
|
||||||
|
: 1));
|
||||||
|
RTC_CHECK_EQ(0, WebRtcOpus_SetBitRate(inst_, GetBitrateBps(config)));
|
||||||
if (config.fec_enabled) {
|
if (config.fec_enabled) {
|
||||||
RTC_CHECK_EQ(0, WebRtcOpus_EnableFec(inst_));
|
RTC_CHECK_EQ(0, WebRtcOpus_EnableFec(inst_));
|
||||||
} else {
|
} else {
|
||||||
@ -645,7 +626,7 @@ bool AudioEncoderOpus::RecreateEncoderInstance(const Config& config) {
|
|||||||
0, WebRtcOpus_SetMaxPlaybackRate(inst_, config.max_playback_rate_hz));
|
0, WebRtcOpus_SetMaxPlaybackRate(inst_, config.max_playback_rate_hz));
|
||||||
// Use the default complexity if the start bitrate is within the hysteresis
|
// Use the default complexity if the start bitrate is within the hysteresis
|
||||||
// window.
|
// window.
|
||||||
complexity_ = config.GetNewComplexity().value_or(config.complexity);
|
complexity_ = GetNewComplexity(config).value_or(config.complexity);
|
||||||
RTC_CHECK_EQ(0, WebRtcOpus_SetComplexity(inst_, complexity_));
|
RTC_CHECK_EQ(0, WebRtcOpus_SetComplexity(inst_, complexity_));
|
||||||
if (config.dtx_enabled) {
|
if (config.dtx_enabled) {
|
||||||
RTC_CHECK_EQ(0, WebRtcOpus_EnableDtx(inst_));
|
RTC_CHECK_EQ(0, WebRtcOpus_EnableDtx(inst_));
|
||||||
@ -665,11 +646,12 @@ bool AudioEncoderOpus::RecreateEncoderInstance(const Config& config) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioEncoderOpus::SetFrameLength(int frame_length_ms) {
|
void AudioEncoderOpusImpl::SetFrameLength(int frame_length_ms) {
|
||||||
next_frame_length_ms_ = frame_length_ms;
|
next_frame_length_ms_ = frame_length_ms;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioEncoderOpus::SetNumChannelsToEncode(size_t num_channels_to_encode) {
|
void AudioEncoderOpusImpl::SetNumChannelsToEncode(
|
||||||
|
size_t num_channels_to_encode) {
|
||||||
RTC_DCHECK_GT(num_channels_to_encode, 0);
|
RTC_DCHECK_GT(num_channels_to_encode, 0);
|
||||||
RTC_DCHECK_LE(num_channels_to_encode, config_.num_channels);
|
RTC_DCHECK_LE(num_channels_to_encode, config_.num_channels);
|
||||||
|
|
||||||
@ -680,7 +662,7 @@ void AudioEncoderOpus::SetNumChannelsToEncode(size_t num_channels_to_encode) {
|
|||||||
num_channels_to_encode_ = num_channels_to_encode;
|
num_channels_to_encode_ = num_channels_to_encode;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioEncoderOpus::SetProjectedPacketLossRate(float fraction) {
|
void AudioEncoderOpusImpl::SetProjectedPacketLossRate(float fraction) {
|
||||||
float opt_loss_rate = OptimizePacketLossRate(fraction, packet_loss_rate_);
|
float opt_loss_rate = OptimizePacketLossRate(fraction, packet_loss_rate_);
|
||||||
if (packet_loss_rate_ != opt_loss_rate) {
|
if (packet_loss_rate_ != opt_loss_rate) {
|
||||||
packet_loss_rate_ = opt_loss_rate;
|
packet_loss_rate_ = opt_loss_rate;
|
||||||
@ -690,19 +672,20 @@ void AudioEncoderOpus::SetProjectedPacketLossRate(float fraction) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioEncoderOpus::SetTargetBitrate(int bits_per_second) {
|
void AudioEncoderOpusImpl::SetTargetBitrate(int bits_per_second) {
|
||||||
config_.bitrate_bps = rtc::Optional<int>(rtc::SafeClamp<int>(
|
config_.bitrate_bps = rtc::SafeClamp<int>(
|
||||||
bits_per_second, kOpusMinBitrateBps, kOpusMaxBitrateBps));
|
bits_per_second, AudioEncoderOpusConfig::kMinBitrateBps,
|
||||||
|
AudioEncoderOpusConfig::kMaxBitrateBps);
|
||||||
RTC_DCHECK(config_.IsOk());
|
RTC_DCHECK(config_.IsOk());
|
||||||
RTC_CHECK_EQ(0, WebRtcOpus_SetBitRate(inst_, config_.GetBitrateBps()));
|
RTC_CHECK_EQ(0, WebRtcOpus_SetBitRate(inst_, GetBitrateBps(config_)));
|
||||||
const auto new_complexity = config_.GetNewComplexity();
|
const auto new_complexity = GetNewComplexity(config_);
|
||||||
if (new_complexity && complexity_ != *new_complexity) {
|
if (new_complexity && complexity_ != *new_complexity) {
|
||||||
complexity_ = *new_complexity;
|
complexity_ = *new_complexity;
|
||||||
RTC_CHECK_EQ(0, WebRtcOpus_SetComplexity(inst_, complexity_));
|
RTC_CHECK_EQ(0, WebRtcOpus_SetComplexity(inst_, complexity_));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioEncoderOpus::ApplyAudioNetworkAdaptor() {
|
void AudioEncoderOpusImpl::ApplyAudioNetworkAdaptor() {
|
||||||
auto config = audio_network_adaptor_->GetEncoderRuntimeConfig();
|
auto config = audio_network_adaptor_->GetEncoderRuntimeConfig();
|
||||||
RTC_DCHECK(!config.frame_length_ms || *config.frame_length_ms == 20 ||
|
RTC_DCHECK(!config.frame_length_ms || *config.frame_length_ms == 20 ||
|
||||||
*config.frame_length_ms == 60);
|
*config.frame_length_ms == 60);
|
||||||
@ -722,20 +705,20 @@ void AudioEncoderOpus::ApplyAudioNetworkAdaptor() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<AudioNetworkAdaptor>
|
std::unique_ptr<AudioNetworkAdaptor>
|
||||||
AudioEncoderOpus::DefaultAudioNetworkAdaptorCreator(
|
AudioEncoderOpusImpl::DefaultAudioNetworkAdaptorCreator(
|
||||||
const ProtoString& config_string,
|
const ProtoString& config_string,
|
||||||
RtcEventLog* event_log) const {
|
RtcEventLog* event_log) const {
|
||||||
AudioNetworkAdaptorImpl::Config config;
|
AudioNetworkAdaptorImpl::Config config;
|
||||||
config.event_log = event_log;
|
config.event_log = event_log;
|
||||||
return std::unique_ptr<AudioNetworkAdaptor>(new AudioNetworkAdaptorImpl(
|
return std::unique_ptr<AudioNetworkAdaptor>(new AudioNetworkAdaptorImpl(
|
||||||
config,
|
config, ControllerManagerImpl::Create(
|
||||||
ControllerManagerImpl::Create(
|
config_string, NumChannels(), supported_frame_lengths_ms(),
|
||||||
config_string, NumChannels(), supported_frame_lengths_ms(),
|
AudioEncoderOpusConfig::kMinBitrateBps,
|
||||||
kOpusMinBitrateBps, num_channels_to_encode_, next_frame_length_ms_,
|
num_channels_to_encode_, next_frame_length_ms_,
|
||||||
GetTargetBitrate(), config_.fec_enabled, GetDtx())));
|
GetTargetBitrate(), config_.fec_enabled, GetDtx())));
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioEncoderOpus::MaybeUpdateUplinkBandwidth() {
|
void AudioEncoderOpusImpl::MaybeUpdateUplinkBandwidth() {
|
||||||
if (audio_network_adaptor_) {
|
if (audio_network_adaptor_) {
|
||||||
int64_t now_ms = rtc::TimeMillis();
|
int64_t now_ms = rtc::TimeMillis();
|
||||||
if (!bitrate_smoother_last_update_time_ ||
|
if (!bitrate_smoother_last_update_time_ ||
|
||||||
|
|||||||
@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
#include "webrtc/api/audio_codecs/audio_encoder.h"
|
#include "webrtc/api/audio_codecs/audio_encoder.h"
|
||||||
#include "webrtc/api/audio_codecs/audio_format.h"
|
#include "webrtc/api/audio_codecs/audio_format.h"
|
||||||
|
#include "webrtc/api/audio_codecs/opus/audio_encoder_opus_config.h"
|
||||||
#include "webrtc/base/constructormagic.h"
|
#include "webrtc/base/constructormagic.h"
|
||||||
#include "webrtc/base/optional.h"
|
#include "webrtc/base/optional.h"
|
||||||
#include "webrtc/base/protobuf_utils.h"
|
#include "webrtc/base/protobuf_utils.h"
|
||||||
@ -31,70 +32,31 @@ class RtcEventLog;
|
|||||||
|
|
||||||
struct CodecInst;
|
struct CodecInst;
|
||||||
|
|
||||||
class AudioEncoderOpus final : public AudioEncoder {
|
class AudioEncoderOpusImpl final : public AudioEncoder {
|
||||||
public:
|
public:
|
||||||
enum ApplicationMode {
|
static AudioEncoderOpusConfig CreateConfig(const CodecInst& codec_inst);
|
||||||
kVoip = 0,
|
static rtc::Optional<AudioEncoderOpusConfig> SdpToConfig(
|
||||||
kAudio = 1,
|
const SdpAudioFormat& format);
|
||||||
};
|
|
||||||
|
|
||||||
struct Config {
|
// Returns empty if the current bitrate falls within the hysteresis window,
|
||||||
Config();
|
// defined by complexity_threshold_bps +/- complexity_threshold_window_bps.
|
||||||
Config(const Config&);
|
// Otherwise, returns the current complexity depending on whether the current
|
||||||
~Config();
|
// bitrate is above or below complexity_threshold_bps.
|
||||||
Config& operator=(const Config&);
|
static rtc::Optional<int> GetNewComplexity(
|
||||||
|
const AudioEncoderOpusConfig& config);
|
||||||
bool IsOk() const;
|
|
||||||
int GetBitrateBps() const;
|
|
||||||
// Returns empty if the current bitrate falls within the hysteresis window,
|
|
||||||
// defined by complexity_threshold_bps +/- complexity_threshold_window_bps.
|
|
||||||
// Otherwise, returns the current complexity depending on whether the
|
|
||||||
// current bitrate is above or below complexity_threshold_bps.
|
|
||||||
rtc::Optional<int> GetNewComplexity() const;
|
|
||||||
|
|
||||||
static constexpr int kDefaultFrameSizeMs = 20;
|
|
||||||
int frame_size_ms = kDefaultFrameSizeMs;
|
|
||||||
size_t num_channels = 1;
|
|
||||||
int payload_type = 120;
|
|
||||||
ApplicationMode application = kVoip;
|
|
||||||
rtc::Optional<int> bitrate_bps; // Unset means to use default value.
|
|
||||||
bool fec_enabled = false;
|
|
||||||
bool cbr_enabled = false;
|
|
||||||
int max_playback_rate_hz = 48000;
|
|
||||||
int complexity = kDefaultComplexity;
|
|
||||||
// This value may change in the struct's constructor.
|
|
||||||
int low_rate_complexity = kDefaultComplexity;
|
|
||||||
// low_rate_complexity is used when the bitrate is below this threshold.
|
|
||||||
int complexity_threshold_bps = 12500;
|
|
||||||
int complexity_threshold_window_bps = 1500;
|
|
||||||
bool dtx_enabled = false;
|
|
||||||
std::vector<int> supported_frame_lengths_ms;
|
|
||||||
int uplink_bandwidth_update_interval_ms = 200;
|
|
||||||
|
|
||||||
private:
|
|
||||||
#if defined(WEBRTC_ANDROID) || defined(WEBRTC_IOS) || defined(WEBRTC_ARCH_ARM)
|
|
||||||
// If we are on Android, iOS and/or ARM, use a lower complexity setting as
|
|
||||||
// default, to save encoder complexity.
|
|
||||||
static const int kDefaultComplexity = 5;
|
|
||||||
#else
|
|
||||||
static const int kDefaultComplexity = 9;
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
static Config CreateConfig(int payload_type, const SdpAudioFormat& format);
|
|
||||||
static Config CreateConfig(const CodecInst& codec_inst);
|
|
||||||
|
|
||||||
using AudioNetworkAdaptorCreator =
|
using AudioNetworkAdaptorCreator =
|
||||||
std::function<std::unique_ptr<AudioNetworkAdaptor>(const std::string&,
|
std::function<std::unique_ptr<AudioNetworkAdaptor>(const std::string&,
|
||||||
RtcEventLog*)>;
|
RtcEventLog*)>;
|
||||||
AudioEncoderOpus(
|
AudioEncoderOpusImpl(
|
||||||
const Config& config,
|
const AudioEncoderOpusConfig& config,
|
||||||
|
int payload_type,
|
||||||
AudioNetworkAdaptorCreator&& audio_network_adaptor_creator = nullptr,
|
AudioNetworkAdaptorCreator&& audio_network_adaptor_creator = nullptr,
|
||||||
std::unique_ptr<SmoothingFilter> bitrate_smoother = nullptr);
|
std::unique_ptr<SmoothingFilter> bitrate_smoother = nullptr);
|
||||||
|
|
||||||
explicit AudioEncoderOpus(const CodecInst& codec_inst);
|
explicit AudioEncoderOpusImpl(const CodecInst& codec_inst);
|
||||||
AudioEncoderOpus(int payload_type, const SdpAudioFormat& format);
|
AudioEncoderOpusImpl(int payload_type, const SdpAudioFormat& format);
|
||||||
~AudioEncoderOpus() override;
|
~AudioEncoderOpusImpl() override;
|
||||||
|
|
||||||
// Static interface for use by BuiltinAudioEncoderFactory.
|
// Static interface for use by BuiltinAudioEncoderFactory.
|
||||||
static constexpr const char* GetPayloadName() { return "opus"; }
|
static constexpr const char* GetPayloadName() { return "opus"; }
|
||||||
@ -138,7 +100,9 @@ class AudioEncoderOpus final : public AudioEncoder {
|
|||||||
|
|
||||||
// Getters for testing.
|
// Getters for testing.
|
||||||
float packet_loss_rate() const { return packet_loss_rate_; }
|
float packet_loss_rate() const { return packet_loss_rate_; }
|
||||||
ApplicationMode application() const { return config_.application; }
|
AudioEncoderOpusConfig::ApplicationMode application() const {
|
||||||
|
return config_.application;
|
||||||
|
}
|
||||||
bool fec_enabled() const { return config_.fec_enabled; }
|
bool fec_enabled() const { return config_.fec_enabled; }
|
||||||
size_t num_channels_to_encode() const { return num_channels_to_encode_; }
|
size_t num_channels_to_encode() const { return num_channels_to_encode_; }
|
||||||
int next_frame_length_ms() const { return next_frame_length_ms_; }
|
int next_frame_length_ms() const { return next_frame_length_ms_; }
|
||||||
@ -154,7 +118,7 @@ class AudioEncoderOpus final : public AudioEncoder {
|
|||||||
size_t Num10msFramesPerPacket() const;
|
size_t Num10msFramesPerPacket() const;
|
||||||
size_t SamplesPer10msFrame() const;
|
size_t SamplesPer10msFrame() const;
|
||||||
size_t SufficientOutputBufferSize() const;
|
size_t SufficientOutputBufferSize() const;
|
||||||
bool RecreateEncoderInstance(const Config& config);
|
bool RecreateEncoderInstance(const AudioEncoderOpusConfig& config);
|
||||||
void SetFrameLength(int frame_length_ms);
|
void SetFrameLength(int frame_length_ms);
|
||||||
void SetNumChannelsToEncode(size_t num_channels_to_encode);
|
void SetNumChannelsToEncode(size_t num_channels_to_encode);
|
||||||
void SetProjectedPacketLossRate(float fraction);
|
void SetProjectedPacketLossRate(float fraction);
|
||||||
@ -170,7 +134,8 @@ class AudioEncoderOpus final : public AudioEncoder {
|
|||||||
|
|
||||||
void MaybeUpdateUplinkBandwidth();
|
void MaybeUpdateUplinkBandwidth();
|
||||||
|
|
||||||
Config config_;
|
AudioEncoderOpusConfig config_;
|
||||||
|
const int payload_type_;
|
||||||
const bool send_side_bwe_with_overhead_;
|
const bool send_side_bwe_with_overhead_;
|
||||||
float packet_loss_rate_;
|
float packet_loss_rate_;
|
||||||
std::vector<int16_t> input_buffer_;
|
std::vector<int16_t> input_buffer_;
|
||||||
@ -186,7 +151,7 @@ class AudioEncoderOpus final : public AudioEncoder {
|
|||||||
const std::unique_ptr<SmoothingFilter> bitrate_smoother_;
|
const std::unique_ptr<SmoothingFilter> bitrate_smoother_;
|
||||||
rtc::Optional<int64_t> bitrate_smoother_last_update_time_;
|
rtc::Optional<int64_t> bitrate_smoother_last_update_time_;
|
||||||
|
|
||||||
RTC_DISALLOW_COPY_AND_ASSIGN(AudioEncoderOpus);
|
RTC_DISALLOW_COPY_AND_ASSIGN(AudioEncoderOpusImpl);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
|||||||
@ -33,30 +33,30 @@ namespace {
|
|||||||
const CodecInst kDefaultOpusSettings = {105, "opus", 48000, 960, 1, 32000};
|
const CodecInst kDefaultOpusSettings = {105, "opus", 48000, 960, 1, 32000};
|
||||||
constexpr int64_t kInitialTimeUs = 12345678;
|
constexpr int64_t kInitialTimeUs = 12345678;
|
||||||
|
|
||||||
AudioEncoderOpus::Config CreateConfig(const CodecInst& codec_inst) {
|
AudioEncoderOpusConfig CreateConfig(const CodecInst& codec_inst) {
|
||||||
AudioEncoderOpus::Config config;
|
AudioEncoderOpusConfig config;
|
||||||
config.frame_size_ms = rtc::CheckedDivExact(codec_inst.pacsize, 48);
|
config.frame_size_ms = rtc::CheckedDivExact(codec_inst.pacsize, 48);
|
||||||
config.num_channels = codec_inst.channels;
|
config.num_channels = codec_inst.channels;
|
||||||
config.bitrate_bps = rtc::Optional<int>(codec_inst.rate);
|
config.bitrate_bps = codec_inst.rate;
|
||||||
config.payload_type = codec_inst.pltype;
|
config.application = config.num_channels == 1
|
||||||
config.application = config.num_channels == 1 ? AudioEncoderOpus::kVoip
|
? AudioEncoderOpusConfig::ApplicationMode::kVoip
|
||||||
: AudioEncoderOpus::kAudio;
|
: AudioEncoderOpusConfig::ApplicationMode::kAudio;
|
||||||
config.supported_frame_lengths_ms.push_back(config.frame_size_ms);
|
config.supported_frame_lengths_ms.push_back(config.frame_size_ms);
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
AudioEncoderOpus::Config CreateConfigWithParameters(
|
AudioEncoderOpusConfig CreateConfigWithParameters(
|
||||||
const SdpAudioFormat::Parameters& params) {
|
const SdpAudioFormat::Parameters& params) {
|
||||||
SdpAudioFormat format("opus", 48000, 2, params);
|
const SdpAudioFormat format("opus", 48000, 2, params);
|
||||||
return AudioEncoderOpus::CreateConfig(0, format);
|
return *AudioEncoderOpusImpl::SdpToConfig(format);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct AudioEncoderOpusStates {
|
struct AudioEncoderOpusStates {
|
||||||
std::shared_ptr<MockAudioNetworkAdaptor*> mock_audio_network_adaptor;
|
std::shared_ptr<MockAudioNetworkAdaptor*> mock_audio_network_adaptor;
|
||||||
MockSmoothingFilter* mock_bitrate_smoother;
|
MockSmoothingFilter* mock_bitrate_smoother;
|
||||||
std::unique_ptr<AudioEncoderOpus> encoder;
|
std::unique_ptr<AudioEncoderOpusImpl> encoder;
|
||||||
std::unique_ptr<rtc::ScopedFakeClock> fake_clock;
|
std::unique_ptr<rtc::ScopedFakeClock> fake_clock;
|
||||||
AudioEncoderOpus::Config config;
|
AudioEncoderOpusConfig config;
|
||||||
};
|
};
|
||||||
|
|
||||||
AudioEncoderOpusStates CreateCodec(size_t num_channels) {
|
AudioEncoderOpusStates CreateCodec(size_t num_channels) {
|
||||||
@ -67,7 +67,7 @@ AudioEncoderOpusStates CreateCodec(size_t num_channels) {
|
|||||||
states.fake_clock->SetTimeMicros(kInitialTimeUs);
|
states.fake_clock->SetTimeMicros(kInitialTimeUs);
|
||||||
std::weak_ptr<MockAudioNetworkAdaptor*> mock_ptr(
|
std::weak_ptr<MockAudioNetworkAdaptor*> mock_ptr(
|
||||||
states.mock_audio_network_adaptor);
|
states.mock_audio_network_adaptor);
|
||||||
AudioEncoderOpus::AudioNetworkAdaptorCreator creator =
|
AudioEncoderOpusImpl::AudioNetworkAdaptorCreator creator =
|
||||||
[mock_ptr](const std::string&, RtcEventLog* event_log) {
|
[mock_ptr](const std::string&, RtcEventLog* event_log) {
|
||||||
std::unique_ptr<MockAudioNetworkAdaptor> adaptor(
|
std::unique_ptr<MockAudioNetworkAdaptor> adaptor(
|
||||||
new NiceMock<MockAudioNetworkAdaptor>());
|
new NiceMock<MockAudioNetworkAdaptor>());
|
||||||
@ -87,8 +87,9 @@ AudioEncoderOpusStates CreateCodec(size_t num_channels) {
|
|||||||
new MockSmoothingFilter());
|
new MockSmoothingFilter());
|
||||||
states.mock_bitrate_smoother = bitrate_smoother.get();
|
states.mock_bitrate_smoother = bitrate_smoother.get();
|
||||||
|
|
||||||
states.encoder.reset(new AudioEncoderOpus(states.config, std::move(creator),
|
states.encoder.reset(new AudioEncoderOpusImpl(
|
||||||
std::move(bitrate_smoother)));
|
states.config, codec_inst.pltype, std::move(creator),
|
||||||
|
std::move(bitrate_smoother)));
|
||||||
return states;
|
return states;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -110,7 +111,7 @@ AudioEncoderRuntimeConfig CreateEncoderRuntimeConfig() {
|
|||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CheckEncoderRuntimeConfig(const AudioEncoderOpus* encoder,
|
void CheckEncoderRuntimeConfig(const AudioEncoderOpusImpl* encoder,
|
||||||
const AudioEncoderRuntimeConfig& config) {
|
const AudioEncoderRuntimeConfig& config) {
|
||||||
EXPECT_EQ(*config.bitrate_bps, encoder->GetTargetBitrate());
|
EXPECT_EQ(*config.bitrate_bps, encoder->GetTargetBitrate());
|
||||||
EXPECT_EQ(*config.frame_length_ms, encoder->next_frame_length_ms());
|
EXPECT_EQ(*config.frame_length_ms, encoder->next_frame_length_ms());
|
||||||
@ -121,7 +122,7 @@ void CheckEncoderRuntimeConfig(const AudioEncoderOpus* encoder,
|
|||||||
|
|
||||||
// Create 10ms audio data blocks for a total packet size of "packet_size_ms".
|
// Create 10ms audio data blocks for a total packet size of "packet_size_ms".
|
||||||
std::unique_ptr<test::AudioLoop> Create10msAudioBlocks(
|
std::unique_ptr<test::AudioLoop> Create10msAudioBlocks(
|
||||||
const std::unique_ptr<AudioEncoderOpus>& encoder,
|
const std::unique_ptr<AudioEncoderOpusImpl>& encoder,
|
||||||
int packet_size_ms) {
|
int packet_size_ms) {
|
||||||
const std::string file_name =
|
const std::string file_name =
|
||||||
test::ResourcePath("audio_coding/testfile32kHz", "pcm");
|
test::ResourcePath("audio_coding/testfile32kHz", "pcm");
|
||||||
@ -142,19 +143,22 @@ std::unique_ptr<test::AudioLoop> Create10msAudioBlocks(
|
|||||||
|
|
||||||
TEST(AudioEncoderOpusTest, DefaultApplicationModeMono) {
|
TEST(AudioEncoderOpusTest, DefaultApplicationModeMono) {
|
||||||
auto states = CreateCodec(1);
|
auto states = CreateCodec(1);
|
||||||
EXPECT_EQ(AudioEncoderOpus::kVoip, states.encoder->application());
|
EXPECT_EQ(AudioEncoderOpusConfig::ApplicationMode::kVoip,
|
||||||
|
states.encoder->application());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(AudioEncoderOpusTest, DefaultApplicationModeStereo) {
|
TEST(AudioEncoderOpusTest, DefaultApplicationModeStereo) {
|
||||||
auto states = CreateCodec(2);
|
auto states = CreateCodec(2);
|
||||||
EXPECT_EQ(AudioEncoderOpus::kAudio, states.encoder->application());
|
EXPECT_EQ(AudioEncoderOpusConfig::ApplicationMode::kAudio,
|
||||||
|
states.encoder->application());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(AudioEncoderOpusTest, ChangeApplicationMode) {
|
TEST(AudioEncoderOpusTest, ChangeApplicationMode) {
|
||||||
auto states = CreateCodec(2);
|
auto states = CreateCodec(2);
|
||||||
EXPECT_TRUE(
|
EXPECT_TRUE(
|
||||||
states.encoder->SetApplication(AudioEncoder::Application::kSpeech));
|
states.encoder->SetApplication(AudioEncoder::Application::kSpeech));
|
||||||
EXPECT_EQ(AudioEncoderOpus::kVoip, states.encoder->application());
|
EXPECT_EQ(AudioEncoderOpusConfig::ApplicationMode::kVoip,
|
||||||
|
states.encoder->application());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(AudioEncoderOpusTest, ResetWontChangeApplicationMode) {
|
TEST(AudioEncoderOpusTest, ResetWontChangeApplicationMode) {
|
||||||
@ -163,17 +167,20 @@ TEST(AudioEncoderOpusTest, ResetWontChangeApplicationMode) {
|
|||||||
// Trigger a reset.
|
// Trigger a reset.
|
||||||
states.encoder->Reset();
|
states.encoder->Reset();
|
||||||
// Verify that the mode is still kAudio.
|
// Verify that the mode is still kAudio.
|
||||||
EXPECT_EQ(AudioEncoderOpus::kAudio, states.encoder->application());
|
EXPECT_EQ(AudioEncoderOpusConfig::ApplicationMode::kAudio,
|
||||||
|
states.encoder->application());
|
||||||
|
|
||||||
// Now change to kVoip.
|
// Now change to kVoip.
|
||||||
EXPECT_TRUE(
|
EXPECT_TRUE(
|
||||||
states.encoder->SetApplication(AudioEncoder::Application::kSpeech));
|
states.encoder->SetApplication(AudioEncoder::Application::kSpeech));
|
||||||
EXPECT_EQ(AudioEncoderOpus::kVoip, states.encoder->application());
|
EXPECT_EQ(AudioEncoderOpusConfig::ApplicationMode::kVoip,
|
||||||
|
states.encoder->application());
|
||||||
|
|
||||||
// Trigger a reset again.
|
// Trigger a reset again.
|
||||||
states.encoder->Reset();
|
states.encoder->Reset();
|
||||||
// Verify that the mode is still kVoip.
|
// Verify that the mode is still kVoip.
|
||||||
EXPECT_EQ(AudioEncoderOpus::kVoip, states.encoder->application());
|
EXPECT_EQ(AudioEncoderOpusConfig::ApplicationMode::kVoip,
|
||||||
|
states.encoder->application());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(AudioEncoderOpusTest, ToggleDtx) {
|
TEST(AudioEncoderOpusTest, ToggleDtx) {
|
||||||
@ -238,7 +245,7 @@ void TestSetPacketLossRate(AudioEncoderOpusStates* states,
|
|||||||
float expected_return) {
|
float expected_return) {
|
||||||
// |kSampleIntervalMs| is chosen to ease the calculation since
|
// |kSampleIntervalMs| is chosen to ease the calculation since
|
||||||
// 0.9999 ^ 184198 = 1e-8. Which minimizes the effect of
|
// 0.9999 ^ 184198 = 1e-8. Which minimizes the effect of
|
||||||
// PacketLossFractionSmoother used in AudioEncoderOpus.
|
// PacketLossFractionSmoother used in AudioEncoderOpusImpl.
|
||||||
constexpr int64_t kSampleIntervalMs = 184198;
|
constexpr int64_t kSampleIntervalMs = 184198;
|
||||||
for (float loss : losses) {
|
for (float loss : losses) {
|
||||||
states->encoder->OnReceivedUplinkPacketLossFraction(loss);
|
states->encoder->OnReceivedUplinkPacketLossFraction(loss);
|
||||||
@ -452,25 +459,29 @@ TEST(AudioEncoderOpusTest, BitrateBounded) {
|
|||||||
|
|
||||||
// Verifies that the complexity adaptation in the config works as intended.
|
// Verifies that the complexity adaptation in the config works as intended.
|
||||||
TEST(AudioEncoderOpusTest, ConfigComplexityAdaptation) {
|
TEST(AudioEncoderOpusTest, ConfigComplexityAdaptation) {
|
||||||
AudioEncoderOpus::Config config;
|
AudioEncoderOpusConfig config;
|
||||||
config.low_rate_complexity = 8;
|
config.low_rate_complexity = 8;
|
||||||
config.complexity = 6;
|
config.complexity = 6;
|
||||||
|
|
||||||
// Bitrate within hysteresis window. Expect empty output.
|
// Bitrate within hysteresis window. Expect empty output.
|
||||||
config.bitrate_bps = rtc::Optional<int>(12500);
|
config.bitrate_bps = 12500;
|
||||||
EXPECT_EQ(rtc::Optional<int>(), config.GetNewComplexity());
|
EXPECT_EQ(rtc::Optional<int>(),
|
||||||
|
AudioEncoderOpusImpl::GetNewComplexity(config));
|
||||||
|
|
||||||
// Bitrate below hysteresis window. Expect higher complexity.
|
// Bitrate below hysteresis window. Expect higher complexity.
|
||||||
config.bitrate_bps = rtc::Optional<int>(10999);
|
config.bitrate_bps = 10999;
|
||||||
EXPECT_EQ(rtc::Optional<int>(8), config.GetNewComplexity());
|
EXPECT_EQ(rtc::Optional<int>(8),
|
||||||
|
AudioEncoderOpusImpl::GetNewComplexity(config));
|
||||||
|
|
||||||
// Bitrate within hysteresis window. Expect empty output.
|
// Bitrate within hysteresis window. Expect empty output.
|
||||||
config.bitrate_bps = rtc::Optional<int>(12500);
|
config.bitrate_bps = 12500;
|
||||||
EXPECT_EQ(rtc::Optional<int>(), config.GetNewComplexity());
|
EXPECT_EQ(rtc::Optional<int>(),
|
||||||
|
AudioEncoderOpusImpl::GetNewComplexity(config));
|
||||||
|
|
||||||
// Bitrate above hysteresis window. Expect lower complexity.
|
// Bitrate above hysteresis window. Expect lower complexity.
|
||||||
config.bitrate_bps = rtc::Optional<int>(14001);
|
config.bitrate_bps = 14001;
|
||||||
EXPECT_EQ(rtc::Optional<int>(6), config.GetNewComplexity());
|
EXPECT_EQ(rtc::Optional<int>(6),
|
||||||
|
AudioEncoderOpusImpl::GetNewComplexity(config));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(AudioEncoderOpusTest, EmptyConfigDoesNotAffectEncoderSettings) {
|
TEST(AudioEncoderOpusTest, EmptyConfigDoesNotAffectEncoderSettings) {
|
||||||
@ -552,84 +563,82 @@ TEST(AudioEncoderOpusTest, EncodeAtMinBitrate) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST(AudioEncoderOpusTest, TestConfigDefaults) {
|
TEST(AudioEncoderOpusTest, TestConfigDefaults) {
|
||||||
const AudioEncoderOpus::Config config =
|
const auto config_opt = AudioEncoderOpusImpl::SdpToConfig({"opus", 48000, 2});
|
||||||
AudioEncoderOpus::CreateConfig(0, {"opus", 48000, 2});
|
ASSERT_TRUE(config_opt);
|
||||||
|
EXPECT_EQ(48000, config_opt->max_playback_rate_hz);
|
||||||
EXPECT_EQ(48000, config.max_playback_rate_hz);
|
EXPECT_EQ(1u, config_opt->num_channels);
|
||||||
EXPECT_EQ(1u, config.num_channels);
|
EXPECT_FALSE(config_opt->fec_enabled);
|
||||||
EXPECT_FALSE(config.fec_enabled);
|
EXPECT_FALSE(config_opt->dtx_enabled);
|
||||||
EXPECT_FALSE(config.dtx_enabled);
|
EXPECT_EQ(20, config_opt->frame_size_ms);
|
||||||
EXPECT_EQ(20, config.frame_size_ms);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(AudioEncoderOpusTest, TestConfigFromParams) {
|
TEST(AudioEncoderOpusTest, TestConfigFromParams) {
|
||||||
AudioEncoderOpus::Config config;
|
const auto config1 = CreateConfigWithParameters({{"stereo", "0"}});
|
||||||
|
EXPECT_EQ(1U, config1.num_channels);
|
||||||
|
|
||||||
config = CreateConfigWithParameters({{"stereo", "0"}});
|
const auto config2 = CreateConfigWithParameters({{"stereo", "1"}});
|
||||||
EXPECT_EQ(1U, config.num_channels);
|
EXPECT_EQ(2U, config2.num_channels);
|
||||||
|
|
||||||
config = CreateConfigWithParameters({{"stereo", "1"}});
|
const auto config3 = CreateConfigWithParameters({{"useinbandfec", "0"}});
|
||||||
EXPECT_EQ(2U, config.num_channels);
|
EXPECT_FALSE(config3.fec_enabled);
|
||||||
|
|
||||||
config = CreateConfigWithParameters({{"useinbandfec", "0"}});
|
const auto config4 = CreateConfigWithParameters({{"useinbandfec", "1"}});
|
||||||
EXPECT_FALSE(config.fec_enabled);
|
EXPECT_TRUE(config4.fec_enabled);
|
||||||
|
|
||||||
config = CreateConfigWithParameters({{"useinbandfec", "1"}});
|
const auto config5 = CreateConfigWithParameters({{"usedtx", "0"}});
|
||||||
EXPECT_TRUE(config.fec_enabled);
|
EXPECT_FALSE(config5.dtx_enabled);
|
||||||
|
|
||||||
config = CreateConfigWithParameters({{"usedtx", "0"}});
|
const auto config6 = CreateConfigWithParameters({{"usedtx", "1"}});
|
||||||
EXPECT_FALSE(config.dtx_enabled);
|
EXPECT_TRUE(config6.dtx_enabled);
|
||||||
|
|
||||||
config = CreateConfigWithParameters({{"usedtx", "1"}});
|
const auto config7 = CreateConfigWithParameters({{"cbr", "0"}});
|
||||||
EXPECT_TRUE(config.dtx_enabled);
|
EXPECT_FALSE(config7.cbr_enabled);
|
||||||
|
|
||||||
config = CreateConfigWithParameters({{"cbr", "0"}});
|
const auto config8 = CreateConfigWithParameters({{"cbr", "1"}});
|
||||||
EXPECT_FALSE(config.cbr_enabled);
|
EXPECT_TRUE(config8.cbr_enabled);
|
||||||
|
|
||||||
config = CreateConfigWithParameters({{"cbr", "1"}});
|
const auto config9 =
|
||||||
EXPECT_TRUE(config.cbr_enabled);
|
CreateConfigWithParameters({{"maxplaybackrate", "12345"}});
|
||||||
|
EXPECT_EQ(12345, config9.max_playback_rate_hz);
|
||||||
|
|
||||||
config = CreateConfigWithParameters({{"maxplaybackrate", "12345"}});
|
const auto config10 =
|
||||||
EXPECT_EQ(12345, config.max_playback_rate_hz);
|
CreateConfigWithParameters({{"maxaveragebitrate", "96000"}});
|
||||||
|
EXPECT_EQ(96000, config10.bitrate_bps);
|
||||||
|
|
||||||
config = CreateConfigWithParameters({{"maxaveragebitrate", "96000"}});
|
const auto config11 = CreateConfigWithParameters({{"maxptime", "40"}});
|
||||||
EXPECT_EQ(96000, config.bitrate_bps);
|
for (int frame_length : config11.supported_frame_lengths_ms) {
|
||||||
|
|
||||||
config = CreateConfigWithParameters({{"maxptime", "40"}});
|
|
||||||
for (int frame_length : config.supported_frame_lengths_ms) {
|
|
||||||
EXPECT_LE(frame_length, 40);
|
EXPECT_LE(frame_length, 40);
|
||||||
}
|
}
|
||||||
|
|
||||||
config = CreateConfigWithParameters({{"minptime", "40"}});
|
const auto config12 = CreateConfigWithParameters({{"minptime", "40"}});
|
||||||
for (int frame_length : config.supported_frame_lengths_ms) {
|
for (int frame_length : config12.supported_frame_lengths_ms) {
|
||||||
EXPECT_GE(frame_length, 40);
|
EXPECT_GE(frame_length, 40);
|
||||||
}
|
}
|
||||||
|
|
||||||
config = CreateConfigWithParameters({{"ptime", "40"}});
|
const auto config13 = CreateConfigWithParameters({{"ptime", "40"}});
|
||||||
EXPECT_EQ(40, config.frame_size_ms);
|
EXPECT_EQ(40, config13.frame_size_ms);
|
||||||
|
|
||||||
constexpr int kMinSupportedFrameLength = 10;
|
constexpr int kMinSupportedFrameLength = 10;
|
||||||
constexpr int kMaxSupportedFrameLength =
|
constexpr int kMaxSupportedFrameLength =
|
||||||
WEBRTC_OPUS_SUPPORT_120MS_PTIME ? 120 : 60;
|
WEBRTC_OPUS_SUPPORT_120MS_PTIME ? 120 : 60;
|
||||||
|
|
||||||
config = CreateConfigWithParameters({{"ptime", "1"}});
|
const auto config14 = CreateConfigWithParameters({{"ptime", "1"}});
|
||||||
EXPECT_EQ(kMinSupportedFrameLength, config.frame_size_ms);
|
EXPECT_EQ(kMinSupportedFrameLength, config14.frame_size_ms);
|
||||||
|
|
||||||
config = CreateConfigWithParameters({{"ptime", "2000"}});
|
const auto config15 = CreateConfigWithParameters({{"ptime", "2000"}});
|
||||||
EXPECT_EQ(kMaxSupportedFrameLength, config.frame_size_ms);
|
EXPECT_EQ(kMaxSupportedFrameLength, config15.frame_size_ms);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(AudioEncoderOpusTest, TestConfigFromInvalidParams) {
|
TEST(AudioEncoderOpusTest, TestConfigFromInvalidParams) {
|
||||||
const webrtc::SdpAudioFormat format("opus", 48000, 2);
|
const webrtc::SdpAudioFormat format("opus", 48000, 2);
|
||||||
const AudioEncoderOpus::Config default_config =
|
const auto default_config = *AudioEncoderOpusImpl::SdpToConfig(format);
|
||||||
AudioEncoderOpus::CreateConfig(0, format);
|
|
||||||
#if WEBRTC_OPUS_SUPPORT_120MS_PTIME
|
#if WEBRTC_OPUS_SUPPORT_120MS_PTIME
|
||||||
const std::vector<int> default_supported_frame_lengths_ms({20, 60, 120});
|
const std::vector<int> default_supported_frame_lengths_ms({20, 60, 120});
|
||||||
#else
|
#else
|
||||||
const std::vector<int> default_supported_frame_lengths_ms({20, 60});
|
const std::vector<int> default_supported_frame_lengths_ms({20, 60});
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
AudioEncoderOpus::Config config;
|
AudioEncoderOpusConfig config;
|
||||||
config = CreateConfigWithParameters({{"stereo", "invalid"}});
|
config = CreateConfigWithParameters({{"stereo", "invalid"}});
|
||||||
EXPECT_EQ(default_config.num_channels, config.num_channels);
|
EXPECT_EQ(default_config.num_channels, config.num_channels);
|
||||||
|
|
||||||
@ -681,18 +690,18 @@ TEST(AudioEncoderOpusTest, TestConfigFromInvalidParams) {
|
|||||||
// range of 6000 and 510000
|
// range of 6000 and 510000
|
||||||
TEST(AudioEncoderOpusTest, SetSendCodecOpusMaxAverageBitrate) {
|
TEST(AudioEncoderOpusTest, SetSendCodecOpusMaxAverageBitrate) {
|
||||||
// Ignore if less than 6000.
|
// Ignore if less than 6000.
|
||||||
const AudioEncoderOpus::Config config1 = AudioEncoderOpus::CreateConfig(
|
const auto config1 = AudioEncoderOpusImpl::SdpToConfig(
|
||||||
0, {"opus", 48000, 2, {{"maxaveragebitrate", "5999"}}});
|
{"opus", 48000, 2, {{"maxaveragebitrate", "5999"}}});
|
||||||
EXPECT_EQ(6000, config1.bitrate_bps);
|
EXPECT_EQ(6000, config1->bitrate_bps);
|
||||||
|
|
||||||
// Ignore if larger than 510000.
|
// Ignore if larger than 510000.
|
||||||
const AudioEncoderOpus::Config config2 = AudioEncoderOpus::CreateConfig(
|
const auto config2 = AudioEncoderOpusImpl::SdpToConfig(
|
||||||
0, {"opus", 48000, 2, {{"maxaveragebitrate", "510001"}}});
|
{"opus", 48000, 2, {{"maxaveragebitrate", "510001"}}});
|
||||||
EXPECT_EQ(510000, config2.bitrate_bps);
|
EXPECT_EQ(510000, config2->bitrate_bps);
|
||||||
|
|
||||||
const AudioEncoderOpus::Config config3 = AudioEncoderOpus::CreateConfig(
|
const auto config3 = AudioEncoderOpusImpl::SdpToConfig(
|
||||||
0, {"opus", 48000, 2, {{"maxaveragebitrate", "200000"}}});
|
{"opus", 48000, 2, {{"maxaveragebitrate", "200000"}}});
|
||||||
EXPECT_EQ(200000, config3.bitrate_bps);
|
EXPECT_EQ(200000, config3->bitrate_bps);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test maxplaybackrate <= 8000 triggers Opus narrow band mode.
|
// Test maxplaybackrate <= 8000 triggers Opus narrow band mode.
|
||||||
|
|||||||
@ -19,9 +19,10 @@
|
|||||||
namespace webrtc {
|
namespace webrtc {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
int64_t RunComplexityTest(const AudioEncoderOpus::Config& config) {
|
int64_t RunComplexityTest(const AudioEncoderOpusConfig& config) {
|
||||||
// Create encoder.
|
// Create encoder.
|
||||||
AudioEncoderOpus encoder(config);
|
constexpr int payload_type = 17;
|
||||||
|
AudioEncoderOpusImpl encoder(config, payload_type);
|
||||||
// Open speech file.
|
// Open speech file.
|
||||||
const std::string kInputFileName =
|
const std::string kInputFileName =
|
||||||
webrtc::test::ResourcePath("audio_coding/speech_mono_32_48kHz", "pcm");
|
webrtc::test::ResourcePath("audio_coding/speech_mono_32_48kHz", "pcm");
|
||||||
@ -60,14 +61,14 @@ int64_t RunComplexityTest(const AudioEncoderOpus::Config& config) {
|
|||||||
// the lower rate.
|
// the lower rate.
|
||||||
TEST(AudioEncoderOpusComplexityAdaptationTest, AdaptationOn) {
|
TEST(AudioEncoderOpusComplexityAdaptationTest, AdaptationOn) {
|
||||||
// Create config.
|
// Create config.
|
||||||
AudioEncoderOpus::Config config;
|
AudioEncoderOpusConfig config;
|
||||||
// The limit -- including the hysteresis window -- at which the complexity
|
// The limit -- including the hysteresis window -- at which the complexity
|
||||||
// shuold be increased.
|
// shuold be increased.
|
||||||
config.bitrate_bps = rtc::Optional<int>(11000 - 1);
|
config.bitrate_bps = 11000 - 1;
|
||||||
config.low_rate_complexity = 9;
|
config.low_rate_complexity = 9;
|
||||||
int64_t runtime_10999bps = RunComplexityTest(config);
|
int64_t runtime_10999bps = RunComplexityTest(config);
|
||||||
|
|
||||||
config.bitrate_bps = rtc::Optional<int>(15500);
|
config.bitrate_bps = 15500;
|
||||||
int64_t runtime_15500bps = RunComplexityTest(config);
|
int64_t runtime_15500bps = RunComplexityTest(config);
|
||||||
|
|
||||||
test::PrintResult("opus_encoding_complexity_ratio", "", "adaptation_on",
|
test::PrintResult("opus_encoding_complexity_ratio", "", "adaptation_on",
|
||||||
@ -80,14 +81,14 @@ TEST(AudioEncoderOpusComplexityAdaptationTest, AdaptationOn) {
|
|||||||
// that the resulting ratio is less than 100% at all times.
|
// that the resulting ratio is less than 100% at all times.
|
||||||
TEST(AudioEncoderOpusComplexityAdaptationTest, AdaptationOff) {
|
TEST(AudioEncoderOpusComplexityAdaptationTest, AdaptationOff) {
|
||||||
// Create config.
|
// Create config.
|
||||||
AudioEncoderOpus::Config config;
|
AudioEncoderOpusConfig config;
|
||||||
// The limit -- including the hysteresis window -- at which the complexity
|
// The limit -- including the hysteresis window -- at which the complexity
|
||||||
// shuold be increased (but not in this test since complexity adaptation is
|
// shuold be increased (but not in this test since complexity adaptation is
|
||||||
// disabled).
|
// disabled).
|
||||||
config.bitrate_bps = rtc::Optional<int>(11000 - 1);
|
config.bitrate_bps = 11000 - 1;
|
||||||
int64_t runtime_10999bps = RunComplexityTest(config);
|
int64_t runtime_10999bps = RunComplexityTest(config);
|
||||||
|
|
||||||
config.bitrate_bps = rtc::Optional<int>(15500);
|
config.bitrate_bps = 15500;
|
||||||
int64_t runtime_15500bps = RunComplexityTest(config);
|
int64_t runtime_15500bps = RunComplexityTest(config);
|
||||||
|
|
||||||
test::PrintResult("opus_encoding_complexity_ratio", "", "adaptation_off",
|
test::PrintResult("opus_encoding_complexity_ratio", "", "adaptation_off",
|
||||||
|
|||||||
@ -17,6 +17,7 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "webrtc/api/audio_codecs/opus/audio_encoder_opus.h"
|
||||||
#include "webrtc/modules/audio_coding/codecs/g711/audio_decoder_pcm.h"
|
#include "webrtc/modules/audio_coding/codecs/g711/audio_decoder_pcm.h"
|
||||||
#include "webrtc/modules/audio_coding/codecs/g711/audio_encoder_pcm.h"
|
#include "webrtc/modules/audio_coding/codecs/g711/audio_encoder_pcm.h"
|
||||||
#include "webrtc/modules/audio_coding/codecs/g722/audio_decoder_g722.h"
|
#include "webrtc/modules/audio_coding/codecs/g722/audio_decoder_g722.h"
|
||||||
@ -28,7 +29,6 @@
|
|||||||
#include "webrtc/modules/audio_coding/codecs/isac/main/include/audio_decoder_isac.h"
|
#include "webrtc/modules/audio_coding/codecs/isac/main/include/audio_decoder_isac.h"
|
||||||
#include "webrtc/modules/audio_coding/codecs/isac/main/include/audio_encoder_isac.h"
|
#include "webrtc/modules/audio_coding/codecs/isac/main/include/audio_encoder_isac.h"
|
||||||
#include "webrtc/modules/audio_coding/codecs/opus/audio_decoder_opus.h"
|
#include "webrtc/modules/audio_coding/codecs/opus/audio_decoder_opus.h"
|
||||||
#include "webrtc/modules/audio_coding/codecs/opus/audio_encoder_opus.h"
|
|
||||||
#include "webrtc/modules/audio_coding/codecs/pcm16b/audio_decoder_pcm16b.h"
|
#include "webrtc/modules/audio_coding/codecs/pcm16b/audio_decoder_pcm16b.h"
|
||||||
#include "webrtc/modules/audio_coding/codecs/pcm16b/audio_encoder_pcm16b.h"
|
#include "webrtc/modules/audio_coding/codecs/pcm16b/audio_encoder_pcm16b.h"
|
||||||
#include "webrtc/modules/audio_coding/neteq/tools/resample_input_audio_file.h"
|
#include "webrtc/modules/audio_coding/neteq/tools/resample_input_audio_file.h"
|
||||||
@ -434,11 +434,10 @@ class AudioDecoderOpusTest : public AudioDecoderTest {
|
|||||||
frame_size_ = 480;
|
frame_size_ = 480;
|
||||||
data_length_ = 10 * frame_size_;
|
data_length_ = 10 * frame_size_;
|
||||||
decoder_ = new AudioDecoderOpus(1);
|
decoder_ = new AudioDecoderOpus(1);
|
||||||
AudioEncoderOpus::Config config;
|
AudioEncoderOpusConfig config;
|
||||||
config.frame_size_ms = static_cast<int>(frame_size_) / 48;
|
config.frame_size_ms = static_cast<int>(frame_size_) / 48;
|
||||||
config.payload_type = payload_type_;
|
config.application = AudioEncoderOpusConfig::ApplicationMode::kVoip;
|
||||||
config.application = AudioEncoderOpus::kVoip;
|
audio_encoder_ = AudioEncoderOpus::MakeAudioEncoder(config, payload_type_);
|
||||||
audio_encoder_.reset(new AudioEncoderOpus(config));
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -448,12 +447,11 @@ class AudioDecoderOpusStereoTest : public AudioDecoderOpusTest {
|
|||||||
channels_ = 2;
|
channels_ = 2;
|
||||||
delete decoder_;
|
delete decoder_;
|
||||||
decoder_ = new AudioDecoderOpus(2);
|
decoder_ = new AudioDecoderOpus(2);
|
||||||
AudioEncoderOpus::Config config;
|
AudioEncoderOpusConfig config;
|
||||||
config.frame_size_ms = static_cast<int>(frame_size_) / 48;
|
config.frame_size_ms = static_cast<int>(frame_size_) / 48;
|
||||||
config.num_channels = 2;
|
config.num_channels = 2;
|
||||||
config.payload_type = payload_type_;
|
config.application = AudioEncoderOpusConfig::ApplicationMode::kAudio;
|
||||||
config.application = AudioEncoderOpus::kAudio;
|
audio_encoder_ = AudioEncoderOpus::MakeAudioEncoder(config, payload_type_);
|
||||||
audio_encoder_.reset(new AudioEncoderOpus(config));
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user