Index: content/renderer/pepper/audio_encoder_shim.cc |
diff --git a/content/renderer/pepper/audio_encoder_shim.cc b/content/renderer/pepper/audio_encoder_shim.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..4a7cffe6be59ead98a38ab94329ea29f525b06fa |
--- /dev/null |
+++ b/content/renderer/pepper/audio_encoder_shim.cc |
@@ -0,0 +1,351 @@ |
+// Copyright 2015 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "base/bind.h" |
+#include "base/message_loop/message_loop.h" |
+#include "content/renderer/pepper/audio_encoder_shim.h" |
+#include "content/renderer/render_thread_impl.h" |
+#include "ppapi/c/ppb_audio_buffer.h" |
+#include "ppapi/proxy/serialized_structs.h" |
+#include "third_party/opus/src/include/opus.h" |
+#include "third_party/speex/include/speex/speex.h" |
+ |
+namespace content { |
+ |
+const int kSpeexEncodingQuality = 8; |
+ |
+class AudioEncoderShim::EncoderImpl { |
+ public: |
+ virtual ~EncoderImpl() {} |
+ |
+ // Called on the media thread. |
+ void Encode(const scoped_refptr<AudioData>& input, |
+ const scoped_refptr<AudioData>& output, |
+ BitstreamBufferReadyCB callback); |
+ void Stop(); |
+ |
+ // Called on the renderer thread. |
+ virtual std::vector<PP_AudioProfileDescription> GetSupportedProfiles() = 0; |
+ virtual bool Initialize( |
+ const ppapi::proxy::PPB_AudioEncodeParameters& parameters) = 0; |
+ virtual int32_t GetNumberOfSamplesPerFrame() = 0; |
+ virtual void RequestBitrateChange(uint32_t bitrate) = 0; |
+ |
+ protected: |
+ EncoderImpl(const base::WeakPtr<AudioEncoderShim>& shim); |
+ |
+ // Called on the media thread. |
+ virtual int32_t EncodeInternal(const scoped_refptr<AudioData>& input, |
+ const scoped_refptr<AudioData>& output) = 0; |
+ |
+ private: |
+ base::WeakPtr<AudioEncoderShim> shim_; |
+ scoped_refptr<base::SingleThreadTaskRunner> renderer_task_runner_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(EncoderImpl); |
+}; |
+ |
+AudioEncoderShim::EncoderImpl::EncoderImpl( |
+ const base::WeakPtr<AudioEncoderShim>& shim) |
+ : shim_(shim), renderer_task_runner_(base::MessageLoopProxy::current()) { |
+} |
+ |
+void AudioEncoderShim::EncoderImpl::Encode( |
+ const scoped_refptr<AudioData>& input, |
+ const scoped_refptr<AudioData>& output, |
+ BitstreamBufferReadyCB callback) { |
+ int32_t size = EncodeInternal(input, output); |
+ renderer_task_runner_->PostTask( |
+ FROM_HERE, base::Bind(&AudioEncoderShim::OnEncodeDone, shim_, output, |
+ size < 0 ? -1 : size, callback)); |
+} |
+ |
+void AudioEncoderShim::EncoderImpl::Stop() { |
+} |
+ |
+class AudioEncoderShim::OpusEncoderImpl : public AudioEncoderShim::EncoderImpl { |
+ public: |
+ OpusEncoderImpl(const base::WeakPtr<AudioEncoderShim>& shim); |
+ ~OpusEncoderImpl() override; |
+ |
+ private: |
+ // AudioEncoderShim::Impl: |
+ std::vector<PP_AudioProfileDescription> GetSupportedProfiles() override; |
+ bool Initialize( |
+ const ppapi::proxy::PPB_AudioEncodeParameters& parameters) override; |
+ int32_t GetNumberOfSamplesPerFrame() override; |
+ int32_t EncodeInternal( |
+ const scoped_refptr<AudioEncoderShim::AudioData>& input, |
+ const scoped_refptr<AudioEncoderShim::AudioData>& output) override; |
+ void RequestBitrateChange(uint32_t bitrate) override; |
+ |
+ scoped_ptr<uint8[]> encoder_memory_; |
+ OpusEncoder* opus_encoder_; |
+ |
+ ppapi::proxy::PPB_AudioEncodeParameters parameters_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(OpusEncoderImpl); |
+}; |
+ |
+AudioEncoderShim::OpusEncoderImpl::OpusEncoderImpl( |
+ const base::WeakPtr<AudioEncoderShim>& shim) |
+ : EncoderImpl(shim), opus_encoder_(nullptr) { |
+} |
+ |
+AudioEncoderShim::OpusEncoderImpl::~OpusEncoderImpl() { |
+} |
+ |
+std::vector<PP_AudioProfileDescription> |
+AudioEncoderShim::OpusEncoderImpl::GetSupportedProfiles() { |
+ std::vector<PP_AudioProfileDescription> profiles; |
+ uint32_t sampling_rates[] = {8000, 12000, 16000, 24000, 48000}; |
+ |
+ for (uint32_t i = 0; i < arraysize(sampling_rates); i++) { |
+ PP_AudioProfileDescription profile; |
+ profile.profile = PP_AUDIOPROFILE_OPUS; |
+ profile.max_channels = 2; |
+ profile.sample_size = PP_AUDIOBUFFER_SAMPLESIZE_16_BITS; |
+ profile.sample_rate = sampling_rates[i]; |
+ profile.hardware_accelerated = PP_FALSE; |
+ profiles.push_back(profile); |
+ } |
+ return profiles; |
+} |
+ |
+bool AudioEncoderShim::OpusEncoderImpl::Initialize( |
+ const ppapi::proxy::PPB_AudioEncodeParameters& parameters) { |
+ DCHECK(!encoder_memory_); |
+ |
+ int32_t encoder_size = opus_encoder_get_size(parameters.channels); |
+ if (encoder_size < 1) |
+ return false; |
+ |
+ encoder_memory_.reset(new uint8[encoder_size]); |
+ opus_encoder_ = reinterpret_cast<OpusEncoder*>(encoder_memory_.get()); |
+ |
+ if (opus_encoder_init(opus_encoder_, parameters.input_sample_rate, |
+ parameters.channels, OPUS_APPLICATION_AUDIO) != OPUS_OK) |
+ return false; |
+ |
+ if (opus_encoder_ctl(opus_encoder_, |
+ OPUS_SET_BITRATE(parameters.initial_bitrate <= 0 |
+ ? OPUS_AUTO |
+ : parameters.initial_bitrate)) != |
+ OPUS_OK) |
+ return false; |
+ |
+ parameters_ = parameters; |
+ |
+ return true; |
+} |
+ |
+int32_t AudioEncoderShim::OpusEncoderImpl::GetNumberOfSamplesPerFrame() { |
+ // Opus supports 2.5, 5, 10, 20, 40 or 60ms audio frames. We take |
+ // 10ms by default. |
+ return parameters_.input_sample_rate / 100; |
+} |
+ |
+int32_t AudioEncoderShim::OpusEncoderImpl::EncodeInternal( |
+ const scoped_refptr<AudioData>& input, |
+ const scoped_refptr<AudioData>& output) { |
+ return opus_encode( |
+ opus_encoder_, reinterpret_cast<opus_int16*>(input->GetData()), |
+ (input->GetSize() / parameters_.channels) / parameters_.input_sample_size, |
+ output->GetData(), output->GetSize()); |
+} |
+ |
+void AudioEncoderShim::OpusEncoderImpl::RequestBitrateChange(uint32_t bitrate) { |
+ opus_encoder_ctl(opus_encoder_, OPUS_SET_BITRATE(bitrate)); |
+} |
+ |
+class AudioEncoderShim::SpeexEncoderImpl |
+ : public AudioEncoderShim::EncoderImpl { |
+ public: |
+ SpeexEncoderImpl(const base::WeakPtr<AudioEncoderShim>& shim); |
+ ~SpeexEncoderImpl() override; |
+ |
+ private: |
+ // AudioEncoderShim::Impl: |
+ std::vector<PP_AudioProfileDescription> GetSupportedProfiles() override; |
+ bool Initialize( |
+ const ppapi::proxy::PPB_AudioEncodeParameters& parameters) override; |
+ int32_t GetNumberOfSamplesPerFrame() override; |
+ int32_t EncodeInternal( |
+ const scoped_refptr<AudioEncoderShim::AudioData>& input, |
+ const scoped_refptr<AudioEncoderShim::AudioData>& output) override; |
+ void RequestBitrateChange(uint32_t bitrate) override; |
+ |
+ bool bits_initialized_; |
+ bool encoder_initialized_; |
+ int32_t samples_per_frame_; |
+ |
+ SpeexBits bits_; |
+ void* encoder_state_; |
+ |
+ ppapi::proxy::PPB_AudioEncodeParameters parameters_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(SpeexEncoderImpl); |
+}; |
+ |
+AudioEncoderShim::SpeexEncoderImpl::SpeexEncoderImpl( |
+ const base::WeakPtr<AudioEncoderShim>& shim) |
+ : EncoderImpl(shim), |
+ bits_initialized_(false), |
+ encoder_initialized_(false), |
+ samples_per_frame_(0), |
+ encoder_state_(nullptr) { |
+} |
+ |
+AudioEncoderShim::SpeexEncoderImpl::~SpeexEncoderImpl() { |
+ if (bits_initialized_) |
+ speex_bits_destroy(&bits_); |
+ if (encoder_initialized_) |
+ speex_encoder_destroy(encoder_state_); |
+} |
+ |
+std::vector<PP_AudioProfileDescription> |
+AudioEncoderShim::SpeexEncoderImpl::GetSupportedProfiles() { |
+ std::vector<PP_AudioProfileDescription> profiles; |
+ uint32_t sampling_rates[] = {8000, 16000, 32000}; |
+ |
+ for (uint32_t i = 0; i < arraysize(sampling_rates); i++) { |
+ PP_AudioProfileDescription profile; |
+ profile.profile = PP_AUDIOPROFILE_SPEEX; |
+ profile.max_channels = 1; |
+ profile.sample_size = PP_AUDIOBUFFER_SAMPLESIZE_16_BITS; |
+ profile.sample_rate = sampling_rates[i]; |
+ profile.hardware_accelerated = PP_FALSE; |
+ profiles.push_back(profile); |
+ } |
+ return profiles; |
+} |
+ |
+bool AudioEncoderShim::SpeexEncoderImpl::Initialize( |
+ const ppapi::proxy::PPB_AudioEncodeParameters& parameters) { |
+ DCHECK(!bits_initialized_); |
+ DCHECK(!encoder_initialized_); |
+ |
+ memset(&bits_, 0, sizeof(bits_)); |
+ speex_bits_init(&bits_); |
+ |
+ switch (parameters.input_sample_rate) { |
+ case 8000: |
+ encoder_state_ = speex_encoder_init(&speex_nb_mode); |
+ break; |
+ case 16000: |
+ encoder_state_ = speex_encoder_init(&speex_wb_mode); |
+ break; |
+ case 32000: |
+ encoder_state_ = speex_encoder_init(&speex_uwb_mode); |
+ break; |
+ default: |
+ return false; |
+ } |
+ |
+ int quality = kSpeexEncodingQuality; |
+ speex_encoder_ctl(encoder_state_, SPEEX_SET_QUALITY, &quality); |
+ int vbr = 1; |
+ speex_encoder_ctl(encoder_state_, SPEEX_SET_VBR, &vbr); |
+ |
+ speex_encoder_ctl(encoder_state_, SPEEX_GET_FRAME_SIZE, &samples_per_frame_); |
+ |
+ parameters_ = parameters; |
+ |
+ return true; |
+} |
+ |
+int32_t AudioEncoderShim::SpeexEncoderImpl::GetNumberOfSamplesPerFrame() { |
+ return samples_per_frame_; |
+} |
+ |
+int32_t AudioEncoderShim::SpeexEncoderImpl::EncodeInternal( |
+ const scoped_refptr<AudioData>& input, |
+ const scoped_refptr<AudioData>& output) { |
+ speex_bits_reset(&bits_); |
+ speex_encode_int(encoder_state_, reinterpret_cast<int16_t*>(input->GetData()), |
+ &bits_); |
+ return speex_bits_write(&bits_, reinterpret_cast<char*>(output->GetData()), |
+ output->GetSize()); |
+} |
+ |
+void AudioEncoderShim::SpeexEncoderImpl::RequestBitrateChange( |
+ uint32_t bitrate) { |
+ int32_t encoder_bitrate = base::checked_cast<int32_t>(bitrate); |
+ speex_encoder_ctl(encoder_state_, SPEEX_SET_BITRATE, &encoder_bitrate); |
+} |
+ |
+AudioEncoderShim::AudioEncoderShim() |
+ : media_task_runner_( |
+ RenderThreadImpl::current()->GetMediaThreadTaskRunner()), |
+ weak_ptr_factory_(this) { |
+} |
+ |
+AudioEncoderShim::~AudioEncoderShim() { |
+ if (encoder_impl_) |
+ media_task_runner_->PostTask( |
+ FROM_HERE, base::Bind(&AudioEncoderShim::EncoderImpl::Stop, |
+ base::Owned(encoder_impl_.release()))); |
+} |
+ |
+std::vector<PP_AudioProfileDescription> |
+AudioEncoderShim::GetSupportedProfiles() { |
+ std::vector<PP_AudioProfileDescription> profiles; |
+ scoped_ptr<EncoderImpl> encoder( |
+ new OpusEncoderImpl(weak_ptr_factory_.GetWeakPtr())); |
+ std::vector<PP_AudioProfileDescription> codec_profiles = |
+ encoder->GetSupportedProfiles(); |
+ for (const PP_AudioProfileDescription desc : codec_profiles) |
+ profiles.push_back(desc); |
+ encoder.reset(new SpeexEncoderImpl(weak_ptr_factory_.GetWeakPtr())); |
+ codec_profiles = encoder->GetSupportedProfiles(); |
+ for (const PP_AudioProfileDescription desc : codec_profiles) |
+ profiles.push_back(desc); |
+ return profiles; |
+} |
+ |
+bool AudioEncoderShim::Initialize( |
+ const ppapi::proxy::PPB_AudioEncodeParameters& parameters) { |
+ if (encoder_impl_) |
+ return false; |
+ |
+ if (parameters.output_profile == PP_AUDIOPROFILE_OPUS) { |
+ encoder_impl_.reset(new OpusEncoderImpl(weak_ptr_factory_.GetWeakPtr())); |
+ return encoder_impl_->Initialize(parameters); |
+ } else if (parameters.output_profile == PP_AUDIOPROFILE_SPEEX) { |
+ encoder_impl_.reset(new SpeexEncoderImpl(weak_ptr_factory_.GetWeakPtr())); |
+ return encoder_impl_->Initialize(parameters); |
+ } |
+ return false; |
+} |
+ |
+int32_t AudioEncoderShim::GetNumberOfSamplesPerFrame() { |
+ DCHECK(encoder_impl_); |
+ return encoder_impl_->GetNumberOfSamplesPerFrame(); |
+} |
+ |
+void AudioEncoderShim::Encode(const scoped_refptr<AudioData>& input, |
+ const scoped_refptr<AudioData>& output, |
+ BitstreamBufferReadyCB callback) { |
+ DCHECK(encoder_impl_); |
+ media_task_runner_->PostTask( |
+ FROM_HERE, base::Bind(&AudioEncoderShim::EncoderImpl::Encode, |
+ base::Unretained(encoder_impl_.get()), input, |
+ output, callback)); |
+} |
+ |
+void AudioEncoderShim::RequestBitrateChange(uint32_t bitrate) { |
+ DCHECK(encoder_impl_); |
+ media_task_runner_->PostTask( |
+ FROM_HERE, |
+ base::Bind(&AudioEncoderShim::EncoderImpl::RequestBitrateChange, |
+ base::Unretained(encoder_impl_.get()), bitrate)); |
+} |
+ |
+void AudioEncoderShim::OnEncodeDone(const scoped_refptr<AudioData>& output, |
+ int32_t size, |
+ BitstreamBufferReadyCB callback) { |
+ callback.Run(output, size); |
+} |
+ |
+} // namespace content |