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