Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(598)

Side by Side Diff: webrtc/modules/audio_coding/codecs/opus/audio_encoder_opus.cc

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

Powered by Google App Engine
This is Rietveld 408576698