blob: 4468c4eeb008d7f0294117b72201245c4c7bf0d5 [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
[email protected]a95986a82010-12-24 06:19:28135bool PPB_Audio_Proxy::OnMessageReceived(const IPC::Message& msg) {
136 bool handled = true;
[email protected]5d84d012010-12-02 17:17:21137 IPC_BEGIN_MESSAGE_MAP(PPB_Audio_Proxy, msg)
138 IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBAudio_Create, OnMsgCreate)
139 IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBAudio_StartOrStop,
140 OnMsgStartOrStop)
[email protected]5d84d012010-12-02 17:17:21141 IPC_MESSAGE_HANDLER(PpapiMsg_PPBAudio_NotifyAudioStreamCreated,
142 OnMsgNotifyAudioStreamCreated)
[email protected]a95986a82010-12-24 06:19:28143 IPC_MESSAGE_UNHANDLED(handled = false)
[email protected]5d84d012010-12-02 17:17:21144 IPC_END_MESSAGE_MAP()
[email protected]a95986a82010-12-24 06:19:28145 return handled;
[email protected]5d84d012010-12-02 17:17:21146}
147
148void PPB_Audio_Proxy::OnMsgCreate(PP_Instance instance_id,
149 PP_Resource config_id,
150 PP_Resource* result) {
151 const PPB_AudioTrusted_Dev* audio_trusted =
152 reinterpret_cast<const PPB_AudioTrusted_Dev*>(
153 dispatcher()->GetLocalInterface(PPB_AUDIO_TRUSTED_DEV_INTERFACE));
154 if (!audio_trusted) {
155 *result = 0;
156 return;
157 }
158
159 *result = audio_trusted->CreateTrusted(instance_id);
160 if (!result)
161 return;
162
163 CompletionCallback callback = callback_factory_.NewCallback(
164 &PPB_Audio_Proxy::AudioChannelConnected, *result);
165 int32_t open_error = audio_trusted->Open(*result, config_id,
166 callback.pp_completion_callback());
167 if (open_error != PP_ERROR_WOULDBLOCK)
168 callback.Run(open_error);
169}
170
171void PPB_Audio_Proxy::OnMsgStartOrStop(PP_Resource audio_id, bool play) {
172 if (play)
173 ppb_audio_target()->StartPlayback(audio_id);
174 else
175 ppb_audio_target()->StopPlayback(audio_id);
176}
177
178void PPB_Audio_Proxy::OnMsgNotifyAudioStreamCreated(
179 PP_Resource audio_id,
180 int32_t result_code,
181 IPC::PlatformFileForTransit socket_handle,
182 base::SharedMemoryHandle handle,
183 uint32_t length) {
184 Audio* object = PluginResource::GetAs<Audio>(audio_id);
185 if (!object || result_code != PP_OK) {
186 // The caller may still have given us these handles in the failure case.
187 // The easiest way to clean these up is to just put them in the objects
188 // and then close them. This failure case is not performance critical.
189 base::SyncSocket temp_socket(
190 IPC::PlatformFileForTransitToPlatformFile(socket_handle));
191 base::SharedMemory temp_mem(handle, false);
192 return;
193 }
194 object->SetStreamInfo(
195 handle, length, IPC::PlatformFileForTransitToPlatformFile(socket_handle));
196}
197
198void PPB_Audio_Proxy::AudioChannelConnected(int32_t result,
199 PP_Resource resource) {
200 IPC::PlatformFileForTransit socket_handle =
201 IPC::InvalidPlatformFileForTransit();
202#if defined(OS_WIN)
203 base::SharedMemoryHandle shared_memory = NULL;
204#elif defined(OS_POSIX)
205 base::SharedMemoryHandle shared_memory(-1, false);
206#else
207 #error Not implemented.
208#endif
209 uint32_t shared_memory_length = 0;
210
211 int32_t result_code = result;
212 if (result_code == PP_OK) {
213 result_code = GetAudioConnectedHandles(resource, &socket_handle,
214 &shared_memory,
215 &shared_memory_length);
216 }
217
218 // Send all the values, even on error. This simplifies some of our cleanup
219 // code since the handles will be in the other process and could be
220 // inconvenient to clean up. Our IPC code will automatically handle this for
221 // us, as long as the remote side always closes the handles it receives
222 // (in OnMsgNotifyAudioStreamCreated), even in the failure case.
223 dispatcher()->Send(new PpapiMsg_PPBAudio_NotifyAudioStreamCreated(
[email protected]5f2517612010-12-02 22:36:48224 INTERFACE_ID_PPB_AUDIO, resource, result_code, socket_handle,
225 shared_memory, shared_memory_length));
[email protected]5d84d012010-12-02 17:17:21226}
227
228int32_t PPB_Audio_Proxy::GetAudioConnectedHandles(
229 PP_Resource resource,
230 IPC::PlatformFileForTransit* foreign_socket_handle,
231 base::SharedMemoryHandle* foreign_shared_memory_handle,
232 uint32_t* shared_memory_length) {
233 // Get the trusted audio interface which will give us the handles.
234 const PPB_AudioTrusted_Dev* audio_trusted =
235 reinterpret_cast<const PPB_AudioTrusted_Dev*>(
236 dispatcher()->GetLocalInterface(PPB_AUDIO_TRUSTED_DEV_INTERFACE));
237 if (!audio_trusted)
238 return PP_ERROR_NOINTERFACE;
239
240 // Get the socket handle for signaling.
241 int32_t socket_handle;
242 int32_t result = audio_trusted->GetSyncSocket(resource, &socket_handle);
243 if (result != PP_OK)
244 return result;
245
246#if defined(OS_WIN)
247 // On Windows, duplicate the socket into the plugin process, this will
248 // automatically close the source handle.
249 ::DuplicateHandle(
250 GetCurrentProcess(),
251 reinterpret_cast<HANDLE>(static_cast<intptr_t>(socket_handle)),
252 dispatcher()->remote_process_handle(), foreign_socket_handle,
253 STANDARD_RIGHTS_REQUIRED | FILE_MAP_READ | FILE_MAP_WRITE,
254 FALSE, DUPLICATE_CLOSE_SOURCE);
255#else
256 // On Posix, the socket handle will be auto-duplicated when we send the
257 // FileDescriptor. Set AutoClose since we don't need the handle any more.
258 *foreign_socket_handle = base::FileDescriptor(socket_handle, true);
259#endif
260
261 // Get the shared memory for the buffer.
262 // TODO(brettw) remove the reinterpret cast when the interface is updated.
263 int shared_memory_handle;
264 result = audio_trusted->GetSharedMemory(resource, &shared_memory_handle,
265 shared_memory_length);
266 if (result != PP_OK)
267 return result;
268
269 base::SharedMemory shared_memory(
270#if defined(OS_WIN)
271 reinterpret_cast<HANDLE>(static_cast<intptr_t>(shared_memory_handle)),
272#else
273 base::FileDescriptor(shared_memory_handle, false),
274#endif
275 false);
276
277 // Duplicate the shared memory to the plugin process. This will automatically
278 // close the source handle.
279 if (!shared_memory.GiveToProcess(dispatcher()->remote_process_handle(),
280 foreign_shared_memory_handle))
281 return PP_ERROR_FAILED;
282
283 return PP_OK;
284}
285
286} // namespace proxy
287} // namespace pp