OLD | NEW |
(Empty) | |
| 1 // Copyright 2015 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 "base/bind.h" |
| 6 #include "base/message_loop/message_loop.h" |
| 7 #include "content/renderer/pepper/audio_encoder_shim.h" |
| 8 #include "content/renderer/render_thread_impl.h" |
| 9 #include "ppapi/c/ppb_audio_buffer.h" |
| 10 #include "ppapi/proxy/serialized_structs.h" |
| 11 #include "third_party/opus/src/include/opus.h" |
| 12 #include "third_party/speex/include/speex/speex.h" |
| 13 |
| 14 namespace content { |
| 15 |
| 16 const int kSpeexEncodingQuality = 8; |
| 17 |
| 18 class AudioEncoderShim::EncoderImpl { |
| 19 public: |
| 20 virtual ~EncoderImpl() {} |
| 21 |
| 22 // Called on the media thread. |
| 23 void Encode(const scoped_refptr<AudioData>& input, |
| 24 const scoped_refptr<AudioData>& output, |
| 25 BitstreamBufferReadyCB callback); |
| 26 void Stop(); |
| 27 |
| 28 // Called on the renderer thread. |
| 29 virtual std::vector<PP_AudioProfileDescription> GetSupportedProfiles() = 0; |
| 30 virtual bool Initialize( |
| 31 const ppapi::proxy::PPB_AudioEncodeParameters& parameters) = 0; |
| 32 virtual int32_t GetNumberOfSamplesPerFrame() = 0; |
| 33 virtual void RequestBitrateChange(uint32_t bitrate) = 0; |
| 34 |
| 35 protected: |
| 36 EncoderImpl(const base::WeakPtr<AudioEncoderShim>& shim); |
| 37 |
| 38 // Called on the media thread. |
| 39 virtual int32_t EncodeInternal(const scoped_refptr<AudioData>& input, |
| 40 const scoped_refptr<AudioData>& output) = 0; |
| 41 |
| 42 private: |
| 43 base::WeakPtr<AudioEncoderShim> shim_; |
| 44 scoped_refptr<base::SingleThreadTaskRunner> renderer_task_runner_; |
| 45 |
| 46 DISALLOW_COPY_AND_ASSIGN(EncoderImpl); |
| 47 }; |
| 48 |
| 49 AudioEncoderShim::EncoderImpl::EncoderImpl( |
| 50 const base::WeakPtr<AudioEncoderShim>& shim) |
| 51 : shim_(shim), renderer_task_runner_(base::MessageLoopProxy::current()) { |
| 52 } |
| 53 |
| 54 void AudioEncoderShim::EncoderImpl::Encode( |
| 55 const scoped_refptr<AudioData>& input, |
| 56 const scoped_refptr<AudioData>& output, |
| 57 BitstreamBufferReadyCB callback) { |
| 58 int32_t size = EncodeInternal(input, output); |
| 59 renderer_task_runner_->PostTask( |
| 60 FROM_HERE, base::Bind(&AudioEncoderShim::OnEncodeDone, shim_, output, |
| 61 size < 0 ? -1 : size, callback)); |
| 62 } |
| 63 |
| 64 void AudioEncoderShim::EncoderImpl::Stop() { |
| 65 } |
| 66 |
| 67 class AudioEncoderShim::OpusEncoderImpl : public AudioEncoderShim::EncoderImpl { |
| 68 public: |
| 69 OpusEncoderImpl(const base::WeakPtr<AudioEncoderShim>& shim); |
| 70 ~OpusEncoderImpl() override; |
| 71 |
| 72 private: |
| 73 // AudioEncoderShim::Impl: |
| 74 std::vector<PP_AudioProfileDescription> GetSupportedProfiles() override; |
| 75 bool Initialize( |
| 76 const ppapi::proxy::PPB_AudioEncodeParameters& parameters) override; |
| 77 int32_t GetNumberOfSamplesPerFrame() override; |
| 78 int32_t EncodeInternal( |
| 79 const scoped_refptr<AudioEncoderShim::AudioData>& input, |
| 80 const scoped_refptr<AudioEncoderShim::AudioData>& output) override; |
| 81 void RequestBitrateChange(uint32_t bitrate) override; |
| 82 |
| 83 scoped_ptr<uint8[]> encoder_memory_; |
| 84 OpusEncoder* opus_encoder_; |
| 85 |
| 86 ppapi::proxy::PPB_AudioEncodeParameters parameters_; |
| 87 |
| 88 DISALLOW_COPY_AND_ASSIGN(OpusEncoderImpl); |
| 89 }; |
| 90 |
| 91 AudioEncoderShim::OpusEncoderImpl::OpusEncoderImpl( |
| 92 const base::WeakPtr<AudioEncoderShim>& shim) |
| 93 : EncoderImpl(shim), opus_encoder_(nullptr) { |
| 94 } |
| 95 |
| 96 AudioEncoderShim::OpusEncoderImpl::~OpusEncoderImpl() { |
| 97 } |
| 98 |
| 99 std::vector<PP_AudioProfileDescription> |
| 100 AudioEncoderShim::OpusEncoderImpl::GetSupportedProfiles() { |
| 101 std::vector<PP_AudioProfileDescription> profiles; |
| 102 uint32_t sampling_rates[] = {8000, 12000, 16000, 24000, 48000}; |
| 103 |
| 104 for (uint32_t i = 0; i < arraysize(sampling_rates); i++) { |
| 105 PP_AudioProfileDescription profile; |
| 106 profile.profile = PP_AUDIOPROFILE_OPUS; |
| 107 profile.max_channels = 2; |
| 108 profile.sample_size = PP_AUDIOBUFFER_SAMPLESIZE_16_BITS; |
| 109 profile.sample_rate = sampling_rates[i]; |
| 110 profile.hardware_accelerated = PP_FALSE; |
| 111 profiles.push_back(profile); |
| 112 } |
| 113 return profiles; |
| 114 } |
| 115 |
| 116 bool AudioEncoderShim::OpusEncoderImpl::Initialize( |
| 117 const ppapi::proxy::PPB_AudioEncodeParameters& parameters) { |
| 118 DCHECK(!encoder_memory_); |
| 119 |
| 120 int32_t encoder_size = opus_encoder_get_size(parameters.channels); |
| 121 if (encoder_size < 1) |
| 122 return false; |
| 123 |
| 124 encoder_memory_.reset(new uint8[encoder_size]); |
| 125 opus_encoder_ = reinterpret_cast<OpusEncoder*>(encoder_memory_.get()); |
| 126 |
| 127 if (opus_encoder_init(opus_encoder_, parameters.input_sample_rate, |
| 128 parameters.channels, OPUS_APPLICATION_AUDIO) != OPUS_OK) |
| 129 return false; |
| 130 |
| 131 if (opus_encoder_ctl(opus_encoder_, |
| 132 OPUS_SET_BITRATE(parameters.initial_bitrate <= 0 |
| 133 ? OPUS_AUTO |
| 134 : parameters.initial_bitrate)) != |
| 135 OPUS_OK) |
| 136 return false; |
| 137 |
| 138 parameters_ = parameters; |
| 139 |
| 140 return true; |
| 141 } |
| 142 |
| 143 int32_t AudioEncoderShim::OpusEncoderImpl::GetNumberOfSamplesPerFrame() { |
| 144 // Opus supports 2.5, 5, 10, 20, 40 or 60ms audio frames. We take |
| 145 // 10ms by default. |
| 146 return parameters_.input_sample_rate / 100; |
| 147 } |
| 148 |
| 149 int32_t AudioEncoderShim::OpusEncoderImpl::EncodeInternal( |
| 150 const scoped_refptr<AudioData>& input, |
| 151 const scoped_refptr<AudioData>& output) { |
| 152 return opus_encode( |
| 153 opus_encoder_, reinterpret_cast<opus_int16*>(input->GetData()), |
| 154 (input->GetSize() / parameters_.channels) / parameters_.input_sample_size, |
| 155 output->GetData(), output->GetSize()); |
| 156 } |
| 157 |
| 158 void AudioEncoderShim::OpusEncoderImpl::RequestBitrateChange(uint32_t bitrate) { |
| 159 opus_encoder_ctl(opus_encoder_, OPUS_SET_BITRATE(bitrate)); |
| 160 } |
| 161 |
| 162 class AudioEncoderShim::SpeexEncoderImpl |
| 163 : public AudioEncoderShim::EncoderImpl { |
| 164 public: |
| 165 SpeexEncoderImpl(const base::WeakPtr<AudioEncoderShim>& shim); |
| 166 ~SpeexEncoderImpl() override; |
| 167 |
| 168 private: |
| 169 // AudioEncoderShim::Impl: |
| 170 std::vector<PP_AudioProfileDescription> GetSupportedProfiles() override; |
| 171 bool Initialize( |
| 172 const ppapi::proxy::PPB_AudioEncodeParameters& parameters) override; |
| 173 int32_t GetNumberOfSamplesPerFrame() override; |
| 174 int32_t EncodeInternal( |
| 175 const scoped_refptr<AudioEncoderShim::AudioData>& input, |
| 176 const scoped_refptr<AudioEncoderShim::AudioData>& output) override; |
| 177 void RequestBitrateChange(uint32_t bitrate) override; |
| 178 |
| 179 bool bits_initialized_; |
| 180 bool encoder_initialized_; |
| 181 int32_t samples_per_frame_; |
| 182 |
| 183 SpeexBits bits_; |
| 184 void* encoder_state_; |
| 185 |
| 186 ppapi::proxy::PPB_AudioEncodeParameters parameters_; |
| 187 |
| 188 DISALLOW_COPY_AND_ASSIGN(SpeexEncoderImpl); |
| 189 }; |
| 190 |
| 191 AudioEncoderShim::SpeexEncoderImpl::SpeexEncoderImpl( |
| 192 const base::WeakPtr<AudioEncoderShim>& shim) |
| 193 : EncoderImpl(shim), |
| 194 bits_initialized_(false), |
| 195 encoder_initialized_(false), |
| 196 samples_per_frame_(0), |
| 197 encoder_state_(nullptr) { |
| 198 } |
| 199 |
| 200 AudioEncoderShim::SpeexEncoderImpl::~SpeexEncoderImpl() { |
| 201 if (bits_initialized_) |
| 202 speex_bits_destroy(&bits_); |
| 203 if (encoder_initialized_) |
| 204 speex_encoder_destroy(encoder_state_); |
| 205 } |
| 206 |
| 207 std::vector<PP_AudioProfileDescription> |
| 208 AudioEncoderShim::SpeexEncoderImpl::GetSupportedProfiles() { |
| 209 std::vector<PP_AudioProfileDescription> profiles; |
| 210 uint32_t sampling_rates[] = {8000, 16000, 32000}; |
| 211 |
| 212 for (uint32_t i = 0; i < arraysize(sampling_rates); i++) { |
| 213 PP_AudioProfileDescription profile; |
| 214 profile.profile = PP_AUDIOPROFILE_SPEEX; |
| 215 profile.max_channels = 1; |
| 216 profile.sample_size = PP_AUDIOBUFFER_SAMPLESIZE_16_BITS; |
| 217 profile.sample_rate = sampling_rates[i]; |
| 218 profile.hardware_accelerated = PP_FALSE; |
| 219 profiles.push_back(profile); |
| 220 } |
| 221 return profiles; |
| 222 } |
| 223 |
| 224 bool AudioEncoderShim::SpeexEncoderImpl::Initialize( |
| 225 const ppapi::proxy::PPB_AudioEncodeParameters& parameters) { |
| 226 DCHECK(!bits_initialized_); |
| 227 DCHECK(!encoder_initialized_); |
| 228 |
| 229 memset(&bits_, 0, sizeof(bits_)); |
| 230 speex_bits_init(&bits_); |
| 231 |
| 232 switch (parameters.input_sample_rate) { |
| 233 case 8000: |
| 234 encoder_state_ = speex_encoder_init(&speex_nb_mode); |
| 235 break; |
| 236 case 16000: |
| 237 encoder_state_ = speex_encoder_init(&speex_wb_mode); |
| 238 break; |
| 239 case 32000: |
| 240 encoder_state_ = speex_encoder_init(&speex_uwb_mode); |
| 241 break; |
| 242 default: |
| 243 return false; |
| 244 } |
| 245 |
| 246 int quality = kSpeexEncodingQuality; |
| 247 speex_encoder_ctl(encoder_state_, SPEEX_SET_QUALITY, &quality); |
| 248 int vbr = 1; |
| 249 speex_encoder_ctl(encoder_state_, SPEEX_SET_VBR, &vbr); |
| 250 |
| 251 speex_encoder_ctl(encoder_state_, SPEEX_GET_FRAME_SIZE, &samples_per_frame_); |
| 252 |
| 253 parameters_ = parameters; |
| 254 |
| 255 return true; |
| 256 } |
| 257 |
| 258 int32_t AudioEncoderShim::SpeexEncoderImpl::GetNumberOfSamplesPerFrame() { |
| 259 return samples_per_frame_; |
| 260 } |
| 261 |
| 262 int32_t AudioEncoderShim::SpeexEncoderImpl::EncodeInternal( |
| 263 const scoped_refptr<AudioData>& input, |
| 264 const scoped_refptr<AudioData>& output) { |
| 265 speex_bits_reset(&bits_); |
| 266 speex_encode_int(encoder_state_, reinterpret_cast<int16_t*>(input->GetData()), |
| 267 &bits_); |
| 268 return speex_bits_write(&bits_, reinterpret_cast<char*>(output->GetData()), |
| 269 output->GetSize()); |
| 270 } |
| 271 |
| 272 void AudioEncoderShim::SpeexEncoderImpl::RequestBitrateChange( |
| 273 uint32_t bitrate) { |
| 274 int32_t encoder_bitrate = base::checked_cast<int32_t>(bitrate); |
| 275 speex_encoder_ctl(encoder_state_, SPEEX_SET_BITRATE, &encoder_bitrate); |
| 276 } |
| 277 |
| 278 AudioEncoderShim::AudioEncoderShim() |
| 279 : media_task_runner_( |
| 280 RenderThreadImpl::current()->GetMediaThreadTaskRunner()), |
| 281 weak_ptr_factory_(this) { |
| 282 } |
| 283 |
| 284 AudioEncoderShim::~AudioEncoderShim() { |
| 285 if (encoder_impl_) |
| 286 media_task_runner_->PostTask( |
| 287 FROM_HERE, base::Bind(&AudioEncoderShim::EncoderImpl::Stop, |
| 288 base::Owned(encoder_impl_.release()))); |
| 289 } |
| 290 |
| 291 std::vector<PP_AudioProfileDescription> |
| 292 AudioEncoderShim::GetSupportedProfiles() { |
| 293 std::vector<PP_AudioProfileDescription> profiles; |
| 294 scoped_ptr<EncoderImpl> encoder( |
| 295 new OpusEncoderImpl(weak_ptr_factory_.GetWeakPtr())); |
| 296 std::vector<PP_AudioProfileDescription> codec_profiles = |
| 297 encoder->GetSupportedProfiles(); |
| 298 for (const PP_AudioProfileDescription desc : codec_profiles) |
| 299 profiles.push_back(desc); |
| 300 encoder.reset(new SpeexEncoderImpl(weak_ptr_factory_.GetWeakPtr())); |
| 301 codec_profiles = encoder->GetSupportedProfiles(); |
| 302 for (const PP_AudioProfileDescription desc : codec_profiles) |
| 303 profiles.push_back(desc); |
| 304 return profiles; |
| 305 } |
| 306 |
| 307 bool AudioEncoderShim::Initialize( |
| 308 const ppapi::proxy::PPB_AudioEncodeParameters& parameters) { |
| 309 if (encoder_impl_) |
| 310 return false; |
| 311 |
| 312 if (parameters.output_profile == PP_AUDIOPROFILE_OPUS) { |
| 313 encoder_impl_.reset(new OpusEncoderImpl(weak_ptr_factory_.GetWeakPtr())); |
| 314 return encoder_impl_->Initialize(parameters); |
| 315 } else if (parameters.output_profile == PP_AUDIOPROFILE_SPEEX) { |
| 316 encoder_impl_.reset(new SpeexEncoderImpl(weak_ptr_factory_.GetWeakPtr())); |
| 317 return encoder_impl_->Initialize(parameters); |
| 318 } |
| 319 return false; |
| 320 } |
| 321 |
| 322 int32_t AudioEncoderShim::GetNumberOfSamplesPerFrame() { |
| 323 DCHECK(encoder_impl_); |
| 324 return encoder_impl_->GetNumberOfSamplesPerFrame(); |
| 325 } |
| 326 |
| 327 void AudioEncoderShim::Encode(const scoped_refptr<AudioData>& input, |
| 328 const scoped_refptr<AudioData>& output, |
| 329 BitstreamBufferReadyCB callback) { |
| 330 DCHECK(encoder_impl_); |
| 331 media_task_runner_->PostTask( |
| 332 FROM_HERE, base::Bind(&AudioEncoderShim::EncoderImpl::Encode, |
| 333 base::Unretained(encoder_impl_.get()), input, |
| 334 output, callback)); |
| 335 } |
| 336 |
| 337 void AudioEncoderShim::RequestBitrateChange(uint32_t bitrate) { |
| 338 DCHECK(encoder_impl_); |
| 339 media_task_runner_->PostTask( |
| 340 FROM_HERE, |
| 341 base::Bind(&AudioEncoderShim::EncoderImpl::RequestBitrateChange, |
| 342 base::Unretained(encoder_impl_.get()), bitrate)); |
| 343 } |
| 344 |
| 345 void AudioEncoderShim::OnEncodeDone(const scoped_refptr<AudioData>& output, |
| 346 int32_t size, |
| 347 BitstreamBufferReadyCB callback) { |
| 348 callback.Run(output, size); |
| 349 } |
| 350 |
| 351 } // namespace content |
OLD | NEW |