| Index: content/renderer/pepper/pepper_audio_encoder_host.cc
|
| diff --git a/content/renderer/pepper/pepper_audio_encoder_host.cc b/content/renderer/pepper/pepper_audio_encoder_host.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..38755998b9b65952a02281bba5d9049b7a2e779c
|
| --- /dev/null
|
| +++ b/content/renderer/pepper/pepper_audio_encoder_host.cc
|
| @@ -0,0 +1,512 @@
|
| +// 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/memory/shared_memory.h"
|
| +#include "base/numerics/safe_math.h"
|
| +#include "content/common/pepper_file_util.h"
|
| +#include "content/public/renderer/renderer_ppapi_host.h"
|
| +#include "content/renderer/pepper/audio_encoder_shim.h"
|
| +#include "content/renderer/pepper/host_globals.h"
|
| +#include "content/renderer/pepper/pepper_audio_encoder_host.h"
|
| +#include "content/renderer/render_thread_impl.h"
|
| +#include "media/base/bind_to_current_loop.h"
|
| +#include "ppapi/c/pp_codecs.h"
|
| +#include "ppapi/c/pp_errors.h"
|
| +#include "ppapi/host/dispatch_host_message.h"
|
| +#include "ppapi/host/ppapi_host.h"
|
| +#include "ppapi/proxy/ppapi_messages.h"
|
| +#include "ppapi/shared_impl/media_stream_buffer.h"
|
| +
|
| +using ppapi::proxy::SerializedHandle;
|
| +
|
| +namespace content {
|
| +
|
| +namespace {
|
| +
|
| +// Number of input audio buffers (150ms at 10ms per frame).
|
| +const uint32_t kDefaultNumberOfAudioFrames = 15;
|
| +
|
| +// Number of bitstream buffers.
|
| +const uint32_t kDefaultNumberOfBitstreamBuffers = 8;
|
| +
|
| +// Default bitstream buffers size.
|
| +// TODO(llandwerlin): Would having this a multiple of the audio frame
|
| +// size would make more sense?
|
| +const uint32_t kDefaultBitstreamBufferSize = 100000;
|
| +
|
| +bool PP_HardwareAccelerationCompatible(bool accelerated,
|
| + PP_HardwareAcceleration requested) {
|
| + switch (requested) {
|
| + case PP_HARDWAREACCELERATION_ONLY:
|
| + return accelerated;
|
| + case PP_HARDWAREACCELERATION_NONE:
|
| + return !accelerated;
|
| + case PP_HARDWAREACCELERATION_WITHFALLBACK:
|
| + return true;
|
| + // No default case, to catch unhandled PP_HardwareAcceleration values.
|
| + }
|
| + return false;
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +class PepperAudioEncoderHost::BufferManager
|
| + : public base::RefCountedThreadSafe<BufferManager> {
|
| + public:
|
| + uint32_t number_of_buffers() const { return number_of_buffers_; }
|
| + size_t buffer_size() const { return buffer_size_; }
|
| +
|
| + virtual bool Initialize(uint32_t number_of_buffers, size_t buffer_size) {
|
| + number_of_buffers_ = number_of_buffers;
|
| + buffer_size_ = buffer_size;
|
| + return true;
|
| + }
|
| + virtual uint8_t* GetBuffer(int32_t id) = 0;
|
| + virtual int32_t DequeueBuffer() = 0;
|
| + virtual void EnqueueBuffer(int32_t id) = 0;
|
| + virtual base::SharedMemory* GetSharedMemory() = 0;
|
| +
|
| + protected:
|
| + friend class base::RefCountedThreadSafe<BufferManager>;
|
| + BufferManager() : number_of_buffers_(0), buffer_size_(0) {}
|
| + virtual ~BufferManager() {}
|
| +
|
| + private:
|
| + uint32_t number_of_buffers_;
|
| + size_t buffer_size_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(BufferManager);
|
| +};
|
| +
|
| +class PepperAudioEncoderHost::PcmBufferManager
|
| + : public BufferManager,
|
| + public ppapi::MediaStreamBufferManager::Delegate {
|
| + public:
|
| + PcmBufferManager(const ppapi::proxy::PPB_AudioEncodeParameters& parameters,
|
| + uint32_t number_of_samples)
|
| + : buffer_manager_(this),
|
| + encode_parameters_(parameters),
|
| + number_of_samples_(number_of_samples) {}
|
| + ~PcmBufferManager() override {}
|
| +
|
| + private:
|
| + // PepperAudioEncoderHost::BufferManager:
|
| + bool Initialize(uint32_t number_of_buffers, size_t buffer_size) override {
|
| + size_t total_buffer_size =
|
| + buffer_size + sizeof(ppapi::MediaStreamBuffer::Audio);
|
| + scoped_ptr<base::SharedMemory> shm(
|
| + RenderThreadImpl::current()
|
| + ->HostAllocateSharedMemoryBuffer(number_of_buffers *
|
| + total_buffer_size)
|
| + .Pass());
|
| + if (!shm ||
|
| + !buffer_manager_.SetBuffers(number_of_buffers, total_buffer_size,
|
| + shm.Pass(), false))
|
| + return false;
|
| +
|
| + for (int32_t i = 0; i < base::checked_cast<int32_t>(number_of_buffers);
|
| + i++) {
|
| + ppapi::MediaStreamBuffer::Audio* buffer =
|
| + &(buffer_manager_.GetBufferPointer(i)->audio);
|
| + buffer->header.size = total_buffer_size;
|
| + buffer->header.type = ppapi::MediaStreamBuffer::TYPE_AUDIO;
|
| + buffer->sample_rate = static_cast<PP_AudioBuffer_SampleRate>(
|
| + encode_parameters_.input_sample_rate);
|
| + buffer->number_of_channels = encode_parameters_.channels;
|
| + buffer->number_of_samples = number_of_samples_;
|
| + buffer->data_size = buffer_size;
|
| + }
|
| +
|
| + return BufferManager::Initialize(number_of_buffers, buffer_size);
|
| + }
|
| + uint8_t* GetBuffer(int32_t id) override {
|
| + return static_cast<uint8_t*>(
|
| + buffer_manager_.GetBufferPointer(id)->audio.data);
|
| + }
|
| + int32_t DequeueBuffer() override { return buffer_manager_.DequeueBuffer(); }
|
| + void EnqueueBuffer(int32_t id) override { buffer_manager_.EnqueueBuffer(id); }
|
| + base::SharedMemory* GetSharedMemory() override {
|
| + return buffer_manager_.shm();
|
| + }
|
| +
|
| + // ppapi::MediaStreamBufferManager::Delegate:
|
| + void OnNewBufferEnqueued() override {}
|
| +
|
| + ppapi::MediaStreamBufferManager buffer_manager_;
|
| + ppapi::proxy::PPB_AudioEncodeParameters encode_parameters_;
|
| + uint32_t number_of_samples_;
|
| +};
|
| +
|
| +class PepperAudioEncoderHost::BitstreamBufferManager : public BufferManager {
|
| + public:
|
| + BitstreamBufferManager() {}
|
| + ~BitstreamBufferManager() override {}
|
| +
|
| + private:
|
| + // PepperAudioEncoderHost::BufferManager:
|
| + bool Initialize(uint32_t number_of_buffers, size_t buffer_size) override {
|
| + size_t total_size = number_of_buffers * buffer_size;
|
| + shm_ = RenderThreadImpl::current()
|
| + ->HostAllocateSharedMemoryBuffer(total_size)
|
| + .Pass();
|
| + if (!shm_ || !shm_->Map(total_size))
|
| + return false;
|
| +
|
| + for (uint32_t i = 0; i < number_of_buffers; i++)
|
| + enqueued_buffers_.push_back(base::checked_cast<int32_t>(i));
|
| +
|
| + return BufferManager::Initialize(number_of_buffers, buffer_size);
|
| + }
|
| + uint8_t* GetBuffer(int32_t id) override {
|
| + return static_cast<uint8_t*>(shm_->memory()) + (id * buffer_size());
|
| + }
|
| + int32_t DequeueBuffer() override {
|
| + if (enqueued_buffers_.empty())
|
| + return -1;
|
| + int32_t id = enqueued_buffers_.front();
|
| + enqueued_buffers_.pop_front();
|
| + return id;
|
| + }
|
| + void EnqueueBuffer(int32_t id) { enqueued_buffers_.push_back(id); }
|
| + base::SharedMemory* GetSharedMemory() override { return shm_.get(); }
|
| +
|
| + scoped_ptr<base::SharedMemory> shm_;
|
| + std::deque<int32_t> enqueued_buffers_;
|
| +};
|
| +
|
| +class PepperAudioEncoderHost::AudioData : public AudioEncoderShim::AudioData {
|
| + public:
|
| + AudioData(int32_t id, const scoped_refptr<BufferManager>& buffer_manager)
|
| + : id_(id), buffer_manager_(buffer_manager) {}
|
| +
|
| + int32_t id() const { return id_; }
|
| +
|
| + private:
|
| + ~AudioData() override {}
|
| +
|
| + // AudioEncoderShim::AudioData:
|
| + uint8_t* GetData() override { return buffer_manager_->GetBuffer(id_); }
|
| + size_t GetSize() override { return buffer_manager_->buffer_size(); }
|
| +
|
| + int32_t id_;
|
| + scoped_refptr<BufferManager> buffer_manager_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(AudioData);
|
| +};
|
| +
|
| +PepperAudioEncoderHost::PepperAudioEncoderHost(RendererPpapiHost* host,
|
| + PP_Instance instance,
|
| + PP_Resource resource)
|
| + : ResourceHost(host->GetPpapiHost(), instance, resource),
|
| + renderer_ppapi_host_(host),
|
| + initialized_(false),
|
| + encoder_last_error_(PP_ERROR_FAILED),
|
| + weak_ptr_factory_(this) {
|
| +}
|
| +
|
| +PepperAudioEncoderHost::~PepperAudioEncoderHost() {
|
| + Close();
|
| +}
|
| +
|
| +int32_t PepperAudioEncoderHost::OnResourceMessageReceived(
|
| + const IPC::Message& msg,
|
| + ppapi::host::HostMessageContext* context) {
|
| + PPAPI_BEGIN_MESSAGE_MAP(PepperAudioEncoderHost, msg)
|
| + PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(
|
| + PpapiHostMsg_AudioEncoder_GetSupportedProfiles,
|
| + OnHostMsgGetSupportedProfiles)
|
| + PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_AudioEncoder_Initialize,
|
| + OnHostMsgInitialize)
|
| + PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_AudioEncoder_GetAudioFrames,
|
| + OnHostMsgGetAudioFrames)
|
| + PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_AudioEncoder_Encode,
|
| + OnHostMsgEncode)
|
| + PPAPI_DISPATCH_HOST_RESOURCE_CALL(
|
| + PpapiHostMsg_AudioEncoder_RecycleBitstreamBuffer,
|
| + OnHostMsgRecycleBitstreamBuffer)
|
| + PPAPI_DISPATCH_HOST_RESOURCE_CALL(
|
| + PpapiHostMsg_AudioEncoder_RequestBitrateChange,
|
| + OnHostMsgRequestBitrateChange)
|
| + PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_AudioEncoder_Close,
|
| + OnHostMsgClose)
|
| + PPAPI_END_MESSAGE_MAP()
|
| + return PP_ERROR_FAILED;
|
| +}
|
| +
|
| +int32_t PepperAudioEncoderHost::OnHostMsgGetSupportedProfiles(
|
| + ppapi::host::HostMessageContext* context) {
|
| + std::vector<PP_AudioProfileDescription> profiles;
|
| + GetSupportedProfiles(&profiles);
|
| +
|
| + host()->SendReply(
|
| + context->MakeReplyMessageContext(),
|
| + PpapiPluginMsg_AudioEncoder_GetSupportedProfilesReply(profiles));
|
| +
|
| + return PP_OK_COMPLETIONPENDING;
|
| +}
|
| +
|
| +int32_t PepperAudioEncoderHost::OnHostMsgInitialize(
|
| + ppapi::host::HostMessageContext* context,
|
| + const ppapi::proxy::PPB_AudioEncodeParameters& parameters) {
|
| + if (initialized_)
|
| + return PP_ERROR_FAILED;
|
| +
|
| + if (!IsInitializationValid(parameters))
|
| + return PP_ERROR_NOTSUPPORTED;
|
| +
|
| + encode_parameters_ = parameters;
|
| + initialize_reply_context_ = context->MakeReplyMessageContext();
|
| +
|
| + int32_t error = PP_ERROR_FAILED;
|
| + if (parameters.acceleration == PP_HARDWAREACCELERATION_NONE ||
|
| + parameters.acceleration == PP_HARDWAREACCELERATION_WITHFALLBACK) {
|
| + encoder_.reset(new AudioEncoderShim);
|
| + if (encoder_->Initialize(parameters)) {
|
| + if (AllocateBitstreamBuffers(kDefaultBitstreamBufferSize)) {
|
| + encoder_last_error_ = PP_OK;
|
| + return PP_OK_COMPLETIONPENDING;
|
| + }
|
| + error = PP_ERROR_NOMEMORY;
|
| + } else
|
| + error = PP_ERROR_FAILED;
|
| + }
|
| +
|
| + initialize_reply_context_ = ppapi::host::ReplyMessageContext();
|
| + encoder_ = nullptr;
|
| +
|
| + return error;
|
| +}
|
| +
|
| +int32_t PepperAudioEncoderHost::OnHostMsgGetAudioFrames(
|
| + ppapi::host::HostMessageContext* context) {
|
| + if (encoder_last_error_)
|
| + return encoder_last_error_;
|
| +
|
| + get_audio_frames_reply_context_ = context->MakeReplyMessageContext();
|
| + if (!AllocateAudioFrames(encoder_->GetNumberOfSamplesPerFrame()))
|
| + return PP_ERROR_NOMEMORY;
|
| +
|
| + return PP_OK_COMPLETIONPENDING;
|
| +}
|
| +
|
| +int32_t PepperAudioEncoderHost::OnHostMsgEncode(
|
| + ppapi::host::HostMessageContext* context,
|
| + uint32_t buffer_id) {
|
| + if (encoder_last_error_)
|
| + return encoder_last_error_;
|
| +
|
| + if (buffer_id >= audio_buffer_manager_->number_of_buffers())
|
| + return PP_ERROR_BADARGUMENT;
|
| +
|
| + // audio_buffer_manager_->EnqueueBuffer(buffer_id);
|
| + pending_encode_buffers_.push_back(buffer_id);
|
| +
|
| + DoEncode();
|
| +
|
| + return PP_OK_COMPLETIONPENDING;
|
| +}
|
| +
|
| +int32_t PepperAudioEncoderHost::OnHostMsgRecycleBitstreamBuffer(
|
| + ppapi::host::HostMessageContext* context,
|
| + uint32_t buffer_id) {
|
| + if (encoder_last_error_)
|
| + return encoder_last_error_;
|
| +
|
| + if (buffer_id >= bitstream_buffer_manager_->number_of_buffers())
|
| + return PP_ERROR_BADARGUMENT;
|
| +
|
| + bitstream_buffer_manager_->EnqueueBuffer(buffer_id);
|
| +
|
| + DoEncode();
|
| +
|
| + return PP_OK;
|
| +}
|
| +
|
| +int32_t PepperAudioEncoderHost::OnHostMsgRequestBitrateChange(
|
| + ppapi::host::HostMessageContext* context,
|
| + uint32_t bitrate) {
|
| + if (encoder_last_error_)
|
| + return encoder_last_error_;
|
| +
|
| + encoder_->RequestBitrateChange(bitrate);
|
| +
|
| + return PP_OK;
|
| +}
|
| +
|
| +int32_t PepperAudioEncoderHost::OnHostMsgClose(
|
| + ppapi::host::HostMessageContext* context) {
|
| + encoder_last_error_ = PP_ERROR_FAILED;
|
| + Close();
|
| +
|
| + return PP_OK;
|
| +}
|
| +
|
| +void PepperAudioEncoderHost::GetSupportedProfiles(
|
| + std::vector<PP_AudioProfileDescription>* profiles) {
|
| + DCHECK(RenderThreadImpl::current());
|
| +
|
| + AudioEncoderShim software_encoder;
|
| + *profiles = software_encoder.GetSupportedProfiles();
|
| +}
|
| +
|
| +bool PepperAudioEncoderHost::IsInitializationValid(
|
| + const ppapi::proxy::PPB_AudioEncodeParameters& parameters) {
|
| + DCHECK(RenderThreadImpl::current());
|
| +
|
| + std::vector<PP_AudioProfileDescription> profiles;
|
| + GetSupportedProfiles(&profiles);
|
| +
|
| + for (const PP_AudioProfileDescription& profile : profiles) {
|
| + if (parameters.output_profile == profile.profile &&
|
| + parameters.input_sample_size == profile.sample_size &&
|
| + parameters.input_sample_rate == profile.sample_rate &&
|
| + parameters.channels <= profile.max_channels &&
|
| + PP_HardwareAccelerationCompatible(profile.hardware_accelerated,
|
| + parameters.acceleration))
|
| + return true;
|
| + }
|
| +
|
| + return false;
|
| +}
|
| +
|
| +bool PepperAudioEncoderHost::AllocateAudioFrames(uint32_t number_of_samples) {
|
| + DCHECK(RenderThreadImpl::current());
|
| + DCHECK(initialized_);
|
| + DCHECK(get_audio_frames_reply_context_.is_valid());
|
| +
|
| + // Frames have already been allocated.
|
| + if (audio_buffer_manager_)
|
| + return false;
|
| +
|
| + scoped_refptr<BufferManager> buffer_manager(
|
| + new PcmBufferManager(encode_parameters_, number_of_samples));
|
| + uint32_t buffer_size = number_of_samples * encode_parameters_.channels *
|
| + encode_parameters_.input_sample_size;
|
| + if (!buffer_manager->Initialize(kDefaultNumberOfAudioFrames, buffer_size)) {
|
| + get_audio_frames_reply_context_ = ppapi::host::ReplyMessageContext();
|
| + return false;
|
| + }
|
| +
|
| + get_audio_frames_reply_context_.params.AppendHandle(
|
| + SerializedHandle(renderer_ppapi_host_->ShareHandleWithRemote(
|
| + PlatformFileFromSharedMemoryHandle(
|
| + buffer_manager->GetSharedMemory()->handle()),
|
| + false),
|
| + buffer_manager->GetSharedMemory()->mapped_size()));
|
| +
|
| + host()->SendReply(
|
| + get_audio_frames_reply_context_,
|
| + PpapiPluginMsg_AudioEncoder_GetAudioFramesReply(
|
| + buffer_manager->number_of_buffers(), buffer_manager->buffer_size()));
|
| +
|
| + audio_buffer_manager_ = buffer_manager;
|
| + get_audio_frames_reply_context_ = ppapi::host::ReplyMessageContext();
|
| +
|
| + return true;
|
| +}
|
| +
|
| +bool PepperAudioEncoderHost::AllocateBitstreamBuffers(size_t buffer_size) {
|
| + DCHECK(RenderThreadImpl::current());
|
| + // We assume RequireBitstreamBuffers is only called once.
|
| + DCHECK(!initialized_);
|
| + DCHECK(initialize_reply_context_.is_valid());
|
| +
|
| + scoped_refptr<BufferManager> buffer_manager(new BitstreamBufferManager);
|
| + if (!buffer_manager->Initialize(kDefaultNumberOfBitstreamBuffers,
|
| + buffer_size)) {
|
| + initialize_reply_context_ = ppapi::host::ReplyMessageContext();
|
| + return false;
|
| + }
|
| +
|
| + initialize_reply_context_.params.AppendHandle(
|
| + SerializedHandle(renderer_ppapi_host_->ShareHandleWithRemote(
|
| + PlatformFileFromSharedMemoryHandle(
|
| + buffer_manager->GetSharedMemory()->handle()),
|
| + false),
|
| + buffer_manager->GetSharedMemory()->mapped_size()));
|
| +
|
| + host()->SendReply(
|
| + initialize_reply_context_,
|
| + PpapiPluginMsg_AudioEncoder_InitializeReply(
|
| + encoder_->GetNumberOfSamplesPerFrame(),
|
| + buffer_manager->number_of_buffers(), buffer_manager->buffer_size()));
|
| +
|
| + bitstream_buffer_manager_ = buffer_manager;
|
| + initialize_reply_context_ = ppapi::host::ReplyMessageContext();
|
| +
|
| + return true;
|
| +}
|
| +
|
| +scoped_refptr<AudioEncoderShim::AudioData> PepperAudioEncoderHost::GetAudioData(
|
| + const scoped_refptr<BufferManager>& buffer_manager) {
|
| + int32_t buffer_id = buffer_manager->DequeueBuffer();
|
| + if (buffer_id < 0)
|
| + return scoped_refptr<AudioData>();
|
| + return make_scoped_refptr(new AudioData(buffer_id, buffer_manager));
|
| +}
|
| +
|
| +void PepperAudioEncoderHost::DoEncode() {
|
| + if (pending_encode_buffers_.empty())
|
| + return;
|
| +
|
| + scoped_refptr<AudioEncoderShim::AudioData> output =
|
| + GetAudioData(bitstream_buffer_manager_);
|
| + if (!output)
|
| + return;
|
| +
|
| + int32_t audio_buffer_id = pending_encode_buffers_.front();
|
| + pending_encode_buffers_.pop_front();
|
| + scoped_refptr<AudioEncoderShim::AudioData> input =
|
| + new AudioData(audio_buffer_id, audio_buffer_manager_);
|
| + DCHECK(input);
|
| +
|
| + encoder_->Encode(input, output,
|
| + base::Bind(&PepperAudioEncoderHost::BitstreamBufferReady,
|
| + weak_ptr_factory_.GetWeakPtr(), audio_buffer_id));
|
| +}
|
| +
|
| +void PepperAudioEncoderHost::BitstreamBufferReady(
|
| + int32_t audio_buffer_id,
|
| + const scoped_refptr<AudioEncoderShim::AudioData>& output,
|
| + int32_t size) {
|
| + DCHECK(RenderThreadImpl::current());
|
| +
|
| + if (encoder_last_error_)
|
| + return;
|
| +
|
| + host()->SendUnsolicitedReply(
|
| + pp_resource(), PpapiPluginMsg_AudioEncoder_EncodeReply(
|
| + base::checked_cast<uint32_t>(audio_buffer_id)));
|
| +
|
| + if (size < 0) {
|
| + NotifyPepperError(PP_ERROR_FAILED);
|
| + return;
|
| + }
|
| +
|
| + host()->SendUnsolicitedReply(pp_resource(),
|
| + PpapiPluginMsg_AudioEncoder_BitstreamBufferReady(
|
| + static_cast<AudioData*>(output.get())->id(),
|
| + base::checked_cast<uint32_t>(size)));
|
| +}
|
| +
|
| +void PepperAudioEncoderHost::NotifyPepperError(int32_t error) {
|
| + DCHECK(RenderThreadImpl::current());
|
| +
|
| + encoder_last_error_ = error;
|
| + Close();
|
| + host()->SendUnsolicitedReply(
|
| + pp_resource(),
|
| + PpapiPluginMsg_AudioEncoder_NotifyError(encoder_last_error_));
|
| +}
|
| +
|
| +void PepperAudioEncoderHost::Close() {
|
| + DCHECK(RenderThreadImpl::current());
|
| +
|
| + encoder_ = nullptr;
|
| + audio_buffer_manager_ = nullptr;
|
| + bitstream_buffer_manager_ = nullptr;
|
| +}
|
| +
|
| +} // namespace content
|
|
|