1 /* 2 * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11 #include "webrtc/modules/audio_coding/codecs/opus/audio_encoder_opus.h" 12 13 #include "webrtc/base/checks.h" 14 #include "webrtc/base/safe_conversions.h" 15 #include "webrtc/common_types.h" 16 #include "webrtc/modules/audio_coding/codecs/opus/opus_interface.h" 17 18 namespace webrtc { 19 20 namespace { 21 22 const int kSampleRateHz = 48000; 23 const int kMinBitrateBps = 500; 24 const int kMaxBitrateBps = 512000; 25 26 AudioEncoderOpus::Config CreateConfig(const CodecInst& codec_inst) { 27 AudioEncoderOpus::Config config; 28 config.frame_size_ms = rtc::CheckedDivExact(codec_inst.pacsize, 48); 29 config.num_channels = codec_inst.channels; 30 config.bitrate_bps = codec_inst.rate; 31 config.payload_type = codec_inst.pltype; 32 config.application = config.num_channels == 1 ? AudioEncoderOpus::kVoip 33 : AudioEncoderOpus::kAudio; 34 return config; 35 } 36 37 // Optimize the loss rate to configure Opus. Basically, optimized loss rate is 38 // the input loss rate rounded down to various levels, because a robustly good 39 // audio quality is achieved by lowering the packet loss down. 40 // Additionally, to prevent toggling, margins are used, i.e., when jumping to 41 // a loss rate from below, a higher threshold is used than jumping to the same 42 // level from above. 43 double OptimizePacketLossRate(double new_loss_rate, double old_loss_rate) { 44 RTC_DCHECK_GE(new_loss_rate, 0.0); 45 RTC_DCHECK_LE(new_loss_rate, 1.0); 46 RTC_DCHECK_GE(old_loss_rate, 0.0); 47 RTC_DCHECK_LE(old_loss_rate, 1.0); 48 const double kPacketLossRate20 = 0.20; 49 const double kPacketLossRate10 = 0.10; 50 const double kPacketLossRate5 = 0.05; 51 const double kPacketLossRate1 = 0.01; 52 const double kLossRate20Margin = 0.02; 53 const double kLossRate10Margin = 0.01; 54 const double kLossRate5Margin = 0.01; 55 if (new_loss_rate >= 56 kPacketLossRate20 + 57 kLossRate20Margin * 58 (kPacketLossRate20 - old_loss_rate > 0 ? 1 : -1)) { 59 return kPacketLossRate20; 60 } else if (new_loss_rate >= 61 kPacketLossRate10 + 62 kLossRate10Margin * 63 (kPacketLossRate10 - old_loss_rate > 0 ? 1 : -1)) { 64 return kPacketLossRate10; 65 } else if (new_loss_rate >= 66 kPacketLossRate5 + 67 kLossRate5Margin * 68 (kPacketLossRate5 - old_loss_rate > 0 ? 1 : -1)) { 69 return kPacketLossRate5; 70 } else if (new_loss_rate >= kPacketLossRate1) { 71 return kPacketLossRate1; 72 } else { 73 return 0.0; 74 } 75 } 76 77 } // namespace 78 79 bool AudioEncoderOpus::Config::IsOk() const { 80 if (frame_size_ms <= 0 || frame_size_ms % 10 != 0) 81 return false; 82 if (num_channels != 1 && num_channels != 2) 83 return false; 84 if (bitrate_bps < kMinBitrateBps || bitrate_bps > kMaxBitrateBps) 85 return false; 86 if (complexity < 0 || complexity > 10) 87 return false; 88 return true; 89 } 90 91 AudioEncoderOpus::AudioEncoderOpus(const Config& config) 92 : packet_loss_rate_(0.0), inst_(nullptr) { 93 RTC_CHECK(RecreateEncoderInstance(config)); 94 } 95 96 AudioEncoderOpus::AudioEncoderOpus(const CodecInst& codec_inst) 97 : AudioEncoderOpus(CreateConfig(codec_inst)) {} 98 99 AudioEncoderOpus::~AudioEncoderOpus() { 100 RTC_CHECK_EQ(0, WebRtcOpus_EncoderFree(inst_)); 101 } 102 103 size_t AudioEncoderOpus::MaxEncodedBytes() const { 104 // Calculate the number of bytes we expect the encoder to produce, 105 // then multiply by two to give a wide margin for error. 106 const size_t bytes_per_millisecond = 107 static_cast<size_t>(config_.bitrate_bps / (1000 * 8) + 1); 108 const size_t approx_encoded_bytes = 109 Num10msFramesPerPacket() * 10 * bytes_per_millisecond; 110 return 2 * approx_encoded_bytes; 111 } 112 113 int AudioEncoderOpus::SampleRateHz() const { 114 return kSampleRateHz; 115 } 116 117 size_t AudioEncoderOpus::NumChannels() const { 118 return config_.num_channels; 119 } 120 121 size_t AudioEncoderOpus::Num10MsFramesInNextPacket() const { 122 return Num10msFramesPerPacket(); 123 } 124 125 size_t AudioEncoderOpus::Max10MsFramesInAPacket() const { 126 return Num10msFramesPerPacket(); 127 } 128 129 int AudioEncoderOpus::GetTargetBitrate() const { 130 return config_.bitrate_bps; 131 } 132 133 AudioEncoder::EncodedInfo AudioEncoderOpus::EncodeInternal( 134 uint32_t rtp_timestamp, 135 rtc::ArrayView<const int16_t> audio, 136 size_t max_encoded_bytes, 137 uint8_t* encoded) { 138 if (input_buffer_.empty()) 139 first_timestamp_in_buffer_ = rtp_timestamp; 140 RTC_DCHECK_EQ(SamplesPer10msFrame(), audio.size()); 141 input_buffer_.insert(input_buffer_.end(), audio.cbegin(), audio.cend()); 142 if (input_buffer_.size() < 143 (Num10msFramesPerPacket() * SamplesPer10msFrame())) { 144 return EncodedInfo(); 145 } 146 RTC_CHECK_EQ(input_buffer_.size(), 147 Num10msFramesPerPacket() * SamplesPer10msFrame()); 148 int status = WebRtcOpus_Encode( 149 inst_, &input_buffer_[0], 150 rtc::CheckedDivExact(input_buffer_.size(), config_.num_channels), 151 rtc::saturated_cast<int16_t>(max_encoded_bytes), encoded); 152 RTC_CHECK_GE(status, 0); // Fails only if fed invalid data. 153 input_buffer_.clear(); 154 EncodedInfo info; 155 info.encoded_bytes = static_cast<size_t>(status); 156 info.encoded_timestamp = first_timestamp_in_buffer_; 157 info.payload_type = config_.payload_type; 158 info.send_even_if_empty = true; // Allows Opus to send empty packets. 159 info.speech = (status > 0); 160 return info; 161 } 162 163 void AudioEncoderOpus::Reset() { 164 RTC_CHECK(RecreateEncoderInstance(config_)); 165 } 166 167 bool AudioEncoderOpus::SetFec(bool enable) { 168 auto conf = config_; 169 conf.fec_enabled = enable; 170 return RecreateEncoderInstance(conf); 171 } 172 173 bool AudioEncoderOpus::SetDtx(bool enable) { 174 auto conf = config_; 175 conf.dtx_enabled = enable; 176 return RecreateEncoderInstance(conf); 177 } 178 179 bool AudioEncoderOpus::SetApplication(Application application) { 180 auto conf = config_; 181 switch (application) { 182 case Application::kSpeech: 183 conf.application = AudioEncoderOpus::kVoip; 184 break; 185 case Application::kAudio: 186 conf.application = AudioEncoderOpus::kAudio; 187 break; 188 } 189 return RecreateEncoderInstance(conf); 190 } 191 192 void AudioEncoderOpus::SetMaxPlaybackRate(int frequency_hz) { 193 auto conf = config_; 194 conf.max_playback_rate_hz = frequency_hz; 195 RTC_CHECK(RecreateEncoderInstance(conf)); 196 } 197 198 void AudioEncoderOpus::SetProjectedPacketLossRate(double fraction) { 199 double opt_loss_rate = OptimizePacketLossRate(fraction, packet_loss_rate_); 200 if (packet_loss_rate_ != opt_loss_rate) { 201 packet_loss_rate_ = opt_loss_rate; 202 RTC_CHECK_EQ( 203 0, WebRtcOpus_SetPacketLossRate( 204 inst_, static_cast<int32_t>(packet_loss_rate_ * 100 + .5))); 205 } 206 } 207 208 void AudioEncoderOpus::SetTargetBitrate(int bits_per_second) { 209 config_.bitrate_bps = 210 std::max(std::min(bits_per_second, kMaxBitrateBps), kMinBitrateBps); 211 RTC_DCHECK(config_.IsOk()); 212 RTC_CHECK_EQ(0, WebRtcOpus_SetBitRate(inst_, config_.bitrate_bps)); 213 } 214 215 size_t AudioEncoderOpus::Num10msFramesPerPacket() const { 216 return static_cast<size_t>(rtc::CheckedDivExact(config_.frame_size_ms, 10)); 217 } 218 219 size_t AudioEncoderOpus::SamplesPer10msFrame() const { 220 return rtc::CheckedDivExact(kSampleRateHz, 100) * config_.num_channels; 221 } 222 223 // If the given config is OK, recreate the Opus encoder instance with those 224 // settings, save the config, and return true. Otherwise, do nothing and return 225 // false. 226 bool AudioEncoderOpus::RecreateEncoderInstance(const Config& config) { 227 if (!config.IsOk()) 228 return false; 229 if (inst_) 230 RTC_CHECK_EQ(0, WebRtcOpus_EncoderFree(inst_)); 231 input_buffer_.clear(); 232 input_buffer_.reserve(Num10msFramesPerPacket() * SamplesPer10msFrame()); 233 RTC_CHECK_EQ(0, WebRtcOpus_EncoderCreate(&inst_, config.num_channels, 234 config.application)); 235 RTC_CHECK_EQ(0, WebRtcOpus_SetBitRate(inst_, config.bitrate_bps)); 236 if (config.fec_enabled) { 237 RTC_CHECK_EQ(0, WebRtcOpus_EnableFec(inst_)); 238 } else { 239 RTC_CHECK_EQ(0, WebRtcOpus_DisableFec(inst_)); 240 } 241 RTC_CHECK_EQ( 242 0, WebRtcOpus_SetMaxPlaybackRate(inst_, config.max_playback_rate_hz)); 243 RTC_CHECK_EQ(0, WebRtcOpus_SetComplexity(inst_, config.complexity)); 244 if (config.dtx_enabled) { 245 RTC_CHECK_EQ(0, WebRtcOpus_EnableDtx(inst_)); 246 } else { 247 RTC_CHECK_EQ(0, WebRtcOpus_DisableDtx(inst_)); 248 } 249 RTC_CHECK_EQ(0, 250 WebRtcOpus_SetPacketLossRate( 251 inst_, static_cast<int32_t>(packet_loss_rate_ * 100 + .5))); 252 config_ = config; 253 return true; 254 } 255 256 } // namespace webrtc 257