Chromium Code Reviews| 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 |