From 5d84d01d904a2e5fa3e17b8d9165e63a20351160 Mon Sep 17 00:00:00 2001 From: "brettw@chromium.org" Date: Thu, 2 Dec 2010 17:17:21 +0000 Subject: 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 --- ppapi/proxy/DEPS | 3 + ppapi/proxy/dispatcher.cc | 12 +- ppapi/proxy/dispatcher.h | 13 +- ppapi/proxy/host_dispatcher.cc | 5 +- ppapi/proxy/host_dispatcher.h | 4 +- ppapi/proxy/interface_id.h | 4 +- ppapi/proxy/plugin_dispatcher.cc | 5 +- ppapi/proxy/plugin_dispatcher.h | 5 +- ppapi/proxy/plugin_resource.h | 2 + ppapi/proxy/ppapi_messages.h | 4 + ppapi/proxy/ppapi_messages_internal.h | 49 +++++- ppapi/proxy/ppb_audio_config_proxy.cc | 135 ++++++++++++++++ ppapi/proxy/ppb_audio_config_proxy.h | 46 ++++++ ppapi/proxy/ppb_audio_proxy.cc | 285 ++++++++++++++++++++++++++++++++++ ppapi/proxy/ppb_audio_proxy.h | 78 ++++++++++ 15 files changed, 635 insertions(+), 15 deletions(-) create mode 100644 ppapi/proxy/ppb_audio_config_proxy.cc create mode 100644 ppapi/proxy/ppb_audio_config_proxy.h create mode 100644 ppapi/proxy/ppb_audio_proxy.cc create mode 100644 ppapi/proxy/ppb_audio_proxy.h (limited to 'ppapi/proxy') 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 #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 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 #include +#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 +#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 #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(sample_rate), sample_frame_count, + &result)); + if (!result) + return 0; + + linked_ptr 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(resource); + return BoolToPPBool(!!object); +} + +PP_AudioSampleRate_Dev GetSampleRate(PP_Resource config_id) { + AudioConfig* object = PluginResource::GetAs(config_id); + if (!object) + return PP_AUDIOSAMPLERATE_NONE; + return object->sample_rate(); +} + +uint32_t GetSampleFrameCount(PP_Resource config_id) { + AudioConfig* object = PluginResource::GetAs(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(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(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