Chromium Code Reviews| Index: ppapi/proxy/ppb_audio_proxy.cc |
| =================================================================== |
| --- ppapi/proxy/ppb_audio_proxy.cc (revision 0) |
| +++ ppapi/proxy/ppb_audio_proxy.cc (revision 0) |
| @@ -0,0 +1,354 @@ |
| +// Copyright (c) 2010 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 "ppapi/proxy/ppb_audio_proxy.h" |
| + |
| +#include "base/simple_thread.h" |
| +#include "ppapi/c/dev/ppb_audio_dev.h" |
| +#include "ppapi/c/dev/ppb_audio_trusted_dev.h" |
| +#include "ppapi/c/pp_errors.h" |
| +#include "ppapi/proxy/interface_id.h" |
| +#include "ppapi/proxy/plugin_dispatcher.h" |
| +#include "ppapi/proxy/plugin_resource.h" |
| +#include "ppapi/proxy/ppapi_messages.h" |
| + |
| +namespace pp { |
| +namespace proxy { |
| + |
| +class Audio : public PluginResource, |
| + public base::DelegateSimpleThread::Delegate { |
| + public: |
| + Audio(PP_Resource config_id, PPB_Audio_Callback callback, void* user_data) |
| + : config_(config_id), |
| + playing_(false), |
| + shared_memory_size_(0), |
| + callback_(callback), |
| + user_data_(user_data) { |
| + PluginDispatcher::Get()->plugin_resource_tracker()->AddRefResource( |
| + config_); |
| + } |
| + virtual ~Audio() { |
| + PluginDispatcher::Get()->plugin_resource_tracker()->ReleaseResource( |
| + config_); |
| + if (audio_thread_.get()) |
| + audio_thread_->Join(); |
| + } |
| + |
| + // Resource overrides. |
| + virtual Audio* AsAudio() { return this; } |
| + |
| + PP_Resource config() const { return config_; } |
| + |
| + void StartPlayback(PP_Resource resource) { |
| + if (playing_) |
| + return; |
| + |
| + DCHECK(!audio_thread_.get()); |
| + if (callback_ && socket_.get()) { |
| + // TODO(brettw) use the same thread for all audio sources! |
| + audio_thread_.reset(new base::DelegateSimpleThread(this, "audio")); |
| + audio_thread_->Start(); |
| + } |
|
piman
2010/12/01 01:44:52
Most of the threading logic and I/O on the socket
|
| + playing_ = true; |
| + PluginDispatcher::Get()->Send(new PpapiHostMsg_PPBAudio_StartOrStop( |
| + INTERFACE_ID_PPB_AUDIO, resource, true)); |
| + } |
| + |
| + void StopPlayback(PP_Resource resource) { |
| + if (!playing_) |
| + return; |
| + |
| + PluginDispatcher::Get()->Send(new PpapiHostMsg_PPBAudio_StartOrStop( |
| + INTERFACE_ID_PPB_AUDIO, resource, false)); |
| + |
| + if (audio_thread_.get()) { |
| + audio_thread_->Join(); |
| + audio_thread_.reset(); |
| + } |
| + playing_ = false; |
| + } |
| + |
| + void StreamCreated(base::SharedMemoryHandle handle, |
| + base::SyncSocket::Handle socket_handle, |
| + uint32_t length) { |
| + socket_.reset(new base::SyncSocket(socket_handle)); |
| + shared_memory_.reset(new base::SharedMemory(handle, false)); |
| + shared_memory_size_ = length; |
| + |
| + if (callback_) { |
| + shared_memory_->Map(shared_memory_size_); |
| + // In common case StartPlayback() was called before StreamCreated(). |
| + if (playing_) { |
| + audio_thread_.reset(new base::DelegateSimpleThread(this, |
| + "plugin_audio_thread")); |
|
piman
2010/12/01 01:44:52
the thread should have the same name wherever it's
|
| + audio_thread_->Start(); |
| + } |
| + } |
| + } |
| + |
| + private: |
| + // DelegateSimpleThread implementation. |
| + virtual void Run() { |
| + int pending_data; |
| + void* buffer = shared_memory_->memory(); |
| + size_t buffer_size_in_bytes = shared_memory_size_; |
| + |
| + while (sizeof(pending_data) == |
| + socket_->Receive(&pending_data, sizeof(pending_data)) && |
| + pending_data >= 0) { |
| + // Exit the thread on pause. |
| + if (pending_data < 0) |
| + return; |
| + callback_(buffer, buffer_size_in_bytes, user_data_); |
| + } |
| + } |
| + |
| + PP_Resource config_; |
| + |
| + bool playing_; |
| + |
| + scoped_ptr<base::DelegateSimpleThread> audio_thread_; |
| + |
| + scoped_ptr<base::SyncSocket> socket_; |
| + scoped_ptr<base::SharedMemory> shared_memory_; |
| + size_t shared_memory_size_; |
| + |
| + volatile PPB_Audio_Callback callback_; |
| + void* user_data_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(Audio); |
| +}; |
| + |
| +namespace { |
| + |
| +PP_Resource Create(PP_Instance instance_id, |
| + PP_Resource config_id, |
| + PPB_Audio_Callback callback, |
| + void* user_data) { |
| + PP_Resource result; |
| + PluginDispatcher::Get()->Send(new PpapiHostMsg_PPBAudio_Create( |
| + INTERFACE_ID_PPB_AUDIO, instance_id, config_id, &result)); |
| + if (!result) |
| + return 0; |
| + |
| + linked_ptr<Audio> object(new Audio(config_id, callback, user_data)); |
| + PluginDispatcher::Get()->plugin_resource_tracker()->AddResource( |
| + result, object); |
| + return result; |
| +} |
| + |
| +PP_Bool IsAudio(PP_Resource resource) { |
| + Audio* object = PluginResource::GetAs<Audio>(resource); |
| + return BoolToPPBool(!!object); |
| +} |
| + |
| +PP_Resource GetCurrentConfiguration(PP_Resource audio_id) { |
| + Audio* object = PluginResource::GetAs<Audio>(audio_id); |
| + if (!object) |
| + return 0; |
| + PP_Resource result = object->config(); |
| + PluginDispatcher::Get()->plugin_resource_tracker()->AddRefResource(result); |
| + return result; |
| +} |
| + |
| +PP_Bool StartPlayback(PP_Resource audio_id) { |
| + Audio* object = PluginResource::GetAs<Audio>(audio_id); |
| + if (!object) |
| + return PP_FALSE; |
| + object->StartPlayback(audio_id); |
| + return PP_TRUE; |
| +} |
| + |
| +PP_Bool StopPlayback(PP_Resource audio_id) { |
| + Audio* object = PluginResource::GetAs<Audio>(audio_id); |
| + if (!object) |
| + return PP_FALSE; |
| + object->StopPlayback(audio_id); |
| + return PP_TRUE; |
| +} |
| + |
| +const PPB_Audio_Dev audio_interface = { |
| + &Create, |
| + &IsAudio, |
| + &GetCurrentConfiguration, |
| + &StartPlayback, |
| + &StopPlayback |
| +}; |
| + |
| +} // namespace |
| + |
| +PPB_Audio_Proxy::PPB_Audio_Proxy(Dispatcher* dispatcher, |
| + const void* target_interface) |
| + : InterfaceProxy(dispatcher, target_interface), |
| + callback_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) { |
| +} |
| + |
| +PPB_Audio_Proxy::~PPB_Audio_Proxy() { |
| +} |
| + |
| +const void* PPB_Audio_Proxy::GetSourceInterface() const { |
| + return &audio_interface; |
| +} |
| + |
| +InterfaceID PPB_Audio_Proxy::GetInterfaceId() const { |
| + return INTERFACE_ID_PPB_AUDIO; |
| +} |
| + |
| +void PPB_Audio_Proxy::OnMessageReceived(const IPC::Message& msg) { |
| + IPC_BEGIN_MESSAGE_MAP(PPB_Audio_Proxy, msg) |
| + IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBAudio_Create, OnMsgCreate) |
| + IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBAudio_StartOrStop, |
| + OnMsgStartOrStop) |
| + |
| + IPC_MESSAGE_HANDLER(PpapiMsg_PPBAudio_NotifyAudioStreamCreated, |
| + OnMsgNotifyAudioStreamCreated) |
| + IPC_END_MESSAGE_MAP() |
| +} |
| + |
| +base::SyncSocket::Handle PPB_Audio_Proxy::HandleFromSocketHandle( |
| + SocketHandle h) { |
| +#if defined(OS_WIN) |
| + return h; |
| +#elif defined(OS_POSIX) |
| + return h.fd; |
| +#endif |
| +} |
| + |
| +void PPB_Audio_Proxy::OnMsgCreate(PP_Instance instance_id, |
| + PP_Resource config_id, |
| + PP_Resource* result) { |
| + const PPB_AudioTrusted_Dev* audio_trusted = |
| + reinterpret_cast<const PPB_AudioTrusted_Dev*>( |
| + dispatcher()->GetLocalInterface(PPB_AUDIO_TRUSTED_DEV_INTERFACE)); |
| + if (!audio_trusted) { |
| + *result = 0; |
| + return; |
| + } |
| + |
| + *result = audio_trusted->CreateTrusted(instance_id); |
| + if (!result) |
| + return; |
| + |
| + CompletionCallback callback = callback_factory_.NewCallback( |
| + &PPB_Audio_Proxy::AudioChannelConnected, *result); |
| + int32_t open_error = audio_trusted->Open(*result, config_id, |
| + callback.pp_completion_callback()); |
| + if (open_error != PP_ERROR_WOULDBLOCK) |
| + callback.Run(open_error); |
| +} |
| + |
| +void PPB_Audio_Proxy::OnMsgStartOrStop(PP_Resource audio_id, bool play) { |
| + if (play) |
| + ppb_audio_target()->StartPlayback(audio_id); |
| + else |
| + ppb_audio_target()->StopPlayback(audio_id); |
| +} |
| + |
| +void PPB_Audio_Proxy::OnMsgNotifyAudioStreamCreated( |
| + PP_Resource audio_id, |
| + int32_t result_code, |
| + SocketHandle socket_handle, |
| + base::SharedMemoryHandle handle, |
| + uint32_t length) { |
| + Audio* object = PluginResource::GetAs<Audio>(audio_id); |
| + if (!object || result_code != PP_OK) { |
| + // The caller may still have given us these handles in the failure case. |
| + // The easiest way to clean these up is to just put them in the objects |
| + // and then close them. This failure case is not performance critical. |
| + base::SyncSocket temp_socket(HandleFromSocketHandle(socket_handle)); |
| + base::SharedMemory temp_mem(handle, false); |
| + return; |
| + } |
| + object->StreamCreated(handle, HandleFromSocketHandle(socket_handle), length); |
| +} |
| + |
| +void PPB_Audio_Proxy::AudioChannelConnected(int32_t result, |
| + PP_Resource resource) { |
| +#if defined(OS_WIN) |
| + base::SharedMemoryHandle shared_memory = NULL; |
| + base::SyncSocket::Handle socket_handle = NULL; |
| +#elif defined(OS_POSIX) |
| + base::SharedMemoryHandle shared_memory(-1, false); |
| + base::FileDescriptor socket_handle(-1, false); |
| +#else |
| + #error Not implemented. |
| +#endif |
| + int32_t shared_memory_length = 0; |
| + |
| + int32_t result_code = result; |
| + if (result_code == PP_OK) { |
| + result_code = GetAudioConnectedHandles(resource, &socket_handle, |
| + &shared_memory, |
| + &shared_memory_length); |
| + } |
| + |
| + // Send all the values, even on error. This simplifies some of our cleanup |
| + // code since the handles will be in the other process and could be |
| + // inconvenient to clean up. Our IPC code will automatically handle this for |
| + // us, as long as the remote side always closes the handles it receives |
| + // (in OnMsgNotifyAudioStreamCreated), even in the failure case. |
| + dispatcher()->Send(new PpapiMsg_PPBAudio_NotifyAudioStreamCreated( |
| + INTERFACE_ID_PPB_AUDIO, resource, result_code, shared_memory, |
| + socket_handle, shared_memory_length)); |
| +} |
| + |
| +int32_t PPB_Audio_Proxy::GetAudioConnectedHandles( |
| + PP_Resource resource, |
| + SocketHandle* foreign_socket_handle, |
| + base::SharedMemoryHandle* foreign_shared_memory_handle, |
| + int32_t* shared_memory_length) { |
| + // Get the trusted audio interface which will give us the handles. |
| + const PPB_AudioTrusted_Dev* audio_trusted = |
| + reinterpret_cast<const PPB_AudioTrusted_Dev*>( |
| + dispatcher()->GetLocalInterface(PPB_AUDIO_TRUSTED_DEV_INTERFACE)); |
| + if (!audio_trusted) |
| + return PP_ERROR_NOINTERFACE; |
| + |
| + // Get the socket handle for signaling. |
| + int32_t socket_handle; |
| + int32_t result = audio_trusted->GetSyncSocket(resource, &socket_handle); |
| + if (result != PP_OK) |
| + return result; |
| + |
| +#if defined(OS_WIN) |
| + // On Windows, duplicate the socket into the plugin process, this will |
| + // automatically close the source handle. |
| + ::DuplicateHandle(GetCurerntProcess(), static_cast<HANDLE>(socket_handle), |
|
piman
2010/12/01 01:44:52
GetCurerntProcess -> GetCurrentProcess
|
| + dispatcher()->GetRemoteProcessHandle(), |
| + foreign_socket_handle, |
| + STANDARD-RIGHTS_REQUIRED | FILE_MAP_READ | FILE_MAP_WRITE, |
| + FALSE, DUPLICATE_CLOSE_SOURCE); |
| +#else |
| + // On Posix, the socket handle will be auto-duplicated when we send the |
| + // FileDescriptor. Set AutoClose since we don't need the handle any more. |
| + *foreign_socket_handle = base::FileDescriptor(static_cast<int>(socket_handle), |
| + true); |
| +#endif |
| + |
| + // Get the shared memory for the buffer. |
| + int shared_memory_handle; |
| + result = audio_trusted->GetSharedMemory(resource, &shared_memory_handle, |
| + shared_memory_length); |
| + if (result != PP_OK) |
| + return result; |
| + |
| +#if defined(OS_WIN) |
| + base::SharedMemory shared_memory(shared_memory_handle, false); |
| +#else |
| + base::SharedMemory shared_memory( |
| + base::FileDescriptor(static_cast<int>(shared_memory_handle), false), |
|
piman
2010/12/01 01:44:52
why the static_cast ? it's an int already.
|
| + false); |
| +#endif |
| + |
| + // Duplicate the shared memory to the plugin process. This will automatically |
| + // close the source handle. |
| + if (!shared_memory.GiveToProcess(dispatcher()->remote_process_handle(), |
| + foreign_shared_memory_handle)) |
| + return PP_ERROR_FAILED; |
| + |
| + return PP_OK; |
| +} |
| + |
| +} // namespace proxy |
| +} // namespace pp |
| Property changes on: ppapi/proxy/ppb_audio_proxy.cc |
| ___________________________________________________________________ |
| Added: svn:eol-style |
| + LF |