OLD | NEW |
---|---|
(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 | |
OLD | NEW |