| Index: ppapi/proxy/audio_encoder_resource.cc
|
| diff --git a/ppapi/proxy/audio_encoder_resource.cc b/ppapi/proxy/audio_encoder_resource.cc
|
| index d1576fb616b6dadd2d7ed8d72fb00fb525be23ed..233af5a2c2b6419eba91c2769a921a47d486fd3d 100644
|
| --- a/ppapi/proxy/audio_encoder_resource.cc
|
| +++ b/ppapi/proxy/audio_encoder_resource.cc
|
| @@ -2,14 +2,39 @@
|
| // Use of this source code is governed by a BSD-style license that can be
|
| // found in the LICENSE file.
|
|
|
| +#include "base/memory/shared_memory.h"
|
| +#include "ppapi/c/pp_array_output.h"
|
| +#include "ppapi/c/pp_codecs.h"
|
| +#include "ppapi/proxy/audio_buffer_resource.h"
|
| #include "ppapi/proxy/audio_encoder_resource.h"
|
| +#include "ppapi/proxy/ppapi_messages.h"
|
| +#include "ppapi/shared_impl/array_writer.h"
|
| +#include "ppapi/shared_impl/media_stream_buffer.h"
|
| +#include "ppapi/thunk/enter.h"
|
|
|
| namespace ppapi {
|
| namespace proxy {
|
|
|
| +namespace {
|
| +
|
| +void RunCallback(scoped_refptr<TrackedCallback>* callback, int32_t error) {
|
| + if (TrackedCallback::IsPending(*callback)) {
|
| + scoped_refptr<TrackedCallback> temp;
|
| + callback->swap(temp);
|
| + temp->Run(error);
|
| + }
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| AudioEncoderResource::AudioEncoderResource(Connection connection,
|
| PP_Instance instance)
|
| - : PluginResource(connection, instance) {
|
| + : PluginResource(connection, instance),
|
| + encoder_last_error_(PP_ERROR_FAILED),
|
| + initialized_(false),
|
| + audio_buffer_manager_(this),
|
| + bitstream_buffer_manager_(this) {
|
| + SendCreate(RENDERER, PpapiHostMsg_AudioEncoder_Create());
|
| }
|
|
|
| AudioEncoderResource::~AudioEncoderResource() {
|
| @@ -22,7 +47,15 @@ thunk::PPB_AudioEncoder_API* AudioEncoderResource::AsPPB_AudioEncoder_API() {
|
| int32_t AudioEncoderResource::GetSupportedProfiles(
|
| const PP_ArrayOutput& output,
|
| const scoped_refptr<TrackedCallback>& callback) {
|
| - return PP_ERROR_NOTSUPPORTED;
|
| + if (TrackedCallback::IsPending(get_supported_profiles_callback_))
|
| + return PP_ERROR_INPROGRESS;
|
| +
|
| + get_supported_profiles_callback_ = callback;
|
| + Call<PpapiPluginMsg_AudioEncoder_GetSupportedProfilesReply>(
|
| + RENDERER, PpapiHostMsg_AudioEncoder_GetSupportedProfiles(),
|
| + base::Bind(&AudioEncoderResource::OnPluginMsgGetSupportedProfilesReply,
|
| + this, output));
|
| + return PP_OK_COMPLETIONPENDING;
|
| }
|
|
|
| int32_t AudioEncoderResource::Initialize(
|
| @@ -33,39 +66,292 @@ int32_t AudioEncoderResource::Initialize(
|
| uint32_t initial_bitrate,
|
| PP_HardwareAcceleration acceleration,
|
| const scoped_refptr<TrackedCallback>& callback) {
|
| - return PP_ERROR_NOTSUPPORTED;
|
| + if (initialized_)
|
| + return PP_ERROR_FAILED;
|
| + if (TrackedCallback::IsPending(initialize_callback_))
|
| + return PP_ERROR_INPROGRESS;
|
| +
|
| + initialize_callback_ = callback;
|
| +
|
| + PPB_AudioEncodeParameters parameters;
|
| + parameters.channels = channels;
|
| + parameters.input_sample_rate = input_sample_rate;
|
| + parameters.input_sample_size = input_sample_size;
|
| + parameters.output_profile = output_profile;
|
| + parameters.initial_bitrate = initial_bitrate;
|
| + parameters.acceleration = acceleration;
|
| +
|
| + Call<PpapiPluginMsg_AudioEncoder_InitializeReply>(
|
| + RENDERER, PpapiHostMsg_AudioEncoder_Initialize(parameters),
|
| + base::Bind(&AudioEncoderResource::OnPluginMsgInitializeReply, this));
|
| + return PP_OK_COMPLETIONPENDING;
|
| }
|
|
|
| int32_t AudioEncoderResource::GetNumberOfSamples() {
|
| - return PP_ERROR_NOTSUPPORTED;
|
| + if (encoder_last_error_)
|
| + return encoder_last_error_;
|
| + return number_of_samples_;
|
| }
|
|
|
| int32_t AudioEncoderResource::GetBuffer(
|
| PP_Resource* audio_buffer,
|
| const scoped_refptr<TrackedCallback>& callback) {
|
| - return PP_ERROR_NOTSUPPORTED;
|
| + if (encoder_last_error_)
|
| + return encoder_last_error_;
|
| + if (TrackedCallback::IsPending(get_buffer_callback_))
|
| + return PP_ERROR_INPROGRESS;
|
| +
|
| + get_buffer_data_ = audio_buffer;
|
| + get_buffer_callback_ = callback;
|
| +
|
| + TryGetAudioBuffer();
|
| +
|
| + return PP_OK_COMPLETIONPENDING;
|
| }
|
|
|
| int32_t AudioEncoderResource::Encode(
|
| PP_Resource audio_buffer,
|
| const scoped_refptr<TrackedCallback>& callback) {
|
| - return PP_ERROR_NOTSUPPORTED;
|
| + if (encoder_last_error_)
|
| + return encoder_last_error_;
|
| +
|
| + AudioBufferMap::iterator it = audio_buffers_.find(audio_buffer);
|
| + if (it == audio_buffers_.end())
|
| + // TODO(llandwerlin): accept MediaStreamAudioTrack's audio buffers.
|
| + return PP_ERROR_BADRESOURCE;
|
| +
|
| + scoped_refptr<AudioBufferResource> buffer_resource = it->second;
|
| +
|
| + encode_callbacks_.insert(
|
| + std::make_pair(buffer_resource->GetBufferIndex(), callback));
|
| +
|
| + Post(RENDERER,
|
| + PpapiHostMsg_AudioEncoder_Encode(buffer_resource->GetBufferIndex()));
|
| +
|
| + // Invalidate the buffer to prevent a CHECK failure when the
|
| + // AudioBufferResource is destructed.
|
| + buffer_resource->Invalidate();
|
| + audio_buffers_.erase(it);
|
| +
|
| + return PP_OK_COMPLETIONPENDING;
|
| }
|
|
|
| int32_t AudioEncoderResource::GetBitstreamBuffer(
|
| PP_AudioBitstreamBuffer* bitstream_buffer,
|
| const scoped_refptr<TrackedCallback>& callback) {
|
| - return PP_ERROR_NOTSUPPORTED;
|
| + if (encoder_last_error_)
|
| + return encoder_last_error_;
|
| + if (TrackedCallback::IsPending(get_bitstream_buffer_callback_))
|
| + return PP_ERROR_INPROGRESS;
|
| +
|
| + get_bitstream_buffer_callback_ = callback;
|
| + get_bitstream_buffer_data_ = bitstream_buffer;
|
| +
|
| + TryWriteBitstreamBuffer();
|
| +
|
| + return PP_OK_COMPLETIONPENDING;
|
| }
|
|
|
| void AudioEncoderResource::RecycleBitstreamBuffer(
|
| const PP_AudioBitstreamBuffer* bitstream_buffer) {
|
| + if (encoder_last_error_)
|
| + return;
|
| +
|
| + BufferMap::const_iterator it =
|
| + bitstream_buffer_map_.find(bitstream_buffer->buffer);
|
| + if (it != bitstream_buffer_map_.end())
|
| + Post(RENDERER,
|
| + PpapiHostMsg_AudioEncoder_RecycleBitstreamBuffer(it->second));
|
| }
|
|
|
| void AudioEncoderResource::RequestBitrateChange(uint32_t bitrate) {
|
| + if (encoder_last_error_)
|
| + return;
|
| + Post(RENDERER, PpapiHostMsg_AudioEncoder_RequestBitrateChange(bitrate));
|
| }
|
|
|
| void AudioEncoderResource::Close() {
|
| + if (encoder_last_error_)
|
| + return;
|
| + Post(RENDERER, PpapiHostMsg_AudioEncoder_Close());
|
| + if (!encoder_last_error_ || !initialized_)
|
| + NotifyError(PP_ERROR_ABORTED);
|
| + ReleaseBuffers();
|
| +}
|
| +
|
| +void AudioEncoderResource::OnReplyReceived(
|
| + const ResourceMessageReplyParams& params,
|
| + const IPC::Message& msg) {
|
| + PPAPI_BEGIN_MESSAGE_MAP(AudioEncoderResource, msg)
|
| + PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL(
|
| + PpapiPluginMsg_AudioEncoder_BitstreamBufferReady,
|
| + OnPluginMsgBitstreamBufferReady)
|
| + PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL(PpapiPluginMsg_AudioEncoder_EncodeReply,
|
| + OnPluginMsgEncodeReply)
|
| + PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL(PpapiPluginMsg_AudioEncoder_NotifyError,
|
| + OnPluginMsgNotifyError)
|
| + PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL_UNHANDLED(
|
| + PluginResource::OnReplyReceived(params, msg))
|
| + PPAPI_END_MESSAGE_MAP()
|
| +}
|
| +
|
| +void AudioEncoderResource::OnPluginMsgGetSupportedProfilesReply(
|
| + const PP_ArrayOutput& output,
|
| + const ResourceMessageReplyParams& params,
|
| + const std::vector<PP_AudioProfileDescription>& profiles) {
|
| + ArrayWriter writer(output);
|
| + if (params.result() != PP_OK || !writer.is_valid() ||
|
| + !writer.StoreVector(profiles)) {
|
| + RunCallback(&get_supported_profiles_callback_, PP_ERROR_FAILED);
|
| + return;
|
| + }
|
| +
|
| + RunCallback(&get_supported_profiles_callback_,
|
| + base::checked_cast<int32_t>(profiles.size()));
|
| +}
|
| +
|
| +void AudioEncoderResource::OnPluginMsgInitializeReply(
|
| + const ResourceMessageReplyParams& params,
|
| + int32_t number_of_samples,
|
| + int32_t audio_buffer_count,
|
| + int32_t audio_buffer_size,
|
| + int32_t bitstream_buffer_count,
|
| + int32_t bitstream_buffer_size) {
|
| + DCHECK(!initialized_);
|
| +
|
| + int32_t error = params.result();
|
| + if (error) {
|
| + RunCallback(&initialize_callback_, error);
|
| + return;
|
| + }
|
| +
|
| + // Get audio buffers shared memory buffer.
|
| + base::SharedMemoryHandle buffer_handle;
|
| + if (!params.TakeSharedMemoryHandleAtIndex(0, &buffer_handle) ||
|
| + !audio_buffer_manager_.SetBuffers(
|
| + audio_buffer_count, audio_buffer_size,
|
| + make_scoped_ptr(new base::SharedMemory(buffer_handle, false)),
|
| + true)) {
|
| + RunCallback(&initialize_callback_, PP_ERROR_NOMEMORY);
|
| + return;
|
| + }
|
| +
|
| + // Get bitstream buffers shared memory buffer.
|
| + if (!params.TakeSharedMemoryHandleAtIndex(1, &buffer_handle) ||
|
| + !bitstream_buffer_manager_.SetBuffers(
|
| + bitstream_buffer_count, bitstream_buffer_size,
|
| + make_scoped_ptr(new base::SharedMemory(buffer_handle, false)),
|
| + false)) {
|
| + RunCallback(&initialize_callback_, PP_ERROR_NOMEMORY);
|
| + return;
|
| + }
|
| +
|
| + for (int32_t i = 0; i < bitstream_buffer_manager_.number_of_buffers(); i++)
|
| + bitstream_buffer_map_.insert(std::make_pair(
|
| + bitstream_buffer_manager_.GetBufferPointer(i)->bitstream.data, i));
|
| +
|
| + encoder_last_error_ = PP_OK;
|
| + number_of_samples_ = number_of_samples;
|
| + initialized_ = true;
|
| +
|
| + RunCallback(&initialize_callback_, PP_OK);
|
| +}
|
| +
|
| +void AudioEncoderResource::OnPluginMsgEncodeReply(
|
| + const ResourceMessageReplyParams& params,
|
| + int32_t buffer_id) {
|
| + // We need to ensure there are still callbacks to be called before
|
| + // processing this message. We might receive an EncodeReply message after
|
| + // having sent a Close message to the renderer. In this case, we don't
|
| + // have any callback left to call.
|
| + if (encode_callbacks_.empty())
|
| + return;
|
| +
|
| + EncodeMap::iterator it = encode_callbacks_.find(buffer_id);
|
| + DCHECK(encode_callbacks_.end() != it);
|
| +
|
| + scoped_refptr<TrackedCallback> callback = it->second;
|
| + encode_callbacks_.erase(it);
|
| + RunCallback(&callback, encoder_last_error_);
|
| +
|
| + audio_buffer_manager_.EnqueueBuffer(buffer_id);
|
| + // If the plugin is waiting for an audio buffer, we can give the one
|
| + // that just became available again.
|
| + if (TrackedCallback::IsPending(get_buffer_callback_))
|
| + TryGetAudioBuffer();
|
| +}
|
| +
|
| +void AudioEncoderResource::OnPluginMsgBitstreamBufferReady(
|
| + const ResourceMessageReplyParams& params,
|
| + int32_t buffer_id) {
|
| + bitstream_buffer_manager_.EnqueueBuffer(buffer_id);
|
| +
|
| + if (TrackedCallback::IsPending(get_bitstream_buffer_callback_))
|
| + TryWriteBitstreamBuffer();
|
| +}
|
| +
|
| +void AudioEncoderResource::OnPluginMsgNotifyError(
|
| + const ResourceMessageReplyParams& params,
|
| + int32_t error) {
|
| + NotifyError(error);
|
| +}
|
| +
|
| +void AudioEncoderResource::NotifyError(int32_t error) {
|
| + DCHECK(error);
|
| +
|
| + encoder_last_error_ = error;
|
| + RunCallback(&get_supported_profiles_callback_, error);
|
| + RunCallback(&initialize_callback_, error);
|
| + RunCallback(&get_buffer_callback_, error);
|
| + get_buffer_data_ = nullptr;
|
| + RunCallback(&get_bitstream_buffer_callback_, error);
|
| + get_bitstream_buffer_data_ = nullptr;
|
| + for (EncodeMap::iterator it = encode_callbacks_.begin();
|
| + it != encode_callbacks_.end(); ++it)
|
| + RunCallback(&it->second, error);
|
| + encode_callbacks_.clear();
|
| +}
|
| +
|
| +void AudioEncoderResource::TryGetAudioBuffer() {
|
| + DCHECK(TrackedCallback::IsPending(get_buffer_callback_));
|
| +
|
| + if (!audio_buffer_manager_.HasAvailableBuffer())
|
| + return;
|
| +
|
| + int32_t buffer_id = audio_buffer_manager_.DequeueBuffer();
|
| + scoped_refptr<AudioBufferResource> resource = new AudioBufferResource(
|
| + pp_instance(), buffer_id,
|
| + audio_buffer_manager_.GetBufferPointer(buffer_id));
|
| + audio_buffers_.insert(
|
| + AudioBufferMap::value_type(resource->pp_resource(), resource));
|
| +
|
| + // Take a reference for the plugin.
|
| + *get_buffer_data_ = resource->GetReference();
|
| + get_buffer_data_ = nullptr;
|
| + RunCallback(&get_buffer_callback_, PP_OK);
|
| +}
|
| +
|
| +void AudioEncoderResource::TryWriteBitstreamBuffer() {
|
| + DCHECK(TrackedCallback::IsPending(get_bitstream_buffer_callback_));
|
| +
|
| + if (!bitstream_buffer_manager_.HasAvailableBuffer())
|
| + return;
|
| +
|
| + int32_t buffer_id = bitstream_buffer_manager_.DequeueBuffer();
|
| + MediaStreamBuffer* buffer =
|
| + bitstream_buffer_manager_.GetBufferPointer(buffer_id);
|
| +
|
| + get_bitstream_buffer_data_->buffer = buffer->bitstream.data;
|
| + get_bitstream_buffer_data_->size = buffer->bitstream.data_size;
|
| + get_bitstream_buffer_data_ = nullptr;
|
| + RunCallback(&get_bitstream_buffer_callback_, PP_OK);
|
| +}
|
| +
|
| +void AudioEncoderResource::ReleaseBuffers() {
|
| + for (AudioBufferMap::iterator it = audio_buffers_.begin();
|
| + it != audio_buffers_.end(); ++it)
|
| + it->second->Invalidate();
|
| + audio_buffers_.clear();
|
| }
|
|
|
| } // namespace proxy
|
|
|