Index: webrtc/modules/audio_coding/codecs/opus/audio_encoder_opus.cc |
diff --git a/webrtc/modules/audio_coding/codecs/opus/audio_encoder_opus.cc b/webrtc/modules/audio_coding/codecs/opus/audio_encoder_opus.cc |
index 515f6c0f4215cf21cb8f1491c1014493df243003..2eedb5285e4f6139495d99d02e7caf2ba8808f8f 100644 |
--- a/webrtc/modules/audio_coding/codecs/opus/audio_encoder_opus.cc |
+++ b/webrtc/modules/audio_coding/codecs/opus/audio_encoder_opus.cc |
@@ -10,6 +10,8 @@ |
#include "webrtc/modules/audio_coding/codecs/opus/audio_encoder_opus.h" |
+#include <stdlib.h> |
+ |
#include <algorithm> |
#include <iterator> |
@@ -17,6 +19,7 @@ |
#include "webrtc/base/logging.h" |
#include "webrtc/base/numerics/exp_filter.h" |
#include "webrtc/base/safe_conversions.h" |
+#include "webrtc/base/string_to_number.h" |
#include "webrtc/base/timeutils.h" |
#include "webrtc/common_types.h" |
#include "webrtc/modules/audio_coding/audio_network_adaptor/audio_network_adaptor_impl.h" |
@@ -28,6 +31,28 @@ namespace webrtc { |
namespace { |
+// Codec parameters for Opus. |
+// draft-spittka-payload-rtp-opus-03 |
+ |
+// Recommended bitrates: |
+// 8-12 kb/s for NB speech, |
+// 16-20 kb/s for WB speech, |
+// 28-40 kb/s for FB speech, |
+// 48-64 kb/s for FB mono music, and |
+// 64-128 kb/s for FB stereo music. |
+// The current implementation applies the following values to mono signals, |
+// and multiplies them by 2 for stereo. |
+const int kOpusBitrateNbBps = 12000; |
+const int kOpusBitrateWbBps = 20000; |
+const int kOpusBitrateFbBps = 32000; |
+ |
+// Opus bitrate should be in the range between 6000 and 510000. |
+const int kOpusMinBitrateBps = 6000; |
+const int kOpusMaxBitrateBps = 510000; |
+ |
+// TODO(ossu): These are from kCodecPrefs in webrtcvoiceengine.cc |
+const int kOpusSupportedFrameLengths[] = {10, 20, 40, 60}; |
+ |
constexpr int kSampleRateHz = 48000; |
// Opus API allows a min bitrate of 500bps, but Opus documentation suggests |
@@ -37,9 +62,9 @@ constexpr int kMinBitrateBps = 6000; |
constexpr int kMaxBitrateBps = 512000; |
#if WEBRTC_OPUS_SUPPORT_120MS_PTIME |
-constexpr int kSupportedFrameLengths[] = {20, 60, 120}; |
+constexpr int kANASupportedFrameLengths[] = {20, 60, 120}; |
#else |
-constexpr int kSupportedFrameLengths[] = {20, 60}; |
+constexpr int kANASupportedFrameLengths[] = {20, 60}; |
#endif |
// PacketLossFractionSmoother uses an exponential filter with a time constant |
@@ -103,6 +128,151 @@ float OptimizePacketLossRate(float new_loss_rate, float old_loss_rate) { |
} // namespace |
+rtc::Optional<AudioFormatInfo> AudioEncoderOpus::QueryAudioFormat( |
+ const SdpAudioFormat& format) { |
+ if (STR_CASE_CMP(format.name.c_str(), GetPayloadName()) == 0 && |
+ format.clockrate_hz == 48000 && format.num_channels == 2) { |
kwiberg-webrtc
2017/02/19 21:41:10
Invert this test and return early, to save lots of
ossu
2017/03/14 20:25:11
I didn't do this, but I simplified the function en
|
+ int num_channels = 1; |
+ const auto stereo_param = format.parameters.find("stereo"); |
+ if (stereo_param != format.parameters.end()) { |
+ if (stereo_param->second == "1") { |
+ num_channels = 2; |
+ } else if (stereo_param->second != "0") { |
+ return rtc::Optional<AudioFormatInfo>(); |
+ } |
+ } |
+ |
+ int max_playback_rate = 48000; |
+ const auto maxplaybackrate_param = |
+ format.parameters.find("maxplaybackrate"); |
+ if (maxplaybackrate_param != format.parameters.end()) { |
+ const auto opt_max_playback_rate = |
+ rtc::StringToNumber<int>(maxplaybackrate_param->second); |
+ if (opt_max_playback_rate) { |
+ max_playback_rate = *opt_max_playback_rate; |
+ if (max_playback_rate <= 0) { |
+ return rtc::Optional<AudioFormatInfo>(); |
+ } |
+ } |
+ } |
+ |
+ int bitrate; |
+ if (max_playback_rate && max_playback_rate <= 8000) { |
kwiberg-webrtc
2017/02/19 21:41:10
max_playback_rate is an int, isn't it? Is 0 specia
ossu
2017/02/20 12:20:26
I'll look through this whole function. It duplicat
|
+ bitrate = kOpusBitrateNbBps * num_channels; |
+ } else if (max_playback_rate && max_playback_rate <= 16000) { |
+ bitrate = kOpusBitrateWbBps * num_channels; |
+ } else { |
+ bitrate = kOpusBitrateFbBps * num_channels; |
+ } |
+ |
+ AudioFormatInfo info(48000, num_channels, bitrate, kOpusMinBitrateBps, |
+ kOpusMaxBitrateBps); |
+ info.allow_comfort_noise = false; |
+ info.supports_network_adaption = true; |
+ |
+ return rtc::Optional<AudioFormatInfo>(info); |
+ } |
+ return rtc::Optional<AudioFormatInfo>(); |
+} |
+ |
+AudioEncoderOpus::Config AudioEncoderOpus::CreateConfig( |
+ int payload_type, |
+ const SdpAudioFormat& format) { |
+ auto get_param_int = |
+ [&format] (const std::string& param) { |
+ auto it = format.parameters.find(param); |
+ if (it != format.parameters.end()) { |
+ return rtc::StringToNumber<int>(it->second.c_str()); |
+ } |
+ return rtc::Optional<int>(); |
+ }; |
+ |
+ auto get_param = |
+ [&format] (const std::string& param) { |
+ auto it = format.parameters.find(param); |
+ return (it == format.parameters.end()) |
+ ? std::string() |
+ : it->second; |
+ }; |
+ |
+ AudioEncoderOpus::Config config; |
+ config.num_channels = (get_param("stereo") == "1") ? 2 : 1; |
+ config.fec_enabled = (get_param("useinbandfec") == "1"); |
+ config.dtx_enabled = (get_param("usedtx") == "1"); |
+ const auto max_playback_rate = get_param_int("maxplaybackrate"); |
+ if (max_playback_rate && *max_playback_rate > 0) { |
+ config.max_playback_rate_hz = *max_playback_rate; |
+ } |
+ const auto max_average_bitrate = get_param_int("maxaveragebitrate"); |
+ bool use_param = true; |
+ int bitrate = 0; |
+ if (max_average_bitrate && *max_average_bitrate > 0) { |
+ bitrate = *max_average_bitrate; |
+ } else { |
+ if (max_playback_rate && *max_playback_rate <= 8000) { |
+ bitrate = kOpusBitrateNbBps * config.num_channels; |
+ } else if (max_playback_rate && *max_playback_rate <= 16000) { |
+ bitrate = kOpusBitrateWbBps * config.num_channels; |
+ } else { |
+ bitrate = kOpusBitrateFbBps * config.num_channels; |
+ } |
+ use_param = false; |
+ } |
+ |
+ if (bitrate < kOpusMinBitrateBps || bitrate > kOpusMaxBitrateBps) { |
+ bitrate = (bitrate < kOpusMinBitrateBps) ? kOpusMinBitrateBps |
+ : kOpusMaxBitrateBps; |
+ std::string rate_source = |
+ use_param ? "Codec parameter \"maxaveragebitrate\"" |
+ : "Default Opus bitrate"; |
+ LOG(LS_WARNING) << rate_source |
+ << " is invalid and is replaced by: " << bitrate; |
+ } |
+ config.bitrate_bps = rtc::Optional<int>(bitrate); |
+ config.payload_type = payload_type; |
+ config.application = config.num_channels == 1 ? AudioEncoderOpus::kVoip |
+ : AudioEncoderOpus::kAudio; |
+#if WEBRTC_OPUS_VARIABLE_COMPLEXITY |
+ config.low_rate_complexity = 9; |
+#endif |
+ |
+ // TODO(ossu): What to do if ptime is not between minptime and maxptime? |
+ const auto ptime = get_param_int("ptime"); |
+ if (ptime) { |
+ // Expects kOpusSupportedFrameLengths to be sorted. |
+ for (const int supported_frame_length : kOpusSupportedFrameLengths) { |
+ if (supported_frame_length >= *ptime) { |
+ config.frame_size_ms = *ptime; |
+ } |
+ } |
+ } |
+ |
+ const int min_frame_length_ms = |
+ std::min(std::max(get_param_int("minptime").value_or(10), 10), 60); |
+ const int max_frame_length_ms = |
+ std::min(std::max(get_param_int("maxptime").value_or(60), 10), 60); |
+ if (min_frame_length_ms <= max_frame_length_ms) { |
+ for (const int frame_length_ms : kANASupportedFrameLengths) { |
+ if (frame_length_ms >= min_frame_length_ms && |
+ frame_length_ms <= max_frame_length_ms) { |
+ config.supported_frame_lengths_ms.push_back(frame_length_ms); |
+ } |
+ } |
+ } |
+ |
+ // As a fallback, just pick the whole set of supported frame lengths. |
+ if (config.supported_frame_lengths_ms.empty()) { |
+ for (const int frame_length_ms : kANASupportedFrameLengths) { |
+ config.supported_frame_lengths_ms.push_back(frame_length_ms); |
+ } |
+ } |
+ |
+ RTC_DCHECK(std::is_sorted(config.supported_frame_lengths_ms.begin(), |
+ config.supported_frame_lengths_ms.end())); |
+ |
+ return config; |
+} |
kwiberg-webrtc
2017/02/19 21:41:10
This function is very long. It has several parts t
ossu
2017/02/20 12:20:26
Agreed.
ossu
2017/03/14 20:25:11
Moved common functionality from QueryAudioFormat a
|
+ |
class AudioEncoderOpus::PacketLossFractionSmoother { |
public: |
explicit PacketLossFractionSmoother(const Clock* clock) |
@@ -206,7 +376,11 @@ AudioEncoderOpus::AudioEncoderOpus( |
} |
AudioEncoderOpus::AudioEncoderOpus(const CodecInst& codec_inst) |
- : AudioEncoderOpus(CreateConfig(codec_inst), nullptr) {} |
+ : AudioEncoderOpus(webrtc::CreateConfig(codec_inst), nullptr) {} |
kwiberg-webrtc
2017/02/19 21:41:10
Wait... webrtc::CreateConfig?
ossu
2017/02/20 12:20:26
When I added a CreateConfig for my case, it needed
|
+ |
+AudioEncoderOpus::AudioEncoderOpus(int payload_type, |
+ const SdpAudioFormat& format) |
+ : AudioEncoderOpus(CreateConfig(payload_type, format), nullptr) {} |
AudioEncoderOpus::~AudioEncoderOpus() { |
RTC_CHECK_EQ(0, WebRtcOpus_EncoderFree(inst_)); |
@@ -367,8 +541,8 @@ void AudioEncoderOpus::SetReceiverFrameLengthRange(int min_frame_length_ms, |
RTC_DCHECK(!audio_network_adaptor_); |
config_.supported_frame_lengths_ms.clear(); |
- std::copy_if(std::begin(kSupportedFrameLengths), |
- std::end(kSupportedFrameLengths), |
+ std::copy_if(std::begin(kANASupportedFrameLengths), |
+ std::end(kANASupportedFrameLengths), |
std::back_inserter(config_.supported_frame_lengths_ms), |
[&](int frame_length_ms) { |
return frame_length_ms >= min_frame_length_ms && |