// 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/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* callback, int32_t error) { if (TrackedCallback::IsPending(*callback)) { scoped_refptr temp; callback->swap(temp); temp->Run(error); } } } // namespace AudioEncoderResource::AudioEncoderResource(Connection connection, PP_Instance 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() { } thunk::PPB_AudioEncoder_API* AudioEncoderResource::AsPPB_AudioEncoder_API() { return this; } int32_t AudioEncoderResource::GetSupportedProfiles( const PP_ArrayOutput& output, const scoped_refptr& callback) { if (TrackedCallback::IsPending(get_supported_profiles_callback_)) return PP_ERROR_INPROGRESS; get_supported_profiles_callback_ = callback; Call( RENDERER, PpapiHostMsg_AudioEncoder_GetSupportedProfiles(), base::Bind(&AudioEncoderResource::OnPluginMsgGetSupportedProfilesReply, this, output)); return PP_OK_COMPLETIONPENDING; } int32_t AudioEncoderResource::Initialize( uint32_t channels, PP_AudioBuffer_SampleRate input_sample_rate, PP_AudioBuffer_SampleSize input_sample_size, PP_AudioProfile output_profile, uint32_t initial_bitrate, PP_HardwareAcceleration acceleration, const scoped_refptr& callback) { 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( RENDERER, PpapiHostMsg_AudioEncoder_Initialize(parameters), base::Bind(&AudioEncoderResource::OnPluginMsgInitializeReply, this)); return PP_OK_COMPLETIONPENDING; } int32_t AudioEncoderResource::GetNumberOfSamples() { if (encoder_last_error_) return encoder_last_error_; return number_of_samples_; } int32_t AudioEncoderResource::GetBuffer( PP_Resource* audio_buffer, const scoped_refptr& callback) { 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& callback) { 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 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& callback) { 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& 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(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 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 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 } // namespace ppapi