blob: bb4069361140d300481bbed0cd837ddc4f779ef9 [file] [log] [blame]
[email protected]5d84d012010-12-02 17:17:211// Copyright (c) 2010 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "ppapi/proxy/ppb_audio_proxy.h"
6
7#include "base/simple_thread.h"
8#include "ppapi/c/dev/ppb_audio_dev.h"
9#include "ppapi/c/dev/ppb_audio_trusted_dev.h"
10#include "ppapi/c/pp_errors.h"
11#include "ppapi/proxy/interface_id.h"
12#include "ppapi/proxy/plugin_dispatcher.h"
13#include "ppapi/proxy/plugin_resource.h"
14#include "ppapi/proxy/ppapi_messages.h"
15#include "ppapi/shared_impl/audio_impl.h"
16
17namespace pp {
18namespace proxy {
19
20class Audio : public PluginResource, public pp::shared_impl::AudioImpl {
21 public:
22 Audio(PP_Resource config_id, PPB_Audio_Callback callback, void* user_data)
23 : config_(config_id) {
24 SetCallback(callback, user_data);
25 PluginDispatcher::Get()->plugin_resource_tracker()->AddRefResource(
26 config_);
27 }
28 virtual ~Audio() {
29 PluginDispatcher::Get()->plugin_resource_tracker()->ReleaseResource(
30 config_);
31 }
32
33 // Resource overrides.
34 virtual Audio* AsAudio() { return this; }
35
36 PP_Resource config() const { return config_; }
37
38 void StartPlayback(PP_Resource resource) {
39 if (playing())
40 return;
41 SetStartPlaybackState();
42 PluginDispatcher::Get()->Send(new PpapiHostMsg_PPBAudio_StartOrStop(
43 INTERFACE_ID_PPB_AUDIO, resource, true));
44 }
45
46 void StopPlayback(PP_Resource resource) {
47 if (!playing())
48 return;
49 PluginDispatcher::Get()->Send(new PpapiHostMsg_PPBAudio_StartOrStop(
50 INTERFACE_ID_PPB_AUDIO, resource, false));
51 SetStopPlaybackState();
52 }
53
54 private:
55 PP_Resource config_;
56
57 DISALLOW_COPY_AND_ASSIGN(Audio);
58};
59
60namespace {
61
62PP_Resource Create(PP_Instance instance_id,
63 PP_Resource config_id,
64 PPB_Audio_Callback callback,
65 void* user_data) {
66 PP_Resource result;
67 PluginDispatcher::Get()->Send(new PpapiHostMsg_PPBAudio_Create(
68 INTERFACE_ID_PPB_AUDIO, instance_id, config_id, &result));
69 if (!result)
70 return 0;
71
72 linked_ptr<Audio> object(new Audio(config_id, callback, user_data));
73 PluginDispatcher::Get()->plugin_resource_tracker()->AddResource(
74 result, object);
75 return result;
76}
77
78PP_Bool IsAudio(PP_Resource resource) {
79 Audio* object = PluginResource::GetAs<Audio>(resource);
80 return BoolToPPBool(!!object);
81}
82
83PP_Resource GetCurrentConfiguration(PP_Resource audio_id) {
84 Audio* object = PluginResource::GetAs<Audio>(audio_id);
85 if (!object)
86 return 0;
87 PP_Resource result = object->config();
88 PluginDispatcher::Get()->plugin_resource_tracker()->AddRefResource(result);
89 return result;
90}
91
92PP_Bool StartPlayback(PP_Resource audio_id) {
93 Audio* object = PluginResource::GetAs<Audio>(audio_id);
94 if (!object)
95 return PP_FALSE;
96 object->StartPlayback(audio_id);
97 return PP_TRUE;
98}
99
100PP_Bool StopPlayback(PP_Resource audio_id) {
101 Audio* object = PluginResource::GetAs<Audio>(audio_id);
102 if (!object)
103 return PP_FALSE;
104 object->StopPlayback(audio_id);
105 return PP_TRUE;
106}
107
108const PPB_Audio_Dev audio_interface = {
109 &Create,
110 &IsAudio,
111 &GetCurrentConfiguration,
112 &StartPlayback,
113 &StopPlayback
114};
115
116} // namespace
117
118PPB_Audio_Proxy::PPB_Audio_Proxy(Dispatcher* dispatcher,
119 const void* target_interface)
120 : InterfaceProxy(dispatcher, target_interface),
121 callback_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {
122}
123
124PPB_Audio_Proxy::~PPB_Audio_Proxy() {
125}
126
127const void* PPB_Audio_Proxy::GetSourceInterface() const {
128 return &audio_interface;
129}
130
131InterfaceID PPB_Audio_Proxy::GetInterfaceId() const {
132 return INTERFACE_ID_PPB_AUDIO;
133}
134
135void PPB_Audio_Proxy::OnMessageReceived(const IPC::Message& msg) {
136 IPC_BEGIN_MESSAGE_MAP(PPB_Audio_Proxy, msg)
137 IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBAudio_Create, OnMsgCreate)
138 IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBAudio_StartOrStop,
139 OnMsgStartOrStop)
140
141 IPC_MESSAGE_HANDLER(PpapiMsg_PPBAudio_NotifyAudioStreamCreated,
142 OnMsgNotifyAudioStreamCreated)
143 IPC_END_MESSAGE_MAP()
144}
145
146void PPB_Audio_Proxy::OnMsgCreate(PP_Instance instance_id,
147 PP_Resource config_id,
148 PP_Resource* result) {
149 const PPB_AudioTrusted_Dev* audio_trusted =
150 reinterpret_cast<const PPB_AudioTrusted_Dev*>(
151 dispatcher()->GetLocalInterface(PPB_AUDIO_TRUSTED_DEV_INTERFACE));
152 if (!audio_trusted) {
153 *result = 0;
154 return;
155 }
156
157 *result = audio_trusted->CreateTrusted(instance_id);
158 if (!result)
159 return;
160
161 CompletionCallback callback = callback_factory_.NewCallback(
162 &PPB_Audio_Proxy::AudioChannelConnected, *result);
163 int32_t open_error = audio_trusted->Open(*result, config_id,
164 callback.pp_completion_callback());
165 if (open_error != PP_ERROR_WOULDBLOCK)
166 callback.Run(open_error);
167}
168
169void PPB_Audio_Proxy::OnMsgStartOrStop(PP_Resource audio_id, bool play) {
170 if (play)
171 ppb_audio_target()->StartPlayback(audio_id);
172 else
173 ppb_audio_target()->StopPlayback(audio_id);
174}
175
176void PPB_Audio_Proxy::OnMsgNotifyAudioStreamCreated(
177 PP_Resource audio_id,
178 int32_t result_code,
179 IPC::PlatformFileForTransit socket_handle,
180 base::SharedMemoryHandle handle,
181 uint32_t length) {
182 Audio* object = PluginResource::GetAs<Audio>(audio_id);
183 if (!object || result_code != PP_OK) {
184 // The caller may still have given us these handles in the failure case.
185 // The easiest way to clean these up is to just put them in the objects
186 // and then close them. This failure case is not performance critical.
187 base::SyncSocket temp_socket(
188 IPC::PlatformFileForTransitToPlatformFile(socket_handle));
189 base::SharedMemory temp_mem(handle, false);
190 return;
191 }
192 object->SetStreamInfo(
193 handle, length, IPC::PlatformFileForTransitToPlatformFile(socket_handle));
194}
195
196void PPB_Audio_Proxy::AudioChannelConnected(int32_t result,
197 PP_Resource resource) {
198 IPC::PlatformFileForTransit socket_handle =
199 IPC::InvalidPlatformFileForTransit();
200#if defined(OS_WIN)
201 base::SharedMemoryHandle shared_memory = NULL;
202#elif defined(OS_POSIX)
203 base::SharedMemoryHandle shared_memory(-1, false);
204#else
205 #error Not implemented.
206#endif
207 uint32_t shared_memory_length = 0;
208
209 int32_t result_code = result;
210 if (result_code == PP_OK) {
211 result_code = GetAudioConnectedHandles(resource, &socket_handle,
212 &shared_memory,
213 &shared_memory_length);
214 }
215
216 // Send all the values, even on error. This simplifies some of our cleanup
217 // code since the handles will be in the other process and could be
218 // inconvenient to clean up. Our IPC code will automatically handle this for
219 // us, as long as the remote side always closes the handles it receives
220 // (in OnMsgNotifyAudioStreamCreated), even in the failure case.
221 dispatcher()->Send(new PpapiMsg_PPBAudio_NotifyAudioStreamCreated(
222 INTERFACE_ID_PPB_AUDIO, resource, result_code, shared_memory,
223 socket_handle, shared_memory_length));
224}
225
226int32_t PPB_Audio_Proxy::GetAudioConnectedHandles(
227 PP_Resource resource,
228 IPC::PlatformFileForTransit* foreign_socket_handle,
229 base::SharedMemoryHandle* foreign_shared_memory_handle,
230 uint32_t* shared_memory_length) {
231 // Get the trusted audio interface which will give us the handles.
232 const PPB_AudioTrusted_Dev* audio_trusted =
233 reinterpret_cast<const PPB_AudioTrusted_Dev*>(
234 dispatcher()->GetLocalInterface(PPB_AUDIO_TRUSTED_DEV_INTERFACE));
235 if (!audio_trusted)
236 return PP_ERROR_NOINTERFACE;
237
238 // Get the socket handle for signaling.
239 int32_t socket_handle;
240 int32_t result = audio_trusted->GetSyncSocket(resource, &socket_handle);
241 if (result != PP_OK)
242 return result;
243
244#if defined(OS_WIN)
245 // On Windows, duplicate the socket into the plugin process, this will
246 // automatically close the source handle.
247 ::DuplicateHandle(
248 GetCurrentProcess(),
249 reinterpret_cast<HANDLE>(static_cast<intptr_t>(socket_handle)),
250 dispatcher()->remote_process_handle(), foreign_socket_handle,
251 STANDARD_RIGHTS_REQUIRED | FILE_MAP_READ | FILE_MAP_WRITE,
252 FALSE, DUPLICATE_CLOSE_SOURCE);
253#else
254 // On Posix, the socket handle will be auto-duplicated when we send the
255 // FileDescriptor. Set AutoClose since we don't need the handle any more.
256 *foreign_socket_handle = base::FileDescriptor(socket_handle, true);
257#endif
258
259 // Get the shared memory for the buffer.
260 // TODO(brettw) remove the reinterpret cast when the interface is updated.
261 int shared_memory_handle;
262 result = audio_trusted->GetSharedMemory(resource, &shared_memory_handle,
263 shared_memory_length);
264 if (result != PP_OK)
265 return result;
266
267 base::SharedMemory shared_memory(
268#if defined(OS_WIN)
269 reinterpret_cast<HANDLE>(static_cast<intptr_t>(shared_memory_handle)),
270#else
271 base::FileDescriptor(shared_memory_handle, false),
272#endif
273 false);
274
275 // Duplicate the shared memory to the plugin process. This will automatically
276 // close the source handle.
277 if (!shared_memory.GiveToProcess(dispatcher()->remote_process_handle(),
278 foreign_shared_memory_handle))
279 return PP_ERROR_FAILED;
280
281 return PP_OK;
282}
283
284} // namespace proxy
285} // namespace pp