• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
CreateConfig(const CodecInst & codec_inst)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.
OptimizePacketLossRate(double new_loss_rate,double old_loss_rate)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 
IsOk() const79 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 
AudioEncoderOpus(const Config & config)91 AudioEncoderOpus::AudioEncoderOpus(const Config& config)
92     : packet_loss_rate_(0.0), inst_(nullptr) {
93   RTC_CHECK(RecreateEncoderInstance(config));
94 }
95 
AudioEncoderOpus(const CodecInst & codec_inst)96 AudioEncoderOpus::AudioEncoderOpus(const CodecInst& codec_inst)
97     : AudioEncoderOpus(CreateConfig(codec_inst)) {}
98 
~AudioEncoderOpus()99 AudioEncoderOpus::~AudioEncoderOpus() {
100   RTC_CHECK_EQ(0, WebRtcOpus_EncoderFree(inst_));
101 }
102 
MaxEncodedBytes() const103 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 
SampleRateHz() const113 int AudioEncoderOpus::SampleRateHz() const {
114   return kSampleRateHz;
115 }
116 
NumChannels() const117 size_t AudioEncoderOpus::NumChannels() const {
118   return config_.num_channels;
119 }
120 
Num10MsFramesInNextPacket() const121 size_t AudioEncoderOpus::Num10MsFramesInNextPacket() const {
122   return Num10msFramesPerPacket();
123 }
124 
Max10MsFramesInAPacket() const125 size_t AudioEncoderOpus::Max10MsFramesInAPacket() const {
126   return Num10msFramesPerPacket();
127 }
128 
GetTargetBitrate() const129 int AudioEncoderOpus::GetTargetBitrate() const {
130   return config_.bitrate_bps;
131 }
132 
EncodeInternal(uint32_t rtp_timestamp,rtc::ArrayView<const int16_t> audio,size_t max_encoded_bytes,uint8_t * encoded)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 
Reset()163 void AudioEncoderOpus::Reset() {
164   RTC_CHECK(RecreateEncoderInstance(config_));
165 }
166 
SetFec(bool enable)167 bool AudioEncoderOpus::SetFec(bool enable) {
168   auto conf = config_;
169   conf.fec_enabled = enable;
170   return RecreateEncoderInstance(conf);
171 }
172 
SetDtx(bool enable)173 bool AudioEncoderOpus::SetDtx(bool enable) {
174   auto conf = config_;
175   conf.dtx_enabled = enable;
176   return RecreateEncoderInstance(conf);
177 }
178 
SetApplication(Application application)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 
SetMaxPlaybackRate(int frequency_hz)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 
SetProjectedPacketLossRate(double fraction)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 
SetTargetBitrate(int bits_per_second)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 
Num10msFramesPerPacket() const215 size_t AudioEncoderOpus::Num10msFramesPerPacket() const {
216   return static_cast<size_t>(rtc::CheckedDivExact(config_.frame_size_ms, 10));
217 }
218 
SamplesPer10msFrame() const219 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.
RecreateEncoderInstance(const Config & config)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