Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(92)

Side by Side Diff: ppapi/proxy/ppb_audio_proxy.cc

Issue 4985001: Initial audio implementation. (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 10 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
Property Changes:
Added: svn:eol-style
+ LF
OLDNEW
(Empty)
1 // 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
16 namespace pp {
17 namespace proxy {
18
19 class Audio : public PluginResource,
20 public base::DelegateSimpleThread::Delegate {
21 public:
22 Audio(PP_Resource config_id, PPB_Audio_Callback callback, void* user_data)
23 : config_(config_id),
24 playing_(false),
25 shared_memory_size_(0),
26 callback_(callback),
27 user_data_(user_data) {
28 PluginDispatcher::Get()->plugin_resource_tracker()->AddRefResource(
29 config_);
30 }
31 virtual ~Audio() {
32 PluginDispatcher::Get()->plugin_resource_tracker()->ReleaseResource(
33 config_);
34 if (audio_thread_.get())
35 audio_thread_->Join();
36 }
37
38 // Resource overrides.
39 virtual Audio* AsAudio() { return this; }
40
41 PP_Resource config() const { return config_; }
42
43 void StartPlayback(PP_Resource resource) {
44 if (playing_)
45 return;
46
47 DCHECK(!audio_thread_.get());
48 if (callback_ && socket_.get()) {
49 // TODO(brettw) use the same thread for all audio sources!
50 audio_thread_.reset(new base::DelegateSimpleThread(this, "audio"));
51 audio_thread_->Start();
52 }
piman 2010/12/01 01:44:52 Most of the threading logic and I/O on the socket
53 playing_ = true;
54 PluginDispatcher::Get()->Send(new PpapiHostMsg_PPBAudio_StartOrStop(
55 INTERFACE_ID_PPB_AUDIO, resource, true));
56 }
57
58 void StopPlayback(PP_Resource resource) {
59 if (!playing_)
60 return;
61
62 PluginDispatcher::Get()->Send(new PpapiHostMsg_PPBAudio_StartOrStop(
63 INTERFACE_ID_PPB_AUDIO, resource, false));
64
65 if (audio_thread_.get()) {
66 audio_thread_->Join();
67 audio_thread_.reset();
68 }
69 playing_ = false;
70 }
71
72 void StreamCreated(base::SharedMemoryHandle handle,
73 base::SyncSocket::Handle socket_handle,
74 uint32_t length) {
75 socket_.reset(new base::SyncSocket(socket_handle));
76 shared_memory_.reset(new base::SharedMemory(handle, false));
77 shared_memory_size_ = length;
78
79 if (callback_) {
80 shared_memory_->Map(shared_memory_size_);
81 // In common case StartPlayback() was called before StreamCreated().
82 if (playing_) {
83 audio_thread_.reset(new base::DelegateSimpleThread(this,
84 "plugin_audio_thread"));
piman 2010/12/01 01:44:52 the thread should have the same name wherever it's
85 audio_thread_->Start();
86 }
87 }
88 }
89
90 private:
91 // DelegateSimpleThread implementation.
92 virtual void Run() {
93 int pending_data;
94 void* buffer = shared_memory_->memory();
95 size_t buffer_size_in_bytes = shared_memory_size_;
96
97 while (sizeof(pending_data) ==
98 socket_->Receive(&pending_data, sizeof(pending_data)) &&
99 pending_data >= 0) {
100 // Exit the thread on pause.
101 if (pending_data < 0)
102 return;
103 callback_(buffer, buffer_size_in_bytes, user_data_);
104 }
105 }
106
107 PP_Resource config_;
108
109 bool playing_;
110
111 scoped_ptr<base::DelegateSimpleThread> audio_thread_;
112
113 scoped_ptr<base::SyncSocket> socket_;
114 scoped_ptr<base::SharedMemory> shared_memory_;
115 size_t shared_memory_size_;
116
117 volatile PPB_Audio_Callback callback_;
118 void* user_data_;
119
120 DISALLOW_COPY_AND_ASSIGN(Audio);
121 };
122
123 namespace {
124
125 PP_Resource Create(PP_Instance instance_id,
126 PP_Resource config_id,
127 PPB_Audio_Callback callback,
128 void* user_data) {
129 PP_Resource result;
130 PluginDispatcher::Get()->Send(new PpapiHostMsg_PPBAudio_Create(
131 INTERFACE_ID_PPB_AUDIO, instance_id, config_id, &result));
132 if (!result)
133 return 0;
134
135 linked_ptr<Audio> object(new Audio(config_id, callback, user_data));
136 PluginDispatcher::Get()->plugin_resource_tracker()->AddResource(
137 result, object);
138 return result;
139 }
140
141 PP_Bool IsAudio(PP_Resource resource) {
142 Audio* object = PluginResource::GetAs<Audio>(resource);
143 return BoolToPPBool(!!object);
144 }
145
146 PP_Resource GetCurrentConfiguration(PP_Resource audio_id) {
147 Audio* object = PluginResource::GetAs<Audio>(audio_id);
148 if (!object)
149 return 0;
150 PP_Resource result = object->config();
151 PluginDispatcher::Get()->plugin_resource_tracker()->AddRefResource(result);
152 return result;
153 }
154
155 PP_Bool StartPlayback(PP_Resource audio_id) {
156 Audio* object = PluginResource::GetAs<Audio>(audio_id);
157 if (!object)
158 return PP_FALSE;
159 object->StartPlayback(audio_id);
160 return PP_TRUE;
161 }
162
163 PP_Bool StopPlayback(PP_Resource audio_id) {
164 Audio* object = PluginResource::GetAs<Audio>(audio_id);
165 if (!object)
166 return PP_FALSE;
167 object->StopPlayback(audio_id);
168 return PP_TRUE;
169 }
170
171 const PPB_Audio_Dev audio_interface = {
172 &Create,
173 &IsAudio,
174 &GetCurrentConfiguration,
175 &StartPlayback,
176 &StopPlayback
177 };
178
179 } // namespace
180
181 PPB_Audio_Proxy::PPB_Audio_Proxy(Dispatcher* dispatcher,
182 const void* target_interface)
183 : InterfaceProxy(dispatcher, target_interface),
184 callback_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {
185 }
186
187 PPB_Audio_Proxy::~PPB_Audio_Proxy() {
188 }
189
190 const void* PPB_Audio_Proxy::GetSourceInterface() const {
191 return &audio_interface;
192 }
193
194 InterfaceID PPB_Audio_Proxy::GetInterfaceId() const {
195 return INTERFACE_ID_PPB_AUDIO;
196 }
197
198 void PPB_Audio_Proxy::OnMessageReceived(const IPC::Message& msg) {
199 IPC_BEGIN_MESSAGE_MAP(PPB_Audio_Proxy, msg)
200 IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBAudio_Create, OnMsgCreate)
201 IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBAudio_StartOrStop,
202 OnMsgStartOrStop)
203
204 IPC_MESSAGE_HANDLER(PpapiMsg_PPBAudio_NotifyAudioStreamCreated,
205 OnMsgNotifyAudioStreamCreated)
206 IPC_END_MESSAGE_MAP()
207 }
208
209 base::SyncSocket::Handle PPB_Audio_Proxy::HandleFromSocketHandle(
210 SocketHandle h) {
211 #if defined(OS_WIN)
212 return h;
213 #elif defined(OS_POSIX)
214 return h.fd;
215 #endif
216 }
217
218 void PPB_Audio_Proxy::OnMsgCreate(PP_Instance instance_id,
219 PP_Resource config_id,
220 PP_Resource* result) {
221 const PPB_AudioTrusted_Dev* audio_trusted =
222 reinterpret_cast<const PPB_AudioTrusted_Dev*>(
223 dispatcher()->GetLocalInterface(PPB_AUDIO_TRUSTED_DEV_INTERFACE));
224 if (!audio_trusted) {
225 *result = 0;
226 return;
227 }
228
229 *result = audio_trusted->CreateTrusted(instance_id);
230 if (!result)
231 return;
232
233 CompletionCallback callback = callback_factory_.NewCallback(
234 &PPB_Audio_Proxy::AudioChannelConnected, *result);
235 int32_t open_error = audio_trusted->Open(*result, config_id,
236 callback.pp_completion_callback());
237 if (open_error != PP_ERROR_WOULDBLOCK)
238 callback.Run(open_error);
239 }
240
241 void PPB_Audio_Proxy::OnMsgStartOrStop(PP_Resource audio_id, bool play) {
242 if (play)
243 ppb_audio_target()->StartPlayback(audio_id);
244 else
245 ppb_audio_target()->StopPlayback(audio_id);
246 }
247
248 void PPB_Audio_Proxy::OnMsgNotifyAudioStreamCreated(
249 PP_Resource audio_id,
250 int32_t result_code,
251 SocketHandle socket_handle,
252 base::SharedMemoryHandle handle,
253 uint32_t length) {
254 Audio* object = PluginResource::GetAs<Audio>(audio_id);
255 if (!object || result_code != PP_OK) {
256 // The caller may still have given us these handles in the failure case.
257 // The easiest way to clean these up is to just put them in the objects
258 // and then close them. This failure case is not performance critical.
259 base::SyncSocket temp_socket(HandleFromSocketHandle(socket_handle));
260 base::SharedMemory temp_mem(handle, false);
261 return;
262 }
263 object->StreamCreated(handle, HandleFromSocketHandle(socket_handle), length);
264 }
265
266 void PPB_Audio_Proxy::AudioChannelConnected(int32_t result,
267 PP_Resource resource) {
268 #if defined(OS_WIN)
269 base::SharedMemoryHandle shared_memory = NULL;
270 base::SyncSocket::Handle socket_handle = NULL;
271 #elif defined(OS_POSIX)
272 base::SharedMemoryHandle shared_memory(-1, false);
273 base::FileDescriptor socket_handle(-1, false);
274 #else
275 #error Not implemented.
276 #endif
277 int32_t shared_memory_length = 0;
278
279 int32_t result_code = result;
280 if (result_code == PP_OK) {
281 result_code = GetAudioConnectedHandles(resource, &socket_handle,
282 &shared_memory,
283 &shared_memory_length);
284 }
285
286 // Send all the values, even on error. This simplifies some of our cleanup
287 // code since the handles will be in the other process and could be
288 // inconvenient to clean up. Our IPC code will automatically handle this for
289 // us, as long as the remote side always closes the handles it receives
290 // (in OnMsgNotifyAudioStreamCreated), even in the failure case.
291 dispatcher()->Send(new PpapiMsg_PPBAudio_NotifyAudioStreamCreated(
292 INTERFACE_ID_PPB_AUDIO, resource, result_code, shared_memory,
293 socket_handle, shared_memory_length));
294 }
295
296 int32_t PPB_Audio_Proxy::GetAudioConnectedHandles(
297 PP_Resource resource,
298 SocketHandle* foreign_socket_handle,
299 base::SharedMemoryHandle* foreign_shared_memory_handle,
300 int32_t* shared_memory_length) {
301 // Get the trusted audio interface which will give us the handles.
302 const PPB_AudioTrusted_Dev* audio_trusted =
303 reinterpret_cast<const PPB_AudioTrusted_Dev*>(
304 dispatcher()->GetLocalInterface(PPB_AUDIO_TRUSTED_DEV_INTERFACE));
305 if (!audio_trusted)
306 return PP_ERROR_NOINTERFACE;
307
308 // Get the socket handle for signaling.
309 int32_t socket_handle;
310 int32_t result = audio_trusted->GetSyncSocket(resource, &socket_handle);
311 if (result != PP_OK)
312 return result;
313
314 #if defined(OS_WIN)
315 // On Windows, duplicate the socket into the plugin process, this will
316 // automatically close the source handle.
317 ::DuplicateHandle(GetCurerntProcess(), static_cast<HANDLE>(socket_handle),
piman 2010/12/01 01:44:52 GetCurerntProcess -> GetCurrentProcess
318 dispatcher()->GetRemoteProcessHandle(),
319 foreign_socket_handle,
320 STANDARD-RIGHTS_REQUIRED | FILE_MAP_READ | FILE_MAP_WRITE,
321 FALSE, DUPLICATE_CLOSE_SOURCE);
322 #else
323 // On Posix, the socket handle will be auto-duplicated when we send the
324 // FileDescriptor. Set AutoClose since we don't need the handle any more.
325 *foreign_socket_handle = base::FileDescriptor(static_cast<int>(socket_handle),
326 true);
327 #endif
328
329 // Get the shared memory for the buffer.
330 int shared_memory_handle;
331 result = audio_trusted->GetSharedMemory(resource, &shared_memory_handle,
332 shared_memory_length);
333 if (result != PP_OK)
334 return result;
335
336 #if defined(OS_WIN)
337 base::SharedMemory shared_memory(shared_memory_handle, false);
338 #else
339 base::SharedMemory shared_memory(
340 base::FileDescriptor(static_cast<int>(shared_memory_handle), false),
piman 2010/12/01 01:44:52 why the static_cast ? it's an int already.
341 false);
342 #endif
343
344 // Duplicate the shared memory to the plugin process. This will automatically
345 // close the source handle.
346 if (!shared_memory.GiveToProcess(dispatcher()->remote_process_handle(),
347 foreign_shared_memory_handle))
348 return PP_ERROR_FAILED;
349
350 return PP_OK;
351 }
352
353 } // namespace proxy
354 } // namespace pp
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698