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

Side by Side Diff: remoting/codec/audio_encoder_opus.cc

Issue 11189047: Add opus audio codec support in remoting (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 8 years, 2 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 | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "remoting/codec/audio_encoder_opus.h"
6
7 #include "base/bind.h"
8 #include "base/logging.h"
9 #include "base/time.h"
10 #include "media/base/audio_bus.h"
11 #include "media/base/multi_channel_resampler.h"
12 #include "third_party/opus/src/include/opus.h"
13
14 namespace remoting {
15
16 namespace {
17
18 // Use 160 kb/s bitrate.
Wez 2012/10/19 01:51:32 nit: When you say "use", do you mean "output"?
Wez 2012/10/19 01:51:32 You mean kilobits/second?
Sergey Ulanov 2012/10/19 20:54:30 Yes, It would be kB/s for kilobytes :)
Sergey Ulanov 2012/10/19 20:54:30 Done.
19 const int kBitrateBps = 160 * 1024;
Wez 2012/10/19 01:51:32 nit: kOutputBps or kOutputBitRateBps
Sergey Ulanov 2012/10/19 20:54:30 Done.
20
21 // Encoded buffer size.
22 const int kFrameDefaultBufferSize = 4096;
23
24 // Maximum buffer size we'll allocate when encoding before giving up.
25 const int kMaxBufferSize = 65536;
26
27 // Opus doesn't support 44100 sampling rate so we always resample to 48kHz.
28 const AudioPacket::SamplingRate kOpusSamplingRate =
29 AudioPacket::SAMPLING_RATE_48000;
30
31 // Opus supports frame sizes of 2.5, 5, 10, 20, 40 and 60 ms. We use 20 ms
32 // frames to balance latency and efficiency.
33 const int kFrameSizeMs = 60;
34
35 // Number of samples per frame when using default sampling rate.
36 const int kFrameSamples =
37 kOpusSamplingRate * kFrameSizeMs / base::Time::kMillisecondsPerSecond;
38
39 const AudioPacket::BytesPerSample kBytesPerSample =
40 AudioPacket::BYTES_PER_SAMPLE_2;
41
42 bool IsSupportedSampleRate(int rate) {
43 return rate == 44100 || rate == 48000;
44 }
45
46 } // namespace
47
48 AudioEncoderOpus::AudioEncoderOpus()
49 : sampling_rate_(0),
50 channels_(AudioPacket::CHANNELS_STEREO),
51 encoder_(NULL),
52 frame_size_(0),
53 resampling_data_(NULL),
54 resampling_data_size_(0),
55 resampling_data_pos_(0) {
56 }
57
58 AudioEncoderOpus::~AudioEncoderOpus() {
59 DestroyEncoder();
60 }
61
62 void AudioEncoderOpus::InitEncoder() {
63 DCHECK(!encoder_);
64 int error;
65 encoder_ = opus_encoder_create(kOpusSamplingRate, channels_,
66 OPUS_APPLICATION_AUDIO, &error);
67 CHECK(!error);
Wez 2012/10/19 01:51:32 Don't CHECK on error; just cope w/ |encoder_| bein
Sergey Ulanov 2012/10/19 20:54:30 Done.
68 CHECK(encoder_);
69
70 opus_encoder_ctl(encoder_, OPUS_SET_BITRATE(kBitrateBps));
71
72 frame_size_ = sampling_rate_ * kFrameSizeMs /
73 base::Time::kMillisecondsPerSecond;
74
75 if (sampling_rate_ != kOpusSamplingRate) {
76 resample_buffer_.reset(
77 new char[kFrameSamples * kBytesPerSample * channels_]);
78 resampler_.reset(new media::MultiChannelResampler(
Wez 2012/10/19 01:51:32 nit: Indentation.
Sergey Ulanov 2012/10/19 20:54:30 Done.
79 channels_,
80 static_cast<double>(sampling_rate_) / kOpusSamplingRate,
81 base::Bind(&AudioEncoderOpus::ResamplerRead,
82 base::Unretained(this))));
83 }
84
85 // Drop leftover data because it's for different sampling rate.
86 leftover_samples_ = 0;
87 leftover_buffer_size_ = frame_size_ + media::SincResampler::kLookAheadSize;
88 leftover_buffer_.reset(
89 new int16[leftover_buffer_size_ * channels_]);
90
Wez 2012/10/19 01:51:32 nit: Lose blank line.
Sergey Ulanov 2012/10/19 20:54:30 Done.
91 }
92
93 void AudioEncoderOpus::DestroyEncoder() {
94 if (encoder_) {
95 opus_encoder_destroy(encoder_);
96 encoder_ = NULL;
97 }
98
99 resampler_.reset();
100 }
101
102 bool AudioEncoderOpus::ResetForPacket(AudioPacket* packet) {
103 if (packet->channels() != channels_ ||
104 packet->sampling_rate() != sampling_rate_) {
105 DestroyEncoder();
106
107 channels_ = packet->channels();
108 sampling_rate_ = packet->sampling_rate();
109
110 if (channels_ <= 0 || channels_ > 2 ||
111 !IsSupportedSampleRate(sampling_rate_)) {
112 LOG(WARNING) << "Unsupported OPUS parameters: "
113 << channels_ << " channels with "
114 << sampling_rate_ << " samples per second.";
115 return false;
116 }
117
118 InitEncoder();
119 DCHECK(encoder_);
Wez 2012/10/19 01:51:32 nit: Remove DCHECK
Sergey Ulanov 2012/10/19 20:54:30 Done.
120 }
121
122 return encoder_ != NULL;
123 }
124
125 void AudioEncoderOpus::ResamplerRead(media::AudioBus* audio_bus) {
Wez 2012/10/19 01:51:32 This needs a better name, e.g. SupplyResampler / F
Sergey Ulanov 2012/10/19 20:54:30 Done.
126 CHECK(resampling_data_);
Wez 2012/10/19 01:51:32 nit: DCHECK, and add a line after the check
Sergey Ulanov 2012/10/19 20:54:30 Done.
127 int samples_left = (resampling_data_size_ - resampling_data_pos_) /
128 kBytesPerSample / channels_;
129 CHECK_LE(audio_bus->frames(), samples_left);
Wez 2012/10/19 01:51:32 nit: DCHECK_LE and add a line after the check
Sergey Ulanov 2012/10/19 20:54:30 Done.
130 audio_bus->FromInterleaved(
131 resampling_data_ + resampling_data_pos_,
132 audio_bus->frames(), kBytesPerSample);
133 resampling_data_pos_ += audio_bus->frames() * kBytesPerSample * channels_;
134 DCHECK_LE(resampling_data_pos_, static_cast<int>(resampling_data_size_));
135 }
136
137 scoped_ptr<AudioPacket> AudioEncoderOpus::Encode(
138 scoped_ptr<AudioPacket> packet) {
139 DCHECK_EQ(AudioPacket::ENCODING_RAW, packet->encoding());
140 DCHECK_EQ(1, packet->data_size());
141 DCHECK_EQ(kBytesPerSample, packet->bytes_per_sample());
142
143 if (!ResetForPacket(packet.get())) {
144 LOG(ERROR) << "Encoder initialization failed";
145 return scoped_ptr<AudioPacket>();
146 }
147
148 int samples_left = packet->data(0).size() / kBytesPerSample / channels_;
Wez 2012/10/19 01:51:32 nit: samples_in_packet?
Sergey Ulanov 2012/10/19 20:54:30 Done.
149 const int16* next_sample =
150 reinterpret_cast<const int16*>(packet->data(0).data());
151
152 // Create a new packet of encoded data.
153 scoped_ptr<AudioPacket> encoded_packet(new AudioPacket());
154 encoded_packet->set_encoding(AudioPacket::ENCODING_OPUS);
155 encoded_packet->set_sampling_rate(kOpusSamplingRate);
156 encoded_packet->set_channels(channels_);
157
158 int preferch_samples =
Wez 2012/10/19 01:51:32 typo: prefetch_samples
Sergey Ulanov 2012/10/19 20:54:30 Done.
159 resampler_.get() ? media::SincResampler::kLookAheadSize : 0;
160 int samples_wanted = frame_size_ + preferch_samples;
161
162 while (leftover_samples_ + samples_left >= samples_wanted) {
Wez 2012/10/19 01:51:32 This loop could use a liberal sprinkling of concis
Sergey Ulanov 2012/10/19 20:54:30 Done.
163 const int16* pcm_buffer = NULL;
164
165 if (leftover_samples_ > 0) {
166 pcm_buffer = leftover_buffer_.get();
167 int samples_to_copy = samples_wanted - leftover_samples_;
168 memcpy(leftover_buffer_.get() + leftover_samples_ * channels_,
169 next_sample, samples_to_copy * kBytesPerSample * channels_);
170 } else {
171 pcm_buffer = next_sample;
172 }
173
174 int samples_consumed = 0;
175 if (resampler_.get()) {
176 resampling_data_ = reinterpret_cast<const char*>(pcm_buffer);
177 resampling_data_pos_ = 0;
178 resampling_data_size_ = samples_wanted * channels_ * kBytesPerSample;
179
180 scoped_ptr<media::AudioBus> bus =
181 media::AudioBus::Create(channels_, kFrameSamples);
Wez 2012/10/19 01:51:32 Do we really want to create a new AudioBus for eac
Sergey Ulanov 2012/10/19 20:54:30 Done.
182
183 resampler_->Resample(bus.get(), kFrameSamples);
184 resampling_data_ = NULL;
185 samples_consumed = resampling_data_pos_ / channels_ / kBytesPerSample;
186
187 bus->ToInterleaved(kFrameSamples, kBytesPerSample,
188 resample_buffer_.get());
189 pcm_buffer = reinterpret_cast<int16*>(resample_buffer_.get());
190 } else {
191 samples_consumed = frame_size_;
192 }
193
194 std::string* data = encoded_packet->add_data();
195
196 data->resize(kFrameSamples * kBytesPerSample * channels_);
197
198 unsigned char* buffer =
199 reinterpret_cast<unsigned char*>(string_as_array(data));
200 int result = opus_encode(encoder_, pcm_buffer, kFrameSamples,
201 buffer, data->length());
202 if (result < 0) {
203 LOG(ERROR) << "opus_encode() failed with error code: " << result;
204 return scoped_ptr<AudioPacket>();
205 }
206
207 CHECK_LE(result, static_cast<int>(data->length()));
Wez 2012/10/19 01:51:32 DCHECK_LE?
Sergey Ulanov 2012/10/19 20:54:30 Done.
208 data->resize(result);
209
210 if (samples_consumed >= leftover_samples_) {
211 samples_consumed -= leftover_samples_;
212 leftover_samples_ = 0;
213 next_sample += samples_consumed * channels_;
214 samples_left -= samples_consumed;
215 } else {
216 leftover_samples_ -= samples_consumed;
217 memmove(leftover_buffer_.get(),
218 leftover_buffer_.get() + samples_consumed * channels_,
219 leftover_samples_ * channels_ * kBytesPerSample);
220 }
221 }
222
223 // Store the leftover samples.
224 if (samples_left > 0) {
225 CHECK_LE(leftover_samples_ + samples_left, leftover_buffer_size_);
226 memmove(leftover_buffer_.get() + leftover_samples_ * channels_,
227 next_sample, samples_left * kBytesPerSample * channels_);
228 leftover_samples_ += samples_left;
229 }
230
231 // Return NULL if there's nothing in the packet.
232 if (encoded_packet->data_size() == 0)
233 return scoped_ptr<AudioPacket>();
234
235 return encoded_packet.Pass();
236 }
237
238 } // namespace remoting
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698