| 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();
|
| + }
|
| + 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"));
|
| + 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),
|
| + 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),
|
| + 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
|
|
|
|
|