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

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 & stuff 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);
hlundin-webrtc 2015/09/07 20:00:01 DCHECKs for old_loss_rate too?
kwiberg-webrtc 2015/09/08 10:47:45 Good idea. Done.
46 const double kPacketLossRate20 = 0.20;
47 const double kPacketLossRate10 = 0.10;
48 const double kPacketLossRate5 = 0.05;
49 const double kPacketLossRate1 = 0.01;
50 const double kLossRate20Margin = 0.02;
51 const double kLossRate10Margin = 0.01;
52 const double kLossRate5Margin = 0.01;
53 if (new_loss_rate >=
54 kPacketLossRate20 +
55 kLossRate20Margin *
56 (kPacketLossRate20 - old_loss_rate > 0 ? 1 : -1)) {
57 return kPacketLossRate20;
58 } else if (new_loss_rate >=
59 kPacketLossRate10 +
60 kLossRate10Margin *
61 (kPacketLossRate10 - old_loss_rate > 0 ? 1 : -1)) {
62 return kPacketLossRate10;
63 } else if (new_loss_rate >=
64 kPacketLossRate5 +
65 kLossRate5Margin *
66 (kPacketLossRate5 - old_loss_rate > 0 ? 1 : -1)) {
67 return kPacketLossRate5;
68 } else if (new_loss_rate >= kPacketLossRate1) {
69 return kPacketLossRate1;
70 } else {
71 return 0.0;
72 }
73 }
36 74
37 } // namespace 75 } // namespace
38 76
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 { 77 bool AudioEncoderOpus::Config::IsOk() const {
52 if (frame_size_ms <= 0 || frame_size_ms % 10 != 0) 78 if (frame_size_ms <= 0 || frame_size_ms % 10 != 0)
53 return false; 79 return false;
54 if (num_channels != 1 && num_channels != 2) 80 if (num_channels != 1 && num_channels != 2)
55 return false; 81 return false;
56 if (bitrate_bps < kMinBitrateBps || bitrate_bps > kMaxBitrateBps) 82 if (bitrate_bps < kMinBitrateBps || bitrate_bps > kMaxBitrateBps)
57 return false; 83 return false;
58 if (complexity < 0 || complexity > 10) 84 if (complexity < 0 || complexity > 10)
59 return false; 85 return false;
60 return true; 86 return true;
61 } 87 }
62 88
63 AudioEncoderOpus::AudioEncoderOpus(const Config& config) 89 AudioEncoderOpus::AudioEncoderOpus(const Config& config)
64 : num_10ms_frames_per_packet_( 90 : packet_loss_rate_(0.0), inst_(nullptr) {
65 static_cast<size_t>(rtc::CheckedDivExact(config.frame_size_ms, 10))), 91 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 } 92 }
91 93
94 AudioEncoderOpus::AudioEncoderOpus(const CodecInst& codec_inst)
95 : AudioEncoderOpus(CreateConfig(codec_inst)) {}
96
92 AudioEncoderOpus::~AudioEncoderOpus() { 97 AudioEncoderOpus::~AudioEncoderOpus() {
93 CHECK_EQ(0, WebRtcOpus_EncoderFree(inst_)); 98 CHECK_EQ(0, WebRtcOpus_EncoderFree(inst_));
94 } 99 }
95 100
101 size_t AudioEncoderOpus::MaxEncodedBytes() const {
102 // Calculate the number of bytes we expect the encoder to produce,
103 // then multiply by two to give a wide margin for error.
104 const size_t bytes_per_millisecond =
105 static_cast<size_t>(config_.bitrate_bps / (1000 * 8) + 1);
106 const size_t approx_encoded_bytes =
107 Num10msFramesPerPacket() * 10 * bytes_per_millisecond;
108 return 2 * approx_encoded_bytes;
109 }
110
96 int AudioEncoderOpus::SampleRateHz() const { 111 int AudioEncoderOpus::SampleRateHz() const {
97 return kSampleRateHz; 112 return kSampleRateHz;
98 } 113 }
99 114
100 int AudioEncoderOpus::NumChannels() const { 115 int AudioEncoderOpus::NumChannels() const {
101 return num_channels_; 116 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 } 117 }
113 118
114 size_t AudioEncoderOpus::Num10MsFramesInNextPacket() const { 119 size_t AudioEncoderOpus::Num10MsFramesInNextPacket() const {
115 return num_10ms_frames_per_packet_; 120 return Num10msFramesPerPacket();
116 } 121 }
117 122
118 size_t AudioEncoderOpus::Max10MsFramesInAPacket() const { 123 size_t AudioEncoderOpus::Max10MsFramesInAPacket() const {
119 return num_10ms_frames_per_packet_; 124 return Num10msFramesPerPacket();
120 } 125 }
121 126
122 int AudioEncoderOpus::GetTargetBitrate() const { 127 int AudioEncoderOpus::GetTargetBitrate() const {
123 return bitrate_bps_; 128 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 } 129 }
178 130
179 AudioEncoder::EncodedInfo AudioEncoderOpus::EncodeInternal( 131 AudioEncoder::EncodedInfo AudioEncoderOpus::EncodeInternal(
180 uint32_t rtp_timestamp, 132 uint32_t rtp_timestamp,
181 const int16_t* audio, 133 const int16_t* audio,
182 size_t max_encoded_bytes, 134 size_t max_encoded_bytes,
183 uint8_t* encoded) { 135 uint8_t* encoded) {
184 if (input_buffer_.empty()) 136 if (input_buffer_.empty())
185 first_timestamp_in_buffer_ = rtp_timestamp; 137 first_timestamp_in_buffer_ = rtp_timestamp;
186 input_buffer_.insert(input_buffer_.end(), audio, 138 input_buffer_.insert(input_buffer_.end(), audio,
187 audio + samples_per_10ms_frame_); 139 audio + SamplesPer10msFrame());
188 if (input_buffer_.size() < 140 if (input_buffer_.size() <
189 (num_10ms_frames_per_packet_ * samples_per_10ms_frame_)) { 141 (static_cast<size_t>(Num10msFramesPerPacket()) * SamplesPer10msFrame())) {
190 return EncodedInfo(); 142 return EncodedInfo();
191 } 143 }
192 CHECK_EQ(input_buffer_.size(), 144 CHECK_EQ(input_buffer_.size(), static_cast<size_t>(Num10msFramesPerPacket()) *
193 num_10ms_frames_per_packet_ * samples_per_10ms_frame_); 145 SamplesPer10msFrame());
194 int status = WebRtcOpus_Encode( 146 int status = WebRtcOpus_Encode(
195 inst_, &input_buffer_[0], 147 inst_, &input_buffer_[0],
196 rtc::CheckedDivExact(input_buffer_.size(), 148 rtc::CheckedDivExact(input_buffer_.size(),
197 static_cast<size_t>(num_channels_)), 149 static_cast<size_t>(config_.num_channels)),
198 max_encoded_bytes, encoded); 150 rtc::saturated_cast<int16_t>(max_encoded_bytes), encoded);
199 CHECK_GE(status, 0); // Fails only if fed invalid data. 151 CHECK_GE(status, 0); // Fails only if fed invalid data.
200 input_buffer_.clear(); 152 input_buffer_.clear();
201 EncodedInfo info; 153 EncodedInfo info;
202 info.encoded_bytes = static_cast<size_t>(status); 154 info.encoded_bytes = static_cast<size_t>(status);
203 info.encoded_timestamp = first_timestamp_in_buffer_; 155 info.encoded_timestamp = first_timestamp_in_buffer_;
204 info.payload_type = payload_type_; 156 info.payload_type = config_.payload_type;
205 info.send_even_if_empty = true; // Allows Opus to send empty packets. 157 info.send_even_if_empty = true; // Allows Opus to send empty packets.
206 info.speech = (status > 0); 158 info.speech = (status > 0);
207 return info; 159 return info;
208 } 160 }
209 161
210 namespace { 162 void AudioEncoderOpus::Reset() {
211 AudioEncoderOpus::Config CreateConfig(const CodecInst& codec_inst) { 163 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 } 164 }
226 165
227 bool AudioEncoderMutableOpus::SetFec(bool enable) { 166 bool AudioEncoderOpus::SetFec(bool enable) {
228 auto conf = config(); 167 auto conf = config_;
229 conf.fec_enabled = enable; 168 conf.fec_enabled = enable;
230 return Reconstruct(conf); 169 return RecreateEncoderInstance(conf);
231 } 170 }
232 171
233 bool AudioEncoderMutableOpus::SetDtx(bool enable) { 172 bool AudioEncoderOpus::SetDtx(bool enable) {
234 auto conf = config(); 173 auto conf = config_;
235 conf.dtx_enabled = enable; 174 conf.dtx_enabled = enable;
236 return Reconstruct(conf); 175 return RecreateEncoderInstance(conf);
237 } 176 }
238 177
239 bool AudioEncoderMutableOpus::SetApplication(Application application) { 178 bool AudioEncoderOpus::SetApplication(Application application) {
240 auto conf = config(); 179 auto conf = config_;
241 switch (application) { 180 switch (application) {
242 case kApplicationSpeech: 181 case Application::kSpeech:
243 conf.application = AudioEncoderOpus::kVoip; 182 conf.application = AudioEncoderOpus::kVoip;
244 break; 183 break;
245 case kApplicationAudio: 184 case Application::kAudio:
246 conf.application = AudioEncoderOpus::kAudio; 185 conf.application = AudioEncoderOpus::kAudio;
247 break; 186 break;
248 } 187 }
249 return Reconstruct(conf); 188 return RecreateEncoderInstance(conf);
250 } 189 }
251 190
252 bool AudioEncoderMutableOpus::SetMaxPlaybackRate(int frequency_hz) { 191 bool AudioEncoderOpus::SetMaxPlaybackRate(int frequency_hz) {
253 auto conf = config(); 192 auto conf = config_;
254 conf.max_playback_rate_hz = frequency_hz; 193 conf.max_playback_rate_hz = frequency_hz;
255 return Reconstruct(conf); 194 return RecreateEncoderInstance(conf);
195 }
196
197 void AudioEncoderOpus::SetProjectedPacketLossRate(double fraction) {
198 double opt_loss_rate = OptimizePacketLossRate(fraction, packet_loss_rate_);
199 if (packet_loss_rate_ != opt_loss_rate) {
200 packet_loss_rate_ = opt_loss_rate;
201 CHECK_EQ(0, WebRtcOpus_SetPacketLossRate(
202 inst_, static_cast<int32_t>(packet_loss_rate_ * 100 + .5)));
203 }
204 }
205
206 void AudioEncoderOpus::SetTargetBitrate(int bits_per_second) {
207 config_.bitrate_bps =
208 std::max(std::min(bits_per_second, kMaxBitrateBps), kMinBitrateBps);
209 DCHECK(config_.IsOk());
210 CHECK_EQ(0, WebRtcOpus_SetBitRate(inst_, config_.bitrate_bps));
211 }
212
213 int AudioEncoderOpus::Num10msFramesPerPacket() const {
214 return rtc::CheckedDivExact(config_.frame_size_ms, 10);
215 }
216
217 int AudioEncoderOpus::SamplesPer10msFrame() const {
218 return rtc::CheckedDivExact(kSampleRateHz, 100) * config_.num_channels;
219 }
220
221 // If the given config is OK, recreate the Opus encoder instance with those
222 // settings, save the config, and return true. Otherwise, do nothing and return
223 // false.
224 bool AudioEncoderOpus::RecreateEncoderInstance(const Config& config) {
225 if (!config.IsOk())
226 return false;
227 if (inst_)
228 CHECK_EQ(0, WebRtcOpus_EncoderFree(inst_));
229 input_buffer_.clear();
230 input_buffer_.reserve(Num10msFramesPerPacket() * SamplesPer10msFrame());
231 CHECK_EQ(0, WebRtcOpus_EncoderCreate(&inst_, config.num_channels,
232 config.application));
233 CHECK_EQ(0, WebRtcOpus_SetBitRate(inst_, config.bitrate_bps));
234 if (config.fec_enabled) {
235 CHECK_EQ(0, WebRtcOpus_EnableFec(inst_));
236 } else {
237 CHECK_EQ(0, WebRtcOpus_DisableFec(inst_));
238 }
239 CHECK_EQ(0,
240 WebRtcOpus_SetMaxPlaybackRate(inst_, config.max_playback_rate_hz));
241 CHECK_EQ(0, WebRtcOpus_SetComplexity(inst_, config.complexity));
242 if (config.dtx_enabled) {
243 CHECK_EQ(0, WebRtcOpus_EnableDtx(inst_));
244 } else {
245 CHECK_EQ(0, WebRtcOpus_DisableDtx(inst_));
246 }
247 CHECK_EQ(0, WebRtcOpus_SetPacketLossRate(
248 inst_, static_cast<int32_t>(packet_loss_rate_ * 100 + .5)));
249 config_ = config;
250 return true;
256 } 251 }
257 252
258 } // namespace webrtc 253 } // namespace webrtc
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698