1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344
|
// Copyright 2012 The Chromium Authors
// 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/compiler_specific.h"
#include "base/threading/simple_thread.h"
#include "build/build_config.h"
#include "ppapi/c/pp_errors.h"
#include "ppapi/c/ppb_audio.h"
#include "ppapi/c/ppb_audio_config.h"
#include "ppapi/c/ppb_var.h"
#include "ppapi/proxy/enter_proxy.h"
#include "ppapi/proxy/plugin_dispatcher.h"
#include "ppapi/proxy/ppapi_messages.h"
#include "ppapi/shared_impl/api_id.h"
#include "ppapi/shared_impl/platform_file.h"
#include "ppapi/shared_impl/ppapi_globals.h"
#include "ppapi/shared_impl/ppb_audio_shared.h"
#include "ppapi/shared_impl/resource.h"
#include "ppapi/thunk/enter.h"
#include "ppapi/thunk/ppb_audio_config_api.h"
#include "ppapi/thunk/resource_creation_api.h"
#include "ppapi/thunk/thunk.h"
using ppapi::IntToPlatformFile;
using ppapi::proxy::SerializedHandle;
using ppapi::thunk::EnterResourceNoLock;
using ppapi::thunk::PPB_Audio_API;
using ppapi::thunk::PPB_AudioConfig_API;
namespace ppapi {
namespace proxy {
class Audio : public Resource, public PPB_Audio_Shared {
public:
Audio(const HostResource& audio_id,
PP_Resource config_id,
const AudioCallbackCombined& callback,
void* user_data);
Audio(const Audio&) = delete;
Audio& operator=(const Audio&) = delete;
~Audio() override;
// Resource overrides.
PPB_Audio_API* AsPPB_Audio_API() override;
// PPB_Audio_API implementation.
PP_Resource GetCurrentConfig() override;
PP_Bool StartPlayback() override;
PP_Bool StopPlayback() override;
int32_t Open(PP_Resource config_id,
scoped_refptr<TrackedCallback> create_callback) override;
int32_t GetSyncSocket(int* sync_socket) override;
int32_t GetSharedMemory(base::UnsafeSharedMemoryRegion** shm) override;
private:
// Owning reference to the current config object. This isn't actually used,
// we just dish it out as requested by the plugin.
PP_Resource config_;
};
Audio::Audio(const HostResource& audio_id,
PP_Resource config_id,
const AudioCallbackCombined& callback,
void* user_data)
: Resource(OBJECT_IS_PROXY, audio_id),
config_(config_id) {
SetCallback(callback, user_data);
PpapiGlobals::Get()->GetResourceTracker()->AddRefResource(config_);
}
Audio::~Audio() {
#if BUILDFLAG(IS_NACL)
// Invoke StopPlayback() to ensure audio back-end has a chance to send the
// escape value over the sync socket, which will terminate the client side
// audio callback loop. This is required for NaCl Plugins that can't escape
// by shutting down the sync_socket.
StopPlayback();
#endif
PpapiGlobals::Get()->GetResourceTracker()->ReleaseResource(config_);
}
PPB_Audio_API* Audio::AsPPB_Audio_API() {
return this;
}
PP_Resource Audio::GetCurrentConfig() {
// AddRef for the caller.
PpapiGlobals::Get()->GetResourceTracker()->AddRefResource(config_);
return config_;
}
PP_Bool Audio::StartPlayback() {
if (playing())
return PP_TRUE;
if (!PPB_Audio_Shared::IsThreadFunctionReady())
return PP_FALSE;
SetStartPlaybackState();
PluginDispatcher::GetForResource(this)->Send(
new PpapiHostMsg_PPBAudio_StartOrStop(
API_ID_PPB_AUDIO, host_resource(), true));
return PP_TRUE;
}
PP_Bool Audio::StopPlayback() {
if (!playing())
return PP_TRUE;
PluginDispatcher::GetForResource(this)->Send(
new PpapiHostMsg_PPBAudio_StartOrStop(
API_ID_PPB_AUDIO, host_resource(), false));
SetStopPlaybackState();
return PP_TRUE;
}
int32_t Audio::Open(PP_Resource config_id,
scoped_refptr<TrackedCallback> create_callback) {
return PP_ERROR_NOTSUPPORTED; // Don't proxy the trusted interface.
}
int32_t Audio::GetSyncSocket(int* sync_socket) {
return PP_ERROR_NOTSUPPORTED; // Don't proxy the trusted interface.
}
int32_t Audio::GetSharedMemory(base::UnsafeSharedMemoryRegion** shm) {
return PP_ERROR_NOTSUPPORTED; // Don't proxy the trusted interface.
}
PPB_Audio_Proxy::PPB_Audio_Proxy(Dispatcher* dispatcher)
: InterfaceProxy(dispatcher),
callback_factory_(this) {
}
PPB_Audio_Proxy::~PPB_Audio_Proxy() {
}
// static
PP_Resource PPB_Audio_Proxy::CreateProxyResource(
PP_Instance instance_id,
PP_Resource config_id,
const AudioCallbackCombined& audio_callback,
void* user_data) {
PluginDispatcher* dispatcher = PluginDispatcher::GetForInstance(instance_id);
if (!dispatcher)
return 0;
EnterResourceNoLock<PPB_AudioConfig_API> config(config_id, true);
if (config.failed())
return 0;
if (!audio_callback.IsValid())
return 0;
HostResource result;
dispatcher->Send(new PpapiHostMsg_PPBAudio_Create(
API_ID_PPB_AUDIO, instance_id,
config.object()->GetSampleRate(), config.object()->GetSampleFrameCount(),
&result));
if (result.is_null())
return 0;
return (new Audio(result, config_id,
audio_callback, user_data))->GetReference();
}
bool PPB_Audio_Proxy::OnMessageReceived(const IPC::Message& msg) {
bool handled = true;
IPC_BEGIN_MESSAGE_MAP(PPB_Audio_Proxy, msg)
// Don't build host side into NaCl IRT.
#if !BUILDFLAG(IS_NACL)
IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBAudio_Create, OnMsgCreate)
IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBAudio_StartOrStop,
OnMsgStartOrStop)
#endif
IPC_MESSAGE_HANDLER(PpapiMsg_PPBAudio_NotifyAudioStreamCreated,
OnMsgNotifyAudioStreamCreated)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
return handled;
}
#if !BUILDFLAG(IS_NACL)
void PPB_Audio_Proxy::OnMsgCreate(PP_Instance instance_id,
int32_t sample_rate,
uint32_t sample_frame_count,
HostResource* result) {
thunk::EnterResourceCreation resource_creation(instance_id);
if (resource_creation.failed())
return;
// Make the resource and get the API pointer to its trusted interface.
result->SetHostResource(
instance_id,
resource_creation.functions()->CreateAudioTrusted(instance_id));
if (result->is_null())
return;
// At this point, we've set the result resource, and this is a sync request.
// Anything below this point must issue the AudioChannelConnected callback
// to the browser. Since that's an async message, it will be issued back to
// the plugin after the Create function returns (which is good because it
// would be weird to get a connected message with a failure code for a
// resource you haven't finished creating yet).
//
// The ...ForceCallback class will help ensure the callback is always called.
// All error cases must call SetResult on this class.
EnterHostFromHostResourceForceCallback<PPB_Audio_API> enter(
*result, callback_factory_,
&PPB_Audio_Proxy::AudioChannelConnected, *result);
if (enter.failed())
return; // When enter fails, it will internally schedule the callback.
// Make an audio config object.
PP_Resource audio_config_res =
resource_creation.functions()->CreateAudioConfig(
instance_id, static_cast<PP_AudioSampleRate>(sample_rate),
sample_frame_count);
if (!audio_config_res) {
enter.SetResult(PP_ERROR_FAILED);
return;
}
// Initiate opening the audio object.
enter.SetResult(enter.object()->Open(audio_config_res,
enter.callback()));
// Clean up the temporary audio config resource we made.
const PPB_Core* core = static_cast<const PPB_Core*>(
dispatcher()->local_get_interface()(PPB_CORE_INTERFACE));
core->ReleaseResource(audio_config_res);
}
void PPB_Audio_Proxy::OnMsgStartOrStop(const HostResource& audio_id,
bool play) {
EnterHostFromHostResource<PPB_Audio_API> enter(audio_id);
if (enter.failed())
return;
if (play)
enter.object()->StartPlayback();
else
enter.object()->StopPlayback();
}
void PPB_Audio_Proxy::AudioChannelConnected(
int32_t result,
const HostResource& resource) {
IPC::PlatformFileForTransit socket_handle =
IPC::InvalidPlatformFileForTransit();
base::UnsafeSharedMemoryRegion shared_memory_region;
int32_t result_code = result;
if (result_code == PP_OK) {
result_code = GetAudioConnectedHandles(resource, &socket_handle,
&shared_memory_region);
}
// 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.
SerializedHandle fd_wrapper(SerializedHandle::SOCKET, socket_handle);
SerializedHandle handle_wrapper(
base::UnsafeSharedMemoryRegion::TakeHandleForSerialization(
std::move(shared_memory_region)));
dispatcher()->Send(new PpapiMsg_PPBAudio_NotifyAudioStreamCreated(
API_ID_PPB_AUDIO, resource, result_code, std::move(fd_wrapper),
std::move(handle_wrapper)));
}
int32_t PPB_Audio_Proxy::GetAudioConnectedHandles(
const HostResource& resource,
IPC::PlatformFileForTransit* foreign_socket_handle,
base::UnsafeSharedMemoryRegion* foreign_shared_memory_region) {
// Get the audio interface which will give us the handles.
EnterHostFromHostResource<PPB_Audio_API> enter(resource);
if (enter.failed())
return PP_ERROR_NOINTERFACE;
// Get the socket handle for signaling.
int32_t socket_handle;
int32_t result = enter.object()->GetSyncSocket(&socket_handle);
if (result != PP_OK)
return result;
// socket_handle doesn't belong to us: don't close it.
*foreign_socket_handle = dispatcher()->ShareHandleWithRemote(
IntToPlatformFile(socket_handle), false);
if (*foreign_socket_handle == IPC::InvalidPlatformFileForTransit())
return PP_ERROR_FAILED;
// Get the shared memory for the buffer.
base::UnsafeSharedMemoryRegion* shared_memory_region;
result = enter.object()->GetSharedMemory(&shared_memory_region);
if (result != PP_OK)
return result;
// shared_memory_region doesn't belong to us: don't close it.
*foreign_shared_memory_region =
dispatcher()->ShareUnsafeSharedMemoryRegionWithRemote(
*shared_memory_region);
if (!foreign_shared_memory_region->IsValid())
return PP_ERROR_FAILED;
return PP_OK;
}
#endif // !BUILDFLAG(IS_NACL)
// Processed in the plugin (message from host).
void PPB_Audio_Proxy::OnMsgNotifyAudioStreamCreated(
const HostResource& audio_id,
int32_t result_code,
SerializedHandle socket_handle,
SerializedHandle handle) {
CHECK(socket_handle.is_socket());
CHECK(handle.is_shmem_region());
EnterPluginFromHostResource<PPB_Audio_API> enter(audio_id);
if (enter.failed() || result_code != PP_OK) {
// The caller may still have given us these handles in the failure case.
// The easiest way to clean socket handle up is to just put them in the
// SyncSocket object and then close it. The shared memory region will be
// cleaned up automatically. This failure case is not performance critical.
base::SyncSocket temp_socket(
IPC::PlatformFileForTransitToPlatformFile(socket_handle.descriptor()));
} else {
EnterResourceNoLock<PPB_AudioConfig_API> config(
static_cast<Audio*>(enter.object())->GetCurrentConfig(), true);
static_cast<Audio*>(enter.object())
->SetStreamInfo(enter.resource()->pp_instance(),
base::UnsafeSharedMemoryRegion::Deserialize(
handle.TakeSharedMemoryRegion()),
base::SyncSocket::ScopedHandle(
IPC::PlatformFileForTransitToPlatformFile(
socket_handle.descriptor())),
config.object()->GetSampleRate(),
config.object()->GetSampleFrameCount());
}
}
} // namespace proxy
} // namespace ppapi
|