diff options
author | brettw@chromium.org <brettw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-12-02 17:17:21 +0000 |
---|---|---|
committer | brettw@chromium.org <brettw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-12-02 17:17:21 +0000 |
commit | 5d84d01d904a2e5fa3e17b8d9165e63a20351160 (patch) | |
tree | dd87a4d94ed6276dc1551cbc5d3c744eb268cebd /ppapi | |
parent | 9a80065bad1506ac11163d597ace3295ddbfa8cb (diff) | |
download | chromium_src-5d84d01d904a2e5fa3e17b8d9165e63a20351160.zip chromium_src-5d84d01d904a2e5fa3e17b8d9165e63a20351160.tar.gz chromium_src-5d84d01d904a2e5fa3e17b8d9165e63a20351160.tar.bz2 |
Implement audio proxy for Pepper.
In addition to the basic proxying, this required some changes to the dispatcher
and process infrastructure to get the handles of the processes available when
I need them so I can duplicate the shared memory handles properly into the
different processes.
TEST=none
BUG=none
Review URL: http://codereview.chromium.org/4985001
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@68026 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ppapi')
-rw-r--r-- | ppapi/ppapi.gyp | 25 | ||||
-rw-r--r-- | ppapi/proxy/DEPS | 3 | ||||
-rw-r--r-- | ppapi/proxy/dispatcher.cc | 12 | ||||
-rw-r--r-- | ppapi/proxy/dispatcher.h | 13 | ||||
-rw-r--r-- | ppapi/proxy/host_dispatcher.cc | 5 | ||||
-rw-r--r-- | ppapi/proxy/host_dispatcher.h | 4 | ||||
-rw-r--r-- | ppapi/proxy/interface_id.h | 4 | ||||
-rw-r--r-- | ppapi/proxy/plugin_dispatcher.cc | 5 | ||||
-rw-r--r-- | ppapi/proxy/plugin_dispatcher.h | 5 | ||||
-rw-r--r-- | ppapi/proxy/plugin_resource.h | 2 | ||||
-rw-r--r-- | ppapi/proxy/ppapi_messages.h | 4 | ||||
-rw-r--r-- | ppapi/proxy/ppapi_messages_internal.h | 49 | ||||
-rw-r--r-- | ppapi/proxy/ppb_audio_config_proxy.cc | 135 | ||||
-rw-r--r-- | ppapi/proxy/ppb_audio_config_proxy.h | 46 | ||||
-rw-r--r-- | ppapi/proxy/ppb_audio_proxy.cc | 285 | ||||
-rw-r--r-- | ppapi/proxy/ppb_audio_proxy.h | 78 | ||||
-rw-r--r-- | ppapi/shared_impl/DEPS | 4 | ||||
-rw-r--r-- | ppapi/shared_impl/README.txt | 2 | ||||
-rw-r--r-- | ppapi/shared_impl/audio_impl.cc | 96 | ||||
-rw-r--r-- | ppapi/shared_impl/audio_impl.h | 85 |
20 files changed, 847 insertions, 15 deletions
diff --git a/ppapi/ppapi.gyp b/ppapi/ppapi.gyp index b9fb347..b321dc6 100644 --- a/ppapi/ppapi.gyp +++ b/ppapi/ppapi.gyp @@ -495,11 +495,32 @@ # ], }, { + 'target_name': 'ppapi_shared_impl', + 'type': 'static_library', + 'dependencies': [ + 'ppapi_c', + '../base/base.gyp:base', + ], + 'include_dirs': [ + '..', + ], + 'sources': [ + 'shared_impl/audio_impl.cc', + 'shared_impl/audio_impl.h', + ], + 'conditions': [ + ['OS=="win"', { + 'msvs_guid': 'E7420D65-A885-41EB-B4BE-04DE0C97033B', + }], + ], + }, + { 'target_name': 'ppapi_proxy', 'type': 'static_library', 'dependencies': [ '../ipc/ipc.gyp:ipc', 'ppapi_c', + 'ppapi_shared_impl', ], 'all_dependent_settings': { 'include_dirs': [ @@ -536,6 +557,10 @@ 'proxy/ppapi_messages_internal.h', 'proxy/ppapi_param_traits.cc', 'proxy/ppapi_param_traits.h', + 'proxy/ppb_audio_config_proxy.cc', + 'proxy/ppb_audio_config_proxy.h', + 'proxy/ppb_audio_proxy.cc', + 'proxy/ppb_audio_proxy.h', 'proxy/ppb_buffer_proxy.cc', 'proxy/ppb_buffer_proxy.h', 'proxy/ppb_char_set_proxy.cc', diff --git a/ppapi/proxy/DEPS b/ppapi/proxy/DEPS index d5d2f9c..7ef8f98 100644 --- a/ppapi/proxy/DEPS +++ b/ppapi/proxy/DEPS @@ -1,4 +1,7 @@ include_rules = [ + "+base", + "+ipc", + # These files are really Chrome-only and we don't want to expose them, but # we need to use them for the proxy. Allow the code here to pull these # headers (which don't depend on anything else). diff --git a/ppapi/proxy/dispatcher.cc b/ppapi/proxy/dispatcher.cc index b1ce1c4..df7383f 100644 --- a/ppapi/proxy/dispatcher.cc +++ b/ppapi/proxy/dispatcher.cc @@ -12,6 +12,8 @@ #include "base/logging.h" #include "ipc/ipc_message.h" #include "ipc/ipc_sync_channel.h" +#include "ppapi/c/dev/ppb_audio_config_dev.h" +#include "ppapi/c/dev/ppb_audio_dev.h" #include "ppapi/c/dev/ppb_buffer_dev.h" #include "ppapi/c/dev/ppb_char_set_dev.h" #include "ppapi/c/dev/ppb_cursor_control_dev.h" @@ -31,6 +33,8 @@ #include "ppapi/c/ppb_url_response_info.h" #include "ppapi/c/ppp_instance.h" #include "ppapi/proxy/ppapi_messages.h" +#include "ppapi/proxy/ppb_audio_config_proxy.h" +#include "ppapi/proxy/ppb_audio_proxy.h" #include "ppapi/proxy/ppb_buffer_proxy.h" #include "ppapi/proxy/ppb_char_set_proxy.h" #include "ppapi/proxy/ppb_core_proxy.h" @@ -54,8 +58,10 @@ namespace pp { namespace proxy { -Dispatcher::Dispatcher(GetInterfaceFunc local_get_interface) +Dispatcher::Dispatcher(base::ProcessHandle remote_process_handle, + GetInterfaceFunc local_get_interface) : pp_module_(0), + remote_process_handle_(remote_process_handle), disallow_trusted_interfaces_(true), local_get_interface_(local_get_interface), declared_supported_remote_interfaces_(false), @@ -216,6 +222,10 @@ void Dispatcher::OnMsgDeclareInterfaces( InterfaceProxy* Dispatcher::CreateProxyForInterface( const std::string& interface_name, const void* interface_functions) { + if (interface_name == PPB_AUDIO_CONFIG_DEV_INTERFACE) + return new PPB_AudioConfig_Proxy(this, interface_functions); + if (interface_name == PPB_AUDIO_DEV_INTERFACE) + return new PPB_Audio_Proxy(this, interface_functions); if (interface_name == PPB_BUFFER_DEV_INTERFACE) return new PPB_Buffer_Proxy(this, interface_functions); if (interface_name == PPB_CHAR_SET_DEV_INTERFACE) diff --git a/ppapi/proxy/dispatcher.h b/ppapi/proxy/dispatcher.h index 356dbcf..deee5fc 100644 --- a/ppapi/proxy/dispatcher.h +++ b/ppapi/proxy/dispatcher.h @@ -10,6 +10,7 @@ #include <vector> #include "base/linked_ptr.h" +#include "base/process.h" #include "base/scoped_ptr.h" #include "ipc/ipc_channel.h" #include "ipc/ipc_channel_handle.h" @@ -86,6 +87,14 @@ class Dispatcher : public IPC::Channel::Listener, // or not supported by the remote side, returns NULL. const void* GetProxiedInterface(const std::string& interface); + // Returns the remote process' handle. For the host dispatcher, this will be + // the plugin process, and for the plugin dispatcher, this will be the + // renderer process. This is used for sharing memory and such and is + // guaranteed valid (unless the remote process has suddenly died). + base::ProcessHandle remote_process_handle() const { + return remote_process_handle_; + } + // Called if the remote side is declaring to us which interfaces it supports // so we don't have to query for each one. We'll pre-create proxies for // each of the given interfaces. @@ -105,7 +114,8 @@ class Dispatcher : public IPC::Channel::Listener, } protected: - Dispatcher(GetInterfaceFunc local_get_interface); + Dispatcher(base::ProcessHandle remote_process_handle, + GetInterfaceFunc local_get_interface); // Setter for the derived classes to set the appropriate var serialization. // Takes ownership of the given pointer, which must be on the heap. @@ -154,6 +164,7 @@ class Dispatcher : public IPC::Channel::Listener, // this dispatcher. PP_Module pp_module_; + base::ProcessHandle remote_process_handle_; // See getter above. scoped_ptr<IPC::SyncChannel> channel_; bool disallow_trusted_interfaces_; diff --git a/ppapi/proxy/host_dispatcher.cc b/ppapi/proxy/host_dispatcher.cc index bb70796..a717af8 100644 --- a/ppapi/proxy/host_dispatcher.cc +++ b/ppapi/proxy/host_dispatcher.cc @@ -19,10 +19,11 @@ InstanceToDispatcherMap* g_instance_to_dispatcher = NULL; } // namespace -HostDispatcher::HostDispatcher(const PPB_Var_Deprecated* var_interface, +HostDispatcher::HostDispatcher(base::ProcessHandle remote_process_handle, + const PPB_Var_Deprecated* var_interface, PP_Module module, GetInterfaceFunc local_get_interface) - : Dispatcher(local_get_interface) { + : Dispatcher(remote_process_handle, local_get_interface) { SetSerializationRules(new HostVarSerializationRules(var_interface, module)); } diff --git a/ppapi/proxy/host_dispatcher.h b/ppapi/proxy/host_dispatcher.h index 94d45d2..f968652 100644 --- a/ppapi/proxy/host_dispatcher.h +++ b/ppapi/proxy/host_dispatcher.h @@ -9,6 +9,7 @@ #include <string> #include <vector> +#include "base/process.h" #include "base/scoped_ptr.h" #include "ppapi/c/pp_instance.h" #include "ppapi/proxy/dispatcher.h" @@ -36,7 +37,8 @@ class HostDispatcher : public Dispatcher { // Constructor for the renderer side. // // You must call Dispatcher::InitWithChannel after the constructor. - HostDispatcher(const PPB_Var_Deprecated* var_interface, + HostDispatcher(base::ProcessHandle host_process_handle, + const PPB_Var_Deprecated* var_interface, PP_Module module, GetInterfaceFunc local_get_interface); ~HostDispatcher(); diff --git a/ppapi/proxy/interface_id.h b/ppapi/proxy/interface_id.h index f0ae597..8d85f03 100644 --- a/ppapi/proxy/interface_id.h +++ b/ppapi/proxy/interface_id.h @@ -12,7 +12,9 @@ namespace proxy { // to route messages to the appropriate message handler. enum InterfaceID { // Zero is reserved for control messages. - INTERFACE_ID_PPB_BUFFER = 1, + INTERFACE_ID_PPB_AUDIO = 1, + INTERFACE_ID_PPB_AUDIO_CONFIG, + INTERFACE_ID_PPB_BUFFER, INTERFACE_ID_PPB_CHAR_SET, INTERFACE_ID_PPB_CORE, INTERFACE_ID_PPB_CURSORCONTROL, diff --git a/ppapi/proxy/plugin_dispatcher.cc b/ppapi/proxy/plugin_dispatcher.cc index 085c15c..4c9adca 100644 --- a/ppapi/proxy/plugin_dispatcher.cc +++ b/ppapi/proxy/plugin_dispatcher.cc @@ -30,10 +30,11 @@ const void* GetInterfaceFromDispatcher(const char* interface) { } // namespace -PluginDispatcher::PluginDispatcher(GetInterfaceFunc get_interface, +PluginDispatcher::PluginDispatcher(base::ProcessHandle remote_process_handle, + GetInterfaceFunc get_interface, InitModuleFunc init_module, ShutdownModuleFunc shutdown_module) - : Dispatcher(get_interface), + : Dispatcher(remote_process_handle, get_interface), init_module_(init_module), shutdown_module_(shutdown_module), plugin_resource_tracker_(new PluginResourceTracker( diff --git a/ppapi/proxy/plugin_dispatcher.h b/ppapi/proxy/plugin_dispatcher.h index 0a97965..79eafb1 100644 --- a/ppapi/proxy/plugin_dispatcher.h +++ b/ppapi/proxy/plugin_dispatcher.h @@ -7,6 +7,7 @@ #include <string> +#include "base/process.h" #include "base/scoped_ptr.h" #include "ppapi/proxy/callback_tracker.h" #include "ppapi/proxy/dispatcher.h" @@ -29,7 +30,8 @@ class PluginDispatcher : public Dispatcher { // module ID will be set upon receipt of the InitializeModule message. // // You must call Dispatcher::InitWithChannel after the constructor. - PluginDispatcher(GetInterfaceFunc get_interface, + PluginDispatcher(base::ProcessHandle remote_process_handle, + GetInterfaceFunc get_interface, InitModuleFunc init_module, ShutdownModuleFunc shutdown_module); ~PluginDispatcher(); @@ -59,6 +61,7 @@ class PluginDispatcher : public Dispatcher { } private: + // IPC message handler. void OnInitializeModule(PP_Module pp_module, bool* result); InitModuleFunc init_module_; diff --git a/ppapi/proxy/plugin_resource.h b/ppapi/proxy/plugin_resource.h index 09ed59f..52a8609 100644 --- a/ppapi/proxy/plugin_resource.h +++ b/ppapi/proxy/plugin_resource.h @@ -11,6 +11,8 @@ // If you inherit from resource, make sure you add the class name here. #define FOR_ALL_RESOURCES(F) \ + F(Audio) \ + F(AudioConfig) \ F(Buffer) \ F(Font) \ F(Graphics2D) \ diff --git a/ppapi/proxy/ppapi_messages.h b/ppapi/proxy/ppapi_messages.h index d0f5bed..dd2b246 100644 --- a/ppapi/proxy/ppapi_messages.h +++ b/ppapi/proxy/ppapi_messages.h @@ -10,8 +10,12 @@ #include <vector> #include "base/basictypes.h" +#include "base/process.h" +#include "base/shared_memory.h" #include "base/string16.h" +#include "base/sync_socket.h" #include "ipc/ipc_message_utils.h" +#include "ipc/ipc_platform_file.h" #include "ppapi/c/pp_bool.h" #include "ppapi/c/pp_instance.h" #include "ppapi/c/pp_module.h" diff --git a/ppapi/proxy/ppapi_messages_internal.h b/ppapi/proxy/ppapi_messages_internal.h index 22d8a52..d7f317e 100644 --- a/ppapi/proxy/ppapi_messages_internal.h +++ b/ppapi/proxy/ppapi_messages_internal.h @@ -13,7 +13,8 @@ // These are from the plugin to the renderer IPC_BEGIN_MESSAGES(Ppapi) // Loads the given plugin. - IPC_MESSAGE_CONTROL2(PpapiMsg_LoadPlugin, + IPC_MESSAGE_CONTROL3(PpapiMsg_LoadPlugin, + base::ProcessHandle /* host_process_handle */, FilePath /* path */, int /* renderer_id */) @@ -36,6 +37,27 @@ IPC_BEGIN_MESSAGES(Ppapi) uint32 /* serialized_callback */, int32 /* param */) + // PPB_Audio. + + // Notifies the result of the audio stream create call. This is called in + // both error cases and in the normal success case. These cases are + // differentiated by the result code, which is one of the standard PPAPI + // result codes. + // + // The handler of this message should always close all of the handles passed + // in, since some could be valid even in the error case. + IPC_MESSAGE_ROUTED5(PpapiMsg_PPBAudio_NotifyAudioStreamCreated, + PP_Resource /* audio_id */, + int32_t /* result_code (will be != PP_OK on failure) */, + IPC::PlatformFileForTransit /* socket_handle */, + base::SharedMemoryHandle /* handle */, + int32_t /* length */) + + // PPB_Graphics2D. + IPC_MESSAGE_ROUTED2(PpapiMsg_PPBGraphics2D_FlushACK, + PP_Resource /* graphics_2d */, + int32_t /* pp_error */) + // PPP_Class. IPC_SYNC_MESSAGE_ROUTED3_2(PpapiMsg_PPPClass_HasProperty, int64 /* ppp_class */, @@ -88,11 +110,6 @@ IPC_BEGIN_MESSAGES(Ppapi) int64 /* ppp_class */, int64 /* object */) - // PPB_Graphics2D. - IPC_MESSAGE_ROUTED2(PpapiMsg_PPBGraphics2D_FlushACK, - PP_Resource /* graphics_2d */, - int32_t /* pp_error */) - // PPP_Instance. IPC_SYNC_MESSAGE_ROUTED3_1(PpapiMsg_PPPInstance_DidCreate, PP_Instance /* instance */, @@ -142,6 +159,26 @@ IPC_BEGIN_MESSAGES(PpapiHost) IPC_MESSAGE_CONTROL1(PpapiHostMsg_PluginLoaded, IPC::ChannelHandle /* handle */) + // PPB_Audio. + IPC_SYNC_MESSAGE_ROUTED2_1(PpapiHostMsg_PPBAudio_Create, + PP_Instance /* instance_id */, + PP_Resource /* config_id */, + PP_Resource /* result */) + IPC_MESSAGE_ROUTED2(PpapiHostMsg_PPBAudio_StartOrStop, + PP_Resource /* audio_id */, + bool /* play */) + + // PPB_AudioConfig. + IPC_SYNC_MESSAGE_ROUTED3_1(PpapiHostMsg_PPBAudioConfig_Create, + PP_Module /* module */, + int32_t /* sample_rate */, + uint32_t /* sample_frame_count */, + PP_Resource /* result */) + IPC_SYNC_MESSAGE_ROUTED1_1( + PpapiHostMsg_PPBAudioConfig_RecommendSampleFrameCount, + uint32_t /* requested */, + uint32_t /* result */) + // PPB_Buffer. IPC_SYNC_MESSAGE_ROUTED2_2(PpapiHostMsg_PPBBuffer_Create, PP_Module /* module */, diff --git a/ppapi/proxy/ppb_audio_config_proxy.cc b/ppapi/proxy/ppb_audio_config_proxy.cc new file mode 100644 index 0000000..7ec3e8f --- /dev/null +++ b/ppapi/proxy/ppb_audio_config_proxy.cc @@ -0,0 +1,135 @@ +// 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_config_proxy.h" + +#include "ppapi/c/dev/ppb_audio_config_dev.h" +#include "ppapi/proxy/plugin_dispatcher.h" +#include "ppapi/proxy/plugin_resource.h" +#include "ppapi/proxy/ppapi_messages.h" + +namespace pp { +namespace proxy { + +class AudioConfig : public PluginResource { + public: + AudioConfig(PP_AudioSampleRate_Dev sample_rate, uint32_t sample_frame_count) + : sample_rate_(sample_rate), + sample_frame_count_(sample_frame_count) { + } + virtual ~AudioConfig() {} + + // Resource overrides. + virtual AudioConfig* AsAudioConfig() { return this; } + + PP_AudioSampleRate_Dev sample_rate() const { return sample_rate_; } + uint32_t sample_frame_count() const { return sample_frame_count_; } + + private: + PP_AudioSampleRate_Dev sample_rate_; + uint32_t sample_frame_count_; + + DISALLOW_COPY_AND_ASSIGN(AudioConfig); +}; + +namespace { + +PP_Resource CreateStereo16bit(PP_Module module_id, + PP_AudioSampleRate_Dev sample_rate, + uint32_t sample_frame_count) { + PP_Resource result = 0; + PluginDispatcher::Get()->Send(new PpapiHostMsg_PPBAudioConfig_Create( + INTERFACE_ID_PPB_AUDIO_CONFIG, module_id, + static_cast<int32_t>(sample_rate), sample_frame_count, + &result)); + if (!result) + return 0; + + linked_ptr<AudioConfig> object( + new AudioConfig(sample_rate, sample_frame_count)); + PluginDispatcher::Get()->plugin_resource_tracker()->AddResource( + result, object); + return result; +} + +uint32_t RecommendSampleFrameCount(uint32_t requested_sample_frame_count) { + uint32_t result = 0; + PluginDispatcher::Get()->Send( + new PpapiHostMsg_PPBAudioConfig_RecommendSampleFrameCount( + INTERFACE_ID_PPB_AUDIO_CONFIG, requested_sample_frame_count, + &result)); + return result; +} + +PP_Bool IsAudioConfig(PP_Resource resource) { + AudioConfig* object = PluginResource::GetAs<AudioConfig>(resource); + return BoolToPPBool(!!object); +} + +PP_AudioSampleRate_Dev GetSampleRate(PP_Resource config_id) { + AudioConfig* object = PluginResource::GetAs<AudioConfig>(config_id); + if (!object) + return PP_AUDIOSAMPLERATE_NONE; + return object->sample_rate(); +} + +uint32_t GetSampleFrameCount(PP_Resource config_id) { + AudioConfig* object = PluginResource::GetAs<AudioConfig>(config_id); + if (!object) + return 0; + return object->sample_frame_count(); +} + +const PPB_AudioConfig_Dev audio_config_interface = { + &CreateStereo16bit, + &RecommendSampleFrameCount, + &IsAudioConfig, + &GetSampleRate, + &GetSampleFrameCount +}; + +} // namespace + +PPB_AudioConfig_Proxy::PPB_AudioConfig_Proxy(Dispatcher* dispatcher, + const void* target_interface) + : InterfaceProxy(dispatcher, target_interface) { +} + +PPB_AudioConfig_Proxy::~PPB_AudioConfig_Proxy() { +} + +const void* PPB_AudioConfig_Proxy::GetSourceInterface() const { + return &audio_config_interface; +} + +InterfaceID PPB_AudioConfig_Proxy::GetInterfaceId() const { + return INTERFACE_ID_PPB_AUDIO_CONFIG; +} + +void PPB_AudioConfig_Proxy::OnMessageReceived(const IPC::Message& msg) { + IPC_BEGIN_MESSAGE_MAP(PPB_AudioConfig_Proxy, msg) + IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBAudioConfig_Create, + OnMsgCreateStereo16Bit) + IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBAudioConfig_RecommendSampleFrameCount, + OnMsgRecommendSampleFrameCount) + IPC_END_MESSAGE_MAP() +} + +void PPB_AudioConfig_Proxy::OnMsgCreateStereo16Bit(PP_Module module, + int32_t sample_rate, + uint32_t sample_frame_count, + PP_Resource* result) { + *result = ppb_audio_config_target()->CreateStereo16Bit( + module, static_cast<PP_AudioSampleRate_Dev>(sample_rate), + sample_frame_count); +} + +void PPB_AudioConfig_Proxy::OnMsgRecommendSampleFrameCount( + uint32_t requested, + uint32_t* result) { + *result = ppb_audio_config_target()->RecommendSampleFrameCount(requested); +} + +} // namespace proxy +} // namespace pp diff --git a/ppapi/proxy/ppb_audio_config_proxy.h b/ppapi/proxy/ppb_audio_config_proxy.h new file mode 100644 index 0000000..b80ea31 --- /dev/null +++ b/ppapi/proxy/ppb_audio_config_proxy.h @@ -0,0 +1,46 @@ +// 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. + +#ifndef PPAPI_PROXY_PPB_AUDIO_CONFIG_PROXY_H_ +#define PPAPI_PROXY_PPB_AUDIO_CONFIG_PROXY_H_ + +#include "base/basictypes.h" +#include "ppapi/c/pp_module.h" +#include "ppapi/c/pp_resource.h" +#include "ppapi/proxy/interface_proxy.h" + +struct PPB_AudioConfig_Dev; + +namespace pp { +namespace proxy { + +class PPB_AudioConfig_Proxy : public InterfaceProxy { + public: + PPB_AudioConfig_Proxy(Dispatcher* dispatcher, const void* target_interface); + virtual ~PPB_AudioConfig_Proxy(); + + const PPB_AudioConfig_Dev* ppb_audio_config_target() const { + return static_cast<const PPB_AudioConfig_Dev*>(target_interface()); + } + + // InterfaceProxy implementation. + virtual const void* GetSourceInterface() const; + virtual InterfaceID GetInterfaceId() const; + virtual void OnMessageReceived(const IPC::Message& msg); + + private: + // Message handlers. + void OnMsgCreateStereo16Bit(PP_Module module, + int32_t sample_rate, + uint32_t sample_frame_count, + PP_Resource* result); + void OnMsgRecommendSampleFrameCount(uint32_t requested, uint32_t* result); + + DISALLOW_COPY_AND_ASSIGN(PPB_AudioConfig_Proxy); +}; + +} // namespace proxy +} // namespace pp + +#endif // PPAPI_PROXY_PPB_AUDIO_CONFIG_PROXY_H_ diff --git a/ppapi/proxy/ppb_audio_proxy.cc b/ppapi/proxy/ppb_audio_proxy.cc new file mode 100644 index 0000000..bb40693 --- /dev/null +++ b/ppapi/proxy/ppb_audio_proxy.cc @@ -0,0 +1,285 @@ +// 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" +#include "ppapi/shared_impl/audio_impl.h" + +namespace pp { +namespace proxy { + +class Audio : public PluginResource, public pp::shared_impl::AudioImpl { + public: + Audio(PP_Resource config_id, PPB_Audio_Callback callback, void* user_data) + : config_(config_id) { + SetCallback(callback, user_data); + PluginDispatcher::Get()->plugin_resource_tracker()->AddRefResource( + config_); + } + virtual ~Audio() { + PluginDispatcher::Get()->plugin_resource_tracker()->ReleaseResource( + config_); + } + + // Resource overrides. + virtual Audio* AsAudio() { return this; } + + PP_Resource config() const { return config_; } + + void StartPlayback(PP_Resource resource) { + if (playing()) + return; + SetStartPlaybackState(); + 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)); + SetStopPlaybackState(); + } + + private: + PP_Resource config_; + + 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() +} + +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, + IPC::PlatformFileForTransit 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( + IPC::PlatformFileForTransitToPlatformFile(socket_handle)); + base::SharedMemory temp_mem(handle, false); + return; + } + object->SetStreamInfo( + handle, length, IPC::PlatformFileForTransitToPlatformFile(socket_handle)); +} + +void PPB_Audio_Proxy::AudioChannelConnected(int32_t result, + PP_Resource resource) { + IPC::PlatformFileForTransit socket_handle = + IPC::InvalidPlatformFileForTransit(); +#if defined(OS_WIN) + base::SharedMemoryHandle shared_memory = NULL; +#elif defined(OS_POSIX) + base::SharedMemoryHandle shared_memory(-1, false); +#else + #error Not implemented. +#endif + uint32_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, + IPC::PlatformFileForTransit* foreign_socket_handle, + base::SharedMemoryHandle* foreign_shared_memory_handle, + uint32_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( + GetCurrentProcess(), + reinterpret_cast<HANDLE>(static_cast<intptr_t>(socket_handle)), + dispatcher()->remote_process_handle(), 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(socket_handle, true); +#endif + + // Get the shared memory for the buffer. + // TODO(brettw) remove the reinterpret cast when the interface is updated. + int shared_memory_handle; + result = audio_trusted->GetSharedMemory(resource, &shared_memory_handle, + shared_memory_length); + if (result != PP_OK) + return result; + + base::SharedMemory shared_memory( +#if defined(OS_WIN) + reinterpret_cast<HANDLE>(static_cast<intptr_t>(shared_memory_handle)), +#else + base::FileDescriptor(shared_memory_handle, false), +#endif + false); + + // 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 diff --git a/ppapi/proxy/ppb_audio_proxy.h b/ppapi/proxy/ppb_audio_proxy.h new file mode 100644 index 0000000..a6d2738 --- /dev/null +++ b/ppapi/proxy/ppb_audio_proxy.h @@ -0,0 +1,78 @@ +// 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. + +#ifndef PPAPI_PROXY_PPB_AUDIO_PROXY_H_ +#define PPAPI_PROXY_PPB_AUDIO_PROXY_H_ + +#include "base/basictypes.h" +#include "base/shared_memory.h" +#include "base/sync_socket.h" +#include "ipc/ipc_platform_file.h" +#include "ppapi/c/pp_instance.h" +#include "ppapi/c/pp_module.h" +#include "ppapi/c/pp_resource.h" +#include "ppapi/cpp/completion_callback.h" +#include "ppapi/proxy/interface_proxy.h" +#include "ppapi/proxy/proxy_non_thread_safe_ref_count.h" + +struct PPB_Audio_Dev; + +namespace pp { +namespace proxy { + +class PPB_Audio_Proxy : public InterfaceProxy { + public: + PPB_Audio_Proxy(Dispatcher* dispatcher, const void* target_interface); + virtual ~PPB_Audio_Proxy(); + + const PPB_Audio_Dev* ppb_audio_target() const { + return static_cast<const PPB_Audio_Dev*>(target_interface()); + } + + // InterfaceProxy implementation. + virtual const void* GetSourceInterface() const; + virtual InterfaceID GetInterfaceId() const; + virtual void OnMessageReceived(const IPC::Message& msg); + + private: + // Plugin->renderer message handlers. + void OnMsgCreate(PP_Instance instance_id, + PP_Resource config_id, + PP_Resource* result); + void OnMsgStartOrStop(PP_Resource audio_id, bool play); + + // Renderer->plugin message handlers. + void OnMsgNotifyAudioStreamCreated( + PP_Resource audio_id, + int32_t result_code, + IPC::PlatformFileForTransit socket_handle, + base::SharedMemoryHandle shared_memory_handle, + uint32_t shared_memory_length); + + void AudioChannelConnected(int32_t result, PP_Resource resource); + + // In the renderer, this is called in response to a stream created message. + // It will retrieve the shared memory and socket handles and place them into + // the given out params. The return value is a PPAPI error code. + // + // The input arguments should be initialized to 0 or -1, depending on the + // platform's default invalid handle values. On error, some of these + // arguments may be written to, and others may be untouched, depending on + // where the error occurred. + int32_t GetAudioConnectedHandles( + PP_Resource resource, + IPC::PlatformFileForTransit* foreign_socket_handle, + base::SharedMemoryHandle* foreign_shared_memory_handle, + uint32_t* shared_memory_length); + + CompletionCallbackFactory<PPB_Audio_Proxy, + ProxyNonThreadSafeRefCount> callback_factory_; + + DISALLOW_COPY_AND_ASSIGN(PPB_Audio_Proxy); +}; + +} // namespace proxy +} // namespace pp + +#endif // PPAPI_PROXY_PPB_AUDIO_PROXY_H_ diff --git a/ppapi/shared_impl/DEPS b/ppapi/shared_impl/DEPS new file mode 100644 index 0000000..84fea55 --- /dev/null +++ b/ppapi/shared_impl/DEPS @@ -0,0 +1,4 @@ +include_rules = [ + "+base", + "-ppapi/cpp", +] diff --git a/ppapi/shared_impl/README.txt b/ppapi/shared_impl/README.txt new file mode 100644 index 0000000..71ff8d6 --- /dev/null +++ b/ppapi/shared_impl/README.txt @@ -0,0 +1,2 @@ +This directory contains implementation code for PPAPI that needs to be shared +between the backend implemntation in the renderer and in the proxy. diff --git a/ppapi/shared_impl/audio_impl.cc b/ppapi/shared_impl/audio_impl.cc new file mode 100644 index 0000000..83b73f6 --- /dev/null +++ b/ppapi/shared_impl/audio_impl.cc @@ -0,0 +1,96 @@ +// 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/shared_impl/audio_impl.h" + +#include "base/logging.h" + +namespace pp { +namespace shared_impl { + +AudioImpl::AudioImpl() + : playing_(false), + shared_memory_size_(0), + callback_(NULL), + user_data_(NULL) { +} + +AudioImpl::~AudioImpl() { + // Closing the socket causes the thread to exit - wait for it. + socket_->Close(); + if (audio_thread_.get()) { + audio_thread_->Join(); + audio_thread_.reset(); + } +} + +void AudioImpl::SetCallback(PPB_Audio_Callback callback, void* user_data) { + callback_ = callback; + user_data_ = user_data; +} + +void AudioImpl::SetStartPlaybackState() { + DCHECK(!playing_); + DCHECK(!audio_thread_.get()); + + // If the socket doesn't exist, that means that the plugin has started before + // the browser has had a chance to create all the shared memory info and + // notify us. This is a common case. In this case, we just set the playing_ + // flag and the playback will automatically start when that data is available + // in SetStreamInfo. + if (callback_ && socket_.get()) + StartThread(); + playing_ = true; +} + +void AudioImpl::SetStopPlaybackState() { + DCHECK(playing_); + + if (audio_thread_.get()) { + audio_thread_->Join(); + audio_thread_.reset(); + } + playing_ = false; +} + +void AudioImpl::SetStreamInfo(base::SharedMemoryHandle shared_memory_handle, + size_t shared_memory_size, + base::SyncSocket::Handle socket_handle) { + socket_.reset(new base::SyncSocket(socket_handle)); + shared_memory_.reset(new base::SharedMemory(shared_memory_handle, false)); + shared_memory_size_ = shared_memory_size; + + if (callback_) { + shared_memory_->Map(shared_memory_size_); + + // In common case StartPlayback() was called before StreamCreated(). + if (playing_) + StartThread(); + } +} + +void AudioImpl::StartThread() { + DCHECK(callback_); + DCHECK(!audio_thread_.get()); + audio_thread_.reset(new base::DelegateSimpleThread( + this, "plugin_audio_thread")); + audio_thread_->Start(); +} + +void AudioImpl::Run() { + int pending_data; + void* buffer = shared_memory_->memory(); + + 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, shared_memory_size_, user_data_); + } +} + +} // namespace shared_impl +} // namespace pp diff --git a/ppapi/shared_impl/audio_impl.h b/ppapi/shared_impl/audio_impl.h new file mode 100644 index 0000000..b4cb077 --- /dev/null +++ b/ppapi/shared_impl/audio_impl.h @@ -0,0 +1,85 @@ +// 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. + +#ifndef PPAPI_SHARED_IMPL_AUDIO_IMPL_H_ +#define PPAPI_SHARED_IMPL_AUDIO_IMPL_H_ + +#include "base/scoped_ptr.h" +#include "base/simple_thread.h" +#include "base/shared_memory.h" +#include "base/sync_socket.h" +#include "ppapi/c/dev/ppb_audio_dev.h" + +namespace pp { +namespace shared_impl { + +// Implements the logic to map shared memory and run the audio thread signaled +// from the sync socket. Both the proxy and the renderer implementation use +// this code. +class AudioImpl : public base::DelegateSimpleThread::Delegate { + public: + AudioImpl(); + virtual ~AudioImpl(); + + bool playing() const { return playing_; } + + // Sets the callback information that the background thread will use. This + // is optional. Without a callback, the thread will not be run. This + // non-callback mode is used in the renderer with the proxy, since the proxy + // handles the callback entirely within the plugin process. + void SetCallback(PPB_Audio_Callback callback, void* user_data); + + // Configures the current state to be playing or not. The caller is + // responsible for ensuring the new state is the opposite of the current one. + // + // This is the implementation for PPB_Audio.Start/StopPlayback, except that + // it does not actually notify the audio system to stop playback, it just + // configures our object to stop generating callbacks. The actual stop + // playback request will be done in the derived classes and will be different + // from the proxy and the renderer. + void SetStartPlaybackState(); + void SetStopPlaybackState(); + + // Sets the shared memory and socket handles. This will automatically start + // playback if we're currently set to play. + void SetStreamInfo(base::SharedMemoryHandle shared_memory_handle, + size_t shared_memory_size, + base::SyncSocket::Handle socket_handle); + + private: + // Starts execution of the audio thread. + void StartThread(); + + // DelegateSimpleThread::Delegate implementation. Run on the audio thread. + void Run(); + + // True if playing the stream. + bool playing_; + + // Socket used to notify us when audio is ready to accept new samples. This + // pointer is created in StreamCreated(). + scoped_ptr<base::SyncSocket> socket_; + + // Sample buffer in shared memory. This pointer is created in + // StreamCreated(). The memory is only mapped when the audio thread is + // created. + scoped_ptr<base::SharedMemory> shared_memory_; + + // The size of the sample buffer in bytes. + size_t shared_memory_size_; + + // When the callback is set, this thread is spawned for calling it. + scoped_ptr<base::DelegateSimpleThread> audio_thread_; + + // Callback to call when audio is ready to accept new samples. + PPB_Audio_Callback callback_; + + // User data pointer passed verbatim to the callback function. + void* user_data_; +}; + +} // namespace shared_impl +} // namespace pp + +#endif // PPAPI_SHARED_IMPL_AUDIO_IMPL_H_ |